diff --git a/.ci/php-cs-fixer/.php-cs-fixer.php b/.ci/php-cs-fixer/.php-cs-fixer.php index 7a869b1c2e..7475d96d5e 100644 --- a/.ci/php-cs-fixer/.php-cs-fixer.php +++ b/.ci/php-cs-fixer/.php-cs-fixer.php @@ -36,15 +36,28 @@ $finder = PhpCsFixer\Finder::create() $config = new PhpCsFixer\Config(); return $config->setRules([ - '@PHP83Migration' => true, - '@PhpCsFixer:risky' => true, - '@PSR12:risky' => true, - 'declare_strict_types' => true, - 'strict_param' => true, - 'comment_to_phpdoc' => false, // breaks phpstan lines in combination with PHPStorm. - 'array_syntax' => ['syntax' => 'short'], - 'native_function_invocation' => false, // annoying - 'php_unit_data_provider_name' => false, // bloody annoying long test names - 'static_lambda' => false, // breaks the Response macro for API's. + 'no_unused_imports' => true, + '@PhpCsFixer' => true, + '@PHP83Migration' => true, + '@PhpCsFixer:risky' => true, + '@PSR12:risky' => true, + 'declare_strict_types' => true, + 'strict_param' => true, + 'comment_to_phpdoc' => false, // breaks phpstan lines in combination with PHPStorm. + 'array_syntax' => ['syntax' => 'short'], + 'native_function_invocation' => false, // annoying + 'php_unit_data_provider_name' => false, // bloody annoying long test names + 'static_lambda' => false, // breaks the Response macro for API's. + 'phpdoc_summary' => false, // annoying. + 'single_space_around_construct' => [ + 'constructs_followed_by_a_single_space' => [ + 'protected', + ], + ], + 'statement_indentation' => true, + 'type_declaration_spaces' => false, + 'cast_spaces' => false, + 'binary_operator_spaces' => false, + 'void_return' => true, ]) ->setFinder($finder); diff --git a/.ci/php-cs-fixer/composer.lock b/.ci/php-cs-fixer/composer.lock index 33b1dd09bc..c27d109b34 100644 --- a/.ci/php-cs-fixer/composer.lock +++ b/.ci/php-cs-fixer/composer.lock @@ -226,16 +226,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.41.1", + "version": "v3.42.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "8b6ae8dcbaf23f09680643ab832a4a3a260265f6" + "reference": "632ef1be3447a9b890bef06147475facee535d0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/8b6ae8dcbaf23f09680643ab832a4a3a260265f6", - "reference": "8b6ae8dcbaf23f09680643ab832a4a3a260265f6", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/632ef1be3447a9b890bef06147475facee535d0f", + "reference": "632ef1be3447a9b890bef06147475facee535d0f", "shasum": "" }, "require": { @@ -266,7 +266,6 @@ "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.4", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.4", "phpunit/phpunit": "^9.6", - "symfony/phpunit-bridge": "^6.3.8 || ^7.0", "symfony/yaml": "^5.4 || ^6.0 || ^7.0" }, "suggest": { @@ -305,7 +304,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.41.1" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.42.0" }, "funding": [ { @@ -313,7 +312,7 @@ "type": "github" } ], - "time": "2023-12-10T19:59:27+00:00" + "time": "2023-12-24T14:38:51+00:00" }, { "name": "psr/container", @@ -470,16 +469,16 @@ }, { "name": "sebastian/diff", - "version": "5.0.3", + "version": "5.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b" + "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b", - "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/fbf413a49e54f6b9b17e12d900ac7f6101591b7f", + "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f", "shasum": "" }, "require": { @@ -492,7 +491,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -525,7 +524,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.0" }, "funding": [ { @@ -533,7 +532,7 @@ "type": "github" } ], - "time": "2023-05-01T07:48:21+00:00" + "time": "2023-12-22T10:55:06+00:00" }, { "name": "symfony/console", diff --git a/.ci/phpmd.sh b/.ci/phpmd.sh index f58b80e6d5..393305ed74 100755 --- a/.ci/phpmd.sh +++ b/.ci/phpmd.sh @@ -44,8 +44,7 @@ EXIT_CODE=$? cd $SCRIPT_DIR/.. -echo "Exit code is $EXIT_CODE, but we ignore this for the time being." +echo "Exit code is $EXIT_CODE." # for the time being, exit 0 -#exit $EXIT_CODE -exit 0 +exit $EXIT_CODE diff --git a/.ci/phpmd/composer.lock b/.ci/phpmd/composer.lock index 89f7e74b07..1fedc00e61 100644 --- a/.ci/phpmd/composer.lock +++ b/.ci/phpmd/composer.lock @@ -146,16 +146,16 @@ }, { "name": "pdepend/pdepend", - "version": "2.16.1", + "version": "2.16.2", "source": { "type": "git", "url": "https://github.com/pdepend/pdepend.git", - "reference": "66ceb05eaa8bf358574143c974b04463911bc700" + "reference": "f942b208dc2a0868454d01b29f0c75bbcfc6ed58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pdepend/pdepend/zipball/66ceb05eaa8bf358574143c974b04463911bc700", - "reference": "66ceb05eaa8bf358574143c974b04463911bc700", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/f942b208dc2a0868454d01b29f0c75bbcfc6ed58", + "reference": "f942b208dc2a0868454d01b29f0c75bbcfc6ed58", "shasum": "" }, "require": { @@ -197,7 +197,7 @@ ], "support": { "issues": "https://github.com/pdepend/pdepend/issues", - "source": "https://github.com/pdepend/pdepend/tree/2.16.1" + "source": "https://github.com/pdepend/pdepend/tree/2.16.2" }, "funding": [ { @@ -205,7 +205,7 @@ "type": "tidelift" } ], - "time": "2023-12-10T18:38:19+00:00" + "time": "2023-12-17T18:09:59+00:00" }, { "name": "phpmd/phpmd", diff --git a/.ci/phpmd/phpmd.xml b/.ci/phpmd/phpmd.xml index ed62aab519..e3b4cd3782 100644 --- a/.ci/phpmd/phpmd.xml +++ b/.ci/phpmd/phpmd.xml @@ -37,14 +37,14 @@ - - + + - - + + @@ -58,20 +58,21 @@ - - + + - - + + + - - + + diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index ff9e1ebe6b..862d9d4dc7 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -15,10 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - + uses: actions/checkout@v4 - name: Setup PHP with Xdebug uses: shivammathur/setup-php@v2 with: diff --git a/app/Api/V1/Controllers/Autocomplete/AccountController.php b/app/Api/V1/Controllers/Autocomplete/AccountController.php index c89d5bf775..95120e2b74 100644 --- a/app/Api/V1/Controllers/Autocomplete/AccountController.php +++ b/app/Api/V1/Controllers/Autocomplete/AccountController.php @@ -32,7 +32,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\User; use Illuminate\Http\JsonResponse; -use JsonException; /** * Class AccountController @@ -61,17 +60,13 @@ class AccountController extends Controller return $next($request); } ); - $this->balanceTypes = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE,]; + $this->balanceTypes = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]; } /** * Documentation for this endpoint: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getAccountsAC * - * @param AutocompleteRequest $request - * - * @return JsonResponse - * @throws JsonException * @throws FireflyException * @throws FireflyException */ @@ -84,7 +79,7 @@ class AccountController extends Controller $return = []; - $result = $this->repository->searchAccount((string)$query, $types, $this->parameters->get('limit')); + $result = $this->repository->searchAccount((string) $query, $types, $this->parameters->get('limit')); // TODO this code is duplicated in the V2 Autocomplete controller, which means this code is due to be deprecated. $defaultCurrency = app('amount')->getDefaultCurrency(); @@ -103,11 +98,11 @@ class AccountController extends Controller } $return[] = [ - 'id' => (string)$account->id, + 'id' => (string) $account->id, 'name' => $account->name, 'name_with_balance' => $nameWithBalance, 'type' => $account->accountType->type, - 'currency_id' => (string)$currency->id, + 'currency_id' => (string) $currency->id, 'currency_name' => $currency->name, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, @@ -120,8 +115,8 @@ class AccountController extends Controller $return, static function (array $left, array $right) { $order = [AccountType::ASSET, AccountType::REVENUE, AccountType::EXPENSE]; - $posA = (int)array_search($left['type'], $order, true); - $posB = (int)array_search($right['type'], $order, true); + $posA = (int) array_search($left['type'], $order, true); + $posB = (int) array_search($right['type'], $order, true); return $posA - $posB; } diff --git a/app/Api/V1/Controllers/Autocomplete/BillController.php b/app/Api/V1/Controllers/Autocomplete/BillController.php index 542fd4a25f..462761bb65 100644 --- a/app/Api/V1/Controllers/Autocomplete/BillController.php +++ b/app/Api/V1/Controllers/Autocomplete/BillController.php @@ -58,10 +58,6 @@ class BillController extends Controller /** * Documentation for this endpoint is at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getBillsAC - * - * @param AutocompleteRequest $request - * - * @return JsonResponse */ public function bills(AutocompleteRequest $request): JsonResponse { @@ -70,7 +66,7 @@ class BillController extends Controller $filtered = $result->map( static function (Bill $item) { return [ - 'id' => (string)$item->id, + 'id' => (string) $item->id, 'name' => $item->name, 'active' => $item->active, ]; diff --git a/app/Api/V1/Controllers/Autocomplete/BudgetController.php b/app/Api/V1/Controllers/Autocomplete/BudgetController.php index f3334df101..3aa3e96011 100644 --- a/app/Api/V1/Controllers/Autocomplete/BudgetController.php +++ b/app/Api/V1/Controllers/Autocomplete/BudgetController.php @@ -58,10 +58,6 @@ class BudgetController extends Controller /** * Documentation for this endpoint is at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getBudgetsAC - * - * @param AutocompleteRequest $request - * - * @return JsonResponse */ public function budgets(AutocompleteRequest $request): JsonResponse { @@ -70,7 +66,7 @@ class BudgetController extends Controller $filtered = $result->map( static function (Budget $item) { return [ - 'id' => (string)$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 a35b7e11a6..1baea11df7 100644 --- a/app/Api/V1/Controllers/Autocomplete/CategoryController.php +++ b/app/Api/V1/Controllers/Autocomplete/CategoryController.php @@ -58,10 +58,6 @@ class CategoryController extends Controller /** * Documentation for this endpoint is at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getCategoriesAC - * - * @param AutocompleteRequest $request - * - * @return JsonResponse */ public function categories(AutocompleteRequest $request): JsonResponse { @@ -70,7 +66,7 @@ class CategoryController extends Controller $filtered = $result->map( static function (Category $item) { return [ - 'id' => (string)$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 4478213d73..4b601cc02d 100644 --- a/app/Api/V1/Controllers/Autocomplete/CurrencyController.php +++ b/app/Api/V1/Controllers/Autocomplete/CurrencyController.php @@ -58,10 +58,6 @@ class CurrencyController extends Controller /** * Documentation for this endpoint is at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getCurrenciesAC - * - * @param AutocompleteRequest $request - * - * @return JsonResponse */ public function currencies(AutocompleteRequest $request): JsonResponse { @@ -72,7 +68,7 @@ class CurrencyController extends Controller /** @var TransactionCurrency $currency */ foreach ($collection as $currency) { $result[] = [ - 'id' => (string)$currency->id, + 'id' => (string) $currency->id, 'name' => $currency->name, 'code' => $currency->code, 'symbol' => $currency->symbol, @@ -87,9 +83,6 @@ class CurrencyController extends Controller * Documentation for this endpoint is at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getCurrenciesCodeAC * - * @param AutocompleteRequest $request - * - * @return JsonResponse * @deprecated */ public function currenciesWithCode(AutocompleteRequest $request): JsonResponse @@ -101,7 +94,7 @@ class CurrencyController extends Controller /** @var TransactionCurrency $currency */ foreach ($collection as $currency) { $result[] = [ - 'id' => (string)$currency->id, + 'id' => (string) $currency->id, 'name' => sprintf('%s (%s)', $currency->name, $currency->code), '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 67cd318e38..9d0d9595e7 100644 --- a/app/Api/V1/Controllers/Autocomplete/ObjectGroupController.php +++ b/app/Api/V1/Controllers/Autocomplete/ObjectGroupController.php @@ -58,10 +58,6 @@ class ObjectGroupController extends Controller /** * Documentation for this endpoint is at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getObjectGroupsAC - * - * @param AutocompleteRequest $request - * - * @return JsonResponse */ public function objectGroups(AutocompleteRequest $request): JsonResponse { @@ -72,7 +68,7 @@ class ObjectGroupController extends Controller /** @var ObjectGroup $objectGroup */ foreach ($result as $objectGroup) { $return[] = [ - 'id' => (string)$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 74ac1c0498..ecab2caa43 100644 --- a/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php +++ b/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php @@ -62,10 +62,6 @@ class PiggyBankController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getPiggiesAC - * - * @param AutocompleteRequest $request - * - * @return JsonResponse */ public function piggyBanks(AutocompleteRequest $request): JsonResponse { @@ -79,14 +75,14 @@ class PiggyBankController extends Controller $currency = $this->accountRepository->getAccountCurrency($piggy->account) ?? $defaultCurrency; $objectGroup = $piggy->objectGroups()->first(); $response[] = [ - 'id' => (string)$piggy->id, + 'id' => (string) $piggy->id, 'name' => $piggy->name, - 'currency_id' => (string)$currency->id, + 'currency_id' => (string) $currency->id, 'currency_name' => $currency->name, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => $currency->decimal_places, - 'object_group_id' => null === $objectGroup ? null : (string)$objectGroup->id, + 'object_group_id' => null === $objectGroup ? null : (string) $objectGroup->id, 'object_group_title' => $objectGroup?->title, ]; } @@ -97,10 +93,6 @@ class PiggyBankController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getPiggiesBalanceAC - * - * @param AutocompleteRequest $request - * - * @return JsonResponse */ public function piggyBanksWithBalance(AutocompleteRequest $request): JsonResponse { @@ -108,13 +100,14 @@ class PiggyBankController extends Controller $piggies = $this->piggyRepository->searchPiggyBank($data['query'], $this->parameters->get('limit')); $defaultCurrency = app('amount')->getDefaultCurrency(); $response = []; + /** @var PiggyBank $piggy */ foreach ($piggies as $piggy) { $currency = $this->accountRepository->getAccountCurrency($piggy->account) ?? $defaultCurrency; $currentAmount = $this->piggyRepository->getRepetition($piggy)->currentamount ?? '0'; $objectGroup = $piggy->objectGroups()->first(); $response[] = [ - 'id' => (string)$piggy->id, + 'id' => (string) $piggy->id, 'name' => $piggy->name, 'name_with_balance' => sprintf( '%s (%s / %s)', @@ -122,12 +115,12 @@ class PiggyBankController extends Controller app('amount')->formatAnything($currency, $currentAmount, false), app('amount')->formatAnything($currency, $piggy->targetamount, false), ), - 'currency_id' => (string)$currency->id, + 'currency_id' => (string) $currency->id, 'currency_name' => $currency->name, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => $currency->decimal_places, - 'object_group_id' => null === $objectGroup ? null : (string)$objectGroup->id, + 'object_group_id' => null === $objectGroup ? null : (string) $objectGroup->id, 'object_group_title' => $objectGroup?->title, ]; } diff --git a/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php b/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php index 74c1262ae4..f0e13bff01 100644 --- a/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php +++ b/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php @@ -56,10 +56,6 @@ class RecurrenceController extends Controller /** * This endpoint is documented at: * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getRecurringAC - * - * @param AutocompleteRequest $request - * - * @return JsonResponse */ public function recurring(AutocompleteRequest $request): JsonResponse { @@ -70,7 +66,7 @@ class RecurrenceController extends Controller /** @var Recurrence $recurrence */ foreach ($recurrences as $recurrence) { $response[] = [ - 'id' => (string)$recurrence->id, + 'id' => (string) $recurrence->id, 'name' => $recurrence->title, 'description' => $recurrence->description, ]; diff --git a/app/Api/V1/Controllers/Autocomplete/RuleController.php b/app/Api/V1/Controllers/Autocomplete/RuleController.php index 0f45c8fd44..e00a97bae8 100644 --- a/app/Api/V1/Controllers/Autocomplete/RuleController.php +++ b/app/Api/V1/Controllers/Autocomplete/RuleController.php @@ -55,10 +55,6 @@ class RuleController extends Controller /** * This endpoint is documented at: * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getRulesAC - * - * @param AutocompleteRequest $request - * - * @return JsonResponse */ public function rules(AutocompleteRequest $request): JsonResponse { @@ -69,7 +65,7 @@ class RuleController extends Controller /** @var Rule $rule */ foreach ($rules as $rule) { $response[] = [ - 'id' => (string)$rule->id, + 'id' => (string) $rule->id, 'name' => $rule->title, 'description' => $rule->description, ]; diff --git a/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php b/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php index c3c1af2503..ea84263593 100644 --- a/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php +++ b/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php @@ -55,10 +55,6 @@ class RuleGroupController extends Controller /** * This endpoint is documented at: * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getRuleGroupsAC - * - * @param AutocompleteRequest $request - * - * @return JsonResponse */ public function ruleGroups(AutocompleteRequest $request): JsonResponse { @@ -69,7 +65,7 @@ class RuleGroupController extends Controller /** @var RuleGroup $group */ foreach ($groups as $group) { $response[] = [ - 'id' => (string)$group->id, + 'id' => (string) $group->id, 'name' => $group->title, 'description' => $group->description, ]; diff --git a/app/Api/V1/Controllers/Autocomplete/TagController.php b/app/Api/V1/Controllers/Autocomplete/TagController.php index c0bf758e75..3d9945a975 100644 --- a/app/Api/V1/Controllers/Autocomplete/TagController.php +++ b/app/Api/V1/Controllers/Autocomplete/TagController.php @@ -58,10 +58,6 @@ class TagController extends Controller /** * This endpoint is documented at: * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getTagAC - * - * @param AutocompleteRequest $request - * - * @return JsonResponse */ public function tags(AutocompleteRequest $request): JsonResponse { @@ -69,10 +65,11 @@ class TagController extends Controller $result = $this->repository->searchTags($data['query'], $this->parameters->get('limit')); $array = []; + /** @var Tag $tag */ foreach ($result as $tag) { $array[] = [ - 'id' => (string)$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 a8033ed624..ec6c2a4dd6 100644 --- a/app/Api/V1/Controllers/Autocomplete/TransactionController.php +++ b/app/Api/V1/Controllers/Autocomplete/TransactionController.php @@ -63,10 +63,6 @@ class TransactionController extends Controller /** * This endpoint is documented at: * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getTransactionsAC - * - * @param AutocompleteRequest $request - * - * @return JsonResponse */ public function transactions(AutocompleteRequest $request): JsonResponse { @@ -80,8 +76,8 @@ class TransactionController extends Controller /** @var TransactionJournal $journal */ foreach ($filtered as $journal) { $array[] = [ - 'id' => (string)$journal->id, - 'transaction_group_id' => (string)$journal->transaction_group_id, + 'id' => (string) $journal->id, + 'transaction_group_id' => (string) $journal->transaction_group_id, 'name' => $journal->description, 'description' => $journal->description, ]; @@ -93,10 +89,6 @@ class TransactionController extends Controller /** * This endpoint is documented at: * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getTransactionsIDAC - * - * @param AutocompleteRequest $request - * - * @return JsonResponse */ public function transactionsWithID(AutocompleteRequest $request): JsonResponse { @@ -104,7 +96,7 @@ class TransactionController extends Controller $result = new Collection(); if (is_numeric($data['query'])) { // search for group, not journal. - $firstResult = $this->groupRepository->find((int)$data['query']); + $firstResult = $this->groupRepository->find((int) $data['query']); if (null !== $firstResult) { // group may contain multiple journals, each a result: foreach ($firstResult->transactionJournals as $journal) { @@ -122,8 +114,8 @@ class TransactionController extends Controller /** @var TransactionJournal $journal */ foreach ($result as $journal) { $array[] = [ - 'id' => (string)$journal->id, - 'transaction_group_id' => (string)$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 66f1f23a8f..852b91fa82 100644 --- a/app/Api/V1/Controllers/Autocomplete/TransactionTypeController.php +++ b/app/Api/V1/Controllers/Autocomplete/TransactionTypeController.php @@ -54,10 +54,6 @@ class TransactionTypeController extends Controller /** * This endpoint is documented at * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getTransactionTypesAC - * - * @param AutocompleteRequest $request - * - * @return JsonResponse */ public function transactionTypes(AutocompleteRequest $request): JsonResponse { @@ -69,7 +65,7 @@ class TransactionTypeController extends Controller foreach ($types as $type) { // different key for consistency. $array[] = [ - 'id' => (string)$type->id, + 'id' => (string) $type->id, 'name' => $type->type, 'type' => $type->type, ]; diff --git a/app/Api/V1/Controllers/Chart/AccountController.php b/app/Api/V1/Controllers/Chart/AccountController.php index 25fe2d8f31..9bc23a457e 100644 --- a/app/Api/V1/Controllers/Chart/AccountController.php +++ b/app/Api/V1/Controllers/Chart/AccountController.php @@ -35,9 +35,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Http\Api\ApiSupport; use FireflyIII\User; use Illuminate\Http\JsonResponse; -use JsonException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class AccountController @@ -50,8 +47,6 @@ class AccountController extends Controller /** * AccountController constructor. - * - */ public function __construct() { @@ -72,25 +67,22 @@ class AccountController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/charts/getChartAccountOverview * - * @param DateRequest $request - * - * @return JsonResponse * @throws FireflyException - * @throws JsonException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function overview(DateRequest $request): JsonResponse { // parameters for chart: $dates = $request->getAll(); + /** @var Carbon $start */ $start = $dates['start']; + /** @var Carbon $end */ $end = $dates['end']; // user's preferences $defaultSet = $this->repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray(); + /** @var Preference $frontPage */ $frontPage = app('preferences')->get('frontPageAccounts', $defaultSet); $default = app('amount')->getDefaultCurrency(); @@ -100,10 +92,10 @@ class AccountController extends Controller $frontPage->save(); } - // get accounts: $accounts = $this->repository->getAccountsById($frontPage->data); $chartData = []; + /** @var Account $account */ foreach ($accounts as $account) { $currency = $this->repository->getAccountCurrency($account); @@ -112,7 +104,7 @@ class AccountController extends Controller } $currentSet = [ 'label' => $account->name, - 'currency_id' => (string)$currency->id, + 'currency_id' => (string) $currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => $currency->decimal_places, diff --git a/app/Api/V1/Controllers/Controller.php b/app/Api/V1/Controllers/Controller.php index 7240fc7ef1..ad6b3e07c0 100644 --- a/app/Api/V1/Controllers/Controller.php +++ b/app/Api/V1/Controllers/Controller.php @@ -35,15 +35,14 @@ use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Routing\Controller as BaseController; use League\Fractal\Manager; use League\Fractal\Serializer\JsonApiSerializer; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; use Symfony\Component\HttpFoundation\Exception\BadRequestException; use Symfony\Component\HttpFoundation\ParameterBag; /** * Class Controller. * - + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.NumberOfChildren) */ abstract class Controller extends BaseController { @@ -52,6 +51,7 @@ abstract class Controller extends BaseController use ValidatesRequests; protected const string CONTENT_TYPE = 'application/vnd.api+json'; + /** @var array */ protected array $allowedSort; protected ParameterBag $parameters; @@ -76,12 +76,40 @@ abstract class Controller extends BaseController ); } + /** + * Method to help build URL's. + */ + final protected function buildParams(): string + { + $return = '?'; + $params = []; + foreach ($this->parameters as $key => $value) { + if ('page' === $key) { + continue; + } + if ($value instanceof Carbon) { + $params[$key] = $value->format('Y-m-d'); + + continue; + } + $params[$key] = $value; + } + + return $return.http_build_query($params); + } + + final protected function getManager(): Manager + { + // create some objects: + $manager = new Manager(); + $baseUrl = request()->getSchemeAndHttpHost().'/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + return $manager; + } + /** * Method to grab all parameters from the URL. - * - * @return ParameterBag - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ private function getParameters(): ParameterBag { @@ -99,6 +127,7 @@ abstract class Controller extends BaseController $dates = ['start', 'end', 'date']; foreach ($dates as $field) { $date = null; + try { $date = request()->query->get($field); } catch (BadRequestException $e) { @@ -111,7 +140,7 @@ abstract class Controller extends BaseController if (null !== $date) { try { $obj = Carbon::parse((string)$date); - } catch (InvalidDateException | InvalidFormatException $e) { + } catch (InvalidDateException|InvalidFormatException $e) { // don't care app('log')->warning( sprintf( @@ -139,12 +168,13 @@ abstract class Controller extends BaseController if (null !== $value) { $bag->set($integer, (int)$value); } - if (null === $value && - 'limit' === $integer && // @phpstan-ignore-line - auth()->check()) { + if (null === $value + && 'limit' === $integer // @phpstan-ignore-line + && auth()->check()) { // set default for user: /** @var User $user */ $user = auth()->user(); + /** @var Preference $pageSize */ $pageSize = (int)app('preferences')->getForUser($user, 'listPageSize', 50)->data; $bag->set($integer, $pageSize); @@ -155,14 +185,10 @@ abstract class Controller extends BaseController return $this->getSortParameters($bag); } - /** - * @param ParameterBag $bag - * - * @return ParameterBag - */ private function getSortParameters(ParameterBag $bag): ParameterBag { $sortParameters = []; + try { $param = (string)request()->query->get('sort'); } catch (BadRequestException $e) { @@ -190,40 +216,4 @@ abstract class Controller extends BaseController return $bag; } - - /** - * Method to help build URL's. - * - * @return string - */ - final protected function buildParams(): string - { - $return = '?'; - $params = []; - foreach ($this->parameters as $key => $value) { - if ('page' === $key) { - continue; - } - if ($value instanceof Carbon) { - $params[$key] = $value->format('Y-m-d'); - continue; - } - $params[$key] = $value; - } - - return $return . http_build_query($params); - } - - /** - * @return Manager - */ - final protected function getManager(): Manager - { - // create some objects: - $manager = new Manager(); - $baseUrl = request()->getSchemeAndHttpHost() . '/api/v1'; - $manager->setSerializer(new JsonApiSerializer($baseUrl)); - - return $manager; - } } diff --git a/app/Api/V1/Controllers/Data/Bulk/TransactionController.php b/app/Api/V1/Controllers/Data/Bulk/TransactionController.php index 38ac56d84e..f274b8b679 100644 --- a/app/Api/V1/Controllers/Data/Bulk/TransactionController.php +++ b/app/Api/V1/Controllers/Data/Bulk/TransactionController.php @@ -44,9 +44,6 @@ class TransactionController extends Controller { private AccountRepositoryInterface $repository; - /** - * - */ public function __construct() { parent::__construct(); @@ -63,10 +60,6 @@ class TransactionController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/bulkUpdateTransactions - * - * @param TransactionRequest $request - * - * @return JsonResponse */ public function update(TransactionRequest $request): JsonResponse { @@ -77,8 +70,8 @@ class TransactionController extends Controller // to respond to what is in the $query. // this is OK because only one thing can be in the query at the moment. if ($this->isUpdateTransactionAccount($params)) { - $original = $this->repository->find((int)$params['where']['account_id']); - $destination = $this->repository->find((int)$params['update']['account_id']); + $original = $this->repository->find((int) $params['where']['account_id']); + $destination = $this->repository->find((int) $params['update']['account_id']); /** @var AccountDestroyService $service */ $service = app(AccountDestroyService::class); @@ -89,9 +82,7 @@ class TransactionController extends Controller } /** - * @param array $params >> - * - * @return bool + * @param array> $params */ private function isUpdateTransactionAccount(array $params): bool { diff --git a/app/Api/V1/Controllers/Data/DestroyController.php b/app/Api/V1/Controllers/Data/DestroyController.php index f46a7f26ff..58ff4d3498 100644 --- a/app/Api/V1/Controllers/Data/DestroyController.php +++ b/app/Api/V1/Controllers/Data/DestroyController.php @@ -57,9 +57,6 @@ class DestroyController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/destroyData * - * @param DestroyRequest $request - * - * @return JsonResponse * @throws FireflyException */ public function destroy(DestroyRequest $request): JsonResponse @@ -67,10 +64,10 @@ class DestroyController extends Controller $objects = $request->getObjects(); $this->unused = $request->boolean('unused', false); - $allExceptAssets = [AccountType::BENEFICIARY, AccountType::CASH, AccountType::CREDITCARD, AccountType::DEFAULT, AccountType::EXPENSE, AccountType::IMPORT, AccountType::INITIAL_BALANCE, AccountType::LIABILITY_CREDIT, AccountType::RECONCILIATION, AccountType::REVENUE,]; - $all = [AccountType::ASSET, AccountType::BENEFICIARY, AccountType::CASH, AccountType::CREDITCARD, AccountType::DEBT, AccountType::DEFAULT, AccountType::EXPENSE, AccountType::IMPORT, AccountType::INITIAL_BALANCE, AccountType::LIABILITY_CREDIT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::RECONCILIATION,]; + $allExceptAssets = [AccountType::BENEFICIARY, AccountType::CASH, AccountType::CREDITCARD, AccountType::DEFAULT, AccountType::EXPENSE, AccountType::IMPORT, AccountType::INITIAL_BALANCE, AccountType::LIABILITY_CREDIT, AccountType::RECONCILIATION, AccountType::REVENUE]; + $all = [AccountType::ASSET, AccountType::BENEFICIARY, AccountType::CASH, AccountType::CREDITCARD, AccountType::DEBT, AccountType::DEFAULT, AccountType::EXPENSE, AccountType::IMPORT, AccountType::INITIAL_BALANCE, AccountType::LIABILITY_CREDIT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::RECONCILIATION]; $liabilities = [AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD]; - $transactions = [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::RECONCILIATION, TransactionType::OPENING_BALANCE,]; + $transactions = [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::RECONCILIATION, TransactionType::OPENING_BALANCE]; match ($objects) { 'budgets' => $this->destroyBudgets(), @@ -99,9 +96,6 @@ class DestroyController extends Controller return response()->json([], 204); } - /** - * - */ private function destroyBudgets(): void { /** @var AvailableBudgetRepositoryInterface $abRepository */ @@ -117,9 +111,6 @@ class DestroyController extends Controller $budgetRepository->destroyAll(); } - /** - * - */ private function destroyBills(): void { /** @var BillRepositoryInterface $repository */ @@ -127,9 +118,6 @@ class DestroyController extends Controller $repository->destroyAll(); } - /** - * - */ private function destroyPiggyBanks(): void { /** @var PiggyBankRepositoryInterface $repository */ @@ -137,9 +125,6 @@ class DestroyController extends Controller $repository->destroyAll(); } - /** - * - */ private function destroyRules(): void { /** @var RuleGroupRepositoryInterface $repository */ @@ -147,9 +132,6 @@ class DestroyController extends Controller $repository->destroyAll(); } - /** - * - */ private function destroyRecurringTransactions(): void { /** @var RecurringRepositoryInterface $repository */ @@ -157,9 +139,6 @@ class DestroyController extends Controller $repository->destroyAll(); } - /** - * - */ private function destroyCategories(): void { /** @var CategoryRepositoryInterface $categoryRepos */ @@ -167,9 +146,6 @@ class DestroyController extends Controller $categoryRepos->destroyAll(); } - /** - * - */ private function destroyTags(): void { /** @var TagRepositoryInterface $tagRepository */ @@ -177,9 +153,6 @@ class DestroyController extends Controller $tagRepository->destroyAll(); } - /** - * @return void - */ private function destroyObjectGroups(): void { /** @var ObjectGroupRepositoryInterface $repository */ @@ -188,7 +161,7 @@ class DestroyController extends Controller } /** - * @param array $types + * @param array $types */ private function destroyAccounts(array $types): void { @@ -203,6 +176,7 @@ class DestroyController extends Controller if (true === $this->unused && 0 === $count) { app('log')->info(sprintf('Deleted unused account #%d "%s"', $account->id, $account->name)); $service->destroy($account, null); + continue; } if (false === $this->unused) { @@ -213,7 +187,7 @@ class DestroyController extends Controller } /** - * @param array $types + * @param array $types */ private function destroyTransactions(array $types): void { @@ -221,6 +195,7 @@ class DestroyController extends Controller $repository = app(JournalRepositoryInterface::class); $journals = $repository->findByType($types); $service = app(JournalDestroyService::class); + /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $service->destroy($journal); diff --git a/app/Api/V1/Controllers/Data/Export/ExportController.php b/app/Api/V1/Controllers/Data/Export/ExportController.php index ffb7b4b9a4..9f75c3503b 100644 --- a/app/Api/V1/Controllers/Data/Export/ExportController.php +++ b/app/Api/V1/Controllers/Data/Export/ExportController.php @@ -29,8 +29,6 @@ use FireflyIII\Api\V1\Requests\Data\Export\ExportRequest; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Support\Export\ExportDataGenerator; use Illuminate\Http\Response as LaravelResponse; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class ExportController @@ -59,12 +57,7 @@ class ExportController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportAccounts * - * @param ExportRequest $request - * - * @return LaravelResponse - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -75,46 +68,12 @@ class ExportController extends Controller return $this->returnExport('accounts'); } - /** - * @param string $key - * - * @return LaravelResponse - * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ - 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', (string)strlen($data[$key])); - - return $response; - } - /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportBills * - * @param ExportRequest $request - * - * @return LaravelResponse - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function bills(ExportRequest $request): LaravelResponse @@ -128,12 +87,8 @@ class ExportController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportBudgets * - * @param ExportRequest $request - * - * @return LaravelResponse - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function budgets(ExportRequest $request): LaravelResponse @@ -147,12 +102,8 @@ class ExportController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportCategories * - * @param ExportRequest $request - * - * @return LaravelResponse - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function categories(ExportRequest $request): LaravelResponse @@ -166,12 +117,8 @@ class ExportController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportPiggies * - * @param ExportRequest $request - * - * @return LaravelResponse - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function piggyBanks(ExportRequest $request): LaravelResponse @@ -185,12 +132,8 @@ class ExportController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportRecurring * - * @param ExportRequest $request - * - * @return LaravelResponse - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function recurring(ExportRequest $request): LaravelResponse @@ -204,12 +147,8 @@ class ExportController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportRules * - * @param ExportRequest $request - * - * @return LaravelResponse - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function rules(ExportRequest $request): LaravelResponse @@ -223,12 +162,8 @@ class ExportController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportTags * - * @param ExportRequest $request - * - * @return LaravelResponse - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function tags(ExportRequest $request): LaravelResponse @@ -242,12 +177,7 @@ class ExportController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportTransactions * - * @param ExportRequest $request - * - * @return LaravelResponse - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface */ public function transactions(ExportRequest $request): LaravelResponse { @@ -259,4 +189,30 @@ class ExportController extends Controller return $this->returnExport('transactions'); } + + /** + * @throws FireflyException + */ + 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', (string) strlen($data[$key])) + ; + + return $response; + } } diff --git a/app/Api/V1/Controllers/Data/PurgeController.php b/app/Api/V1/Controllers/Data/PurgeController.php index d97773763a..f35ba7dfbc 100644 --- a/app/Api/V1/Controllers/Data/PurgeController.php +++ b/app/Api/V1/Controllers/Data/PurgeController.php @@ -47,8 +47,6 @@ class PurgeController extends Controller * TODO cleanup and use repositories. * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/purgeData - * - * @return JsonResponse */ public function purge(): JsonResponse { @@ -65,7 +63,9 @@ class PurgeController extends Controller // piggies $set = PiggyBank::leftJoin('accounts', 'accounts.id', 'piggy_banks.account_id') - ->where('accounts.user_id', $user->id)->onlyTrashed()->get(['piggy_banks.*']); + ->where('accounts.user_id', $user->id)->onlyTrashed()->get(['piggy_banks.*']) + ; + /** @var PiggyBank $piggy */ foreach ($set as $piggy) { $piggy->forceDelete(); @@ -86,7 +86,6 @@ class PurgeController extends Controller // tags Tag::whereUserId($user->id)->onlyTrashed()->forceDelete(); - // accounts Account::whereUserId($user->id)->onlyTrashed()->forceDelete(); diff --git a/app/Api/V1/Controllers/Insight/Expense/AccountController.php b/app/Api/V1/Controllers/Insight/Expense/AccountController.php index e79d6097af..24b501f08c 100644 --- a/app/Api/V1/Controllers/Insight/Expense/AccountController.php +++ b/app/Api/V1/Controllers/Insight/Expense/AccountController.php @@ -32,7 +32,6 @@ use FireflyIII\Support\Http\Api\ApiSupport; use Illuminate\Http\JsonResponse; /** - * * Class AccountController * * Shows expense information grouped or limited by date. @@ -47,8 +46,6 @@ class AccountController extends Controller /** * AccountController constructor. - * - */ public function __construct() { @@ -70,10 +67,6 @@ class AccountController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseAsset - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function asset(GenericRequest $request): JsonResponse { @@ -101,10 +94,6 @@ class AccountController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseExpense - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function expense(GenericRequest $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Insight/Expense/BillController.php b/app/Api/V1/Controllers/Insight/Expense/BillController.php index 07e914a7b0..5ccd79084f 100644 --- a/app/Api/V1/Controllers/Insight/Expense/BillController.php +++ b/app/Api/V1/Controllers/Insight/Expense/BillController.php @@ -60,10 +60,6 @@ class BillController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseBill * * Expenses per bill, possibly filtered by bill and account. - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function bill(GenericRequest $request): JsonResponse { @@ -123,10 +119,6 @@ class BillController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseNoBill * * Expenses for no bill filtered by account. - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function noBill(GenericRequest $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Insight/Expense/BudgetController.php b/app/Api/V1/Controllers/Insight/Expense/BudgetController.php index 27e600482b..475ea2f5fb 100644 --- a/app/Api/V1/Controllers/Insight/Expense/BudgetController.php +++ b/app/Api/V1/Controllers/Insight/Expense/BudgetController.php @@ -43,8 +43,6 @@ class BudgetController extends Controller /** * AccountController constructor. - * - */ public function __construct() { @@ -67,10 +65,6 @@ class BudgetController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseBudget - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function budget(GenericRequest $request): JsonResponse { @@ -82,9 +76,11 @@ class BudgetController extends Controller 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])); + /** @var array $expense */ foreach ($expenses as $expense) { $result[] = [ @@ -104,10 +100,6 @@ class BudgetController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseNoBudget - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function noBudget(GenericRequest $request): JsonResponse { @@ -116,6 +108,7 @@ class BudgetController extends Controller $assetAccounts = $request->getAssetAccounts(); $result = []; $expenses = $this->noRepository->sumExpenses($start, $end, $assetAccounts); + /** @var array $expense */ foreach ($expenses as $expense) { $result[] = [ diff --git a/app/Api/V1/Controllers/Insight/Expense/CategoryController.php b/app/Api/V1/Controllers/Insight/Expense/CategoryController.php index b7972097f7..5ee6e77378 100644 --- a/app/Api/V1/Controllers/Insight/Expense/CategoryController.php +++ b/app/Api/V1/Controllers/Insight/Expense/CategoryController.php @@ -44,8 +44,6 @@ class CategoryController extends Controller /** * AccountController constructor. - * - */ public function __construct() { @@ -68,10 +66,6 @@ class CategoryController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferCategory - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function category(GenericRequest $request): JsonResponse { @@ -83,9 +77,11 @@ class CategoryController extends Controller 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[] = [ @@ -105,10 +101,6 @@ class CategoryController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferNoCategory - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function noCategory(GenericRequest $request): JsonResponse { @@ -117,6 +109,7 @@ class CategoryController extends Controller $assetAccounts = $request->getAssetAccounts(); $result = []; $expenses = $this->noRepository->sumExpenses($start, $end, $assetAccounts); + /** @var array $expense */ foreach ($expenses as $expense) { $result[] = [ diff --git a/app/Api/V1/Controllers/Insight/Expense/PeriodController.php b/app/Api/V1/Controllers/Insight/Expense/PeriodController.php index fd10d7a7fe..b3fef62b52 100644 --- a/app/Api/V1/Controllers/Insight/Expense/PeriodController.php +++ b/app/Api/V1/Controllers/Insight/Expense/PeriodController.php @@ -37,10 +37,6 @@ class PeriodController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseTotal - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function total(GenericRequest $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Insight/Expense/TagController.php b/app/Api/V1/Controllers/Insight/Expense/TagController.php index 0de98065d9..6af63a7f8b 100644 --- a/app/Api/V1/Controllers/Insight/Expense/TagController.php +++ b/app/Api/V1/Controllers/Insight/Expense/TagController.php @@ -59,10 +59,6 @@ class TagController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseNoTag * * Expenses for no tag filtered by account. - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function noTag(GenericRequest $request): JsonResponse { @@ -112,10 +108,6 @@ class TagController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseTag * * Expenses per tag, possibly filtered by tag and account. - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function tag(GenericRequest $request): JsonResponse { @@ -135,6 +127,7 @@ class TagController extends Controller $collector->setTypes([TransactionType::WITHDRAWAL])->setRange($start, $end)->setSourceAccounts($accounts); $collector->setTags($tags); $genericSet = $collector->getExtractedJournals(); + /** @var array $journal */ foreach ($genericSet as $journal) { $currencyId = (int)$journal['currency_id']; diff --git a/app/Api/V1/Controllers/Insight/Income/AccountController.php b/app/Api/V1/Controllers/Insight/Income/AccountController.php index 91ab40a071..bc99892c4c 100644 --- a/app/Api/V1/Controllers/Insight/Income/AccountController.php +++ b/app/Api/V1/Controllers/Insight/Income/AccountController.php @@ -32,7 +32,6 @@ use FireflyIII\Support\Http\Api\ApiSupport; use Illuminate\Http\JsonResponse; /** - * * Class AccountController * * Shows income information grouped or limited by date. @@ -47,8 +46,6 @@ class AccountController extends Controller /** * AccountController constructor. - * - */ public function __construct() { @@ -70,10 +67,6 @@ class AccountController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeAsset - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function asset(GenericRequest $request): JsonResponse { @@ -82,6 +75,7 @@ class AccountController extends Controller $assetAccounts = $request->getAssetAccounts(); $income = $this->opsRepository->sumIncomeByDestination($start, $end, $assetAccounts); $result = []; + /** @var array $entry */ foreach ($income as $entry) { $result[] = [ @@ -100,10 +94,6 @@ class AccountController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeRevenue - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function revenue(GenericRequest $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Insight/Income/CategoryController.php b/app/Api/V1/Controllers/Insight/Income/CategoryController.php index f992540f1b..1f989dfe81 100644 --- a/app/Api/V1/Controllers/Insight/Income/CategoryController.php +++ b/app/Api/V1/Controllers/Insight/Income/CategoryController.php @@ -44,8 +44,6 @@ class CategoryController extends Controller /** * AccountController constructor. - * - */ public function __construct() { @@ -68,10 +66,6 @@ class CategoryController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeCategory - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function category(GenericRequest $request): JsonResponse { @@ -83,9 +77,11 @@ class CategoryController extends Controller 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[] = [ @@ -105,10 +101,6 @@ class CategoryController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeNoCategory - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function noCategory(GenericRequest $request): JsonResponse { @@ -117,6 +109,7 @@ class CategoryController extends Controller $assetAccounts = $request->getAssetAccounts(); $result = []; $expenses = $this->noRepository->sumIncome($start, $end, $assetAccounts); + /** @var array $expense */ foreach ($expenses as $expense) { $result[] = [ diff --git a/app/Api/V1/Controllers/Insight/Income/PeriodController.php b/app/Api/V1/Controllers/Insight/Income/PeriodController.php index aa2722c7d7..c99ce413d2 100644 --- a/app/Api/V1/Controllers/Insight/Income/PeriodController.php +++ b/app/Api/V1/Controllers/Insight/Income/PeriodController.php @@ -37,10 +37,6 @@ class PeriodController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeTotal - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function total(GenericRequest $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Insight/Income/TagController.php b/app/Api/V1/Controllers/Insight/Income/TagController.php index 520bf40814..cc182b63bc 100644 --- a/app/Api/V1/Controllers/Insight/Income/TagController.php +++ b/app/Api/V1/Controllers/Insight/Income/TagController.php @@ -60,10 +60,6 @@ class TagController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeTag * * Expenses for no tag filtered by account. - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function noTag(GenericRequest $request): JsonResponse { @@ -116,10 +112,6 @@ class TagController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeNoTag * * Expenses per tag, possibly filtered by tag and account. - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function tag(GenericRequest $request): JsonResponse { @@ -139,6 +131,7 @@ class TagController extends Controller $collector->setTypes([TransactionType::DEPOSIT])->setRange($start, $end)->setDestinationAccounts($accounts); $collector->setTags($tags); $genericSet = $collector->getExtractedJournals(); + /** @var array $journal */ foreach ($genericSet as $journal) { $currencyId = (int)$journal['currency_id']; diff --git a/app/Api/V1/Controllers/Insight/Transfer/AccountController.php b/app/Api/V1/Controllers/Insight/Transfer/AccountController.php index 09d1fc2b8b..95fff5b468 100644 --- a/app/Api/V1/Controllers/Insight/Transfer/AccountController.php +++ b/app/Api/V1/Controllers/Insight/Transfer/AccountController.php @@ -40,8 +40,6 @@ class AccountController extends Controller /** * AccountController constructor. - * - */ public function __construct() { @@ -60,10 +58,6 @@ class AccountController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransfers - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function asset(GenericRequest $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php b/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php index f88bb8dd75..12c4f02f34 100644 --- a/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php +++ b/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php @@ -43,8 +43,6 @@ class CategoryController extends Controller /** * AccountController constructor. - * - */ public function __construct() { @@ -67,10 +65,6 @@ class CategoryController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferCategory - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function category(GenericRequest $request): JsonResponse { @@ -82,9 +76,11 @@ class CategoryController extends Controller 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[] = [ @@ -104,10 +100,6 @@ class CategoryController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferNoCategory - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function noCategory(GenericRequest $request): JsonResponse { @@ -116,6 +108,7 @@ class CategoryController extends Controller $assetAccounts = $request->getAssetAccounts(); $result = []; $expenses = $this->noRepository->sumTransfers($start, $end, $assetAccounts); + /** @var array $expense */ foreach ($expenses as $expense) { $result[] = [ diff --git a/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php b/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php index 75eb95613b..7aac24202b 100644 --- a/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php +++ b/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php @@ -37,10 +37,6 @@ class PeriodController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferTotal - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function total(GenericRequest $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Insight/Transfer/TagController.php b/app/Api/V1/Controllers/Insight/Transfer/TagController.php index a95f28ccea..83b9f0600b 100644 --- a/app/Api/V1/Controllers/Insight/Transfer/TagController.php +++ b/app/Api/V1/Controllers/Insight/Transfer/TagController.php @@ -57,10 +57,6 @@ class TagController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferNoTag - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function noTag(GenericRequest $request): JsonResponse { @@ -113,10 +109,6 @@ class TagController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferTag * * Transfers per tag, possibly filtered by tag and account. - * - * @param GenericRequest $request - * - * @return JsonResponse */ public function tag(GenericRequest $request): JsonResponse { @@ -136,6 +128,7 @@ class TagController extends Controller $collector->setTypes([TransactionType::TRANSFER])->setRange($start, $end)->setDestinationAccounts($accounts); $collector->setTags($tags); $genericSet = $collector->getExtractedJournals(); + /** @var array $journal */ foreach ($genericSet as $journal) { $currencyId = (int)$journal['currency_id']; diff --git a/app/Api/V1/Controllers/Models/Account/DestroyController.php b/app/Api/V1/Controllers/Models/Account/DestroyController.php index bd46cad13a..cebf3afc26 100644 --- a/app/Api/V1/Controllers/Models/Account/DestroyController.php +++ b/app/Api/V1/Controllers/Models/Account/DestroyController.php @@ -39,8 +39,6 @@ class DestroyController extends Controller /** * AccountController constructor. - * - */ public function __construct() { @@ -60,10 +58,6 @@ class DestroyController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/accounts/deleteAccount * * Remove the specified resource from storage. - * - * @param Account $account - * - * @return JsonResponse */ public function destroy(Account $account): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Account/ListController.php b/app/Api/V1/Controllers/Models/Account/ListController.php index 2820397718..31abbeb341 100644 --- a/app/Api/V1/Controllers/Models/Account/ListController.php +++ b/app/Api/V1/Controllers/Models/Account/ListController.php @@ -53,8 +53,6 @@ class ListController extends Controller /** * AccountController constructor. - * - */ public function __construct() { @@ -73,9 +71,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/accounts/listAttachmentByAccount * - * @param Account $account - * - * @return JsonResponse * @throws FireflyException */ public function attachments(Account $account): JsonResponse @@ -89,7 +84,7 @@ class ListController extends Controller // make paginator: $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.accounts.attachments', [$account->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.accounts.attachments', [$account->id]).$this->buildParams()); /** @var AttachmentTransformer $transformer */ $transformer = app(AttachmentTransformer::class); @@ -105,9 +100,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/accounts/listPiggyBankByAccount * - * @param Account $account - * - * @return JsonResponse * @throws FireflyException */ public function piggyBanks(Account $account): JsonResponse @@ -125,7 +117,7 @@ class ListController extends Controller // make paginator: $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.accounts.piggy-banks', [$account->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.accounts.piggy-banks', [$account->id]).$this->buildParams()); /** @var PiggyBankTransformer $transformer */ $transformer = app(PiggyBankTransformer::class); @@ -143,11 +135,6 @@ class ListController extends Controller * * Show all transaction groups related to the account. * - * - * @param Request $request - * @param Account $account - * - * @return JsonResponse * @throws FireflyException */ public function transactions(Request $request, Account $account): JsonResponse @@ -157,6 +144,7 @@ class ListController extends Controller $this->parameters->set('type', $type); $types = $this->mapTransactionTypes($this->parameters->get('type')); $manager = $this->getManager(); + /** @var User $admin */ $admin = auth()->user(); @@ -164,7 +152,8 @@ class ListController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setUser($admin)->setAccounts(new Collection([$account])) - ->withAPIInformation()->setLimit($pageSize)->setPage($this->parameters->get('page'))->setTypes($types); + ->withAPIInformation()->setLimit($pageSize)->setPage($this->parameters->get('page'))->setTypes($types) + ; if (null !== $this->parameters->get('start')) { $collector->setStart($this->parameters->get('start')); @@ -174,7 +163,7 @@ class ListController extends Controller } $paginator = $collector->getPaginatedGroups(); - $paginator->setPath(route('api.v1.accounts.transactions', [$account->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.accounts.transactions', [$account->id]).$this->buildParams()); $groups = $paginator->getCollection(); /** @var TransactionGroupTransformer $transformer */ diff --git a/app/Api/V1/Controllers/Models/Account/ShowController.php b/app/Api/V1/Controllers/Models/Account/ShowController.php index 32101e0aff..b371a65f63 100644 --- a/app/Api/V1/Controllers/Models/Account/ShowController.php +++ b/app/Api/V1/Controllers/Models/Account/ShowController.php @@ -49,8 +49,6 @@ class ShowController extends Controller /** * AccountController constructor. - * - */ public function __construct() { @@ -71,9 +69,6 @@ class ShowController extends Controller * * Display a listing of the resource. * - * @param Request $request - * - * @return JsonResponse * @throws FireflyException */ public function index(Request $request): JsonResponse @@ -93,12 +88,11 @@ class ShowController extends Controller // continue sort: - $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()); + $paginator->setPath(route('api.v1.accounts.index').$this->buildParams()); /** @var AccountTransformer $transformer */ $transformer = app(AccountTransformer::class); @@ -115,10 +109,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/accounts/getAccount * * Show single instance. - * - * @param Account $account - * - * @return JsonResponse */ public function show(Account $account): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Account/StoreController.php b/app/Api/V1/Controllers/Models/Account/StoreController.php index 4e80df8f56..bdd27aa5a2 100644 --- a/app/Api/V1/Controllers/Models/Account/StoreController.php +++ b/app/Api/V1/Controllers/Models/Account/StoreController.php @@ -41,8 +41,6 @@ class StoreController extends Controller /** * AccountController constructor. - * - */ public function __construct() { @@ -62,10 +60,6 @@ class StoreController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/accounts/storeAccount * * Store a new instance. - * - * @param StoreRequest $request - * - * @return JsonResponse */ public function store(StoreRequest $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Account/UpdateController.php b/app/Api/V1/Controllers/Models/Account/UpdateController.php index c519421b84..c51dcb1c55 100644 --- a/app/Api/V1/Controllers/Models/Account/UpdateController.php +++ b/app/Api/V1/Controllers/Models/Account/UpdateController.php @@ -42,8 +42,6 @@ class UpdateController extends Controller /** * AccountController constructor. - * - */ public function __construct() { @@ -63,17 +61,12 @@ class UpdateController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/accounts/updateAccount * * Update account. - * - * @param UpdateRequest $request - * @param Account $account - * - * @return JsonResponse */ public function update(UpdateRequest $request, Account $account): JsonResponse { app('log')->debug(sprintf('Now in %s', __METHOD__)); $data = $request->getUpdateData(); - $data['type'] = config('firefly.shortNamesByFullName.' . $account->accountType->type); + $data['type'] = config('firefly.shortNamesByFullName.'.$account->accountType->type); $account = $this->repository->update($account, $data); $manager = $this->getManager(); $account->refresh(); diff --git a/app/Api/V1/Controllers/Models/Attachment/DestroyController.php b/app/Api/V1/Controllers/Models/Attachment/DestroyController.php index ba81be4ecf..d4c95c948b 100644 --- a/app/Api/V1/Controllers/Models/Attachment/DestroyController.php +++ b/app/Api/V1/Controllers/Models/Attachment/DestroyController.php @@ -39,8 +39,6 @@ class DestroyController extends Controller /** * DestroyController constructor. - * - */ public function __construct() { @@ -63,11 +61,6 @@ class DestroyController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/attachments/deleteAttachment * * Remove the specified resource from storage. - * - * - * @param Attachment $attachment - * - * @return JsonResponse */ public function destroy(Attachment $attachment): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Attachment/ShowController.php b/app/Api/V1/Controllers/Models/Attachment/ShowController.php index 12e53d298c..301cbde6b3 100644 --- a/app/Api/V1/Controllers/Models/Attachment/ShowController.php +++ b/app/Api/V1/Controllers/Models/Attachment/ShowController.php @@ -46,8 +46,6 @@ class ShowController extends Controller /** * ShowController constructor. - * - */ public function __construct() { @@ -71,10 +69,7 @@ class ShowController extends Controller * * Download an attachment. * - * @param Attachment $attachment - * - * @return LaravelResponse - * @throws FireflyException + * @throws FireflyException */ public function download(Attachment $attachment): LaravelResponse { @@ -96,16 +91,18 @@ class ShowController extends Controller $response ->header('Content-Description', 'File Transfer') ->header('Content-Type', 'application/octet-stream') - ->header('Content-Disposition', 'attachment; filename=' . $quoted) + ->header('Content-Disposition', 'attachment; filename='.$quoted) ->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', (string)strlen($content)); + ->header('Content-Length', (string)strlen($content)) + ; return $response; } + throw new FireflyException('200003: File does not exist.'); } @@ -115,7 +112,6 @@ class ShowController extends Controller * * Display a listing of the resource. * - * @return JsonResponse * @throws FireflyException */ public function index(): JsonResponse @@ -132,7 +128,7 @@ class ShowController extends Controller // make paginator: $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.attachments.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.attachments.index').$this->buildParams()); /** @var AttachmentTransformer $transformer */ $transformer = app(AttachmentTransformer::class); @@ -149,14 +145,11 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/attachments/getAttachment * * Display the specified resource. - * - * @param Attachment $attachment - * - * @return JsonResponse */ public function show(Attachment $attachment): JsonResponse { $manager = $this->getManager(); + /** @var AttachmentTransformer $transformer */ $transformer = app(AttachmentTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/Attachment/StoreController.php b/app/Api/V1/Controllers/Models/Attachment/StoreController.php index c089b020e8..f16ebda590 100644 --- a/app/Api/V1/Controllers/Models/Attachment/StoreController.php +++ b/app/Api/V1/Controllers/Models/Attachment/StoreController.php @@ -45,8 +45,6 @@ class StoreController extends Controller /** * StoreController constructor. - * - */ public function __construct() { @@ -70,9 +68,6 @@ class StoreController extends Controller * * Store a newly created resource in storage. * - * @param StoreRequest $request - * - * @return JsonResponse * @throws FireflyException */ public function store(StoreRequest $request): JsonResponse @@ -93,12 +88,6 @@ class StoreController extends Controller /** * Upload an attachment. - * - * - * @param Request $request - * @param Attachment $attachment - * - * @return JsonResponse */ public function upload(Request $request, Attachment $attachment): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Attachment/UpdateController.php b/app/Api/V1/Controllers/Models/Attachment/UpdateController.php index e50ce2cc80..5e2bd07702 100644 --- a/app/Api/V1/Controllers/Models/Attachment/UpdateController.php +++ b/app/Api/V1/Controllers/Models/Attachment/UpdateController.php @@ -42,8 +42,6 @@ class UpdateController extends Controller /** * UpdateController constructor. - * - */ public function __construct() { @@ -66,11 +64,6 @@ class UpdateController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/attachments/updateAttachment * * Update the specified resource in storage. - * - * @param UpdateRequest $request - * @param Attachment $attachment - * - * @return JsonResponse */ public function update(UpdateRequest $request, Attachment $attachment): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php b/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php index 268fa4a317..881343225f 100644 --- a/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php +++ b/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php @@ -44,8 +44,6 @@ class ShowController extends Controller /** * AvailableBudgetController constructor. - * - */ public function __construct() { @@ -68,7 +66,6 @@ class ShowController extends Controller * * Display a listing of the resource. * - * @return JsonResponse * @throws FireflyException */ public function index(): JsonResponse @@ -88,7 +85,7 @@ class ShowController extends Controller // make paginator: $paginator = new LengthAwarePaginator($availableBudgets, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.available-budgets.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.available-budgets.index').$this->buildParams()); /** @var AvailableBudgetTransformer $transformer */ $transformer = app(AvailableBudgetTransformer::class); @@ -105,10 +102,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/available_budgets/getAvailableBudget * * Display the specified resource. - * - * @param AvailableBudget $availableBudget - * - * @return JsonResponse */ public function show(AvailableBudget $availableBudget): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Bill/DestroyController.php b/app/Api/V1/Controllers/Models/Bill/DestroyController.php index befa62ddc1..d589a1c218 100644 --- a/app/Api/V1/Controllers/Models/Bill/DestroyController.php +++ b/app/Api/V1/Controllers/Models/Bill/DestroyController.php @@ -37,8 +37,6 @@ class DestroyController extends Controller /** * BillController constructor. - * - */ public function __construct() { @@ -58,10 +56,6 @@ class DestroyController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/bills/deleteBill * * Remove the specified resource from storage. - * - * @param Bill $bill - * - * @return JsonResponse */ public function destroy(Bill $bill): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Bill/ListController.php b/app/Api/V1/Controllers/Models/Bill/ListController.php index df2535bb49..23bf285aa1 100644 --- a/app/Api/V1/Controllers/Models/Bill/ListController.php +++ b/app/Api/V1/Controllers/Models/Bill/ListController.php @@ -50,8 +50,6 @@ class ListController extends Controller /** * BillController constructor. - * - */ public function __construct() { @@ -72,9 +70,6 @@ class ListController extends Controller * * Display a listing of the resource. * - * @param Bill $bill - * - * @return JsonResponse * @throws FireflyException */ public function attachments(Bill $bill): JsonResponse @@ -88,7 +83,7 @@ class ListController extends Controller // make paginator: $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.bills.attachments', [$bill->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.bills.attachments', [$bill->id]).$this->buildParams()); /** @var AttachmentTransformer $transformer */ $transformer = app(AttachmentTransformer::class); @@ -106,9 +101,6 @@ class ListController extends Controller * * List all of them. * - * @param Bill $bill - * - * @return JsonResponse * @throws FireflyException */ public function rules(Bill $bill): JsonResponse @@ -125,7 +117,7 @@ class ListController extends Controller // make paginator: $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.bills.rules', [$bill->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.bills.rules', [$bill->id]).$this->buildParams()); /** @var RuleTransformer $transformer */ $transformer = app(RuleTransformer::class); @@ -142,11 +134,6 @@ class ListController extends Controller * * Show all transactions. * - * @param Request $request - * - * @param Bill $bill - * - * @return JsonResponse * @throws FireflyException */ public function transactions(Request $request, Bill $bill): JsonResponse @@ -175,7 +162,8 @@ class ListController extends Controller // set page to retrieve ->setPage($this->parameters->get('page')) // set types of transactions to return. - ->setTypes($types); + ->setTypes($types) + ; if (null !== $this->parameters->get('start')) { $collector->setStart($this->parameters->get('start')); @@ -186,7 +174,7 @@ class ListController extends Controller // get paginator. $paginator = $collector->getPaginatedGroups(); - $paginator->setPath(route('api.v1.bills.transactions', [$bill->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.bills.transactions', [$bill->id]).$this->buildParams()); $transactions = $paginator->getCollection(); /** @var TransactionGroupTransformer $transformer */ diff --git a/app/Api/V1/Controllers/Models/Bill/ShowController.php b/app/Api/V1/Controllers/Models/Bill/ShowController.php index 7848ce9cc9..8107008b99 100644 --- a/app/Api/V1/Controllers/Models/Bill/ShowController.php +++ b/app/Api/V1/Controllers/Models/Bill/ShowController.php @@ -43,8 +43,6 @@ class ShowController extends Controller /** * BillController constructor. - * - */ public function __construct() { @@ -65,7 +63,6 @@ class ShowController extends Controller * * Display a listing of the resource. * - * @return JsonResponse * @throws FireflyException */ public function index(): JsonResponse @@ -93,14 +90,11 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/bills/getBill * * Show the specified bill. - * - * @param Bill $bill - * - * @return JsonResponse */ public function show(Bill $bill): JsonResponse { $manager = $this->getManager(); + /** @var BillTransformer $transformer */ $transformer = app(BillTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/Bill/StoreController.php b/app/Api/V1/Controllers/Models/Bill/StoreController.php index 2a819f8912..2989ad0b74 100644 --- a/app/Api/V1/Controllers/Models/Bill/StoreController.php +++ b/app/Api/V1/Controllers/Models/Bill/StoreController.php @@ -43,8 +43,6 @@ class StoreController extends Controller /** * BillController constructor. - * - */ public function __construct() { @@ -65,9 +63,6 @@ class StoreController extends Controller * * Store a bill. * - * @param StoreRequest $request - * - * @return JsonResponse * @throws FireflyException */ public function store(StoreRequest $request): JsonResponse diff --git a/app/Api/V1/Controllers/Models/Bill/UpdateController.php b/app/Api/V1/Controllers/Models/Bill/UpdateController.php index e17e2945f9..f3f3e2e86b 100644 --- a/app/Api/V1/Controllers/Models/Bill/UpdateController.php +++ b/app/Api/V1/Controllers/Models/Bill/UpdateController.php @@ -40,8 +40,6 @@ class UpdateController extends Controller /** * BillController constructor. - * - */ public function __construct() { @@ -61,11 +59,6 @@ class UpdateController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/bills/updateBill * * Update a bill. - * - * @param UpdateRequest $request - * @param Bill $bill - * - * @return JsonResponse */ public function update(UpdateRequest $request, Bill $bill): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Budget/DestroyController.php b/app/Api/V1/Controllers/Models/Budget/DestroyController.php index 8f6847aee9..d7a473e7de 100644 --- a/app/Api/V1/Controllers/Models/Budget/DestroyController.php +++ b/app/Api/V1/Controllers/Models/Budget/DestroyController.php @@ -37,8 +37,6 @@ class DestroyController extends Controller /** * DestroyController constructor. - * - */ public function __construct() { @@ -58,10 +56,6 @@ class DestroyController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/deleteBudget * * Remove the specified resource from storage. - * - * @param Budget $budget - * - * @return JsonResponse */ public function destroy(Budget $budget): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Budget/ListController.php b/app/Api/V1/Controllers/Models/Budget/ListController.php index 88bf49569c..0b0213ce68 100644 --- a/app/Api/V1/Controllers/Models/Budget/ListController.php +++ b/app/Api/V1/Controllers/Models/Budget/ListController.php @@ -40,9 +40,7 @@ use Illuminate\Pagination\LengthAwarePaginator; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; -/*** - * Class ListController - */ +// Class ListController class ListController extends Controller { use TransactionFilter; @@ -52,8 +50,6 @@ class ListController extends Controller /** * ListController constructor. - * - */ public function __construct() { @@ -74,9 +70,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/listAttachmentByBudget * - * @param Budget $budget - * - * @return JsonResponse * @throws FireflyException */ public function attachments(Budget $budget): JsonResponse @@ -90,7 +83,7 @@ class ListController extends Controller // make paginator: $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.budgets.attachments', [$budget->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.budgets.attachments', [$budget->id]).$this->buildParams()); /** @var AttachmentTransformer $transformer */ $transformer = app(AttachmentTransformer::class); @@ -108,9 +101,6 @@ class ListController extends Controller * * Display a listing of the resource. * - * @param Budget $budget - * - * @return JsonResponse * @throws FireflyException */ public function budgetLimits(Budget $budget): JsonResponse @@ -122,7 +112,7 @@ class ListController extends Controller $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()); + $paginator->setPath(route('api.v1.budgets.budget-limits', [$budget->id]).$this->buildParams()); /** @var BudgetLimitTransformer $transformer */ $transformer = app(BudgetLimitTransformer::class); @@ -139,11 +129,6 @@ class ListController extends Controller * * Show all transactions. * - * @param Request $request - * - * @param Budget $budget - * - * @return JsonResponse * @throws FireflyException */ public function transactions(Request $request, Budget $budget): JsonResponse @@ -173,7 +158,8 @@ class ListController extends Controller // set page to retrieve ->setPage($this->parameters->get('page')) // set types of transactions to return. - ->setTypes($types); + ->setTypes($types) + ; if (null !== $this->parameters->get('start')) { $collector->setStart($this->parameters->get('start')); @@ -183,7 +169,7 @@ class ListController extends Controller } $paginator = $collector->getPaginatedGroups(); - $paginator->setPath(route('api.v1.budgets.transactions', [$budget->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.budgets.transactions', [$budget->id]).$this->buildParams()); $transactions = $paginator->getCollection(); /** @var TransactionGroupTransformer $transformer */ @@ -201,9 +187,6 @@ class ListController extends Controller * * Show all transactions. * - * @param Request $request - * - * @return JsonResponse * @throws FireflyException */ public function withoutBudget(Request $request): JsonResponse @@ -233,7 +216,8 @@ class ListController extends Controller // set page to retrieve ->setPage($this->parameters->get('page')) // set types of transactions to return. - ->setTypes($types); + ->setTypes($types) + ; if (null !== $this->parameters->get('start')) { $collector->setStart($this->parameters->get('start')); @@ -243,7 +227,7 @@ class ListController extends Controller } $paginator = $collector->getPaginatedGroups(); - $paginator->setPath(route('api.v1.budgets.without-budget') . $this->buildParams()); + $paginator->setPath(route('api.v1.budgets.without-budget').$this->buildParams()); $transactions = $paginator->getCollection(); /** @var TransactionGroupTransformer $transformer */ diff --git a/app/Api/V1/Controllers/Models/Budget/ShowController.php b/app/Api/V1/Controllers/Models/Budget/ShowController.php index 8f36f0fbfb..c80d3000b3 100644 --- a/app/Api/V1/Controllers/Models/Budget/ShowController.php +++ b/app/Api/V1/Controllers/Models/Budget/ShowController.php @@ -45,8 +45,6 @@ class ShowController extends Controller /** * ListController constructor. - * - */ public function __construct() { @@ -69,7 +67,6 @@ class ShowController extends Controller * * Display a listing of the resource. * - * @return JsonResponse * @throws FireflyException */ public function index(): JsonResponse @@ -86,7 +83,7 @@ class ShowController extends Controller // make paginator: $paginator = new LengthAwarePaginator($budgets, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.budgets.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.budgets.index').$this->buildParams()); /** @var BudgetTransformer $transformer */ $transformer = app(BudgetTransformer::class); @@ -100,10 +97,6 @@ class ShowController extends Controller /** * Show a budget. - * - * @param Budget $budget - * - * @return JsonResponse */ public function show(Budget $budget): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Budget/StoreController.php b/app/Api/V1/Controllers/Models/Budget/StoreController.php index a70f823a9b..f262712cb0 100644 --- a/app/Api/V1/Controllers/Models/Budget/StoreController.php +++ b/app/Api/V1/Controllers/Models/Budget/StoreController.php @@ -40,8 +40,6 @@ class StoreController extends Controller /** * StoreController constructor. - * - */ public function __construct() { @@ -62,11 +60,7 @@ class StoreController extends Controller * * Store a budget. * - * @param StoreRequest $request - * - * @return JsonResponse * @throws FireflyException - * */ public function store(StoreRequest $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Budget/UpdateController.php b/app/Api/V1/Controllers/Models/Budget/UpdateController.php index 5836be33f1..2057a484a5 100644 --- a/app/Api/V1/Controllers/Models/Budget/UpdateController.php +++ b/app/Api/V1/Controllers/Models/Budget/UpdateController.php @@ -40,8 +40,6 @@ class UpdateController extends Controller /** * UpdateController constructor. - * - */ public function __construct() { @@ -61,11 +59,6 @@ class UpdateController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/updateBudget * * Update a budget. - * - * @param UpdateRequest $request - * @param Budget $budget - * - * @return JsonResponse */ public function update(UpdateRequest $request, Budget $budget): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/DestroyController.php b/app/Api/V1/Controllers/Models/BudgetLimit/DestroyController.php index c459b1cee3..968ad1434d 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/DestroyController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/DestroyController.php @@ -40,8 +40,6 @@ class DestroyController extends Controller /** * BudgetLimitController constructor. - * - */ public function __construct() { @@ -64,10 +62,6 @@ class DestroyController extends Controller * * Remove the specified resource from storage. * - * @param Budget $budget - * @param BudgetLimit $budgetLimit - * - * @return JsonResponse * @throws FireflyException */ public function destroy(Budget $budget, BudgetLimit $budgetLimit): JsonResponse diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php b/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php index ab3f4f2a4f..5d8017ac78 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php @@ -48,11 +48,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/listTransactionByBudgetLimit * Show all transactions. * - * @param Request $request - * @param Budget $budget - * @param BudgetLimit $budgetLimit - * - * @return JsonResponse * @throws FireflyException */ public function transactions(Request $request, Budget $budget, BudgetLimit $budgetLimit): JsonResponse @@ -81,12 +76,13 @@ class ListController extends Controller // set page to retrieve ->setPage($this->parameters->get('page')) // set types of transactions to return. - ->setTypes($types); + ->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()); + $paginator->setPath(route('api.v1.budgets.limits.transactions', [$budget->id, $budgetLimit->id]).$this->buildParams()); $transactions = $paginator->getCollection(); /** @var TransactionGroupTransformer $transformer */ diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php b/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php index 972a8315d4..19181562bd 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php @@ -48,8 +48,6 @@ class ShowController extends Controller /** * BudgetLimitController constructor. - * - */ public function __construct() { @@ -73,10 +71,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/listBudgetLimitByBudget * * Display a listing of the budget limits for this budget. - * - * @param Budget $budget - * - * @return JsonResponse */ public function index(Budget $budget): JsonResponse { @@ -87,7 +81,7 @@ class ShowController extends Controller $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()); + $paginator->setPath(route('api.v1.budgets.limits.index', [$budget->id]).$this->buildParams()); /** @var BudgetLimitTransformer $transformer */ $transformer = app(BudgetLimitTransformer::class); @@ -105,9 +99,6 @@ class ShowController extends Controller * * Display a listing of the budget limits for this budget. * - * @param SameDateRequest $request - * - * @return JsonResponse * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function indexAll(SameDateRequest $request): JsonResponse @@ -119,7 +110,7 @@ class ShowController extends Controller $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()); + $paginator->setPath(route('api.v1.budget-limits.index').$this->buildParams()); /** @var BudgetLimitTransformer $transformer */ $transformer = app(BudgetLimitTransformer::class); @@ -135,10 +126,6 @@ class ShowController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/getBudgetLimit * - * @param Budget $budget - * @param BudgetLimit $budgetLimit - * - * @return JsonResponse * @throws FireflyException */ public function show(Budget $budget, BudgetLimit $budgetLimit): JsonResponse diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php b/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php index 3cb8d1858b..2388e46c9a 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php @@ -41,8 +41,6 @@ class StoreController extends Controller /** * BudgetLimitController constructor. - * - */ public function __construct() { @@ -64,11 +62,6 @@ class StoreController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/storeBudgetLimit * * Store a newly created resource in storage. - * - * @param StoreRequest $request - * @param Budget $budget - * - * @return JsonResponse */ public function store(StoreRequest $request, Budget $budget): JsonResponse { @@ -79,6 +72,7 @@ class StoreController extends Controller $budgetLimit = $this->blRepository->store($data); $manager = $this->getManager(); + /** @var BudgetLimitTransformer $transformer */ $transformer = app(BudgetLimitTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php b/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php index 100d6da9ce..dedd8d5777 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php @@ -46,8 +46,6 @@ class UpdateController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/updateBudgetLimit * * BudgetLimitController constructor. - * - */ public function __construct() { @@ -69,11 +67,6 @@ class UpdateController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/updateBudgetLimit * - * @param UpdateRequest $request - * @param Budget $budget - * @param BudgetLimit $budgetLimit - * - * @return JsonResponse * @throws FireflyException */ public function update(UpdateRequest $request, Budget $budget, BudgetLimit $budgetLimit): JsonResponse diff --git a/app/Api/V1/Controllers/Models/Category/DestroyController.php b/app/Api/V1/Controllers/Models/Category/DestroyController.php index 3e977e948d..10d6b8ebe6 100644 --- a/app/Api/V1/Controllers/Models/Category/DestroyController.php +++ b/app/Api/V1/Controllers/Models/Category/DestroyController.php @@ -37,8 +37,6 @@ class DestroyController extends Controller /** * CategoryController constructor. - * - */ public function __construct() { @@ -58,10 +56,6 @@ class DestroyController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/categories/deleteCategory * * Remove the specified resource from storage. - * - * @param Category $category - * - * @return JsonResponse */ public function destroy(Category $category): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Category/ListController.php b/app/Api/V1/Controllers/Models/Category/ListController.php index ffc37839e1..3add877e7a 100644 --- a/app/Api/V1/Controllers/Models/Category/ListController.php +++ b/app/Api/V1/Controllers/Models/Category/ListController.php @@ -49,8 +49,6 @@ class ListController extends Controller /** * CategoryController constructor. - * - */ public function __construct() { @@ -69,9 +67,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/categories/listAttachmentByCategory * - * @param Category $category - * - * @return JsonResponse * @throws FireflyException */ public function attachments(Category $category): JsonResponse @@ -85,7 +80,7 @@ class ListController extends Controller // make paginator: $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.categories.attachments', [$category->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.categories.attachments', [$category->id]).$this->buildParams()); /** @var AttachmentTransformer $transformer */ $transformer = app(AttachmentTransformer::class); @@ -103,11 +98,6 @@ class ListController extends Controller * * Show all transactions. * - * @param Request $request - * - * @param Category $category - * - * @return JsonResponse * @throws FireflyException */ public function transactions(Request $request, Category $category): JsonResponse @@ -136,7 +126,8 @@ class ListController extends Controller // set page to retrieve ->setPage($this->parameters->get('page')) // set types of transactions to return. - ->setTypes($types); + ->setTypes($types) + ; if (null !== $this->parameters->get('start')) { $collector->setStart($this->parameters->get('start')); @@ -146,7 +137,7 @@ class ListController extends Controller } $paginator = $collector->getPaginatedGroups(); - $paginator->setPath(route('api.v1.categories.transactions', [$category->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.categories.transactions', [$category->id]).$this->buildParams()); $transactions = $paginator->getCollection(); /** @var TransactionGroupTransformer $transformer */ diff --git a/app/Api/V1/Controllers/Models/Category/ShowController.php b/app/Api/V1/Controllers/Models/Category/ShowController.php index 5a772647a1..2598f924f2 100644 --- a/app/Api/V1/Controllers/Models/Category/ShowController.php +++ b/app/Api/V1/Controllers/Models/Category/ShowController.php @@ -43,8 +43,6 @@ class ShowController extends Controller /** * CategoryController constructor. - * - */ public function __construct() { @@ -65,7 +63,6 @@ class ShowController extends Controller * * Display a listing of the resource. * - * @return JsonResponse * @throws FireflyException */ public function index(): JsonResponse @@ -82,7 +79,7 @@ class ShowController extends Controller // make paginator: $paginator = new LengthAwarePaginator($categories, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.categories.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.categories.index').$this->buildParams()); /** @var CategoryTransformer $transformer */ $transformer = app(CategoryTransformer::class); @@ -98,10 +95,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/categories/getCategory * * Show the category. - * - * @param Category $category - * - * @return JsonResponse */ public function show(Category $category): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Category/StoreController.php b/app/Api/V1/Controllers/Models/Category/StoreController.php index 9ae15f6382..bc678fffe2 100644 --- a/app/Api/V1/Controllers/Models/Category/StoreController.php +++ b/app/Api/V1/Controllers/Models/Category/StoreController.php @@ -40,8 +40,6 @@ class StoreController extends Controller /** * CategoryController constructor. - * - */ public function __construct() { @@ -62,9 +60,6 @@ class StoreController extends Controller * * Store new category. * - * @param StoreRequest $request - * - * @return JsonResponse * @throws FireflyException */ public function store(StoreRequest $request): JsonResponse diff --git a/app/Api/V1/Controllers/Models/Category/UpdateController.php b/app/Api/V1/Controllers/Models/Category/UpdateController.php index c5bf2f04f4..049fbc0c08 100644 --- a/app/Api/V1/Controllers/Models/Category/UpdateController.php +++ b/app/Api/V1/Controllers/Models/Category/UpdateController.php @@ -40,8 +40,6 @@ class UpdateController extends Controller /** * CategoryController constructor. - * - */ public function __construct() { @@ -61,11 +59,6 @@ class UpdateController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/categories/updateCategory * * Update the category. - * - * @param UpdateRequest $request - * @param Category $category - * - * @return JsonResponse */ public function update(UpdateRequest $request, Category $category): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/ObjectGroup/DestroyController.php b/app/Api/V1/Controllers/Models/ObjectGroup/DestroyController.php index d9da7ec3e9..f253d9a956 100644 --- a/app/Api/V1/Controllers/Models/ObjectGroup/DestroyController.php +++ b/app/Api/V1/Controllers/Models/ObjectGroup/DestroyController.php @@ -38,8 +38,6 @@ class DestroyController extends Controller /** * ObjectGroupController constructor. - * - */ public function __construct() { @@ -61,10 +59,6 @@ class DestroyController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/object_groups/deleteObjectGroup * * Remove the specified resource from storage. - * - * @param ObjectGroup $objectGroup - * - * @return JsonResponse */ public function destroy(ObjectGroup $objectGroup): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php b/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php index 33dc0d4e5b..b7105f80e5 100644 --- a/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php +++ b/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php @@ -44,8 +44,6 @@ class ListController extends Controller /** * ObjectGroupController constructor. - * - */ public function __construct() { @@ -68,9 +66,6 @@ class ListController extends Controller * * List all bills in this object group * - * @param ObjectGroup $objectGroup - * - * @return JsonResponse * @throws FireflyException */ public function bills(ObjectGroup $objectGroup): JsonResponse @@ -85,7 +80,7 @@ class ListController extends Controller // make paginator: $paginator = new LengthAwarePaginator($bills, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.currencies.bills', [$objectGroup->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.currencies.bills', [$objectGroup->id]).$this->buildParams()); /** @var BillTransformer $transformer */ $transformer = app(BillTransformer::class); @@ -103,9 +98,6 @@ class ListController extends Controller * * List all piggies under the object group. * - * @param ObjectGroup $objectGroup - * - * @return JsonResponse * @throws FireflyException */ public function piggyBanks(ObjectGroup $objectGroup): JsonResponse @@ -123,7 +115,7 @@ class ListController extends Controller // 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()); + $paginator->setPath(route('api.v1.object-groups.piggy-banks', [$objectGroup->id]).$this->buildParams()); /** @var PiggyBankTransformer $transformer */ $transformer = app(PiggyBankTransformer::class); diff --git a/app/Api/V1/Controllers/Models/ObjectGroup/ShowController.php b/app/Api/V1/Controllers/Models/ObjectGroup/ShowController.php index 427403b5de..68dfa3e3d9 100644 --- a/app/Api/V1/Controllers/Models/ObjectGroup/ShowController.php +++ b/app/Api/V1/Controllers/Models/ObjectGroup/ShowController.php @@ -43,8 +43,6 @@ class ShowController extends Controller /** * ObjectGroupController constructor. - * - */ public function __construct() { @@ -66,8 +64,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/object_groups/listObjectGroups * * Display a listing of the resource. - * - * @return JsonResponse */ public function index(): JsonResponse { @@ -83,7 +79,7 @@ class ShowController extends Controller // make paginator: $paginator = new LengthAwarePaginator($objectGroups, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.object-groups.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.object-groups.index').$this->buildParams()); /** @var ObjectGroupTransformer $transformer */ $transformer = app(ObjectGroupTransformer::class); @@ -100,10 +96,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/object_groups/getObjectGroup * * Show single instance. - * - * @param ObjectGroup $objectGroup - * - * @return JsonResponse */ public function show(ObjectGroup $objectGroup): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/ObjectGroup/UpdateController.php b/app/Api/V1/Controllers/Models/ObjectGroup/UpdateController.php index 21ebcbf6fc..1480954ef3 100644 --- a/app/Api/V1/Controllers/Models/ObjectGroup/UpdateController.php +++ b/app/Api/V1/Controllers/Models/ObjectGroup/UpdateController.php @@ -41,8 +41,6 @@ class UpdateController extends Controller /** * ObjectGroupController constructor. - * - */ public function __construct() { @@ -62,11 +60,6 @@ class UpdateController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/object_groups/updateObjectGroup - * - * @param UpdateRequest $request - * @param ObjectGroup $objectGroup - * - * @return JsonResponse */ public function update(UpdateRequest $request, ObjectGroup $objectGroup): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/PiggyBank/DestroyController.php b/app/Api/V1/Controllers/Models/PiggyBank/DestroyController.php index 6c1c4fd021..69dfa0485b 100644 --- a/app/Api/V1/Controllers/Models/PiggyBank/DestroyController.php +++ b/app/Api/V1/Controllers/Models/PiggyBank/DestroyController.php @@ -37,8 +37,6 @@ class DestroyController extends Controller /** * Constructor. - * - */ public function __construct() { @@ -58,10 +56,6 @@ class DestroyController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/piggy_banks/deletePiggyBank * * Delete the resource. - * - * @param PiggyBank $piggyBank - * - * @return JsonResponse */ public function destroy(PiggyBank $piggyBank): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/PiggyBank/ListController.php b/app/Api/V1/Controllers/Models/PiggyBank/ListController.php index 96a62a4940..a8af7b8d63 100644 --- a/app/Api/V1/Controllers/Models/PiggyBank/ListController.php +++ b/app/Api/V1/Controllers/Models/PiggyBank/ListController.php @@ -43,8 +43,6 @@ class ListController extends Controller /** * Constructor. - * - */ public function __construct() { @@ -63,9 +61,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/piggy_banks/listAttachmentByPiggyBank * - * @param PiggyBank $piggyBank - * - * @return JsonResponse * @throws FireflyException */ public function attachments(PiggyBank $piggyBank): JsonResponse @@ -79,7 +74,7 @@ class ListController extends Controller // make paginator: $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.piggy-banks.attachments', [$piggyBank->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.piggy-banks.attachments', [$piggyBank->id]).$this->buildParams()); /** @var AttachmentTransformer $transformer */ $transformer = app(AttachmentTransformer::class); @@ -97,9 +92,6 @@ class ListController extends Controller * * List single resource. * - * @param PiggyBank $piggyBank - * - * @return JsonResponse * @throws FireflyException */ public function piggyBankEvents(PiggyBank $piggyBank): JsonResponse @@ -114,7 +106,7 @@ class ListController extends Controller // make paginator: $paginator = new LengthAwarePaginator($events, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.piggy-banks.events', [$piggyBank->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.piggy-banks.events', [$piggyBank->id]).$this->buildParams()); /** @var PiggyBankEventTransformer $transformer */ $transformer = app(PiggyBankEventTransformer::class); diff --git a/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php b/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php index 22bb4f3624..9b3524a2b3 100644 --- a/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php +++ b/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php @@ -43,8 +43,6 @@ class ShowController extends Controller /** * Constructor. - * - */ public function __construct() { @@ -65,7 +63,6 @@ class ShowController extends Controller * * List all of them. * - * @return JsonResponse * @throws FireflyException */ public function index(): JsonResponse @@ -81,7 +78,7 @@ class ShowController extends Controller // make paginator: $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.piggy-banks.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.piggy-banks.index').$this->buildParams()); /** @var PiggyBankTransformer $transformer */ $transformer = app(PiggyBankTransformer::class); @@ -98,10 +95,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/piggy_banks/getPiggyBank * * List single resource. - * - * @param PiggyBank $piggyBank - * - * @return JsonResponse */ public function show(PiggyBank $piggyBank): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/PiggyBank/StoreController.php b/app/Api/V1/Controllers/Models/PiggyBank/StoreController.php index a29e0b03d8..a13b4f03e0 100644 --- a/app/Api/V1/Controllers/Models/PiggyBank/StoreController.php +++ b/app/Api/V1/Controllers/Models/PiggyBank/StoreController.php @@ -40,8 +40,6 @@ class StoreController extends Controller /** * Constructor. - * - */ public function __construct() { @@ -62,9 +60,6 @@ class StoreController extends Controller * * Store new object. * - * @param StoreRequest $request - * - * @return JsonResponse * @throws FireflyException */ public function store(StoreRequest $request): JsonResponse diff --git a/app/Api/V1/Controllers/Models/PiggyBank/UpdateController.php b/app/Api/V1/Controllers/Models/PiggyBank/UpdateController.php index 9e7e841ee9..b85a9c21da 100644 --- a/app/Api/V1/Controllers/Models/PiggyBank/UpdateController.php +++ b/app/Api/V1/Controllers/Models/PiggyBank/UpdateController.php @@ -40,8 +40,6 @@ class UpdateController extends Controller /** * Constructor. - * - */ public function __construct() { @@ -61,11 +59,6 @@ class UpdateController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/piggy_banks/updatePiggyBank * * Update piggy bank. - * - * @param UpdateRequest $request - * @param PiggyBank $piggyBank - * - * @return JsonResponse */ public function update(UpdateRequest $request, PiggyBank $piggyBank): JsonResponse { @@ -77,6 +70,7 @@ class UpdateController extends Controller } $manager = $this->getManager(); + /** @var PiggyBankTransformer $transformer */ $transformer = app(PiggyBankTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/Recurrence/DestroyController.php b/app/Api/V1/Controllers/Models/Recurrence/DestroyController.php index 41018c5403..9985c6e826 100644 --- a/app/Api/V1/Controllers/Models/Recurrence/DestroyController.php +++ b/app/Api/V1/Controllers/Models/Recurrence/DestroyController.php @@ -37,8 +37,6 @@ class DestroyController extends Controller /** * RecurrenceController constructor. - * - */ public function __construct() { @@ -58,10 +56,6 @@ class DestroyController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/recurrences/deleteRecurrence * * Delete the resource. - * - * @param Recurrence $recurrence - * - * @return JsonResponse */ public function destroy(Recurrence $recurrence): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Recurrence/ListController.php b/app/Api/V1/Controllers/Models/Recurrence/ListController.php index 9d81586362..29426c6e8a 100644 --- a/app/Api/V1/Controllers/Models/Recurrence/ListController.php +++ b/app/Api/V1/Controllers/Models/Recurrence/ListController.php @@ -47,8 +47,6 @@ class ListController extends Controller /** * RecurrenceController constructor. - * - */ public function __construct() { @@ -69,10 +67,6 @@ class ListController extends Controller * * Show transactions for this recurrence. * - * @param Request $request - * @param Recurrence $recurrence - * - * @return JsonResponse * @throws FireflyException */ public function transactions(Request $request, Recurrence $recurrence): JsonResponse @@ -103,7 +97,8 @@ class ListController extends Controller // set page to retrieve ->setPage($this->parameters->get('page')) // set types of transactions to return. - ->setTypes($types); + ->setTypes($types) + ; if (null !== $this->parameters->get('start')) { $collector->setStart($this->parameters->get('start')); @@ -113,7 +108,7 @@ class ListController extends Controller } $paginator = $collector->getPaginatedGroups(); - $paginator->setPath(route('api.v1.transactions.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.transactions.index').$this->buildParams()); $transactions = $paginator->getCollection(); /** @var TransactionGroupTransformer $transformer */ diff --git a/app/Api/V1/Controllers/Models/Recurrence/ShowController.php b/app/Api/V1/Controllers/Models/Recurrence/ShowController.php index 54d1f86c30..4ba6f320e0 100644 --- a/app/Api/V1/Controllers/Models/Recurrence/ShowController.php +++ b/app/Api/V1/Controllers/Models/Recurrence/ShowController.php @@ -43,8 +43,6 @@ class ShowController extends Controller /** * RecurrenceController constructor. - * - */ public function __construct() { @@ -65,7 +63,6 @@ class ShowController extends Controller * * List all of them. * - * @return JsonResponse * @throws FireflyException */ public function index(): JsonResponse @@ -82,7 +79,7 @@ class ShowController extends Controller // make paginator: $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.recurrences.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.recurrences.index').$this->buildParams()); /** @var RecurrenceTransformer $transformer */ $transformer = app(RecurrenceTransformer::class); @@ -99,10 +96,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/recurrences/getRecurrence * * List single resource. - * - * @param Recurrence $recurrence - * - * @return JsonResponse */ public function show(Recurrence $recurrence): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Recurrence/StoreController.php b/app/Api/V1/Controllers/Models/Recurrence/StoreController.php index e213ce03ea..5d56fea11b 100644 --- a/app/Api/V1/Controllers/Models/Recurrence/StoreController.php +++ b/app/Api/V1/Controllers/Models/Recurrence/StoreController.php @@ -40,8 +40,6 @@ class StoreController extends Controller /** * RecurrenceController constructor. - * - */ public function __construct() { @@ -62,9 +60,6 @@ class StoreController extends Controller * * Store new object. * - * @param StoreRequest $request - * - * @return JsonResponse * @throws FireflyException */ public function store(StoreRequest $request): JsonResponse diff --git a/app/Api/V1/Controllers/Models/Recurrence/UpdateController.php b/app/Api/V1/Controllers/Models/Recurrence/UpdateController.php index 04a1a92a19..cd16cfa517 100644 --- a/app/Api/V1/Controllers/Models/Recurrence/UpdateController.php +++ b/app/Api/V1/Controllers/Models/Recurrence/UpdateController.php @@ -40,8 +40,6 @@ class UpdateController extends Controller /** * RecurrenceController constructor. - * - */ public function __construct() { @@ -61,11 +59,6 @@ class UpdateController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/recurrences/updateRecurrence * * Update single recurrence. - * - * @param UpdateRequest $request - * @param Recurrence $recurrence - * - * @return JsonResponse */ public function update(UpdateRequest $request, Recurrence $recurrence): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Rule/DestroyController.php b/app/Api/V1/Controllers/Models/Rule/DestroyController.php index 20e035f882..771990980b 100644 --- a/app/Api/V1/Controllers/Models/Rule/DestroyController.php +++ b/app/Api/V1/Controllers/Models/Rule/DestroyController.php @@ -38,8 +38,6 @@ class DestroyController extends Controller /** * RuleController constructor. - * - */ public function __construct() { @@ -62,10 +60,6 @@ class DestroyController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rules/deleteRule * * Delete the resource. - * - * @param Rule $rule - * - * @return JsonResponse */ public function destroy(Rule $rule): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Rule/ShowController.php b/app/Api/V1/Controllers/Models/Rule/ShowController.php index b4b93c7767..d1b3a1921c 100644 --- a/app/Api/V1/Controllers/Models/Rule/ShowController.php +++ b/app/Api/V1/Controllers/Models/Rule/ShowController.php @@ -44,8 +44,6 @@ class ShowController extends Controller /** * RuleController constructor. - * - */ public function __construct() { @@ -69,7 +67,6 @@ class ShowController extends Controller * * List all of them. * - * @return JsonResponse * @throws FireflyException */ public function index(): JsonResponse @@ -86,7 +83,7 @@ class ShowController extends Controller // make paginator: $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.rules.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.rules.index').$this->buildParams()); /** @var RuleTransformer $transformer */ $transformer = app(RuleTransformer::class); @@ -103,14 +100,11 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rules/getRule * * List single resource. - * - * @param Rule $rule - * - * @return JsonResponse */ public function show(Rule $rule): JsonResponse { $manager = $this->getManager(); + /** @var RuleTransformer $transformer */ $transformer = app(RuleTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/Rule/StoreController.php b/app/Api/V1/Controllers/Models/Rule/StoreController.php index 1d5e7ac89c..e4fd127f76 100644 --- a/app/Api/V1/Controllers/Models/Rule/StoreController.php +++ b/app/Api/V1/Controllers/Models/Rule/StoreController.php @@ -40,8 +40,6 @@ class StoreController extends Controller /** * RuleController constructor. - * - */ public function __construct() { @@ -64,15 +62,12 @@ class StoreController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rules/storeRule * * 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); diff --git a/app/Api/V1/Controllers/Models/Rule/TriggerController.php b/app/Api/V1/Controllers/Models/Rule/TriggerController.php index 1f409a592d..9caed6296e 100644 --- a/app/Api/V1/Controllers/Models/Rule/TriggerController.php +++ b/app/Api/V1/Controllers/Models/Rule/TriggerController.php @@ -46,8 +46,6 @@ class TriggerController extends Controller /** * RuleController constructor. - * - */ public function __construct() { @@ -68,11 +66,6 @@ class TriggerController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rules/testRule - * - * @param TestRequest $request - * @param Rule $rule - * - * @return JsonResponse */ public function testRule(TestRequest $request, Rule $rule): JsonResponse { @@ -96,16 +89,16 @@ class TriggerController extends Controller $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()); + $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); @@ -121,11 +114,6 @@ class TriggerController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rules/fireRule * * 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 { @@ -150,7 +138,6 @@ class TriggerController extends Controller $ruleEngine->addOperator(['type' => 'account_id', 'value' => implode(',', $parameters['accounts'])]); } - // fire the rule(s) $ruleEngine->fire(); diff --git a/app/Api/V1/Controllers/Models/Rule/UpdateController.php b/app/Api/V1/Controllers/Models/Rule/UpdateController.php index f4b1f1a8a5..57d9ece59d 100644 --- a/app/Api/V1/Controllers/Models/Rule/UpdateController.php +++ b/app/Api/V1/Controllers/Models/Rule/UpdateController.php @@ -41,8 +41,6 @@ class UpdateController extends Controller /** * RuleController constructor. - * - */ public function __construct() { @@ -65,11 +63,6 @@ class UpdateController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rules/updateRule * * Update a rule. - * - * @param UpdateRequest $request - * @param Rule $rule - * - * @return JsonResponse */ public function update(UpdateRequest $request, Rule $rule): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/RuleGroup/DestroyController.php b/app/Api/V1/Controllers/Models/RuleGroup/DestroyController.php index 62f390aef4..1e5facfdd1 100644 --- a/app/Api/V1/Controllers/Models/RuleGroup/DestroyController.php +++ b/app/Api/V1/Controllers/Models/RuleGroup/DestroyController.php @@ -38,8 +38,6 @@ class DestroyController extends Controller /** * RuleGroupController constructor. - * - */ public function __construct() { @@ -62,10 +60,6 @@ class DestroyController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rule_groups/deleteRuleGroup * * Delete the resource. - * - * @param RuleGroup $ruleGroup - * - * @return JsonResponse */ public function destroy(RuleGroup $ruleGroup): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/RuleGroup/ListController.php b/app/Api/V1/Controllers/Models/RuleGroup/ListController.php index 5c197da60d..9827b12bd8 100644 --- a/app/Api/V1/Controllers/Models/RuleGroup/ListController.php +++ b/app/Api/V1/Controllers/Models/RuleGroup/ListController.php @@ -43,8 +43,6 @@ class ListController extends Controller /** * RuleGroupController constructor. - * - */ public function __construct() { @@ -66,9 +64,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rule_groups/listRuleByGroup * - * @param RuleGroup $group - * - * @return JsonResponse * @throws FireflyException */ public function rules(RuleGroup $group): JsonResponse @@ -84,7 +79,7 @@ class ListController extends Controller // make paginator: $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.rule-groups.rules', [$group->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.rule-groups.rules', [$group->id]).$this->buildParams()); /** @var RuleTransformer $transformer */ $transformer = app(RuleTransformer::class); diff --git a/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php b/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php index cfe41894c4..67739a1f8e 100644 --- a/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php +++ b/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php @@ -44,8 +44,6 @@ class ShowController extends Controller /** * RuleGroupController constructor. - * - */ public function __construct() { @@ -68,7 +66,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rule_groups/listRuleGroup * List all of them. * - * @return JsonResponse * @throws FireflyException */ public function index(): JsonResponse @@ -84,7 +81,7 @@ class ShowController extends Controller // make paginator: $paginator = new LengthAwarePaginator($ruleGroups, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.rule-groups.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.rule-groups.index').$this->buildParams()); /** @var RuleGroupTransformer $transformer */ $transformer = app(RuleGroupTransformer::class); @@ -101,14 +98,11 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rule_groups/getRuleGroup * * List single resource. - * - * @param RuleGroup $ruleGroup - * - * @return JsonResponse */ public function show(RuleGroup $ruleGroup): JsonResponse { $manager = $this->getManager(); + /** @var RuleGroupTransformer $transformer */ $transformer = app(RuleGroupTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/RuleGroup/StoreController.php b/app/Api/V1/Controllers/Models/RuleGroup/StoreController.php index 02e1433a6b..26ce71af29 100644 --- a/app/Api/V1/Controllers/Models/RuleGroup/StoreController.php +++ b/app/Api/V1/Controllers/Models/RuleGroup/StoreController.php @@ -42,8 +42,6 @@ class StoreController extends Controller /** * RuleGroupController constructor. - * - */ public function __construct() { @@ -69,10 +67,6 @@ class StoreController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rule_groups/storeRuleGroup * * Store new object. - * - * @param StoreRequest $request - * - * @return JsonResponse */ public function store(StoreRequest $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/RuleGroup/TriggerController.php b/app/Api/V1/Controllers/Models/RuleGroup/TriggerController.php index cda13829b7..1cc5133f6d 100644 --- a/app/Api/V1/Controllers/Models/RuleGroup/TriggerController.php +++ b/app/Api/V1/Controllers/Models/RuleGroup/TriggerController.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\RuleGroup; -use Exception; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Models\RuleGroup\TestRequest; use FireflyIII\Api\V1\Requests\Models\RuleGroup\TriggerRequest; @@ -47,8 +46,6 @@ class TriggerController extends Controller /** * RuleGroupController constructor. - * - */ public function __construct() { @@ -70,12 +67,7 @@ class TriggerController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rule_groups/testRuleGroup * - * @param TestRequest $request - * @param RuleGroup $group - * - * @return JsonResponse * @throws FireflyException - * */ public function testGroup(TestRequest $request, RuleGroup $group): JsonResponse { @@ -108,10 +100,11 @@ class TriggerController extends Controller $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()); + $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); @@ -128,11 +121,7 @@ class TriggerController extends Controller * * Execute the given rule group on a set of existing transactions. * - * @param TriggerRequest $request - * @param RuleGroup $group - * - * @return JsonResponse - * @throws Exception + * @throws \Exception */ public function triggerGroup(TriggerRequest $request, RuleGroup $group): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/RuleGroup/UpdateController.php b/app/Api/V1/Controllers/Models/RuleGroup/UpdateController.php index 1d2b19b6ac..af67753d21 100644 --- a/app/Api/V1/Controllers/Models/RuleGroup/UpdateController.php +++ b/app/Api/V1/Controllers/Models/RuleGroup/UpdateController.php @@ -41,8 +41,6 @@ class UpdateController extends Controller /** * RuleGroupController constructor. - * - */ public function __construct() { @@ -65,11 +63,6 @@ class UpdateController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rule_groups/updateRuleGroup * * Update a rule group. - * - * @param UpdateRequest $request - * @param RuleGroup $ruleGroup - * - * @return JsonResponse */ public function update(UpdateRequest $request, RuleGroup $ruleGroup): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Tag/DestroyController.php b/app/Api/V1/Controllers/Models/Tag/DestroyController.php index a0b7f2c3b9..619c187a67 100644 --- a/app/Api/V1/Controllers/Models/Tag/DestroyController.php +++ b/app/Api/V1/Controllers/Models/Tag/DestroyController.php @@ -38,8 +38,6 @@ class DestroyController extends Controller /** * TagController constructor. - * - */ public function __construct() { @@ -62,10 +60,6 @@ class DestroyController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/tags/deleteTag * * Delete the resource. - * - * @param Tag $tag - * - * @return JsonResponse */ public function destroy(Tag $tag): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Tag/ListController.php b/app/Api/V1/Controllers/Models/Tag/ListController.php index 864c01385c..f33414eae2 100644 --- a/app/Api/V1/Controllers/Models/Tag/ListController.php +++ b/app/Api/V1/Controllers/Models/Tag/ListController.php @@ -49,8 +49,6 @@ class ListController extends Controller /** * TagController constructor. - * - */ public function __construct() { @@ -72,9 +70,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/tags/listAttachmentByTag * - * @param Tag $tag - * - * @return JsonResponse * @throws FireflyException */ public function attachments(Tag $tag): JsonResponse @@ -88,7 +83,7 @@ class ListController extends Controller // make paginator: $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.tags.attachments', [$tag->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.tags.attachments', [$tag->id]).$this->buildParams()); /** @var AttachmentTransformer $transformer */ $transformer = app(AttachmentTransformer::class); @@ -106,10 +101,6 @@ class ListController extends Controller * * Show all transactions. * - * @param Request $request - * @param Tag $tag - * - * @return JsonResponse * @throws FireflyException */ public function transactions(Request $request, Tag $tag): JsonResponse @@ -120,6 +111,7 @@ class ListController extends Controller $types = $this->mapTransactionTypes($this->parameters->get('type')); $manager = $this->getManager(); + /** @var User $admin */ $admin = auth()->user(); @@ -137,7 +129,8 @@ class ListController extends Controller // set page to retrieve ->setPage($this->parameters->get('page')) // set types of transactions to return. - ->setTypes($types); + ->setTypes($types) + ; if (null !== $this->parameters->get('start')) { $collector->setStart($this->parameters->get('start')); @@ -146,7 +139,7 @@ class ListController extends Controller $collector->setEnd($this->parameters->get('end')); } $paginator = $collector->getPaginatedGroups(); - $paginator->setPath(route('api.v1.tags.transactions', [$tag->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.tags.transactions', [$tag->id]).$this->buildParams()); $transactions = $paginator->getCollection(); /** @var TransactionGroupTransformer $transformer */ diff --git a/app/Api/V1/Controllers/Models/Tag/ShowController.php b/app/Api/V1/Controllers/Models/Tag/ShowController.php index 9c50ddf6c1..93bb735754 100644 --- a/app/Api/V1/Controllers/Models/Tag/ShowController.php +++ b/app/Api/V1/Controllers/Models/Tag/ShowController.php @@ -44,8 +44,6 @@ class ShowController extends Controller /** * TagController constructor. - * - */ public function __construct() { @@ -69,7 +67,6 @@ class ShowController extends Controller * * List all of them. * - * @return JsonResponse * @throws FireflyException */ public function index(): JsonResponse @@ -85,7 +82,7 @@ class ShowController extends Controller // make paginator: $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.tags.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.tags.index').$this->buildParams()); /** @var TagTransformer $transformer */ $transformer = app(TagTransformer::class); @@ -102,14 +99,11 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/tags/getTag * * List single resource. - * - * @param Tag $tag - * - * @return JsonResponse */ public function show(Tag $tag): JsonResponse { $manager = $this->getManager(); + /** @var TagTransformer $transformer */ $transformer = app(TagTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/Tag/StoreController.php b/app/Api/V1/Controllers/Models/Tag/StoreController.php index 309ee7a14b..a431ce7f50 100644 --- a/app/Api/V1/Controllers/Models/Tag/StoreController.php +++ b/app/Api/V1/Controllers/Models/Tag/StoreController.php @@ -40,8 +40,6 @@ class StoreController extends Controller /** * TagController constructor. - * - */ public function __construct() { @@ -64,15 +62,12 @@ class StoreController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/tags/storeTag * * 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); diff --git a/app/Api/V1/Controllers/Models/Tag/UpdateController.php b/app/Api/V1/Controllers/Models/Tag/UpdateController.php index 89c9eafa22..54643501b1 100644 --- a/app/Api/V1/Controllers/Models/Tag/UpdateController.php +++ b/app/Api/V1/Controllers/Models/Tag/UpdateController.php @@ -41,8 +41,6 @@ class UpdateController extends Controller /** * TagController constructor. - * - */ public function __construct() { @@ -65,16 +63,12 @@ class UpdateController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/tags/updateTag * * 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); diff --git a/app/Api/V1/Controllers/Models/Transaction/DestroyController.php b/app/Api/V1/Controllers/Models/Transaction/DestroyController.php index 377441d20a..87d6293c15 100644 --- a/app/Api/V1/Controllers/Models/Transaction/DestroyController.php +++ b/app/Api/V1/Controllers/Models/Transaction/DestroyController.php @@ -44,8 +44,6 @@ class DestroyController extends Controller /** * TransactionController constructor. - * - */ public function __construct() { @@ -71,16 +69,13 @@ class DestroyController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/deleteTransaction * * Remove the specified resource from storage. - * - * @param TransactionGroup $transactionGroup - * - * @return JsonResponse */ public function destroy(TransactionGroup $transactionGroup): JsonResponse { app('log')->debug(sprintf('Now in %s', __METHOD__)); // grab asset account(s) from group: $accounts = []; + /** @var TransactionJournal $journal */ foreach ($transactionGroup->transactionJournals as $journal) { /** @var Transaction $transaction */ @@ -111,10 +106,6 @@ class DestroyController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/deleteTransactionJournal * * Remove the specified resource from storage. - * - * @param TransactionJournal $transactionJournal - * - * @return JsonResponse */ public function destroyJournal(TransactionJournal $transactionJournal): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/Transaction/ListController.php b/app/Api/V1/Controllers/Models/Transaction/ListController.php index 25d9d098d0..3bed690910 100644 --- a/app/Api/V1/Controllers/Models/Transaction/ListController.php +++ b/app/Api/V1/Controllers/Models/Transaction/ListController.php @@ -47,8 +47,6 @@ class ListController extends Controller /** * TransactionController constructor. - * - */ public function __construct() { @@ -70,9 +68,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/listAttachmentByTransaction * - * @param TransactionGroup $transactionGroup - * - * @return JsonResponse * @throws FireflyException */ public function attachments(TransactionGroup $transactionGroup): JsonResponse @@ -89,7 +84,7 @@ class ListController extends Controller // make paginator: $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.transactions.attachments', [$transactionGroup->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.transactions.attachments', [$transactionGroup->id]).$this->buildParams()); /** @var AttachmentTransformer $transformer */ $transformer = app(AttachmentTransformer::class); @@ -105,9 +100,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/listEventByTransaction * - * @param TransactionGroup $transactionGroup - * - * @return JsonResponse * @throws FireflyException */ public function piggyBankEvents(TransactionGroup $transactionGroup): JsonResponse @@ -122,7 +114,7 @@ class ListController extends Controller $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()); + $paginator->setPath(route('api.v1.transactions.piggy-bank-events', [$transactionGroup->id]).$this->buildParams()); /** @var PiggyBankEventTransformer $transformer */ $transformer = app(PiggyBankEventTransformer::class); @@ -143,9 +135,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/listLinksByJournal * - * @param TransactionJournal $transactionJournal - * - * @return JsonResponse * @throws FireflyException */ public function transactionLinks(TransactionJournal $transactionJournal): JsonResponse @@ -158,7 +147,7 @@ class ListController extends Controller // 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()); + $paginator->setPath(route('api.v1.transaction-journals.transaction-links', [$transactionJournal->id]).$this->buildParams()); /** @var TransactionLinkTransformer $transformer */ $transformer = app(TransactionLinkTransformer::class); diff --git a/app/Api/V1/Controllers/Models/Transaction/ShowController.php b/app/Api/V1/Controllers/Models/Transaction/ShowController.php index 2819c50fb2..8afe265bf6 100644 --- a/app/Api/V1/Controllers/Models/Transaction/ShowController.php +++ b/app/Api/V1/Controllers/Models/Transaction/ShowController.php @@ -51,9 +51,6 @@ class ShowController extends Controller * * Show all transactions. * - * @param Request $request - * - * @return JsonResponse * @throws FireflyException */ public function index(Request $request): JsonResponse @@ -64,6 +61,7 @@ class ShowController extends Controller $types = $this->mapTransactionTypes($this->parameters->get('type')); $manager = $this->getManager(); + /** @var User $admin */ $admin = auth()->user(); @@ -79,12 +77,13 @@ class ShowController extends Controller // set page to retrieve ->setPage($this->parameters->get('page')) // set types of transactions to return. - ->setTypes($types); + ->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()); + $paginator->setPath(route('api.v1.transactions.index').$this->buildParams()); $transactions = $paginator->getCollection(); /** @var TransactionGroupTransformer $transformer */ @@ -102,10 +101,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/getTransactionByJournal * * Show a single transaction, by transaction journal. - * - * @param TransactionJournal $transactionJournal - * - * @return JsonResponse */ public function showJournal(TransactionJournal $transactionJournal): JsonResponse { @@ -117,16 +112,14 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/getTransaction * * Show a single transaction. - * - * @param TransactionGroup $transactionGroup - * - * @return JsonResponse */ 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); @@ -135,12 +128,14 @@ class ShowController extends Controller // filter on transaction group. ->setTransactionGroup($transactionGroup) // all info needed for the API: - ->withAPIInformation(); + ->withAPIInformation() + ; $selectedGroup = $collector->getGroups()->first(); if (null === $selectedGroup) { throw new NotFoundHttpException(); } + /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/Transaction/StoreController.php b/app/Api/V1/Controllers/Models/Transaction/StoreController.php index f33b725f5b..288f80670f 100644 --- a/app/Api/V1/Controllers/Models/Transaction/StoreController.php +++ b/app/Api/V1/Controllers/Models/Transaction/StoreController.php @@ -38,7 +38,6 @@ use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Log; use Illuminate\Validation\ValidationException; use League\Fractal\Resource\Item; -use Validator; /** * Class StoreController @@ -51,8 +50,6 @@ class StoreController extends Controller /** * TransactionController constructor. - * - */ public function __construct() { @@ -76,9 +73,6 @@ class StoreController extends Controller * * Store a new transaction. * - * @param StoreRequest $request - * - * @return JsonResponse * @throws FireflyException|ValidationException */ public function store(StoreRequest $request): JsonResponse @@ -88,22 +82,25 @@ class StoreController extends Controller $data['user'] = auth()->user()->id; Log::channel('audit') - ->info('Store new transaction over API.', $data); + ->info('Store new transaction over API.', $data) + ; try { $transactionGroup = $this->groupRepository->store($data); } catch (DuplicateTransactionException $e) { // @phpstan-ignore-line app('log')->warning('Caught a duplicate transaction. Return error message.'); - $validator = Validator::make( + $validator = \Validator::make( ['transactions' => [['description' => $e->getMessage()]]], ['transactions.0.description' => new IsDuplicateTransaction()] ); + throw new ValidationException($validator); // @phpstan-ignore-line } catch (FireflyException $e) { // @phpstan-ignore-line app('log')->warning('Caught an exception. Return error message.'); app('log')->error($e->getMessage()); $message = sprintf('Internal exception: %s', $e->getMessage()); - $validator = Validator::make(['transactions' => [['description' => $message]]], ['transactions.0.description' => new IsDuplicateTransaction()]); + $validator = \Validator::make(['transactions' => [['description' => $message]]], ['transactions.0.description' => new IsDuplicateTransaction()]); + throw new ValidationException($validator); // @phpstan-ignore-line } app('preferences')->mark(); @@ -112,8 +109,10 @@ class StoreController extends Controller event(new StoredTransactionGroup($transactionGroup, $applyRules, $fireWebhooks)); $manager = $this->getManager(); + /** @var User $admin */ $admin = auth()->user(); + // use new group collector: /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); @@ -122,12 +121,14 @@ class StoreController extends Controller // filter on transaction group. ->setTransactionGroup($transactionGroup) // all info needed for the API: - ->withAPIInformation(); + ->withAPIInformation() + ; $selectedGroup = $collector->getGroups()->first(); if (null === $selectedGroup) { throw new FireflyException('200032: Cannot find transaction. Possibly, a rule deleted this transaction after its creation.'); } + /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/Transaction/UpdateController.php b/app/Api/V1/Controllers/Models/Transaction/UpdateController.php index 1cfe099ab0..ed9e2fb17a 100644 --- a/app/Api/V1/Controllers/Models/Transaction/UpdateController.php +++ b/app/Api/V1/Controllers/Models/Transaction/UpdateController.php @@ -44,8 +44,6 @@ class UpdateController extends Controller /** * TransactionController constructor. - * - */ public function __construct() { @@ -68,11 +66,6 @@ class UpdateController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/updateTransaction * * Update a transaction. - * - * @param UpdateRequest $request - * @param TransactionGroup $transactionGroup - * - * @return JsonResponse */ public function update(UpdateRequest $request, TransactionGroup $transactionGroup): JsonResponse { @@ -89,6 +82,7 @@ class UpdateController extends Controller /** @var User $admin */ $admin = auth()->user(); + // use new group collector: /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); @@ -97,12 +91,14 @@ class UpdateController extends Controller // filter on transaction group. ->setTransactionGroup($transactionGroup) // all info needed for the API: - ->withAPIInformation(); + ->withAPIInformation() + ; $selectedGroup = $collector->getGroups()->first(); if (null === $selectedGroup) { throw new NotFoundHttpException(); } + /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php index 951bdfb474..e2512cf47c 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php @@ -31,7 +31,6 @@ use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\User; use Illuminate\Http\JsonResponse; -use Validator; /** * Class DestroyController @@ -43,8 +42,6 @@ class DestroyController extends Controller /** * CurrencyRepository constructor. - * - */ public function __construct() { @@ -66,9 +63,6 @@ class DestroyController extends Controller * * Remove the specified resource from storage. * - * @param TransactionCurrency $currency - * - * @return JsonResponse * @throws FireflyException */ public function destroy(TransactionCurrency $currency): JsonResponse @@ -80,15 +74,15 @@ class DestroyController extends Controller if (!$this->userRepository->hasRole($admin, 'owner')) { // access denied: $messages = ['currency_code' => '200005: You need the "owner" role to do this.']; - Validator::make([], $rules, $messages)->validate(); + \Validator::make([], $rules, $messages)->validate(); } if ($this->repository->currencyInUse($currency)) { $messages = ['currency_code' => '200006: Currency in use.']; - Validator::make([], $rules, $messages)->validate(); + \Validator::make([], $rules, $messages)->validate(); } if ($this->repository->isFallbackCurrency($currency)) { $messages = ['currency_code' => '200026: Currency is fallback.']; - Validator::make([], $rules, $messages)->validate(); + \Validator::make([], $rules, $messages)->validate(); } $this->repository->destroy($currency); diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php index f4a2fa05b3..8f895230cc 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php @@ -69,10 +69,6 @@ class ListController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/listAccountByCurrency * Display a list of accounts. * - * @param Request $request - * @param TransactionCurrency $currency - * - * @return JsonResponse * @throws FireflyException */ public function accounts(Request $request, TransactionCurrency $currency): JsonResponse @@ -106,7 +102,7 @@ class ListController extends Controller // make paginator: $paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.currencies.accounts', [$currency->code]) . $this->buildParams()); + $paginator->setPath(route('api.v1.currencies.accounts', [$currency->code]).$this->buildParams()); /** @var AccountTransformer $transformer */ $transformer = app(AccountTransformer::class); @@ -123,9 +119,6 @@ class ListController extends Controller * * Display a listing of the resource. * - * @param TransactionCurrency $currency - * - * @return JsonResponse * @throws FireflyException */ public function availableBudgets(TransactionCurrency $currency): JsonResponse @@ -143,7 +136,7 @@ class ListController extends Controller $availableBudgets = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); // make paginator: $paginator = new LengthAwarePaginator($availableBudgets, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.currencies.available-budgets', [$currency->code]) . $this->buildParams()); + $paginator->setPath(route('api.v1.currencies.available-budgets', [$currency->code]).$this->buildParams()); /** @var AvailableBudgetTransformer $transformer */ $transformer = app(AvailableBudgetTransformer::class); @@ -161,9 +154,6 @@ class ListController extends Controller * * List all bills * - * @param TransactionCurrency $currency - * - * @return JsonResponse * @throws FireflyException */ public function bills(TransactionCurrency $currency): JsonResponse @@ -186,7 +176,7 @@ class ListController extends Controller // make paginator: $paginator = new LengthAwarePaginator($bills, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.currencies.bills', [$currency->code]) . $this->buildParams()); + $paginator->setPath(route('api.v1.currencies.bills', [$currency->code]).$this->buildParams()); /** @var BillTransformer $transformer */ $transformer = app(BillTransformer::class); @@ -204,9 +194,6 @@ class ListController extends Controller * * List all budget limits * - * @param TransactionCurrency $currency - * - * @return JsonResponse * @throws FireflyException */ public function budgetLimits(TransactionCurrency $currency): JsonResponse @@ -220,7 +207,7 @@ class ListController extends Controller $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.currencies.budget-limits', [$currency->code]) . $this->buildParams()); + $paginator->setPath(route('api.v1.currencies.budget-limits', [$currency->code]).$this->buildParams()); /** @var BudgetLimitTransformer $transformer */ $transformer = app(BudgetLimitTransformer::class); @@ -238,9 +225,6 @@ class ListController extends Controller * * List all recurring transactions. * - * @param TransactionCurrency $currency - * - * @return JsonResponse * @throws FireflyException */ public function recurrences(TransactionCurrency $currency): JsonResponse @@ -272,7 +256,7 @@ class ListController extends Controller // make paginator: $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.currencies.recurrences', [$currency->code]) . $this->buildParams()); + $paginator->setPath(route('api.v1.currencies.recurrences', [$currency->code]).$this->buildParams()); /** @var RecurrenceTransformer $transformer */ $transformer = app(RecurrenceTransformer::class); @@ -290,9 +274,6 @@ class ListController extends Controller * * List all of them. * - * @param TransactionCurrency $currency - * - * @return JsonResponse * @throws FireflyException */ public function rules(TransactionCurrency $currency): JsonResponse @@ -323,7 +304,7 @@ class ListController extends Controller // make paginator: $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.rules.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.rules.index').$this->buildParams()); /** @var RuleTransformer $transformer */ $transformer = app(RuleTransformer::class); @@ -341,11 +322,6 @@ class ListController extends Controller * * Show all transactions. * - * @param Request $request - * - * @param TransactionCurrency $currency - * - * @return JsonResponse * @throws FireflyException */ public function transactions(Request $request, TransactionCurrency $currency): JsonResponse @@ -374,7 +350,8 @@ class ListController extends Controller // set page to retrieve ->setPage($this->parameters->get('page')) // set types of transactions to return. - ->setTypes($types); + ->setTypes($types) + ; if (null !== $this->parameters->get('start')) { $collector->setStart($this->parameters->get('start')); } @@ -382,7 +359,7 @@ class ListController extends Controller $collector->setEnd($this->parameters->get('end')); } $paginator = $collector->getPaginatedGroups(); - $paginator->setPath(route('api.v1.currencies.transactions', [$currency->code]) . $this->buildParams()); + $paginator->setPath(route('api.v1.currencies.transactions', [$currency->code]).$this->buildParams()); $transactions = $paginator->getCollection(); /** @var TransactionGroupTransformer $transformer */ diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php index 3e7c74fcb4..b2ed6a1625 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php @@ -34,7 +34,6 @@ use FireflyIII\Transformers\CurrencyTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Pagination\LengthAwarePaginator; -use JsonException; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Item; @@ -51,8 +50,6 @@ class ShowController extends Controller /** * CurrencyRepository constructor. - * - */ public function __construct() { @@ -73,9 +70,7 @@ class ShowController extends Controller * * Display a listing of the resource. * - * @return JsonResponse * @throws FireflyException - * @throws JsonException */ public function index(): JsonResponse { @@ -86,7 +81,7 @@ class ShowController extends Controller // 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()); + $paginator->setPath(route('api.v1.currencies.index').$this->buildParams()); $manager = $this->getManager(); /** @var CurrencyTransformer $transformer */ @@ -105,11 +100,7 @@ class ShowController extends Controller * * Show a currency. * - * @param TransactionCurrency $currency - * - * @return JsonResponse * @throws FireflyException - * @throws JsonException */ public function show(TransactionCurrency $currency): JsonResponse { @@ -137,9 +128,7 @@ class ShowController extends Controller * * Show a currency. * - * @return JsonResponse * @throws FireflyException - * @throws JsonException */ public function showDefault(): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php index f861fe998e..3244d9aba3 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php @@ -33,7 +33,6 @@ use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Transformers\CurrencyTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; -use JsonException; use League\Fractal\Resource\Item; /** @@ -48,8 +47,6 @@ class StoreController extends Controller /** * CurrencyRepository constructor. - * - */ public function __construct() { @@ -70,11 +67,7 @@ class StoreController extends Controller * * Store new currency. * - * @param StoreRequest $request - * - * @return JsonResponse * @throws FireflyException - * @throws JsonException */ public function store(StoreRequest $request): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php index 9bb531a603..ffa8e959be 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php @@ -34,7 +34,6 @@ use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Transformers\CurrencyTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; -use JsonException; use League\Fractal\Resource\Item; /** @@ -49,8 +48,6 @@ class UpdateController extends Controller /** * CurrencyRepository constructor. - * - */ public function __construct() { @@ -71,11 +68,7 @@ class UpdateController extends Controller * * Disable a currency. * - * @param TransactionCurrency $currency - * - * @return JsonResponse * @throws FireflyException - * @throws JsonException */ public function disable(TransactionCurrency $currency): JsonResponse { @@ -87,6 +80,7 @@ class UpdateController extends Controller if (1 === $this->repository->get()->count()) { return response()->json([], 409); } + /** @var User $user */ $user = auth()->user(); $this->repository->disable($currency); @@ -109,9 +103,6 @@ class UpdateController extends Controller * * Make the currency a default currency. * - * @param TransactionCurrency $currency - * - * @return JsonResponse * @throws FireflyException */ public function makeDefault(TransactionCurrency $currency): JsonResponse @@ -141,16 +132,13 @@ class UpdateController extends Controller * * Enable a currency. * - * @param TransactionCurrency $currency - * - * @return JsonResponse * @throws FireflyException - * @throws JsonException */ public function enable(TransactionCurrency $currency): JsonResponse { $this->repository->enable($currency); $manager = $this->getManager(); + /** @var User $user */ $user = auth()->user(); @@ -171,12 +159,7 @@ class UpdateController extends Controller * * Update a currency. * - * @param UpdateRequest $request - * @param TransactionCurrency $currency - * - * @return JsonResponse * @throws FireflyException - * @throws JsonException */ public function update(UpdateRequest $request, TransactionCurrency $currency): JsonResponse { @@ -189,7 +172,6 @@ class UpdateController extends Controller $set = $this->repository->get(); if (array_key_exists('enabled', $data) && false === $data['enabled'] && 1 === count($set) && $set->first()->id === $currency->id) { return response()->json([], 409); - } $currency = $this->repository->update($currency, $data); diff --git a/app/Api/V1/Controllers/Models/TransactionLink/DestroyController.php b/app/Api/V1/Controllers/Models/TransactionLink/DestroyController.php index 1c9b1fc9a7..82fc1d2738 100644 --- a/app/Api/V1/Controllers/Models/TransactionLink/DestroyController.php +++ b/app/Api/V1/Controllers/Models/TransactionLink/DestroyController.php @@ -39,8 +39,6 @@ class DestroyController extends Controller /** * TransactionLinkController constructor. - * - */ public function __construct() { @@ -64,10 +62,6 @@ class DestroyController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/links/deleteTransactionLink * * Delete the resource. - * - * @param TransactionJournalLink $link - * - * @return JsonResponse */ public function destroy(TransactionJournalLink $link): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php b/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php index 485cbd51b2..99e0d04859 100644 --- a/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php +++ b/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php @@ -46,8 +46,6 @@ class ShowController extends Controller /** * TransactionLinkController constructor. - * - */ public function __construct() { @@ -72,9 +70,6 @@ class ShowController extends Controller * * List all transaction links there are. * - * @param Request $request - * - * @return JsonResponse * @throws FireflyException */ public function index(Request $request): JsonResponse @@ -95,7 +90,7 @@ class ShowController extends Controller // make paginator: $paginator = new LengthAwarePaginator($journalLinks, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.transaction-links.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.transaction-links.index').$this->buildParams()); /** @var TransactionLinkTransformer $transformer */ $transformer = app(TransactionLinkTransformer::class); @@ -112,10 +107,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/links/getTransactionLink * * List single resource. - * - * @param TransactionJournalLink $journalLink - * - * @return JsonResponse */ public function show(TransactionJournalLink $journalLink): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionLink/StoreController.php b/app/Api/V1/Controllers/Models/TransactionLink/StoreController.php index 861dbbcc79..dee4ef45db 100644 --- a/app/Api/V1/Controllers/Models/TransactionLink/StoreController.php +++ b/app/Api/V1/Controllers/Models/TransactionLink/StoreController.php @@ -47,8 +47,6 @@ class StoreController extends Controller /** * TransactionLinkController constructor. - * - */ public function __construct() { @@ -75,9 +73,6 @@ class StoreController extends Controller * * Store new object. * - * @param StoreRequest $request - * - * @return JsonResponse * @throws FireflyException */ public function store(StoreRequest $request): JsonResponse diff --git a/app/Api/V1/Controllers/Models/TransactionLink/UpdateController.php b/app/Api/V1/Controllers/Models/TransactionLink/UpdateController.php index f432b93c70..242707baee 100644 --- a/app/Api/V1/Controllers/Models/TransactionLink/UpdateController.php +++ b/app/Api/V1/Controllers/Models/TransactionLink/UpdateController.php @@ -44,8 +44,6 @@ class UpdateController extends Controller /** * TransactionLinkController constructor. - * - */ public function __construct() { @@ -71,11 +69,6 @@ class UpdateController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/links/updateTransactionLink * * Update object. - * - * @param UpdateRequest $request - * @param TransactionJournalLink $journalLink - * - * @return JsonResponse */ public function update(UpdateRequest $request, TransactionJournalLink $journalLink): JsonResponse { diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/DestroyController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/DestroyController.php index 4eca004d35..7abbff55d7 100644 --- a/app/Api/V1/Controllers/Models/TransactionLinkType/DestroyController.php +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/DestroyController.php @@ -43,8 +43,6 @@ class DestroyController extends Controller /** * LinkTypeController constructor. - * - */ public function __construct() { @@ -67,9 +65,6 @@ class DestroyController extends Controller * * Delete the resource. * - * @param LinkType $linkType - * - * @return JsonResponse * @throws FireflyException */ public function destroy(LinkType $linkType): JsonResponse diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php index 3e18860ccb..aa68d371d8 100644 --- a/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php @@ -48,8 +48,6 @@ class ListController extends Controller /** * LinkTypeController constructor. - * - */ public function __construct() { @@ -70,10 +68,6 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/links/listTransactionByLinkType * - * @param Request $request - * @param LinkType $linkType - * - * @return JsonResponse * @throws FireflyException */ public function transactions(Request $request, LinkType $linkType): JsonResponse @@ -105,7 +99,8 @@ class ListController extends Controller // set page to retrieve ->setPage($this->parameters->get('page')) // set types of transactions to return. - ->setTypes($types); + ->setTypes($types) + ; if (null !== $this->parameters->get('start')) { $collector->setStart($this->parameters->get('start')); } @@ -113,7 +108,7 @@ class ListController extends Controller $collector->setEnd($this->parameters->get('end')); } $paginator = $collector->getPaginatedGroups(); - $paginator->setPath(route('api.v1.transactions.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.transactions.index').$this->buildParams()); $transactions = $paginator->getCollection(); /** @var TransactionGroupTransformer $transformer */ diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php index cc63966521..aa56e344a7 100644 --- a/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php @@ -48,8 +48,6 @@ class ShowController extends Controller /** * LinkTypeController constructor. - * - */ public function __construct() { @@ -70,8 +68,6 @@ class ShowController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/links/listLinkType * - * - * @return JsonResponse * @throws FireflyException */ public function index(): JsonResponse @@ -87,7 +83,7 @@ class ShowController extends Controller // make paginator: $paginator = new LengthAwarePaginator($linkTypes, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.link-types.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.link-types.index').$this->buildParams()); /** @var LinkTypeTransformer $transformer */ $transformer = app(LinkTypeTransformer::class); @@ -104,14 +100,11 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/links/getLinkType * * List single resource. - * - * @param LinkType $linkType - * - * @return JsonResponse */ public function show(LinkType $linkType): JsonResponse { $manager = $this->getManager(); + /** @var LinkTypeTransformer $transformer */ $transformer = app(LinkTypeTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/StoreController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/StoreController.php index e42f5d74c2..d2f6037304 100644 --- a/app/Api/V1/Controllers/Models/TransactionLinkType/StoreController.php +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/StoreController.php @@ -34,7 +34,6 @@ use FireflyIII\Transformers\LinkTypeTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; use League\Fractal\Resource\Item; -use Validator; /** * Class StoreController @@ -48,8 +47,6 @@ class StoreController extends Controller /** * LinkTypeController constructor. - * - */ public function __construct() { @@ -73,9 +70,6 @@ class StoreController extends Controller * * Store new object. * - * @param StoreRequest $request - * - * @return JsonResponse * @throws FireflyException */ public function store(StoreRequest $request): JsonResponse @@ -87,7 +81,7 @@ class StoreController extends Controller if (!$this->userRepository->hasRole($admin, 'owner')) { // access denied: $messages = ['name' => '200005: You need the "owner" role to do this.']; - Validator::make([], $rules, $messages)->validate(); + \Validator::make([], $rules, $messages)->validate(); } $data = $request->getAll(); // if currency ID is 0, find the currency by the code: diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/UpdateController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/UpdateController.php index 60bab00568..77d25a9634 100644 --- a/app/Api/V1/Controllers/Models/TransactionLinkType/UpdateController.php +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/UpdateController.php @@ -35,7 +35,6 @@ use FireflyIII\Transformers\LinkTypeTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; use League\Fractal\Resource\Item; -use Validator; /** * Class UpdateController @@ -49,8 +48,6 @@ class UpdateController extends Controller /** * LinkTypeController constructor. - * - */ public function __construct() { @@ -74,10 +71,6 @@ class UpdateController extends Controller * * Update object. * - * @param UpdateRequest $request - * @param LinkType $linkType - * - * @return JsonResponse * @throws FireflyException */ public function update(UpdateRequest $request, LinkType $linkType): JsonResponse @@ -92,12 +85,13 @@ class UpdateController extends Controller if (!$this->userRepository->hasRole($admin, 'owner')) { $messages = ['name' => '200005: You need the "owner" role to do this.']; - Validator::make([], $rules, $messages)->validate(); + \Validator::make([], $rules, $messages)->validate(); } $data = $request->getAll(); $this->repository->update($linkType, $data); $manager = $this->getManager(); + /** @var LinkTypeTransformer $transformer */ $transformer = app(LinkTypeTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Search/AccountController.php b/app/Api/V1/Controllers/Search/AccountController.php index b64b7f7150..2fa471c634 100644 --- a/app/Api/V1/Controllers/Search/AccountController.php +++ b/app/Api/V1/Controllers/Search/AccountController.php @@ -59,12 +59,8 @@ class AccountController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/search/searchAccounts - * - * @param Request $request - * - * @return JsonResponse|Response */ - public function search(Request $request): JsonResponse | Response + public function search(Request $request): JsonResponse|Response { app('log')->debug('Now in account search()'); $manager = $this->getManager(); @@ -84,6 +80,7 @@ class AccountController extends Controller $search->setQuery($query); $accounts = $search->search(); + /** @var AccountTransformer $transformer */ $transformer = app(AccountTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Search/TransactionController.php b/app/Api/V1/Controllers/Search/TransactionController.php index 2588d0520a..4930cc1689 100644 --- a/app/Api/V1/Controllers/Search/TransactionController.php +++ b/app/Api/V1/Controllers/Search/TransactionController.php @@ -42,10 +42,6 @@ class TransactionController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/search/searchTransactions * - * @param Request $request - * @param SearchInterface $searcher - * - * @return JsonResponse * @throws FireflyException */ public function search(Request $request, SearchInterface $searcher): JsonResponse @@ -59,7 +55,7 @@ class TransactionController extends Controller $searcher->setLimit($pageSize); $groups = $searcher->searchTransactions(); $parameters = ['search' => $fullQuery]; - $url = route('api.v1.search.transactions') . '?' . http_build_query($parameters); + $url = route('api.v1.search.transactions').'?'.http_build_query($parameters); $groups->setPath($url); $transactions = $groups->getCollection(); diff --git a/app/Api/V1/Controllers/Summary/BasicController.php b/app/Api/V1/Controllers/Summary/BasicController.php index 9d9078c343..7c9c6014be 100644 --- a/app/Api/V1/Controllers/Summary/BasicController.php +++ b/app/Api/V1/Controllers/Summary/BasicController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Summary; use Carbon\Carbon; -use Exception; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Data\DateRequest; use FireflyIII\Helpers\Collector\GroupCollectorInterface; @@ -56,8 +55,6 @@ class BasicController extends Controller /** * BasicController constructor. - * - */ public function __construct() { @@ -89,10 +86,7 @@ class BasicController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/summary/getBasicSummary * - * @param DateRequest $request - * - * @return JsonResponse - * @throws Exception + * @throws \Exception */ public function basic(DateRequest $request): JsonResponse { @@ -107,7 +101,11 @@ class BasicController extends Controller $billData = $this->getBillInformation($start, $end); $spentData = $this->getLeftToSpendInfo($start, $end); $netWorthData = $this->getNetWorthInfo($start, $end); - $total = array_merge($balanceData, $billData, $spentData, $netWorthData); + // $balanceData = []; + // $billData = []; + // $spentData = []; + // $netWorthData = []; + $total = array_merge($balanceData, $billData, $spentData, $netWorthData); // give new keys $return = []; @@ -121,11 +119,22 @@ class BasicController extends Controller } /** - * @param Carbon $start - * @param Carbon $end - * - * @return array + * Check if date is outside session range. */ + protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference + { + $result = false; + if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) { + $result = true; + } + // start and end in the past? use $end + if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) { + $result = true; + } + + return $result; + } + private function getBalanceInformation(Carbon $start, Carbon $end): array { // prep some arrays: @@ -137,17 +146,13 @@ class BasicController extends Controller // collect income of user using the new group collector. /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $collector - ->setRange($start, $end) - // set page to retrieve - ->setPage($this->parameters->get('page')) - // set types of transactions to return. - ->setTypes([TransactionType::DEPOSIT]); + $collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionType::DEPOSIT]); $set = $collector->getExtractedJournals(); + /** @var array $transactionJournal */ foreach ($set as $transactionJournal) { - $currencyId = (int)$transactionJournal['currency_id']; + $currencyId = (int) $transactionJournal['currency_id']; $incomes[$currencyId] ??= '0'; $incomes[$currencyId] = bcadd( $incomes[$currencyId], @@ -160,17 +165,12 @@ class BasicController extends Controller // collect expenses of user using the new group collector. /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $collector - ->setRange($start, $end) - // set page to retrieve - ->setPage($this->parameters->get('page')) - // set types of transactions to return. - ->setTypes([TransactionType::WITHDRAWAL]); + $collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionType::WITHDRAWAL]); $set = $collector->getExtractedJournals(); /** @var array $transactionJournal */ foreach ($set as $transactionJournal) { - $currencyId = (int)$transactionJournal['currency_id']; + $currencyId = (int) $transactionJournal['currency_id']; $expenses[$currencyId] ??= '0'; $expenses[$currencyId] = bcadd($expenses[$currencyId], $transactionJournal['amount']); $sums[$currencyId] ??= '0'; @@ -189,20 +189,20 @@ class BasicController extends Controller 'key' => sprintf('balance-in-%s', $currency->code), 'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]), 'monetary_value' => $sums[$currencyId] ?? '0', - 'currency_id' => (string)$currency->id, + 'currency_id' => (string) $currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => $currency->decimal_places, 'value_parsed' => app('amount')->formatAnything($currency, $sums[$currencyId] ?? '0', false), 'local_icon' => 'balance-scale', - 'sub_title' => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false) . - ' + ' . app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false), + 'sub_title' => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false). + ' + '.app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false), ]; $return[] = [ 'key' => sprintf('spent-in-%s', $currency->code), 'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]), 'monetary_value' => $expenses[$currencyId] ?? '0', - 'currency_id' => (string)$currency->id, + 'currency_id' => (string) $currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => $currency->decimal_places, @@ -214,7 +214,7 @@ class BasicController extends Controller 'key' => sprintf('earned-in-%s', $currency->code), 'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]), 'monetary_value' => $incomes[$currencyId] ?? '0', - 'currency_id' => (string)$currency->id, + 'currency_id' => (string) $currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => $currency->decimal_places, @@ -227,12 +227,6 @@ class BasicController extends Controller return $return; } - /** - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ private function getBillInformation(Carbon $start, Carbon $end): array { app('log')->debug(sprintf('Now in getBillInformation("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d-'))); @@ -244,6 +238,7 @@ class BasicController extends Controller $unpaidAmount = $this->billRepository->sumUnpaidInRange($start, $end); $return = []; + /** * @var array $info */ @@ -253,7 +248,7 @@ class BasicController extends Controller 'key' => sprintf('bills-paid-in-%s', $info['code']), 'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $info['symbol']]), 'monetary_value' => $amount, - 'currency_id' => (string)$info['id'], + 'currency_id' => (string) $info['id'], 'currency_code' => $info['code'], 'currency_symbol' => $info['symbol'], 'currency_decimal_places' => $info['decimal_places'], @@ -272,7 +267,7 @@ class BasicController extends Controller 'key' => sprintf('bills-unpaid-in-%s', $info['code']), 'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $info['symbol']]), 'monetary_value' => $amount, - 'currency_id' => (string)$info['id'], + 'currency_id' => (string) $info['id'], 'currency_code' => $info['code'], 'currency_symbol' => $info['symbol'], 'currency_decimal_places' => $info['decimal_places'], @@ -282,15 +277,12 @@ class BasicController extends Controller ]; } app('log')->debug(sprintf('Done with getBillInformation("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d-'))); + return $return; } /** - * @param Carbon $start - * @param Carbon $end - * - * @return array - * @throws Exception + * @throws \Exception */ private function getLeftToSpendInfo(Carbon $start, Carbon $end): array { @@ -302,21 +294,21 @@ class BasicController extends Controller foreach ($spent as $row) { // either an amount was budgeted or 0 is available. - $amount = (string)($available[$row['currency_id']] ?? '0'); + $amount = (string) ($available[$row['currency_id']] ?? '0'); $spentInCurrency = $row['sum']; $leftToSpend = bcadd($amount, $spentInCurrency); $days = $today->diffInDays($end) + 1; $perDay = '0'; if (0 !== $days && bccomp($leftToSpend, '0') > -1) { - $perDay = bcdiv($leftToSpend, (string)$days); + $perDay = bcdiv($leftToSpend, (string) $days); } $return[] = [ 'key' => sprintf('left-to-spend-in-%s', $row['currency_code']), 'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $row['currency_symbol']]), 'monetary_value' => $leftToSpend, - 'currency_id' => (string)$row['currency_id'], + 'currency_id' => (string) $row['currency_id'], 'currency_code' => $row['currency_code'], 'currency_symbol' => $row['currency_symbol'], 'currency_decimal_places' => $row['currency_decimal_places'], @@ -334,12 +326,6 @@ class BasicController extends Controller return $return; } - /** - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ private function getNetWorthInfo(Carbon $start, Carbon $end): array { /** @var User $user */ @@ -382,7 +368,7 @@ class BasicController extends Controller 'key' => sprintf('net-worth-in-%s', $data['currency_code']), 'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $data['currency_symbol']]), 'monetary_value' => $amount, - 'currency_id' => (string)$data['currency_id'], + 'currency_id' => (string) $data['currency_id'], 'currency_code' => $data['currency_code'], 'currency_symbol' => $data['currency_symbol'], 'currency_decimal_places' => $data['currency_decimal_places'], @@ -391,30 +377,7 @@ class BasicController extends Controller 'sub_title' => '', ]; } + return $return; } - - /** - * Check if date is outside session range. - * - * @param Carbon $date - * - * @param Carbon $start - * @param Carbon $end - * - * @return bool - */ - protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference - { - $result = false; - if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) { - $result = true; - } - // start and end in the past? use $end - if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) { - $result = true; - } - - return $result; - } } diff --git a/app/Api/V1/Controllers/System/AboutController.php b/app/Api/V1/Controllers/System/AboutController.php index 390309d991..20259be481 100644 --- a/app/Api/V1/Controllers/System/AboutController.php +++ b/app/Api/V1/Controllers/System/AboutController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\System; -use DB; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Transformers\UserTransformer; use Illuminate\Http\JsonResponse; @@ -42,8 +41,6 @@ class AboutController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/about/getAbout * * Returns system information. - * - * @return JsonResponse */ public function about(): JsonResponse { @@ -51,15 +48,15 @@ class AboutController extends Controller $replace = ['\~', '# ']; $phpVersion = str_replace($search, $replace, PHP_VERSION); $phpOs = str_replace($search, $replace, PHP_OS); - $currentDriver = DB::getDriverName(); + $currentDriver = \DB::getDriverName(); $data = [ - 'version' => config('firefly.version'), - 'api_version' => config('firefly.api_version'), - 'php_version' => $phpVersion, - 'os' => $phpOs, - 'driver' => $currentDriver, - ]; + 'version' => config('firefly.version'), + 'api_version' => config('firefly.api_version'), + 'php_version' => $phpVersion, + 'os' => $phpOs, + 'driver' => $currentDriver, + ]; return response()->api(['data' => $data])->header('Content-Type', self::CONTENT_TYPE); } @@ -69,8 +66,6 @@ class AboutController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/about/getCurrentUser * * Returns information about the user. - * - * @return JsonResponse */ public function user(): JsonResponse { diff --git a/app/Api/V1/Controllers/System/ConfigurationController.php b/app/Api/V1/Controllers/System/ConfigurationController.php index fd4da1c827..7b03eeb8b0 100644 --- a/app/Api/V1/Controllers/System/ConfigurationController.php +++ b/app/Api/V1/Controllers/System/ConfigurationController.php @@ -29,9 +29,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Support\Binder\EitherConfigKey; use Illuminate\Http\JsonResponse; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; -use Validator; /** * Class ConfigurationController @@ -59,10 +56,7 @@ class ConfigurationController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/configuration/getConfiguration * - * @return JsonResponse - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface */ public function index(): JsonResponse { @@ -71,6 +65,7 @@ class ConfigurationController extends Controller } catch (FireflyException $e) { app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); + throw new FireflyException('200030: Could not load config variables.', 0, $e); } $staticData = $this->getStaticConfiguration(); @@ -93,51 +88,9 @@ class ConfigurationController extends Controller return response()->json($return); } - /** - * Get all config values. - * - * @return array - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ - 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' => $isDemoSite?->data, - 'permission_update_check' => null === $updateCheck ? null : (int)$updateCheck->data, - 'last_update_check' => null === $lastCheck ? null : (int)$lastCheck->data, - 'single_user_mode' => $singleUser?->data, - ]; - } - - /** - * @return array - */ - private function getStaticConfiguration(): array - { - $list = EitherConfigKey::$static; - $return = []; - foreach ($list as $key) { - $return[$key] = config($key); - } - - return $return; - } - /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/configuration/getSingleConfiguration - * - * @param string $configKey - * - * @return JsonResponse - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function show(string $configKey): JsonResponse { @@ -168,20 +121,14 @@ class ConfigurationController extends Controller * * Update the configuration. * - * @param UpdateRequest $request - * @param string $name - * - * @return JsonResponse - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface */ public function update(UpdateRequest $request, string $name): JsonResponse { $rules = ['value' => 'required']; if (!$this->repository->hasRole(auth()->user(), 'owner')) { $messages = ['value' => '200005: You need the "owner" role to do this.']; - Validator::make([], $rules, $messages)->validate(); + \Validator::make([], $rules, $messages)->validate(); } $data = $request->getAll(); $shortName = str_replace('configuration.', '', $name); @@ -198,4 +145,33 @@ class ConfigurationController extends Controller return response()->json(['data' => $data])->header('Content-Type', self::CONTENT_TYPE); } + + /** + * Get all config values. + */ + 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' => $isDemoSite?->data, + 'permission_update_check' => null === $updateCheck ? null : (int)$updateCheck->data, + 'last_update_check' => null === $lastCheck ? null : (int)$lastCheck->data, + 'single_user_mode' => $singleUser?->data, + ]; + } + + private function getStaticConfiguration(): array + { + $list = EitherConfigKey::$static; + $return = []; + foreach ($list as $key) { + $return[$key] = config($key); + } + + return $return; + } } diff --git a/app/Api/V1/Controllers/System/CronController.php b/app/Api/V1/Controllers/System/CronController.php index eec18ecf8c..247ed97bf0 100644 --- a/app/Api/V1/Controllers/System/CronController.php +++ b/app/Api/V1/Controllers/System/CronController.php @@ -27,8 +27,6 @@ use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\System\CronRequest; use FireflyIII\Support\Http\Controllers\CronRunner; use Illuminate\Http\JsonResponse; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class CronController @@ -40,12 +38,6 @@ class CronController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/about/getCron - * - * @param CronRequest $request - * - * @return JsonResponse - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function cron(CronRequest $request): JsonResponse { diff --git a/app/Api/V1/Controllers/System/UserController.php b/app/Api/V1/Controllers/System/UserController.php index 45233925dc..31a5b98881 100644 --- a/app/Api/V1/Controllers/System/UserController.php +++ b/app/Api/V1/Controllers/System/UserController.php @@ -46,8 +46,6 @@ class UserController extends Controller /** * UserController constructor. - * - */ public function __construct() { @@ -67,9 +65,6 @@ class UserController extends Controller * * Remove the specified resource from storage. * - * @param User $user - * - * @return JsonResponse * @throws FireflyException */ public function destroy(User $user): JsonResponse @@ -85,6 +80,7 @@ class UserController extends Controller return response()->json([], 204); } + throw new FireflyException('200025: No access to function.'); } @@ -94,7 +90,6 @@ class UserController extends Controller * * Display a listing of the resource. * - * @return JsonResponse * @throws FireflyException */ public function index(): JsonResponse @@ -110,7 +105,7 @@ class UserController extends Controller // make paginator: $paginator = new LengthAwarePaginator($users, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.users.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.users.index').$this->buildParams()); // make resource /** @var UserTransformer $transformer */ @@ -128,15 +123,12 @@ class UserController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/users/getUser * * Show a single user. - * - * @param User $user - * - * @return JsonResponse */ public function show(User $user): JsonResponse { // make manager $manager = $this->getManager(); + // make resource /** @var UserTransformer $transformer */ $transformer = app(UserTransformer::class); @@ -152,10 +144,6 @@ class UserController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/users/storeUser * * Store a new user. - * - * @param UserStoreRequest $request - * - * @return JsonResponse */ public function store(UserStoreRequest $request): JsonResponse { @@ -179,11 +167,6 @@ class UserController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/users/updateUser * * Update a user. - * - * @param UserUpdateRequest $request - * @param User $user - * - * @return JsonResponse */ public function update(UserUpdateRequest $request, User $user): JsonResponse { @@ -197,6 +180,7 @@ class UserController extends Controller $user = $this->repository->update($user, $data); $manager = $this->getManager(); + // make resource /** @var UserTransformer $transformer */ $transformer = app(UserTransformer::class); diff --git a/app/Api/V1/Controllers/User/PreferencesController.php b/app/Api/V1/Controllers/User/PreferencesController.php index fdb4eb7382..b4b72f1fa7 100644 --- a/app/Api/V1/Controllers/User/PreferencesController.php +++ b/app/Api/V1/Controllers/User/PreferencesController.php @@ -50,7 +50,6 @@ class PreferencesController extends Controller * * List all of them. * - * @return JsonResponse * @throws FireflyException */ public function index(): JsonResponse @@ -63,7 +62,7 @@ class PreferencesController extends Controller // make paginator: $paginator = new LengthAwarePaginator($preferences, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.preferences.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.preferences.index').$this->buildParams()); /** @var PreferenceTransformer $transformer */ $transformer = app(PreferenceTransformer::class); @@ -80,14 +79,11 @@ class PreferencesController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/preferences/getPreference * * Return a single preference by name. - * - * @param Preference $preference - * - * @return JsonResponse */ public function show(Preference $preference): JsonResponse { $manager = $this->getManager(); + /** @var PreferenceTransformer $transformer */ $transformer = app(PreferenceTransformer::class); $transformer->setParameters($this->parameters); @@ -101,9 +97,6 @@ class PreferencesController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/preferences/storePreference * - * @param PreferenceStoreRequest $request - * - * @return JsonResponse * @throws FireflyException */ public function store(PreferenceStoreRequest $request): JsonResponse @@ -125,10 +118,6 @@ class PreferencesController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/preferences/updatePreference * - * @param PreferenceUpdateRequest $request - * @param Preference $preference - * - * @return JsonResponse * @throws FireflyException */ public function update(PreferenceUpdateRequest $request, Preference $preference): JsonResponse diff --git a/app/Api/V1/Controllers/Webhook/AttemptController.php b/app/Api/V1/Controllers/Webhook/AttemptController.php index 8288e41856..70507a3f87 100644 --- a/app/Api/V1/Controllers/Webhook/AttemptController.php +++ b/app/Api/V1/Controllers/Webhook/AttemptController.php @@ -44,8 +44,6 @@ class AttemptController extends Controller public const string RESOURCE_KEY = 'webhook_attempts'; private WebhookRepositoryInterface $repository; - /** - */ public function __construct() { parent::__construct(); @@ -63,10 +61,6 @@ class AttemptController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/webhooks/getWebhookMessageAttempts * - * @param Webhook $webhook - * @param WebhookMessage $message - * - * @return JsonResponse * @throws FireflyException */ public function index(Webhook $webhook, WebhookMessage $message): JsonResponse @@ -83,7 +77,7 @@ class AttemptController extends Controller // 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()); + $paginator->setPath(route('api.v1.webhooks.attempts.index', [$webhook->id, $message->id]).$this->buildParams()); /** @var WebhookAttemptTransformer $transformer */ $transformer = app(WebhookAttemptTransformer::class); @@ -101,11 +95,6 @@ class AttemptController extends Controller * * 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 diff --git a/app/Api/V1/Controllers/Webhook/DestroyController.php b/app/Api/V1/Controllers/Webhook/DestroyController.php index da914b57d4..41c4d01d92 100644 --- a/app/Api/V1/Controllers/Webhook/DestroyController.php +++ b/app/Api/V1/Controllers/Webhook/DestroyController.php @@ -39,8 +39,6 @@ class DestroyController extends Controller { private WebhookRepositoryInterface $repository; - /** - */ public function __construct() { parent::__construct(); @@ -59,10 +57,6 @@ class DestroyController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/webhooks/deleteWebhook * * Remove the specified resource from storage. - * - * @param Webhook $webhook - * - * @return JsonResponse */ public function destroy(Webhook $webhook): JsonResponse { @@ -78,11 +72,6 @@ class DestroyController extends Controller * * Remove the specified resource from storage. * - * @param Webhook $webhook - * @param WebhookMessage $message - * @param WebhookAttempt $attempt - * - * @return JsonResponse * @throws FireflyException */ public function destroyAttempt(Webhook $webhook, WebhookMessage $message, WebhookAttempt $attempt): JsonResponse @@ -106,10 +95,6 @@ class DestroyController extends Controller * * Remove the specified resource from storage. * - * @param Webhook $webhook - * @param WebhookMessage $message - * - * @return JsonResponse * @throws FireflyException */ public function destroyMessage(Webhook $webhook, WebhookMessage $message): JsonResponse diff --git a/app/Api/V1/Controllers/Webhook/MessageController.php b/app/Api/V1/Controllers/Webhook/MessageController.php index 08baafd97d..81ad20e22a 100644 --- a/app/Api/V1/Controllers/Webhook/MessageController.php +++ b/app/Api/V1/Controllers/Webhook/MessageController.php @@ -43,8 +43,6 @@ class MessageController extends Controller public const string RESOURCE_KEY = 'webhook_messages'; private WebhookRepositoryInterface $repository; - /** - */ public function __construct() { parent::__construct(); @@ -62,9 +60,6 @@ class MessageController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/webhooks/getWebhookMessages * - * @param Webhook $webhook - * - * @return JsonResponse * @throws FireflyException */ public function index(Webhook $webhook): JsonResponse @@ -78,7 +73,7 @@ class MessageController extends Controller // make paginator: $paginator = new LengthAwarePaginator($messages, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.webhooks.messages.index', [$webhook->id]) . $this->buildParams()); + $paginator->setPath(route('api.v1.webhooks.messages.index', [$webhook->id]).$this->buildParams()); /** @var WebhookMessageTransformer $transformer */ $transformer = app(WebhookMessageTransformer::class); @@ -96,10 +91,6 @@ class MessageController extends Controller * * Show single instance. * - * @param Webhook $webhook - * @param WebhookMessage $message - * - * @return JsonResponse * @throws FireflyException */ public function show(Webhook $webhook, WebhookMessage $message): JsonResponse diff --git a/app/Api/V1/Controllers/Webhook/ShowController.php b/app/Api/V1/Controllers/Webhook/ShowController.php index fbfa4bf411..283ede40ee 100644 --- a/app/Api/V1/Controllers/Webhook/ShowController.php +++ b/app/Api/V1/Controllers/Webhook/ShowController.php @@ -46,8 +46,6 @@ class ShowController extends Controller public const string RESOURCE_KEY = 'webhooks'; private WebhookRepositoryInterface $repository; - /** - */ public function __construct() { parent::__construct(); @@ -67,7 +65,6 @@ class ShowController extends Controller * * Display a listing of the webhooks of the user. * - * @return JsonResponse * @throws FireflyException */ public function index(): JsonResponse @@ -80,7 +77,7 @@ class ShowController extends Controller // make paginator: $paginator = new LengthAwarePaginator($webhooks, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.webhooks.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.webhooks.index').$this->buildParams()); /** @var WebhookTransformer $transformer */ $transformer = app(WebhookTransformer::class); @@ -97,10 +94,6 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/webhooks/getWebhook * * Show single instance. - * - * @param Webhook $webhook - * - * @return JsonResponse */ public function show(Webhook $webhook): JsonResponse { @@ -119,15 +112,11 @@ class ShowController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/webhooks/triggerWebhookTransaction * * This method recycles part of the code of the StoredGroupEventHandler. - * - * @param Webhook $webhook - * @param TransactionGroup $group - * - * @return JsonResponse */ public function triggerTransaction(Webhook $webhook, TransactionGroup $group): JsonResponse { app('log')->debug(sprintf('Now in triggerTransaction(%d, %d)', $webhook->id, $group->id)); + /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); $engine->setUser(auth()->user()); diff --git a/app/Api/V1/Controllers/Webhook/StoreController.php b/app/Api/V1/Controllers/Webhook/StoreController.php index 5458060b03..9e0431a180 100644 --- a/app/Api/V1/Controllers/Webhook/StoreController.php +++ b/app/Api/V1/Controllers/Webhook/StoreController.php @@ -38,8 +38,6 @@ class StoreController extends Controller public const string RESOURCE_KEY = 'webhooks'; private WebhookRepositoryInterface $repository; - /** - */ public function __construct() { parent::__construct(); @@ -56,16 +54,13 @@ class StoreController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/webhooks/storeWebhook - * - * @param CreateRequest $request - * - * @return JsonResponse */ public function store(CreateRequest $request): JsonResponse { $data = $request->getData(); $webhook = $this->repository->store($data); $manager = $this->getManager(); + /** @var WebhookTransformer $transformer */ $transformer = app(WebhookTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Webhook/SubmitController.php b/app/Api/V1/Controllers/Webhook/SubmitController.php index bf325fdbb4..71dd2de278 100644 --- a/app/Api/V1/Controllers/Webhook/SubmitController.php +++ b/app/Api/V1/Controllers/Webhook/SubmitController.php @@ -36,8 +36,6 @@ class SubmitController extends Controller { private WebhookRepositoryInterface $repository; - /** - */ public function __construct() { parent::__construct(); @@ -54,10 +52,6 @@ class SubmitController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/webhooks/submitWebook - * - * @param Webhook $webhook - * - * @return JsonResponse */ public function submit(Webhook $webhook): JsonResponse { diff --git a/app/Api/V1/Controllers/Webhook/UpdateController.php b/app/Api/V1/Controllers/Webhook/UpdateController.php index 658412801b..599c9b3567 100644 --- a/app/Api/V1/Controllers/Webhook/UpdateController.php +++ b/app/Api/V1/Controllers/Webhook/UpdateController.php @@ -38,8 +38,6 @@ class UpdateController extends Controller { private WebhookRepositoryInterface $repository; - /** - */ public function __construct() { parent::__construct(); @@ -56,17 +54,13 @@ class UpdateController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/webhooks/updateWebhook - * - * @param Webhook $webhook - * @param UpdateRequest $request - * - * @return JsonResponse */ public function update(Webhook $webhook, UpdateRequest $request): JsonResponse { $data = $request->getData(); $webhook = $this->repository->update($webhook, $data); $manager = $this->getManager(); + /** @var WebhookTransformer $transformer */ $transformer = app(WebhookTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Middleware/ApiDemoUser.php b/app/Api/V1/Middleware/ApiDemoUser.php index 02553d67f0..e031bfa039 100644 --- a/app/Api/V1/Middleware/ApiDemoUser.php +++ b/app/Api/V1/Middleware/ApiDemoUser.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Middleware; -use Closure; use FireflyIII\User; use Illuminate\Http\Request; @@ -35,14 +34,11 @@ class ApiDemoUser /** * Handle an incoming request. * - * @param Request $request - * @param Closure $next - * * @return mixed */ - public function handle(Request $request, Closure $next) + public function handle(Request $request, \Closure $next) { - /** @var User|null $user */ + /** @var null|User $user */ $user = $request->user(); if (null === $user) { diff --git a/app/Api/V1/Requests/Autocomplete/AutocompleteRequest.php b/app/Api/V1/Requests/Autocomplete/AutocompleteRequest.php index c1d6069f15..39bafac82c 100644 --- a/app/Api/V1/Requests/Autocomplete/AutocompleteRequest.php +++ b/app/Api/V1/Requests/Autocomplete/AutocompleteRequest.php @@ -36,9 +36,6 @@ class AutocompleteRequest extends FormRequest use ChecksLogin; use ConvertsDataTypes; - /** - * @return array - */ public function getData(): array { $types = $this->convertString('types'); @@ -57,9 +54,6 @@ class AutocompleteRequest extends FormRequest ]; } - /** - * @return array - */ public function rules(): array { return [ diff --git a/app/Api/V1/Requests/Data/Bulk/MoveTransactionsRequest.php b/app/Api/V1/Requests/Data/Bulk/MoveTransactionsRequest.php index 59df3d2678..bcd0cb019c 100644 --- a/app/Api/V1/Requests/Data/Bulk/MoveTransactionsRequest.php +++ b/app/Api/V1/Requests/Data/Bulk/MoveTransactionsRequest.php @@ -38,9 +38,6 @@ class MoveTransactionsRequest extends FormRequest use ChecksLogin; use ConvertsDataTypes; - /** - * @return array - */ public function getAll(): array { return [ @@ -63,15 +60,13 @@ class MoveTransactionsRequest extends FormRequest /** * Configure the validator instance with special rules for after the basic validation rules. * - * @param Validator $validator - * TODO this is duplicate. - * - * @return void + * @param validator $validator + * TODO this is duplicate */ public function withValidator(Validator $validator): void { $validator->after( - function (Validator $validator) { + function (Validator $validator): void { // validate start before end only if both are there. $data = $validator->getData(); if (array_key_exists('original_account', $data) && array_key_exists('destination_account', $data)) { @@ -81,11 +76,6 @@ class MoveTransactionsRequest extends FormRequest ); } - /** - * @param Validator $validator - * - * @return void - */ private function validateMove(Validator $validator): void { $data = $validator->getData(); diff --git a/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php b/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php index 5ce3a5502a..0ef3692397 100644 --- a/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php +++ b/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php @@ -31,7 +31,6 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\Api\Data\Bulk\ValidatesBulkTransactionQuery; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Validator; -use JsonException; /** * Class TransactionRequest @@ -42,17 +41,15 @@ class TransactionRequest extends FormRequest use ConvertsDataTypes; use ValidatesBulkTransactionQuery; - /** - * @return array - */ public function getAll(): array { $data = []; + try { $data = [ 'query' => json_decode($this->get('query'), true, 8, JSON_THROW_ON_ERROR), ]; - } catch (JsonException $e) { + } catch (\JsonException $e) { // dont really care. the validation should catch invalid json. app('log')->error($e->getMessage()); } @@ -60,9 +57,6 @@ class TransactionRequest extends FormRequest return $data; } - /** - * @return array - */ public function rules(): array { return [ @@ -70,15 +64,10 @@ class TransactionRequest extends FormRequest ]; } - /** - * @param Validator $validator - * - * @return void - */ public function withValidator(Validator $validator): void { $validator->after( - function (Validator $validator) { + function (Validator $validator): void { // validate transaction query data. $this->validateTransactionQuery($validator); } diff --git a/app/Api/V1/Requests/Data/DateRequest.php b/app/Api/V1/Requests/Data/DateRequest.php index 882412a46b..0a5aab91f9 100644 --- a/app/Api/V1/Requests/Data/DateRequest.php +++ b/app/Api/V1/Requests/Data/DateRequest.php @@ -40,8 +40,6 @@ class DateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -53,8 +51,6 @@ class DateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/Data/DestroyRequest.php b/app/Api/V1/Requests/Data/DestroyRequest.php index 3a9ca8d1d9..5296652d51 100644 --- a/app/Api/V1/Requests/Data/DestroyRequest.php +++ b/app/Api/V1/Requests/Data/DestroyRequest.php @@ -37,8 +37,6 @@ class DestroyRequest extends FormRequest /** * Get all data from the request. - * - * @return string */ public function getObjects(): string { @@ -47,13 +45,11 @@ class DestroyRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { - $valid = 'budgets,bills,piggy_banks,rules,recurring,categories,tags,object_groups' . - ',accounts,asset_accounts,expense_accounts,revenue_accounts,liabilities,transactions,withdrawals,deposits,transfers' . + $valid = 'budgets,bills,piggy_banks,rules,recurring,categories,tags,object_groups'. + ',accounts,asset_accounts,expense_accounts,revenue_accounts,liabilities,transactions,withdrawals,deposits,transfers'. ',not_assets_liabilities'; return [ diff --git a/app/Api/V1/Requests/Data/Export/ExportRequest.php b/app/Api/V1/Requests/Data/Export/ExportRequest.php index bb74c60cdb..8016e94209 100644 --- a/app/Api/V1/Requests/Data/Export/ExportRequest.php +++ b/app/Api/V1/Requests/Data/Export/ExportRequest.php @@ -38,9 +38,6 @@ class ExportRequest extends FormRequest use ChecksLogin; use ConvertsDataTypes; - /** - * @return array - */ public function getAll(): array { $result = [ @@ -69,8 +66,6 @@ class ExportRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/Data/SameDateRequest.php b/app/Api/V1/Requests/Data/SameDateRequest.php index 19fe7829a6..9f419653f7 100644 --- a/app/Api/V1/Requests/Data/SameDateRequest.php +++ b/app/Api/V1/Requests/Data/SameDateRequest.php @@ -40,8 +40,6 @@ class SameDateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -53,8 +51,6 @@ class SameDateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/Insight/GenericRequest.php b/app/Api/V1/Requests/Insight/GenericRequest.php index 82624ba7df..e822c2143c 100644 --- a/app/Api/V1/Requests/Insight/GenericRequest.php +++ b/app/Api/V1/Requests/Insight/GenericRequest.php @@ -53,8 +53,6 @@ class GenericRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -64,13 +62,11 @@ class GenericRequest extends FormRequest ]; } - /** - * @return Collection - */ public function getAssetAccounts(): Collection { $this->parseAccounts(); $return = new Collection(); + /** @var Account $account */ foreach ($this->accounts as $account) { $type = $account->accountType->type; @@ -82,9 +78,100 @@ class GenericRequest extends FormRequest return $return; } + public function getBills(): Collection + { + $this->parseBills(); + + return $this->bills; + } + + public function getBudgets(): Collection + { + $this->parseBudgets(); + + return $this->budgets; + } + + public function getCategories(): Collection + { + $this->parseCategories(); + + return $this->categories; + } + + public function getEnd(): Carbon + { + $date = $this->getCarbonDate('end'); + $date->endOfDay(); + + return $date; + } + + public function getExpenseAccounts(): Collection + { + $this->parseAccounts(); + $return = new Collection(); + + /** @var Account $account */ + foreach ($this->accounts as $account) { + $type = $account->accountType->type; + if (AccountType::EXPENSE === $type) { + $return->push($account); + } + } + + return $return; + } + + public function getRevenueAccounts(): Collection + { + $this->parseAccounts(); + $return = new Collection(); + + /** @var Account $account */ + foreach ($this->accounts as $account) { + $type = $account->accountType->type; + if (AccountType::REVENUE === $type) { + $return->push($account); + } + } + + return $return; + } + + public function getStart(): Carbon + { + $date = $this->getCarbonDate('start'); + $date->startOfDay(); + + return $date; + } + + public function getTags(): Collection + { + $this->parseTags(); + + return $this->tags; + } + /** - * + * The rules that the incoming request must be matched against. */ + public function rules(): array + { + // this is cheating, but it works to initialize the collections. + $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_or_equal:start', + ]; + } + private function parseAccounts(): void { if (0 !== $this->accounts->count()) { @@ -104,19 +191,6 @@ class GenericRequest extends FormRequest } } - /** - * @return Collection - */ - public function getBills(): Collection - { - $this->parseBills(); - - return $this->bills; - } - - /** - * - */ private function parseBills(): void { if (0 !== $this->bills->count()) { @@ -136,19 +210,6 @@ class GenericRequest extends FormRequest } } - /** - * @return Collection - */ - public function getBudgets(): Collection - { - $this->parseBudgets(); - - return $this->budgets; - } - - /** - * - */ private function parseBudgets(): void { if (0 !== $this->budgets->count()) { @@ -168,19 +229,6 @@ class GenericRequest extends FormRequest } } - /** - * @return Collection - */ - public function getCategories(): Collection - { - $this->parseCategories(); - - return $this->categories; - } - - /** - * - */ private function parseCategories(): void { if (0 !== $this->categories->count()) { @@ -200,77 +248,6 @@ class GenericRequest extends FormRequest } } - /** - * @return Carbon - */ - public function getEnd(): Carbon - { - $date = $this->getCarbonDate('end'); - $date->endOfDay(); - - return $date; - } - - /** - * @return Collection - */ - public function getExpenseAccounts(): Collection - { - $this->parseAccounts(); - $return = new Collection(); - /** @var Account $account */ - foreach ($this->accounts as $account) { - $type = $account->accountType->type; - if ($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 ($type === AccountType::REVENUE) { - $return->push($account); - } - } - - return $return; - } - - /** - * @return Carbon - */ - public function getStart(): Carbon - { - $date = $this->getCarbonDate('start'); - $date->startOfDay(); - - return $date; - } - - /** - * @return Collection - */ - public function getTags(): Collection - { - $this->parseTags(); - - return $this->tags; - } - - /** - * - */ private function parseTags(): void { if (0 !== $this->tags->count()) { @@ -289,24 +266,4 @@ class GenericRequest extends FormRequest } } } - - /** - * The rules that the incoming request must be matched against. - * - * @return array - */ - public function rules(): array - { - // this is cheating, but it works to initialize the collections. - $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_or_equal:start', - ]; - } } diff --git a/app/Api/V1/Requests/Models/Account/StoreRequest.php b/app/Api/V1/Requests/Models/Account/StoreRequest.php index 3f42208f1b..64de772905 100644 --- a/app/Api/V1/Requests/Models/Account/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Account/StoreRequest.php @@ -35,8 +35,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class StoreRequest - * - */ class StoreRequest extends FormRequest { @@ -44,9 +42,6 @@ class StoreRequest extends FormRequest use ChecksLogin; use ConvertsDataTypes; - /** - * @return array - */ public function getAllAccountData(): array { $active = true; @@ -93,8 +88,6 @@ class StoreRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -104,7 +97,7 @@ class StoreRequest extends FormRequest $type = $this->convertString('type'); $rules = [ 'name' => 'required|max:1024|min:1|uniqueAccountForUser', - 'type' => 'required|max:1024|min:1|' . sprintf('in:%s', $types), + 'type' => 'required|max:1024|min:1|'.sprintf('in:%s', $types), 'iban' => ['iban', 'nullable', new UniqueIban(null, $type)], 'bic' => 'bic|nullable', 'account_number' => ['between:1,255', 'nullable', new UniqueAccountNumber(null, $type)], diff --git a/app/Api/V1/Requests/Models/Account/UpdateRequest.php b/app/Api/V1/Requests/Models/Account/UpdateRequest.php index 9428fa545a..211c968944 100644 --- a/app/Api/V1/Requests/Models/Account/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Account/UpdateRequest.php @@ -36,8 +36,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class UpdateRequest - * - */ class UpdateRequest extends FormRequest { @@ -45,9 +43,6 @@ class UpdateRequest extends FormRequest use ChecksLogin; use ConvertsDataTypes; - /** - * @return array - */ public function getUpdateData(): array { $fields = [ @@ -82,8 +77,6 @@ class UpdateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -109,7 +102,7 @@ class UpdateRequest extends FormRequest 'include_net_worth' => [new IsBoolean()], 'account_role' => sprintf('in:%s|nullable|required_if:type,asset', $accountRoles), 'credit_card_type' => sprintf('in:%s|nullable|required_if:account_role,ccAsset', $ccPaymentTypes), - 'monthly_payment_date' => 'date' . '|nullable|required_if:account_role,ccAsset|required_if:credit_card_type,monthlyFull', + 'monthly_payment_date' => 'date|nullable|required_if:account_role,ccAsset|required_if:credit_card_type,monthlyFull', 'liability_type' => 'required_if:type,liability|in:loan,debt,mortgage', 'liability_direction' => 'required_if:type,liability|in:credit,debit', 'interest' => 'required_if:type,liability|between:0,100|numeric', diff --git a/app/Api/V1/Requests/Models/Attachment/StoreRequest.php b/app/Api/V1/Requests/Models/Attachment/StoreRequest.php index aca785df2f..f41a4657f1 100644 --- a/app/Api/V1/Requests/Models/Attachment/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Attachment/StoreRequest.php @@ -30,8 +30,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class StoreRequest - * - */ class StoreRequest extends FormRequest { @@ -40,8 +38,6 @@ class StoreRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -56,8 +52,6 @@ class StoreRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/Models/Attachment/UpdateRequest.php b/app/Api/V1/Requests/Models/Attachment/UpdateRequest.php index 98734b2713..db3adf119e 100644 --- a/app/Api/V1/Requests/Models/Attachment/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Attachment/UpdateRequest.php @@ -30,8 +30,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class UpdateRequest - * - */ class UpdateRequest extends FormRequest { @@ -40,8 +38,6 @@ class UpdateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -58,8 +54,6 @@ class UpdateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/Models/AvailableBudget/Request.php b/app/Api/V1/Requests/Models/AvailableBudget/Request.php index 637f1ca192..1bbee10c3a 100644 --- a/app/Api/V1/Requests/Models/AvailableBudget/Request.php +++ b/app/Api/V1/Requests/Models/AvailableBudget/Request.php @@ -31,8 +31,6 @@ use Illuminate\Validation\Validator; /** * Class Request - * - */ class Request extends FormRequest { @@ -41,8 +39,6 @@ class Request extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -60,8 +56,6 @@ class Request extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -76,15 +70,11 @@ class Request extends FormRequest /** * 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( - static function (Validator $validator) { + static function (Validator $validator): void { // validate start before end only if both are there. $data = $validator->getData(); if (array_key_exists('start', $data) && array_key_exists('end', $data)) { diff --git a/app/Api/V1/Requests/Models/Bill/StoreRequest.php b/app/Api/V1/Requests/Models/Bill/StoreRequest.php index 3dfbe6c012..41c0bf0c4a 100644 --- a/app/Api/V1/Requests/Models/Bill/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Bill/StoreRequest.php @@ -32,8 +32,6 @@ use Illuminate\Validation\Validator; /** * Class StoreRequest - * - */ class StoreRequest extends FormRequest { @@ -42,8 +40,6 @@ class StoreRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -71,8 +67,6 @@ class StoreRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -94,15 +88,11 @@ class StoreRequest extends FormRequest /** * Configure the validator instance. - * - * @param Validator $validator - * - * @return void */ public function withValidator(Validator $validator): void { $validator->after( - static function (Validator $validator) { + static function (Validator $validator): void { $data = $validator->getData(); $min = (string)($data['amount_min'] ?? '0'); $max = (string)($data['amount_max'] ?? '0'); diff --git a/app/Api/V1/Requests/Models/Bill/UpdateRequest.php b/app/Api/V1/Requests/Models/Bill/UpdateRequest.php index dc912d1197..fe7e1dc543 100644 --- a/app/Api/V1/Requests/Models/Bill/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Bill/UpdateRequest.php @@ -33,8 +33,6 @@ use Illuminate\Validation\Validator; /** * Class UpdateRequest - * - */ class UpdateRequest extends FormRequest { @@ -43,8 +41,6 @@ class UpdateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -71,8 +67,6 @@ class UpdateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -97,15 +91,11 @@ class UpdateRequest extends FormRequest /** * Configure the validator instance. - * - * @param Validator $validator - * - * @return void */ public function withValidator(Validator $validator): void { $validator->after( - static function (Validator $validator) { + static function (Validator $validator): void { $data = $validator->getData(); if (array_key_exists('amount_min', $data) && array_key_exists('amount_max', $data)) { $min = $data['amount_min'] ?? '0'; diff --git a/app/Api/V1/Requests/Models/Budget/StoreRequest.php b/app/Api/V1/Requests/Models/Budget/StoreRequest.php index a4e762411b..62911d258b 100644 --- a/app/Api/V1/Requests/Models/Budget/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Budget/StoreRequest.php @@ -32,8 +32,6 @@ use Illuminate\Validation\Validator; /** * Class StoreRequest - * - */ class StoreRequest extends FormRequest { @@ -43,8 +41,6 @@ class StoreRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -67,8 +63,6 @@ class StoreRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -87,15 +81,11 @@ class StoreRequest extends FormRequest /** * 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) { + function (Validator $validator): void { // validate all account info $this->validateAutoBudgetAmount($validator); } diff --git a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php index 4365619962..08c7680f17 100644 --- a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php @@ -33,8 +33,6 @@ use Illuminate\Validation\Validator; /** * Class UpdateRequest - * - */ class UpdateRequest extends FormRequest { @@ -44,8 +42,6 @@ class UpdateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -77,8 +73,6 @@ class UpdateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -99,15 +93,11 @@ class UpdateRequest extends FormRequest /** * 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) { + function (Validator $validator): void { // validate all account info $this->validateAutoBudgetAmount($validator); } diff --git a/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php b/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php index d3f9de820c..f1f593f30a 100644 --- a/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php +++ b/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php @@ -29,8 +29,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class StoreRequest - * - */ class StoreRequest extends FormRequest { @@ -39,8 +37,6 @@ class StoreRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -55,8 +51,6 @@ class StoreRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php b/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php index 81b4d3499a..875d5f3191 100644 --- a/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php @@ -31,8 +31,6 @@ use Illuminate\Validation\Validator; /** * Class UpdateRequest - * - */ class UpdateRequest extends FormRequest { @@ -41,8 +39,6 @@ class UpdateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -59,8 +55,6 @@ class UpdateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -77,14 +71,12 @@ class UpdateRequest extends FormRequest * Configure the validator instance with special rules for after the basic validation rules. * * @param Validator $validator - * TODO duplicate code - * - * @return void + * TODO duplicate code */ public function withValidator(Validator $validator): void { $validator->after( - static function (Validator $validator) { + static function (Validator $validator): void { // validate start before end only if both are there. $data = $validator->getData(); if (array_key_exists('start', $data) && array_key_exists('end', $data)) { diff --git a/app/Api/V1/Requests/Models/Category/StoreRequest.php b/app/Api/V1/Requests/Models/Category/StoreRequest.php index 4d06ada65b..c37695f265 100644 --- a/app/Api/V1/Requests/Models/Category/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Category/StoreRequest.php @@ -23,15 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Category; -use FireflyIII\Rules\ZeroOrMore; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; /** * Class StoreRequest - * - */ class StoreRequest extends FormRequest { @@ -40,8 +37,6 @@ class StoreRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -53,8 +48,6 @@ class StoreRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/Models/Category/UpdateRequest.php b/app/Api/V1/Requests/Models/Category/UpdateRequest.php index 43e1f4cc5f..7d26e95c11 100644 --- a/app/Api/V1/Requests/Models/Category/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Category/UpdateRequest.php @@ -30,8 +30,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class UpdateRequest - * - */ class UpdateRequest extends FormRequest { @@ -40,8 +38,6 @@ class UpdateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -55,8 +51,6 @@ class UpdateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/Models/ObjectGroup/UpdateRequest.php b/app/Api/V1/Requests/Models/ObjectGroup/UpdateRequest.php index afd86d9ca9..17b5a41e16 100644 --- a/app/Api/V1/Requests/Models/ObjectGroup/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/ObjectGroup/UpdateRequest.php @@ -31,17 +31,12 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class UpdateRequest - * - */ class UpdateRequest extends FormRequest { use ChecksLogin; use ConvertsDataTypes; - /** - * @return array - */ public function getUpdateData(): array { $fields = [ @@ -54,8 +49,6 @@ class UpdateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php b/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php index 237134bd50..35428faca7 100644 --- a/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php +++ b/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php @@ -29,8 +29,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class StoreRequest - * - */ class StoreRequest extends FormRequest { @@ -39,8 +37,6 @@ class StoreRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -63,8 +59,6 @@ class StoreRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/Models/PiggyBank/UpdateRequest.php b/app/Api/V1/Requests/Models/PiggyBank/UpdateRequest.php index f133190265..e8448bcdf5 100644 --- a/app/Api/V1/Requests/Models/PiggyBank/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/PiggyBank/UpdateRequest.php @@ -32,8 +32,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class UpdateRequest - * - */ class UpdateRequest extends FormRequest { @@ -42,8 +40,6 @@ class UpdateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -65,8 +61,6 @@ class UpdateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -74,7 +68,7 @@ class UpdateRequest extends FormRequest $piggyBank = $this->route()->parameter('piggyBank'); return [ - 'name' => 'between:1,255|uniquePiggyBankForUser:' . $piggyBank->id, + 'name' => 'between:1,255|uniquePiggyBankForUser:'.$piggyBank->id, 'current_amount' => ['numeric', 'gte:0', new LessThanPiggyTarget()], 'target_amount' => 'numeric|gte:0', 'start_date' => 'date|nullable', diff --git a/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php b/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php index 5cd4ea8071..4c4bf8a162 100644 --- a/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php @@ -48,8 +48,6 @@ class StoreRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -73,69 +71,8 @@ class StoreRequest extends FormRequest ]; } - /** - * 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 - */ - private function getTransactionData(): array - { - $return = []; - // transaction data: - /** @var array|null $transactions */ - $transactions = $this->get('transactions'); - if (null === $transactions) { - return []; - } - /** @var array $transaction */ - foreach ($transactions as $transaction) { - $return[] = $this->getSingleTransactionData($transaction); - } - - return $return; - } - - /** - * Returns the repetition data as it is found in the submitted data. - * - * @return array - */ - private function getRepetitionData(): array - { - $return = []; - // repetition data: - /** @var array|null $repetitions */ - $repetitions = $this->get('repetitions'); - if (null === $repetitions) { - return []; - } - /** @var array $repetition */ - foreach ($repetitions as $repetition) { - $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; - } - /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -179,15 +116,11 @@ class StoreRequest extends FormRequest /** * Configure the validator instance. - * - * @param Validator $validator - * - * @return void */ public function withValidator(Validator $validator): void { $validator->after( - function (Validator $validator) { + function (Validator $validator): void { $this->validateRecurringConfig($validator); $this->validateOneRecurrenceTransaction($validator); $this->validateOneRepetition($validator); @@ -198,4 +131,63 @@ class StoreRequest extends FormRequest } ); } + + /** + * 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. + */ + private function getTransactionData(): array + { + $return = []; + + // transaction data: + /** @var null|array $transactions */ + $transactions = $this->get('transactions'); + if (null === $transactions) { + return []; + } + + /** @var array $transaction */ + foreach ($transactions as $transaction) { + $return[] = $this->getSingleTransactionData($transaction); + } + + return $return; + } + + /** + * Returns the repetition data as it is found in the submitted data. + */ + private function getRepetitionData(): array + { + $return = []; + + // repetition data: + /** @var null|array $repetitions */ + $repetitions = $this->get('repetitions'); + if (null === $repetitions) { + return []; + } + + /** @var array $repetition */ + foreach ($repetitions as $repetition) { + $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; + } } diff --git a/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php b/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php index a8b8405d8b..d0cc79925c 100644 --- a/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php @@ -49,8 +49,6 @@ class UpdateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -78,74 +76,8 @@ class UpdateRequest extends FormRequest return $return; } - /** - * Returns the repetition data as it is found in the submitted data. - * - * @return array|null - */ - private function getRepetitionData(): ?array - { - $return = []; - // repetition data: - /** @var array|null $repetitions */ - $repetitions = $this->get('repetitions'); - if (null === $repetitions) { - return null; - } - /** @var array $repetition */ - foreach ($repetitions as $repetition) { - $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; - } - - /** - * 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 - */ - private function getTransactionData(): array - { - $return = []; - // transaction data: - /** @var array|null $transactions */ - $transactions = $this->get('transactions'); - if (null === $transactions) { - return []; - } - /** @var array $transaction */ - foreach ($transactions as $transaction) { - $return[] = $this->getSingleTransactionData($transaction); - } - - return $return; - } - /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -186,24 +118,18 @@ class UpdateRequest extends FormRequest 'transactions.*.piggy_bank_id' => ['nullable', 'numeric', 'mustExist:piggy_banks,id', new BelongsUser()], 'transactions.*.piggy_bank_name' => ['between:1,255', 'nullable', new BelongsUser()], 'transactions.*.tags' => 'nullable|between:1,64000', - ]; } /** * Configure the validator instance. - * - * @param Validator $validator - * - * @return void */ public function withValidator(Validator $validator): void { $validator->after( - function (Validator $validator) { - //$this->validateOneRecurrenceTransaction($validator); - //$this->validateOneRepetitionUpdate($validator); - + function (Validator $validator): void { + // $this->validateOneRecurrenceTransaction($validator); + // $this->validateOneRepetitionUpdate($validator); /** @var Recurrence $recurrence */ $recurrence = $this->route()->parameter('recurrence'); @@ -216,4 +142,67 @@ class UpdateRequest extends FormRequest ); } + /** + * Returns the repetition data as it is found in the submitted data. + */ + private function getRepetitionData(): ?array + { + $return = []; + + // repetition data: + /** @var null|array $repetitions */ + $repetitions = $this->get('repetitions'); + if (null === $repetitions) { + return null; + } + + /** @var array $repetition */ + foreach ($repetitions as $repetition) { + $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; + } + + /** + * 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. + */ + private function getTransactionData(): array + { + $return = []; + + // transaction data: + /** @var null|array $transactions */ + $transactions = $this->get('transactions'); + if (null === $transactions) { + return []; + } + + /** @var array $transaction */ + foreach ($transactions as $transaction) { + $return[] = $this->getSingleTransactionData($transaction); + } + + return $return; + } } diff --git a/app/Api/V1/Requests/Models/Rule/StoreRequest.php b/app/Api/V1/Requests/Models/Rule/StoreRequest.php index b9ae6ac673..bd74fc0ade 100644 --- a/app/Api/V1/Requests/Models/Rule/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Rule/StoreRequest.php @@ -41,8 +41,6 @@ class StoreRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -65,52 +63,8 @@ class StoreRequest extends FormRequest return $data; } - /** - * @return array - */ - private function getRuleTriggers(): array - { - $triggers = $this->get('triggers'); - $return = []; - if (is_array($triggers)) { - foreach ($triggers as $trigger) { - $return[] = [ - 'type' => $trigger['type'], - 'value' => $trigger['value'], - 'active' => $this->convertBoolean((string)($trigger['active'] ?? 'true')), - 'stop_processing' => $this->convertBoolean((string)($trigger['stop_processing'] ?? 'false')), - ]; - } - } - - return $return; - } - - /** - * @return array - */ - private function getRuleActions(): array - { - $actions = $this->get('actions'); - $return = []; - if (is_array($actions)) { - foreach ($actions as $action) { - $return[] = [ - 'type' => $action['type'], - 'value' => $action['value'], - 'active' => $this->convertBoolean((string)($action['active'] ?? 'true')), - 'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')), - ]; - } - } - - return $return; - } - /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -127,12 +81,12 @@ class StoreRequest extends FormRequest '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), - 'triggers.*.value' => 'required_if:actions.*.type,' . $contextTriggers . '|min:1|ruleTriggerValue|max:1024', + 'triggers.*.type' => 'required|in:'.implode(',', $validTriggers), + 'triggers.*.value' => 'required_if:actions.*.type,'.$contextTriggers.'|min:1|ruleTriggerValue|max:1024', 'triggers.*.stop_processing' => [new IsBoolean()], 'triggers.*.active' => [new IsBoolean()], - 'actions.*.type' => 'required|in:' . implode(',', $validActions), - 'actions.*.value' => 'required_if:actions.*.type,' . $contextActions . '|ruleActionValue', + 'actions.*.type' => 'required|in:'.implode(',', $validActions), + 'actions.*.value' => 'required_if:actions.*.type,'.$contextActions.'|ruleActionValue', 'actions.*.stop_processing' => [new IsBoolean()], 'actions.*.active' => [new IsBoolean()], 'strict' => [new IsBoolean()], @@ -143,15 +97,11 @@ class StoreRequest extends FormRequest /** * Configure the validator instance. - * - * @param Validator $validator - * - * @return void */ public function withValidator(Validator $validator): void { $validator->after( - function (Validator $validator) { + function (Validator $validator): void { $this->atLeastOneTrigger($validator); $this->atLeastOneAction($validator); $this->atLeastOneActiveTrigger($validator); @@ -162,8 +112,6 @@ class StoreRequest extends FormRequest /** * Adds an error to the validator when there are no triggers in the array of data. - * - * @param Validator $validator */ protected function atLeastOneTrigger(Validator $validator): void { @@ -177,8 +125,6 @@ class StoreRequest extends FormRequest /** * Adds an error to the validator when there are no repetitions in the array of data. - * - * @param Validator $validator */ protected function atLeastOneAction(Validator $validator): void { @@ -192,13 +138,12 @@ class StoreRequest extends FormRequest /** * Adds an error to the validator when there are no ACTIVE triggers in the array of data. - * - * @param Validator $validator */ protected function atLeastOneActiveTrigger(Validator $validator): void { $data = $validator->getData(); - /** @var string|int|array|null $triggers */ + + /** @var null|array|int|string $triggers */ $triggers = $data['triggers'] ?? []; // need at least one trigger if (!is_countable($triggers) || 0 === count($triggers)) { @@ -222,13 +167,12 @@ class StoreRequest extends FormRequest /** * Adds an error to the validator when there are no ACTIVE actions in the array of data. - * - * @param Validator $validator */ protected function atLeastOneActiveAction(Validator $validator): void { $data = $validator->getData(); - /** @var string|int|array|null $actions */ + + /** @var null|array|int|string $actions */ $actions = $data['actions'] ?? []; // need at least one trigger if (!is_countable($actions) || 0 === count($actions)) { @@ -249,4 +193,40 @@ class StoreRequest extends FormRequest $validator->errors()->add(sprintf('actions.%d.active', $inactiveIndex), (string)trans('validation.at_least_one_active_action')); } } + + private function getRuleTriggers(): array + { + $triggers = $this->get('triggers'); + $return = []; + if (is_array($triggers)) { + foreach ($triggers as $trigger) { + $return[] = [ + 'type' => $trigger['type'], + 'value' => $trigger['value'], + 'active' => $this->convertBoolean((string)($trigger['active'] ?? 'true')), + 'stop_processing' => $this->convertBoolean((string)($trigger['stop_processing'] ?? 'false')), + ]; + } + } + + return $return; + } + + private function getRuleActions(): array + { + $actions = $this->get('actions'); + $return = []; + if (is_array($actions)) { + foreach ($actions as $action) { + $return[] = [ + 'type' => $action['type'], + 'value' => $action['value'], + 'active' => $this->convertBoolean((string)($action['active'] ?? 'true')), + 'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')), + ]; + } + } + + return $return; + } } diff --git a/app/Api/V1/Requests/Models/Rule/TestRequest.php b/app/Api/V1/Requests/Models/Rule/TestRequest.php index 479941f43d..87598bfa01 100644 --- a/app/Api/V1/Requests/Models/Rule/TestRequest.php +++ b/app/Api/V1/Requests/Models/Rule/TestRequest.php @@ -37,9 +37,6 @@ class TestRequest extends FormRequest use ChecksLogin; use ConvertsDataTypes; - /** - * @return array - */ public function getTestParameters(): array { return [ @@ -47,23 +44,24 @@ class TestRequest extends FormRequest 'start' => $this->getDate('start'), 'end' => $this->getDate('end'), 'accounts' => $this->getAccounts(), - ]; } - /** - * @return int - */ + public function rules(): array + { + return [ + 'start' => 'date', + 'end' => 'date|after_or_equal:start', + 'accounts' => '', + 'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts', + ]; + } + private function getPage(): int { return 0 === (int)$this->query('page') ? 1 : (int)$this->query('page'); } - /** - * @param string $field - * - * @return Carbon|null - */ private function getDate(string $field): ?Carbon { $value = $this->query($field); @@ -75,27 +73,12 @@ class TestRequest extends FormRequest if (false === $result) { return null; } + return $result; } - /** - * @return array - */ private function getAccounts(): array { return $this->get('accounts'); } - - /** - * @return array - */ - public function rules(): array - { - return [ - 'start' => 'date', - 'end' => 'date|after_or_equal:start', - 'accounts' => '', - 'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts', - ]; - } } diff --git a/app/Api/V1/Requests/Models/Rule/TriggerRequest.php b/app/Api/V1/Requests/Models/Rule/TriggerRequest.php index 5786a4f9cc..3e60816fe6 100644 --- a/app/Api/V1/Requests/Models/Rule/TriggerRequest.php +++ b/app/Api/V1/Requests/Models/Rule/TriggerRequest.php @@ -37,9 +37,6 @@ class TriggerRequest extends FormRequest use ChecksLogin; use ConvertsDataTypes; - /** - * @return array - */ public function getTriggerParameters(): array { return [ @@ -49,11 +46,16 @@ class TriggerRequest extends FormRequest ]; } - /** - * @param string $field - * - * @return Carbon|null - */ + public function rules(): array + { + return [ + 'start' => 'date', + 'end' => 'date|after_or_equal:start', + 'accounts' => '', + 'accounts.*' => 'exists:accounts,id|belongsToUser:accounts', + ]; + } + private function getDate(string $field): ?Carbon { $value = $this->query($field); @@ -65,27 +67,12 @@ class TriggerRequest extends FormRequest if (false === $result) { return null; } + return $result; } - /** - * @return array - */ private function getAccounts(): array { return $this->get('accounts') ?? []; } - - /** - * @return array - */ - public function rules(): array - { - return [ - 'start' => 'date', - 'end' => 'date|after_or_equal:start', - 'accounts' => '', - 'accounts.*' => 'exists:accounts,id|belongsToUser:accounts', - ]; - } } diff --git a/app/Api/V1/Requests/Models/Rule/UpdateRequest.php b/app/Api/V1/Requests/Models/Rule/UpdateRequest.php index e99b1ebca0..4b033dc014 100644 --- a/app/Api/V1/Requests/Models/Rule/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Rule/UpdateRequest.php @@ -42,8 +42,6 @@ class UpdateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -71,60 +69,8 @@ class UpdateRequest extends FormRequest return $return; } - /** - * @return array|null - */ - private function getRuleTriggers(): ?array - { - if (!$this->has('triggers')) { - return null; - } - $triggers = $this->get('triggers'); - $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' => $active, - 'stop_processing' => $stopProcessing, - ]; - } - } - - return $return; - } - - /** - * @return array|null - */ - private function getRuleActions(): ?array - { - if (!$this->has('actions')) { - return null; - } - $actions = $this->get('actions'); - $return = []; - if (is_array($actions)) { - foreach ($actions as $action) { - $return[] = [ - 'type' => $action['type'], - 'value' => $action['value'], - 'active' => $this->convertBoolean((string)($action['active'] ?? 'false')), - 'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')), - ]; - } - } - - return $return; - } - /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -144,12 +90,12 @@ class UpdateRequest extends FormRequest 'rule_group_id' => 'belongsToUser:rule_groups', 'rule_group_title' => 'nullable|between:1,255|belongsToUser:rule_groups,title', 'trigger' => 'in:store-journal,update-journal', - 'triggers.*.type' => 'required|in:' . implode(',', $validTriggers), - 'triggers.*.value' => 'required_if:actions.*.type,' . $contextTriggers . '|min:1|ruleTriggerValue|max:1024', + 'triggers.*.type' => 'required|in:'.implode(',', $validTriggers), + 'triggers.*.value' => 'required_if:actions.*.type,'.$contextTriggers.'|min:1|ruleTriggerValue|max:1024', 'triggers.*.stop_processing' => [new IsBoolean()], 'triggers.*.active' => [new IsBoolean()], - 'actions.*.type' => 'required|in:' . implode(',', $validActions), - 'actions.*.value' => 'required_if:actions.*.type,' . $contextActions . '|ruleActionValue', + 'actions.*.type' => 'required|in:'.implode(',', $validActions), + 'actions.*.value' => 'required_if:actions.*.type,'.$contextActions.'|ruleActionValue', 'actions.*.stop_processing' => [new IsBoolean()], 'actions.*.active' => [new IsBoolean()], 'strict' => [new IsBoolean()], @@ -161,15 +107,11 @@ class UpdateRequest extends FormRequest /** * Configure the validator instance. - * - * @param Validator $validator - * - * @return void */ public function withValidator(Validator $validator): void { $validator->after( - function (Validator $validator) { + function (Validator $validator): void { $this->atLeastOneTrigger($validator); $this->atLeastOneValidTrigger($validator); $this->atLeastOneAction($validator); @@ -180,8 +122,6 @@ class UpdateRequest extends FormRequest /** * Adds an error to the validator when there are no repetitions in the array of data. - * - * @param Validator $validator */ protected function atLeastOneTrigger(Validator $validator): void { @@ -195,8 +135,6 @@ class UpdateRequest extends FormRequest /** * Adds an error to the validator when there are no repetitions in the array of data. - * - * @param Validator $validator */ protected function atLeastOneValidTrigger(Validator $validator): void { @@ -224,8 +162,6 @@ class UpdateRequest extends FormRequest /** * Adds an error to the validator when there are no repetitions in the array of data. - * - * @param Validator $validator */ protected function atLeastOneAction(Validator $validator): void { @@ -239,8 +175,6 @@ class UpdateRequest extends FormRequest /** * Adds an error to the validator when there are no repetitions in the array of data. - * - * @param Validator $validator */ protected function atLeastOneValidAction(Validator $validator): void { @@ -266,4 +200,48 @@ class UpdateRequest extends FormRequest $validator->errors()->add(sprintf('actions.%d.active', $inactiveIndex), (string)trans('validation.at_least_one_active_action')); } } + + private function getRuleTriggers(): ?array + { + if (!$this->has('triggers')) { + return null; + } + $triggers = $this->get('triggers'); + $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' => $active, + 'stop_processing' => $stopProcessing, + ]; + } + } + + return $return; + } + + private function getRuleActions(): ?array + { + if (!$this->has('actions')) { + return null; + } + $actions = $this->get('actions'); + $return = []; + if (is_array($actions)) { + foreach ($actions as $action) { + $return[] = [ + 'type' => $action['type'], + 'value' => $action['value'], + 'active' => $this->convertBoolean((string)($action['active'] ?? 'false')), + 'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')), + ]; + } + } + + return $return; + } } diff --git a/app/Api/V1/Requests/Models/RuleGroup/StoreRequest.php b/app/Api/V1/Requests/Models/RuleGroup/StoreRequest.php index afbf3a1c83..4adf20d099 100644 --- a/app/Api/V1/Requests/Models/RuleGroup/StoreRequest.php +++ b/app/Api/V1/Requests/Models/RuleGroup/StoreRequest.php @@ -38,8 +38,6 @@ class StoreRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -62,8 +60,6 @@ class StoreRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/Models/RuleGroup/TestRequest.php b/app/Api/V1/Requests/Models/RuleGroup/TestRequest.php index 65e1c4c352..31a48da49f 100644 --- a/app/Api/V1/Requests/Models/RuleGroup/TestRequest.php +++ b/app/Api/V1/Requests/Models/RuleGroup/TestRequest.php @@ -37,9 +37,6 @@ class TestRequest extends FormRequest use ChecksLogin; use ConvertsDataTypes; - /** - * @return array - */ public function getTestParameters(): array { return [ @@ -49,11 +46,16 @@ class TestRequest extends FormRequest ]; } - /** - * @param string $field - * - * @return Carbon|null - */ + public function rules(): array + { + return [ + 'start' => 'date', + 'end' => 'date|after_or_equal:start', + 'accounts' => '', + 'accounts.*' => 'exists:accounts,id|belongsToUser:accounts', + ]; + } + private function getDate(string $field): ?Carbon { $value = $this->query($field); @@ -65,27 +67,12 @@ class TestRequest extends FormRequest if (false === $result) { return null; } + return $result; } - /** - * @return array - */ private function getAccounts(): array { return $this->get('accounts'); } - - /** - * @return array - */ - public function rules(): array - { - return [ - 'start' => 'date', - 'end' => 'date|after_or_equal:start', - 'accounts' => '', - 'accounts.*' => 'exists:accounts,id|belongsToUser:accounts', - ]; - } } diff --git a/app/Api/V1/Requests/Models/RuleGroup/TriggerRequest.php b/app/Api/V1/Requests/Models/RuleGroup/TriggerRequest.php index 671fc4f12c..830e2145ca 100644 --- a/app/Api/V1/Requests/Models/RuleGroup/TriggerRequest.php +++ b/app/Api/V1/Requests/Models/RuleGroup/TriggerRequest.php @@ -37,9 +37,6 @@ class TriggerRequest extends FormRequest use ChecksLogin; use ConvertsDataTypes; - /** - * @return array - */ public function getTriggerParameters(): array { return [ @@ -49,11 +46,14 @@ class TriggerRequest extends FormRequest ]; } - /** - * @param string $field - * - * @return Carbon|null - */ + public function rules(): array + { + return [ + 'start' => 'date', + 'end' => 'date|after_or_equal:start', + ]; + } + private function getDate(string $field): ?Carbon { $value = $this->query($field); @@ -65,25 +65,12 @@ class TriggerRequest extends FormRequest if (false === $result) { return null; } + return $result; } - /** - * @return array - */ private function getAccounts(): array { return $this->get('accounts'); } - - /** - * @return array - */ - public function rules(): array - { - return [ - 'start' => 'date', - 'end' => 'date|after_or_equal:start', - ]; - } } diff --git a/app/Api/V1/Requests/Models/RuleGroup/UpdateRequest.php b/app/Api/V1/Requests/Models/RuleGroup/UpdateRequest.php index 1989cdaaf5..8d5b6afa65 100644 --- a/app/Api/V1/Requests/Models/RuleGroup/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/RuleGroup/UpdateRequest.php @@ -39,8 +39,6 @@ class UpdateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -57,8 +55,6 @@ class UpdateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -66,7 +62,7 @@ class UpdateRequest extends FormRequest $ruleGroup = $this->route()->parameter('ruleGroup'); return [ - 'title' => '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/Models/Tag/StoreRequest.php b/app/Api/V1/Requests/Models/Tag/StoreRequest.php index a68ca04c61..3b8085493f 100644 --- a/app/Api/V1/Requests/Models/Tag/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Tag/StoreRequest.php @@ -31,8 +31,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class StoreRequest - * - */ class StoreRequest extends FormRequest { @@ -42,8 +40,6 @@ class StoreRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -59,8 +55,6 @@ class StoreRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/Models/Tag/UpdateRequest.php b/app/Api/V1/Requests/Models/Tag/UpdateRequest.php index 52b64662ed..62c011bc08 100644 --- a/app/Api/V1/Requests/Models/Tag/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Tag/UpdateRequest.php @@ -33,8 +33,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class UpdateRequest - * - */ class UpdateRequest extends FormRequest { @@ -44,8 +42,6 @@ class UpdateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -62,8 +58,6 @@ class UpdateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -71,7 +65,7 @@ class UpdateRequest extends FormRequest $tag = $this->route()->parameter('tagOrId'); // TODO check if uniqueObjectForUser is obsolete $rules = [ - 'tag' => 'min:1|max:1024|uniqueObjectForUser:tags,tag,' . $tag->id, + 'tag' => 'min:1|max:1024|uniqueObjectForUser:tags,tag,'.$tag->id, 'description' => 'min:1|nullable|max:65536', 'date' => 'date|nullable', ]; diff --git a/app/Api/V1/Requests/Models/Transaction/StoreRequest.php b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php index b060432b5f..1e7143bc44 100644 --- a/app/Api/V1/Requests/Models/Transaction/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php @@ -51,8 +51,6 @@ class StoreRequest extends FormRequest /** * Get all data. Is pretty complex because of all the ??-statements. - * - * @return array */ public function getAll(): array { @@ -68,112 +66,14 @@ class StoreRequest extends FormRequest // TODO include location and ability to process it. } - /** - * Get transaction data. - * - * @return array - */ - private function getTransactionData(): array - { - $return = []; - /** - * @var array $transaction - */ - foreach ($this->get('transactions') as $transaction) { - $object = new NullArrayObject($transaction); - $return[] = [ - 'type' => $this->clearString($object['type']), - 'date' => $this->dateFromValue($object['date']), - 'order' => $this->integerFromValue((string)$object['order']), - - 'currency_id' => $this->integerFromValue((string)$object['currency_id']), - 'currency_code' => $this->clearString((string)$object['currency_code']), - - // foreign currency info: - 'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']), - 'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code']), - - // amount and foreign amount. Cannot be 0. - 'amount' => $this->clearString((string)$object['amount']), - 'foreign_amount' => $this->clearString((string)$object['foreign_amount']), - - // description. - 'description' => $this->clearString($object['description']), - - // source of transaction. If everything is null, assume cash account. - 'source_id' => $this->integerFromValue((string)$object['source_id']), - 'source_name' => $this->clearString((string)$object['source_name']), - 'source_iban' => $this->clearString((string)$object['source_iban']), - 'source_number' => $this->clearString((string)$object['source_number']), - 'source_bic' => $this->clearString((string)$object['source_bic']), - - // destination of transaction. If everything is null, assume cash account. - 'destination_id' => $this->integerFromValue((string)$object['destination_id']), - 'destination_name' => $this->clearString((string)$object['destination_name']), - 'destination_iban' => $this->clearString((string)$object['destination_iban']), - 'destination_number' => $this->clearString((string)$object['destination_number']), - 'destination_bic' => $this->clearString((string)$object['destination_bic']), - - // budget info - 'budget_id' => $this->integerFromValue((string)$object['budget_id']), - 'budget_name' => $this->clearString((string)$object['budget_name']), - - // category info - 'category_id' => $this->integerFromValue((string)$object['category_id']), - 'category_name' => $this->clearString((string)$object['category_name']), - - // journal bill reference. Optional. Will only work for withdrawals - 'bill_id' => $this->integerFromValue((string)$object['bill_id']), - 'bill_name' => $this->clearString((string)$object['bill_name']), - - // piggy bank reference. Optional. Will only work for transfers - 'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']), - 'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name']), - - // some other interesting properties - 'reconciled' => $this->convertBoolean((string)$object['reconciled']), - 'notes' => $this->clearStringKeepNewlines((string)$object['notes']), - 'tags' => $this->arrayFromValue($object['tags']), - - // all custom fields: - 'internal_reference' => $this->clearString((string)$object['internal_reference']), - 'external_id' => $this->clearString((string)$object['external_id']), - 'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')), - 'recurrence_id' => $this->integerFromValue($object['recurrence_id']), - 'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id']), - 'external_url' => $this->clearString((string)$object['external_url']), - - 'sepa_cc' => $this->clearString((string)$object['sepa_cc']), - 'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op']), - 'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id']), - 'sepa_db' => $this->clearString((string)$object['sepa_db']), - 'sepa_country' => $this->clearString((string)$object['sepa_country']), - 'sepa_ep' => $this->clearString((string)$object['sepa_ep']), - 'sepa_ci' => $this->clearString((string)$object['sepa_ci']), - 'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id']), - // custom date fields. Must be Carbon objects. Presence is optional. - 'interest_date' => $this->dateFromValue($object['interest_date']), - 'book_date' => $this->dateFromValue($object['book_date']), - 'process_date' => $this->dateFromValue($object['process_date']), - 'due_date' => $this->dateFromValue($object['due_date']), - 'payment_date' => $this->dateFromValue($object['payment_date']), - 'invoice_date' => $this->dateFromValue($object['invoice_date']), - - ]; - } - - return $return; - } - /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { app('log')->debug('Collect rules of TransactionStoreRequest'); $validProtocols = config('firefly.valid_url_protocols'); + return [ // basic fields for group: 'group_title' => 'between:1,1000|nullable', @@ -256,15 +156,11 @@ class StoreRequest extends FormRequest /** * Configure the validator instance. - * - * @param Validator $validator - * - * @return void */ public function withValidator(Validator $validator): void { $validator->after( - function (Validator $validator) { + function (Validator $validator): void { // must be valid array. $this->validateTransactionArray($validator); @@ -293,4 +189,99 @@ class StoreRequest extends FormRequest } ); } + + /** + * Get transaction data. + */ + private function getTransactionData(): array + { + $return = []; + + /** + * @var array $transaction + */ + foreach ($this->get('transactions') as $transaction) { + $object = new NullArrayObject($transaction); + $return[] = [ + 'type' => $this->clearString($object['type']), + 'date' => $this->dateFromValue($object['date']), + 'order' => $this->integerFromValue((string)$object['order']), + + 'currency_id' => $this->integerFromValue((string)$object['currency_id']), + 'currency_code' => $this->clearString((string)$object['currency_code']), + + // foreign currency info: + 'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']), + 'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code']), + + // amount and foreign amount. Cannot be 0. + 'amount' => $this->clearString((string)$object['amount']), + 'foreign_amount' => $this->clearString((string)$object['foreign_amount']), + + // description. + 'description' => $this->clearString($object['description']), + + // source of transaction. If everything is null, assume cash account. + 'source_id' => $this->integerFromValue((string)$object['source_id']), + 'source_name' => $this->clearString((string)$object['source_name']), + 'source_iban' => $this->clearString((string)$object['source_iban']), + 'source_number' => $this->clearString((string)$object['source_number']), + 'source_bic' => $this->clearString((string)$object['source_bic']), + + // destination of transaction. If everything is null, assume cash account. + 'destination_id' => $this->integerFromValue((string)$object['destination_id']), + 'destination_name' => $this->clearString((string)$object['destination_name']), + 'destination_iban' => $this->clearString((string)$object['destination_iban']), + 'destination_number' => $this->clearString((string)$object['destination_number']), + 'destination_bic' => $this->clearString((string)$object['destination_bic']), + + // budget info + 'budget_id' => $this->integerFromValue((string)$object['budget_id']), + 'budget_name' => $this->clearString((string)$object['budget_name']), + + // category info + 'category_id' => $this->integerFromValue((string)$object['category_id']), + 'category_name' => $this->clearString((string)$object['category_name']), + + // journal bill reference. Optional. Will only work for withdrawals + 'bill_id' => $this->integerFromValue((string)$object['bill_id']), + 'bill_name' => $this->clearString((string)$object['bill_name']), + + // piggy bank reference. Optional. Will only work for transfers + 'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']), + 'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name']), + + // some other interesting properties + 'reconciled' => $this->convertBoolean((string)$object['reconciled']), + 'notes' => $this->clearStringKeepNewlines((string)$object['notes']), + 'tags' => $this->arrayFromValue($object['tags']), + + // all custom fields: + 'internal_reference' => $this->clearString((string)$object['internal_reference']), + 'external_id' => $this->clearString((string)$object['external_id']), + 'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')), + 'recurrence_id' => $this->integerFromValue($object['recurrence_id']), + 'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id']), + 'external_url' => $this->clearString((string)$object['external_url']), + + 'sepa_cc' => $this->clearString((string)$object['sepa_cc']), + 'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op']), + 'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id']), + 'sepa_db' => $this->clearString((string)$object['sepa_db']), + 'sepa_country' => $this->clearString((string)$object['sepa_country']), + 'sepa_ep' => $this->clearString((string)$object['sepa_ep']), + 'sepa_ci' => $this->clearString((string)$object['sepa_ci']), + 'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id']), + // custom date fields. Must be Carbon objects. Presence is optional. + 'interest_date' => $this->dateFromValue($object['interest_date']), + 'book_date' => $this->dateFromValue($object['book_date']), + 'process_date' => $this->dateFromValue($object['process_date']), + 'due_date' => $this->dateFromValue($object['due_date']), + 'payment_date' => $this->dateFromValue($object['payment_date']), + 'invoice_date' => $this->dateFromValue($object['invoice_date']), + ]; + } + + return $return; + } } diff --git a/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php b/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php index d03d8bef72..99f256c669 100644 --- a/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php @@ -57,81 +57,20 @@ class UpdateRequest extends FormRequest /** * Get all data. Is pretty complex because of all the ??-statements. * - * @return array * @throws FireflyException */ public function getAll(): array { app('log')->debug(sprintf('Now in %s', __METHOD__)); - $this->integerFields = [ - 'order', - 'currency_id', - 'foreign_currency_id', - 'transaction_journal_id', - 'source_id', - 'destination_id', - 'budget_id', - 'category_id', - 'bill_id', - 'recurrence_id', - ]; - - $this->dateFields = [ - 'date', - 'interest_date', - 'book_date', - 'process_date', - 'due_date', - 'payment_date', - 'invoice_date', - ]; - - $this->textareaFields = [ - 'notes', - ]; - - $this->floatFields = [ // not really floats, for validation. - 'amount', - 'foreign_amount', - ]; - - $this->stringFields = [ - 'type', - 'currency_code', - 'foreign_currency_code', - 'description', - 'source_name', - 'source_iban', - 'source_number', - 'source_bic', - 'destination_name', - 'destination_iban', - 'destination_number', - 'destination_bic', - 'budget_name', - 'category_name', - 'bill_name', - 'internal_reference', - 'external_id', - 'bunq_payment_id', - 'sepa_cc', - 'sepa_ct_op', - 'sepa_ct_id', - 'sepa_db', - 'sepa_country', - 'sepa_ep', - 'sepa_ci', - 'sepa_batch_id', - 'external_url', - ]; - $this->booleanFields = [ - 'reconciled', - ]; - - $this->arrayFields = [ - 'tags', - ]; - $data = []; + $this->integerFields = ['order', 'currency_id', 'foreign_currency_id', 'transaction_journal_id', 'source_id', 'destination_id', 'budget_id', 'category_id', 'bill_id', 'recurrence_id']; + $this->dateFields = ['date', 'interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date']; + $this->textareaFields = ['notes']; + // not really floats, for validation. + $this->floatFields = ['amount', 'foreign_amount']; + $this->stringFields = ['type', 'currency_code', 'foreign_currency_code', 'description', 'source_name', 'source_iban', 'source_number', 'source_bic', 'destination_name', 'destination_iban', 'destination_number', 'destination_bic', 'budget_name', 'category_name', 'bill_name', 'internal_reference', 'external_id', 'bunq_payment_id', 'sepa_cc', 'sepa_ct_op', 'sepa_ct_id', 'sepa_db', 'sepa_country', 'sepa_ep', 'sepa_ci', 'sepa_batch_id', 'external_url']; + $this->booleanFields = ['reconciled']; + $this->arrayFields = ['tags']; + $data = []; if ($this->has('transactions')) { $data['transactions'] = $this->getTransactionData(); } @@ -148,182 +87,14 @@ class UpdateRequest extends FormRequest return $data; } - /** - * Get transaction data. - * - * @return array - * @throws FireflyException - */ - private function getTransactionData(): array - { - app('log')->debug(sprintf('Now in %s', __METHOD__)); - $return = []; - - /** @var array|null $transactions */ - $transactions = $this->get('transactions'); - - if (!is_countable($transactions)) { - return $return; - } - - /** @var array|null $transaction */ - foreach ($transactions as $transaction) { - if (!is_array($transaction)) { - throw new FireflyException('Invalid data submitted: transaction is not array.'); - } - // 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 = $this->getFloatData($current, $transaction); - $return[] = $current; - } - - return $return; - } - - /** - * For each field, add it to the array if a reference is present in the request: - * - * @param array $current - * @param array $transaction - * - * @return array - */ - private function getIntegerData(array $current, array $transaction): array - { - foreach ($this->integerFields as $fieldName) { - if (array_key_exists($fieldName, $transaction)) { - $current[$fieldName] = $this->integerFromValue((string)$transaction[$fieldName]); - } - } - - return $current; - } - - /** - * @param array $current - * @param array $transaction - * - * @return array - */ - private function getStringData(array $current, array $transaction): array - { - foreach ($this->stringFields as $fieldName) { - if (array_key_exists($fieldName, $transaction)) { - $current[$fieldName] = $this->clearString((string)$transaction[$fieldName]); - } - } - - return $current; - } - - /** - * @param array $current - * @param array $transaction - * - * @return array - */ - private function getNlStringData(array $current, array $transaction): array - { - foreach ($this->textareaFields as $fieldName) { - if (array_key_exists($fieldName, $transaction)) { - $current[$fieldName] = $this->clearStringKeepNewlines((string)$transaction[$fieldName]); // keep newlines - } - } - - return $current; - } - - /** - * @param array $current - * @param array $transaction - * - * @return array - */ - private function getDateData(array $current, array $transaction): array - { - foreach ($this->dateFields as $fieldName) { - app('log')->debug(sprintf('Now at date field %s', $fieldName)); - if (array_key_exists($fieldName, $transaction)) { - app('log')->debug(sprintf('New value: "%s"', (string)$transaction[$fieldName])); - $current[$fieldName] = $this->dateFromValue((string)$transaction[$fieldName]); - } - } - - return $current; - } - - /** - * @param array $current - * @param array $transaction - * - * @return array - */ - private function getBooleanData(array $current, array $transaction): array - { - foreach ($this->booleanFields as $fieldName) { - if (array_key_exists($fieldName, $transaction)) { - $current[$fieldName] = $this->convertBoolean((string)$transaction[$fieldName]); - } - } - - return $current; - } - - /** - * @param array $current - * @param array $transaction - * - * @return array - */ - private function getArrayData(array $current, array $transaction): array - { - foreach ($this->arrayFields as $fieldName) { - if (array_key_exists($fieldName, $transaction)) { - $current[$fieldName] = $this->arrayFromValue($transaction[$fieldName]); - } - } - - return $current; - } - - /** - * @param array $current - * @param array $transaction - * - * @return array - */ - private function getFloatData(array $current, array $transaction): array - { - foreach ($this->floatFields as $fieldName) { - if (array_key_exists($fieldName, $transaction)) { - $value = $transaction[$fieldName]; - if (is_float($value)) { - $current[$fieldName] = sprintf('%.12f', $value); - } - if (!is_float($value)) { - $current[$fieldName] = (string)$value; - } - } - } - - return $current; - } - /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { app('log')->debug(sprintf('Now in %s', __METHOD__)); $validProtocols = config('firefly.valid_url_protocols'); + return [ // basic fields for group: 'group_title' => 'between:1,1000|nullable', @@ -337,7 +108,6 @@ class UpdateRequest extends FormRequest // group id: 'transactions.*.transaction_journal_id' => ['nullable', 'numeric', new BelongsUser()], - // currency info 'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|nullable', 'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable', @@ -401,25 +171,21 @@ class UpdateRequest extends FormRequest /** * Configure the validator instance. - * - * @param Validator $validator - * - * @return void */ public function withValidator(Validator $validator): void { app('log')->debug('Now in withValidator'); + /** @var TransactionGroup $transactionGroup */ $transactionGroup = $this->route()->parameter('transactionGroup'); $validator->after( - function (Validator $validator) use ($transactionGroup) { + function (Validator $validator) use ($transactionGroup): void { // 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); - // user wants to update a reconciled transaction. // source, destination, amount + foreign_amount cannot be changed // and must be omitted from the request. @@ -429,13 +195,164 @@ class UpdateRequest extends FormRequest $this->validateEqualAccountsForUpdate($validator, $transactionGroup); // see method: - //$this->preventNoAccountInfo($validator, ); + // $this->preventNoAccountInfo($validator, ); // validate that the currency fits the source and/or destination account. // validate all account info $this->validateAccountInformationUpdate($validator, $transactionGroup); - } ); } + + /** + * Get transaction data. + * + * @throws FireflyException + */ + private function getTransactionData(): array + { + app('log')->debug(sprintf('Now in %s', __METHOD__)); + $return = []; + + /** @var null|array $transactions */ + $transactions = $this->get('transactions'); + + if (!is_countable($transactions)) { + return $return; + } + + /** @var null|array $transaction */ + foreach ($transactions as $transaction) { + if (!is_array($transaction)) { + throw new FireflyException('Invalid data submitted: transaction is not array.'); + } + // 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 = $this->getFloatData($current, $transaction); + $return[] = $current; + } + + return $return; + } + + /** + * For each field, add it to the array if a reference is present in the request: + * + * @param array $current + * @param array $transaction + */ + private function getIntegerData(array $current, array $transaction): array + { + foreach ($this->integerFields as $fieldName) { + if (array_key_exists($fieldName, $transaction)) { + $current[$fieldName] = $this->integerFromValue((string) $transaction[$fieldName]); + } + } + + return $current; + } + + /** + * @param array $current + * @param array $transaction + */ + private function getStringData(array $current, array $transaction): array + { + foreach ($this->stringFields as $fieldName) { + if (array_key_exists($fieldName, $transaction)) { + $current[$fieldName] = $this->clearString((string) $transaction[$fieldName]); + } + } + + return $current; + } + + /** + * @param array $current + * @param array $transaction + */ + private function getNlStringData(array $current, array $transaction): array + { + foreach ($this->textareaFields as $fieldName) { + if (array_key_exists($fieldName, $transaction)) { + $current[$fieldName] = $this->clearStringKeepNewlines((string) $transaction[$fieldName]); // keep newlines + } + } + + return $current; + } + + /** + * @param array $current + * @param array $transaction + */ + private function getDateData(array $current, array $transaction): array + { + foreach ($this->dateFields as $fieldName) { + app('log')->debug(sprintf('Now at date field %s', $fieldName)); + if (array_key_exists($fieldName, $transaction)) { + app('log')->debug(sprintf('New value: "%s"', (string) $transaction[$fieldName])); + $current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]); + } + } + + return $current; + } + + /** + * @param array $current + * @param array $transaction + */ + private function getBooleanData(array $current, array $transaction): array + { + foreach ($this->booleanFields as $fieldName) { + if (array_key_exists($fieldName, $transaction)) { + $current[$fieldName] = $this->convertBoolean((string) $transaction[$fieldName]); + } + } + + return $current; + } + + /** + * @param array $current + * @param array $transaction + */ + private function getArrayData(array $current, array $transaction): array + { + foreach ($this->arrayFields as $fieldName) { + if (array_key_exists($fieldName, $transaction)) { + $current[$fieldName] = $this->arrayFromValue($transaction[$fieldName]); + } + } + + return $current; + } + + /** + * @param array $current + * @param array $transaction + */ + private function getFloatData(array $current, array $transaction): array + { + foreach ($this->floatFields as $fieldName) { + if (array_key_exists($fieldName, $transaction)) { + $value = $transaction[$fieldName]; + if (is_float($value)) { + $current[$fieldName] = sprintf('%.12f', $value); + } + if (!is_float($value)) { + $current[$fieldName] = (string) $value; + } + } + } + + return $current; + } } diff --git a/app/Api/V1/Requests/Models/TransactionCurrency/StoreRequest.php b/app/Api/V1/Requests/Models/TransactionCurrency/StoreRequest.php index 955f6a0aaa..63ffd590bc 100644 --- a/app/Api/V1/Requests/Models/TransactionCurrency/StoreRequest.php +++ b/app/Api/V1/Requests/Models/TransactionCurrency/StoreRequest.php @@ -30,8 +30,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class StoreRequest - * - */ class StoreRequest extends FormRequest { @@ -40,8 +38,6 @@ class StoreRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -66,8 +62,6 @@ class StoreRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -78,7 +72,6 @@ class StoreRequest extends FormRequest 'decimal_places' => 'between:0,20|numeric|min:0|max:12', 'enabled' => [new IsBoolean()], 'default' => [new IsBoolean()], - ]; } } diff --git a/app/Api/V1/Requests/Models/TransactionCurrency/UpdateRequest.php b/app/Api/V1/Requests/Models/TransactionCurrency/UpdateRequest.php index 8e2e8b99d8..b5fd776f0e 100644 --- a/app/Api/V1/Requests/Models/TransactionCurrency/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/TransactionCurrency/UpdateRequest.php @@ -31,8 +31,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class UpdateRequest - * - */ class UpdateRequest extends FormRequest { @@ -41,8 +39,6 @@ class UpdateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -61,8 +57,6 @@ class UpdateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php b/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php index 7857e5df0a..73f03f71f5 100644 --- a/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php @@ -41,8 +41,6 @@ class StoreRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -57,8 +55,6 @@ class StoreRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -73,27 +69,21 @@ class StoreRequest extends FormRequest /** * Configure the validator instance. - * - * @param Validator $validator - * - * @return void */ public function withValidator(Validator $validator): void { $validator->after( - function (Validator $validator) { + function (Validator $validator): void { $this->validateExistingLink($validator); } ); } - /** - * @param Validator $validator - */ private function validateExistingLink(Validator $validator): void { /** @var User $user */ $user = auth()->user(); + /** @var LinkTypeRepositoryInterface $repository */ $repository = app(LinkTypeRepositoryInterface::class); $repository->setUser($user); diff --git a/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php b/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php index 8fd2aba40c..f25a84912c 100644 --- a/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php @@ -41,8 +41,6 @@ class UpdateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -57,8 +55,6 @@ class UpdateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -73,28 +69,22 @@ class UpdateRequest extends FormRequest /** * Configure the validator instance. - * - * @param Validator $validator - * - * @return void */ public function withValidator(Validator $validator): void { $validator->after( - function (Validator $validator) { + function (Validator $validator): void { $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()); diff --git a/app/Api/V1/Requests/Models/TransactionLinkType/StoreRequest.php b/app/Api/V1/Requests/Models/TransactionLinkType/StoreRequest.php index ecc201b9fb..abcde7dd2c 100644 --- a/app/Api/V1/Requests/Models/TransactionLinkType/StoreRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLinkType/StoreRequest.php @@ -29,8 +29,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class StoreRequest - * - */ class StoreRequest extends FormRequest { @@ -39,8 +37,6 @@ class StoreRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -53,8 +49,6 @@ class StoreRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/Models/TransactionLinkType/UpdateRequest.php b/app/Api/V1/Requests/Models/TransactionLinkType/UpdateRequest.php index f7e98e94c6..7f660107e4 100644 --- a/app/Api/V1/Requests/Models/TransactionLinkType/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLinkType/UpdateRequest.php @@ -31,8 +31,6 @@ use Illuminate\Validation\Rule; /** * Class UpdateRequest - * - */ class UpdateRequest extends FormRequest { @@ -41,8 +39,6 @@ class UpdateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -55,8 +51,6 @@ class UpdateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/Models/Webhook/CreateRequest.php b/app/Api/V1/Requests/Models/Webhook/CreateRequest.php index 9aab513693..50d19872bd 100644 --- a/app/Api/V1/Requests/Models/Webhook/CreateRequest.php +++ b/app/Api/V1/Requests/Models/Webhook/CreateRequest.php @@ -37,9 +37,6 @@ class CreateRequest extends FormRequest use ChecksLogin; use ConvertsDataTypes; - /** - * @return array - */ public function getData(): array { $triggers = Webhook::getTriggersForValidation(); @@ -57,17 +54,15 @@ class CreateRequest extends FormRequest // this is the way. $return = $this->getAllData($fields); - $return['trigger'] = $triggers[$return['trigger']] ?? (int)($return['trigger']); - $return['response'] = $responses[$return['response']] ?? (int)($return['response']); - $return['delivery'] = $deliveries[$return['delivery']] ?? (int)($return['delivery']); + $return['trigger'] = $triggers[$return['trigger']] ?? (int)$return['trigger']; + $return['response'] = $responses[$return['response']] ?? (int)$return['response']; + $return['delivery'] = $deliveries[$return['delivery']] ?? (int)$return['delivery']; return $return; } /** * Rules for this request. - * - * @return array */ public function rules(): array { @@ -75,6 +70,7 @@ class CreateRequest extends FormRequest $responses = implode(',', array_keys(Webhook::getResponsesForValidation())); $deliveries = implode(',', array_keys(Webhook::getDeliveriesForValidation())); $validProtocols = config('firefly.valid_url_protocols'); + return [ 'title' => 'required|between:1,512|uniqueObjectForUser:webhooks,title', 'active' => [new IsBoolean()], diff --git a/app/Api/V1/Requests/Models/Webhook/UpdateRequest.php b/app/Api/V1/Requests/Models/Webhook/UpdateRequest.php index b2b8187031..ab5b733015 100644 --- a/app/Api/V1/Requests/Models/Webhook/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Webhook/UpdateRequest.php @@ -37,9 +37,6 @@ class UpdateRequest extends FormRequest use ChecksLogin; use ConvertsDataTypes; - /** - * @return array - */ public function getData(): array { $triggers = Webhook::getTriggersForValidation(); @@ -76,8 +73,6 @@ class UpdateRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { @@ -85,6 +80,7 @@ class UpdateRequest extends FormRequest $responses = implode(',', array_keys(Webhook::getResponsesForValidation())); $deliveries = implode(',', array_keys(Webhook::getDeliveriesForValidation())); $validProtocols = config('firefly.valid_url_protocols'); + /** @var Webhook $webhook */ $webhook = $this->route()->parameter('webhook'); diff --git a/app/Api/V1/Requests/System/CronRequest.php b/app/Api/V1/Requests/System/CronRequest.php index 3af1094aa2..4ce959c635 100644 --- a/app/Api/V1/Requests/System/CronRequest.php +++ b/app/Api/V1/Requests/System/CronRequest.php @@ -29,8 +29,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class CronRequest - * - */ class CronRequest extends FormRequest { @@ -38,8 +36,6 @@ class CronRequest extends FormRequest /** * Verify the request. - * - * @return bool */ public function authorize(): bool { @@ -48,8 +44,6 @@ class CronRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -69,8 +63,6 @@ class CronRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/System/UpdateRequest.php b/app/Api/V1/Requests/System/UpdateRequest.php index 173f49fa01..92bbf52ca3 100644 --- a/app/Api/V1/Requests/System/UpdateRequest.php +++ b/app/Api/V1/Requests/System/UpdateRequest.php @@ -31,8 +31,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class UpdateRequest - * - */ class UpdateRequest extends FormRequest { @@ -41,17 +39,15 @@ class UpdateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { $name = $this->route()->parameter('dynamicConfigKey'); - if ($name === 'configuration.is_demo_site' || $name === 'configuration.single_user_mode') { + if ('configuration.is_demo_site' === $name || 'configuration.single_user_mode' === $name) { return ['value' => $this->boolean('value')]; } - if ($name === 'configuration.permission_update_check' || $name === 'configuration.last_update_check') { + if ('configuration.permission_update_check' === $name || 'configuration.last_update_check' === $name) { return ['value' => $this->convertInteger('value')]; } @@ -60,20 +56,18 @@ class UpdateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { $name = $this->route()->parameter('configName'); - if ($name === 'configuration.is_demo_site' || $name === 'configuration.single_user_mode') { + if ('configuration.is_demo_site' === $name || 'configuration.single_user_mode' === $name) { return ['value' => ['required', new IsBoolean()]]; } - if ($name === 'configuration.permission_update_check') { + if ('configuration.permission_update_check' === $name) { return ['value' => 'required|numeric|between:-1,1']; } - if ($name === 'configuration.last_update_check') { + if ('configuration.last_update_check' === $name) { return ['value' => 'required|numeric|min:464272080']; } diff --git a/app/Api/V1/Requests/System/UserStoreRequest.php b/app/Api/V1/Requests/System/UserStoreRequest.php index b8e44ebc5d..9ca9409ec8 100644 --- a/app/Api/V1/Requests/System/UserStoreRequest.php +++ b/app/Api/V1/Requests/System/UserStoreRequest.php @@ -39,8 +39,6 @@ class UserStoreRequest extends FormRequest /** * Logged in + owner - * - * @return bool */ public function authorize(): bool { @@ -49,8 +47,6 @@ class UserStoreRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -69,8 +65,6 @@ class UserStoreRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V1/Requests/System/UserUpdateRequest.php b/app/Api/V1/Requests/System/UserUpdateRequest.php index 46280e7b2b..0d00b6f464 100644 --- a/app/Api/V1/Requests/System/UserUpdateRequest.php +++ b/app/Api/V1/Requests/System/UserUpdateRequest.php @@ -41,8 +41,6 @@ class UserUpdateRequest extends FormRequest /** * Logged in + owner - * - * @return bool */ public function authorize(): bool { @@ -51,8 +49,6 @@ class UserUpdateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -71,8 +67,6 @@ class UserUpdateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -89,17 +83,13 @@ class UserUpdateRequest extends FormRequest /** * Configure the validator instance. - * - * @param Validator $validator - * - * @return void */ public function withValidator(Validator $validator): void { - /** @var User|null $current */ + /** @var null|User $current */ $current = $this->route()->parameter('user'); $validator->after( - static function (Validator $validator) use ($current) { + static function (Validator $validator) use ($current): void { $isAdmin = auth()->user()->hasRole('owner'); // not admin, and not own user? if (auth()->check() && false === $isAdmin && $current?->id !== auth()->user()->id) { diff --git a/app/Api/V1/Requests/User/PreferenceStoreRequest.php b/app/Api/V1/Requests/User/PreferenceStoreRequest.php index 9a4a896682..21be1db0d4 100644 --- a/app/Api/V1/Requests/User/PreferenceStoreRequest.php +++ b/app/Api/V1/Requests/User/PreferenceStoreRequest.php @@ -35,9 +35,6 @@ class PreferenceStoreRequest extends FormRequest use ChecksLogin; use ConvertsDataTypes; - /** - * @return array - */ public function getAll(): array { $array = [ diff --git a/app/Api/V1/Requests/User/PreferenceUpdateRequest.php b/app/Api/V1/Requests/User/PreferenceUpdateRequest.php index cac9397713..3f62c47f87 100644 --- a/app/Api/V1/Requests/User/PreferenceUpdateRequest.php +++ b/app/Api/V1/Requests/User/PreferenceUpdateRequest.php @@ -36,9 +36,6 @@ class PreferenceUpdateRequest extends FormRequest use ChecksLogin; use ConvertsDataTypes; - /** - * @return array - */ public function getAll(): array { $array = [ diff --git a/app/Api/V2/Controllers/Autocomplete/AccountController.php b/app/Api/V2/Controllers/Autocomplete/AccountController.php index 834dbb5cbc..534681eb8e 100644 --- a/app/Api/V2/Controllers/Autocomplete/AccountController.php +++ b/app/Api/V2/Controllers/Autocomplete/AccountController.php @@ -64,7 +64,7 @@ class AccountController extends Controller return $next($request); } ); - $this->balanceTypes = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE,]; + $this->balanceTypes = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]; } /** @@ -76,9 +76,6 @@ class AccountController extends Controller * 4. Endpoint is documented. * 5. Collector uses user_group_id * - * @param AutocompleteRequest $request - * - * @return JsonResponse * @throws FireflyException * @throws FireflyException */ @@ -92,6 +89,7 @@ class AccountController extends Controller $defaultCurrency = app('amount')->getDefaultCurrency(); $groupedResult = []; $allItems = []; + /** @var Account $account */ foreach ($result as $account) { $nameWithBalance = $account->name; @@ -131,6 +129,7 @@ class AccountController extends Controller return $posLeft - $posRight; } ); + return response()->json($allItems); } } diff --git a/app/Api/V2/Controllers/Autocomplete/TransactionController.php b/app/Api/V2/Controllers/Autocomplete/TransactionController.php index 175e787969..5aa81372d3 100644 --- a/app/Api/V2/Controllers/Autocomplete/TransactionController.php +++ b/app/Api/V2/Controllers/Autocomplete/TransactionController.php @@ -64,9 +64,6 @@ class TransactionController extends Controller * 3. Request includes user_group_id * 4. Endpoint is documented. * 5. Collector uses user_group_id - * - * - * @return JsonResponse */ public function transactionDescriptions(AutocompleteRequest $request): JsonResponse { @@ -88,7 +85,5 @@ class TransactionController extends Controller } return response()->json($array); - } - } diff --git a/app/Api/V2/Controllers/Chart/AccountController.php b/app/Api/V2/Controllers/Chart/AccountController.php index 0e8a132a46..0b756b9efb 100644 --- a/app/Api/V2/Controllers/Chart/AccountController.php +++ b/app/Api/V2/Controllers/Chart/AccountController.php @@ -36,8 +36,6 @@ use FireflyIII\Support\Http\Api\CleansChartData; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class AccountController @@ -49,9 +47,6 @@ class AccountController extends Controller private AccountRepositoryInterface $repository; - /** - * - */ public function __construct() { parent::__construct(); @@ -79,18 +74,15 @@ class AccountController extends Controller * * TODO validate and set user_group_id from request * - * @param DashboardChartRequest $request - * - * @return JsonResponse - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface * @throws FireflyException + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function dashboard(DashboardChartRequest $request): JsonResponse { /** @var Carbon $start */ $start = $this->parameters->get('start'); + /** @var Carbon $end */ $end = $this->parameters->get('end'); $end->endOfDay(); @@ -98,6 +90,7 @@ class AccountController extends Controller /** @var TransactionCurrency $default */ $default = app('amount')->getDefaultCurrency(); $params = $request->getAll(); + /** @var Collection $accounts */ $accounts = $params['accounts']; $chartData = []; @@ -174,5 +167,4 @@ class AccountController extends Controller return response()->json($this->clean($chartData)); } - } diff --git a/app/Api/V2/Controllers/Chart/BalanceController.php b/app/Api/V2/Controllers/Chart/BalanceController.php index 45caf1405c..cef7ab8dcb 100644 --- a/app/Api/V2/Controllers/Chart/BalanceController.php +++ b/app/Api/V2/Controllers/Chart/BalanceController.php @@ -1,6 +1,5 @@ getAll(); + /** @var Carbon $start */ $start = $this->parameters->get('start'); + /** @var Carbon $end */ $end = $this->parameters->get('end'); $end->endOfDay(); + /** @var Collection $accounts */ $accounts = $params['accounts']; + + /** @var string $preferredRange */ $preferredRange = $params['period']; - // set some formats, based on input parameters. - $format = app('navigation')->preferredCarbonFormatByPeriod($preferredRange); - // prepare for currency conversion and data collection: - $ids = $accounts->pluck('id')->toArray(); /** @var TransactionCurrency $default */ $default = app('amount')->getDefaultCurrency(); - $converter = new ExchangeRateConverter(); - $currencies = [$default->id => $default,]; // currency cache - $data = []; - $chartData = []; // get journals for entire period: /** @var GroupCollectorInterface $collector */ @@ -93,152 +86,16 @@ class BalanceController extends Controller $collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::RECONCILIATION, TransactionType::TRANSFER]); $journals = $collector->getExtractedJournals(); - // set array for default currency (even if unused later on) - $defaultCurrencyId = $default->id; - $data[$defaultCurrencyId] = [ - 'currency_id' => (string)$defaultCurrencyId, - 'currency_symbol' => $default->symbol, - 'currency_code' => $default->code, - 'currency_name' => $default->name, - 'currency_decimal_places' => $default->decimal_places, - 'native_id' => (string)$defaultCurrencyId, - 'native_symbol' => $default->symbol, - 'native_code' => $default->code, - 'native_name' => $default->name, - 'native_decimal_places' => $default->decimal_places, - ]; + $object = new AccountBalanceGrouped(); + $object->setPreferredRange($preferredRange); + $object->setDefault($default); + $object->setAccounts($accounts); + $object->setJournals($journals); + $object->setStart($start); + $object->setEnd($end); + $object->groupByCurrencyAndPeriod(); + $chartData = $object->convertToChartData(); - - // loop. group by currency and by period. - /** @var array $journal */ - foreach ($journals as $journal) { - // format the date according to the period - $period = $journal['date']->format($format); - - // collect (and cache) currency information for this journal. - $currencyId = (int)$journal['currency_id']; - $currency = $currencies[$currencyId] ?? TransactionCurrency::find($currencyId); - $currencies[$currencyId] = $currency; // may just re-assign itself, don't mind. - - // set the array with monetary info, if it does not exist. - $data[$currencyId] ??= [ - 'currency_id' => (string)$currencyId, - 'currency_symbol' => $journal['currency_symbol'], - 'currency_code' => $journal['currency_code'], - 'currency_name' => $journal['currency_name'], - 'currency_decimal_places' => $journal['currency_decimal_places'], - // native currency info (could be the same) - 'native_id' => (string)$default->id, - 'native_code' => $default->code, - 'native_symbol' => $default->symbol, - 'native_decimal_places' => $default->decimal_places, - ]; - - // set the array (in monetary info) with spent/earned in this $period, if it does not exist. - $data[$currencyId][$period] ??= [ - 'period' => $period, - 'spent' => '0', - 'earned' => '0', - 'native_spent' => '0', - 'native_earned' => '0', - ]; - // is this journal's amount in- our outgoing? - $key = 'spent'; - $amount = app('steam')->negative($journal['amount']); - // deposit = incoming - // transfer or reconcile or opening balance, and these accounts are the destination. - if ( - TransactionType::DEPOSIT === $journal['transaction_type_type'] - || - - ( - ( - TransactionType::TRANSFER === $journal['transaction_type_type'] - || TransactionType::RECONCILIATION === $journal['transaction_type_type'] - || TransactionType::OPENING_BALANCE === $journal['transaction_type_type'] - ) - && in_array($journal['destination_account_id'], $ids, true) - ) - ) { - $key = 'earned'; - $amount = app('steam')->positive($journal['amount']); - } - // get conversion rate - $rate = $converter->getCurrencyRate($currency, $default, $journal['date']); - $amountConverted = bcmul($amount, $rate); - - // perhaps transaction already has the foreign amount in the native currency. - if ((int)$journal['foreign_currency_id'] === $default->id) { - $amountConverted = $journal['foreign_amount'] ?? '0'; - $amountConverted = 'earned' === $key ? app('steam')->positive($amountConverted) : app('steam')->negative($amountConverted); - } - - // add normal entry - $data[$currencyId][$period][$key] = bcadd($data[$currencyId][$period][$key], $amount); - - // add converted entry - $convertedKey = sprintf('native_%s', $key); - $data[$currencyId][$period][$convertedKey] = bcadd($data[$currencyId][$period][$convertedKey], $amountConverted); - } - - // loop this data, make chart bars for each currency: - /** @var array $currency */ - foreach ($data as $currency) { - // income and expense array prepped: - $income = [ - 'label' => 'earned', - 'currency_id' => (string)$currency['currency_id'], - 'currency_symbol' => $currency['currency_symbol'], - 'currency_code' => $currency['currency_code'], - 'currency_decimal_places' => $currency['currency_decimal_places'], - 'native_id' => (string)$currency['native_id'], - 'native_symbol' => $currency['native_symbol'], - 'native_code' => $currency['native_code'], - 'native_decimal_places' => $currency['native_decimal_places'], - 'start' => $start->toAtomString(), - 'end' => $end->toAtomString(), - 'period' => $preferredRange, - 'entries' => [], - 'native_entries' => [], - ]; - $expense = [ - 'label' => 'spent', - 'currency_id' => (string)$currency['currency_id'], - 'currency_symbol' => $currency['currency_symbol'], - 'currency_code' => $currency['currency_code'], - 'currency_decimal_places' => $currency['currency_decimal_places'], - 'native_id' => (string)$currency['native_id'], - 'native_symbol' => $currency['native_symbol'], - 'native_code' => $currency['native_code'], - 'native_decimal_places' => $currency['native_decimal_places'], - 'start' => $start->toAtomString(), - 'end' => $end->toAtomString(), - 'period' => $preferredRange, - 'entries' => [], - 'native_entries' => [], - - ]; - // loop all possible periods between $start and $end, and add them to the correct dataset. - $currentStart = clone $start; - while ($currentStart <= $end) { - $key = $currentStart->format($format); - $label = $currentStart->toAtomString(); - // normal entries - $income['entries'][$label] = app('steam')->bcround(($currency[$key]['earned'] ?? '0'), $currency['currency_decimal_places']); - $expense['entries'][$label] = app('steam')->bcround(($currency[$key]['spent'] ?? '0'), $currency['currency_decimal_places']); - - // converted entries - $income['native_entries'][$label] = app('steam')->bcround(($currency[$key]['native_earned'] ?? '0'), $currency['native_decimal_places']); - $expense['native_entries'][$label] = app('steam')->bcround(($currency[$key]['native_spent'] ?? '0'), $currency['native_decimal_places']); - - // next loop - $currentStart = app('navigation')->addPeriod($currentStart, $preferredRange, 0); - } - - $chartData[] = $income; - $chartData[] = $expense; - } return response()->json($this->clean($chartData)); } - } diff --git a/app/Api/V2/Controllers/Chart/BudgetController.php b/app/Api/V2/Controllers/Chart/BudgetController.php index acd1f3c5c7..20d70cb1df 100644 --- a/app/Api/V2/Controllers/Chart/BudgetController.php +++ b/app/Api/V2/Controllers/Chart/BudgetController.php @@ -1,6 +1,5 @@ getAll(); + /** @var Carbon $start */ $start = $params['start']; + /** @var Carbon $end */ $end = $params['end']; // code from FrontpageChartGenerator, but not in separate class $budgets = $this->repository->getActiveBudgets(); $data = []; + /** @var Budget $budget */ foreach ($budgets as $budget) { // could return multiple arrays, so merge. $data = array_merge($data, $this->processBudget($budget, $start, $end)); } + return response()->json($this->clean($data)); } /** - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * - * @return array * @throws FireflyException */ private function processBudget(Budget $budget, Carbon $start, Carbon $end): array @@ -154,6 +151,7 @@ class BudgetController extends Controller ]; $return[] = $current; } + return $return; } @@ -161,28 +159,20 @@ class BudgetController extends Controller * When no budget limits are present, the expenses of the whole period are collected and grouped. * This is grouped per currency. Because there is no limit set, "left to spend" and "overspent" are empty. * - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * - * @return array * @throws FireflyException */ private function noBudgetLimits(Budget $budget, Carbon $start, Carbon $end): array { $spent = $this->opsRepository->listExpenses($start, $end, null, new Collection([$budget])); + return $this->processExpenses($budget->id, $spent, $start, $end); } /** - * Shared between the "noBudgetLimits" function and "processLimit". + * Shared between the "noBudgetLimits" function and "processLimit". Will take a single set of expenses and return its info. * - * Will take a single set of expenses and return its info. + * @param array> $array * - * @param int $budgetId - * @param array $array - * - * @return array * @throws FireflyException */ private function processExpenses(int $budgetId, array $array, Carbon $start, Carbon $end): array @@ -218,13 +208,12 @@ class BudgetController extends Controller 'native_left' => '0', 'overspent' => '0', 'native_overspent' => '0', - ]; $currentBudgetArray = $block['budgets'][$budgetId]; - //var_dump($return); + + // var_dump($return); /** @var array $journal */ foreach ($currentBudgetArray['transaction_journals'] as $journal) { - // convert the amount to the native currency. $rate = $converter->getCurrencyRate($this->currencies[$currencyId], $this->currency, $journal['date']); $convertedAmount = bcmul($journal['amount'], $rate); @@ -236,6 +225,8 @@ class BudgetController extends Controller $return[$currencyId]['native_spent'] = bcadd($return[$currencyId]['native_spent'], $convertedAmount); } } + $converter->summarize(); + return $return; } @@ -247,16 +238,13 @@ class BudgetController extends Controller * * If you have a budget limit in EUR, and a transaction in GBP, it will not be considered for the EUR budget limit. * - * @param Budget $budget - * @param Collection $limits - * - * @return array * @throws FireflyException */ private function budgetLimits(Budget $budget, Collection $limits): array { app('log')->debug(sprintf('Now in budgetLimits(#%d)', $budget->id)); $data = []; + /** @var BudgetLimit $limit */ foreach ($limits as $limit) { $data = array_merge($data, $this->processLimit($budget, $limit)); @@ -266,10 +254,6 @@ class BudgetController extends Controller } /** - * @param Budget $budget - * @param BudgetLimit $limit - * - * @return array * @throws FireflyException */ private function processLimit(Budget $budget, BudgetLimit $limit): array @@ -284,7 +268,6 @@ class BudgetController extends Controller $rate = $converter->getCurrencyRate($limitCurrency, $this->currency, $limit->start_date); $convertedLimitAmount = bcmul($limit->amount, $rate); - /** @var array $entry */ foreach ($spent as $currencyId => $entry) { // only spent the entry where the entry's currency matches the budget limit's currency @@ -306,8 +289,8 @@ class BudgetController extends Controller $result[$limitCurrencyId]['native_overspent'] = app('steam')->positive(bcadd($convertedLimitAmount, $result[$limitCurrencyId]['native_spent'])); } } + $converter->summarize(); + return $result; } - - } diff --git a/app/Api/V2/Controllers/Chart/CategoryController.php b/app/Api/V2/Controllers/Chart/CategoryController.php index 3171448c7a..32e41992b6 100644 --- a/app/Api/V2/Controllers/Chart/CategoryController.php +++ b/app/Api/V2/Controllers/Chart/CategoryController.php @@ -1,6 +1,5 @@ accountRepos->setUserGroup($userGroup); } + return $next($request); } ); @@ -70,16 +70,15 @@ class CategoryController extends Controller * TODO may be worth to move to a handler but the data is simple enough. * TODO see autoComplete/account controller * - * @param DateRequest $request - * - * @return JsonResponse * @throws FireflyException + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function dashboard(DateRequest $request): JsonResponse { /** @var Carbon $start */ $start = $this->parameters->get('start'); + /** @var Carbon $end */ $end = $this->parameters->get('end'); $accounts = $this->accountRepos->getAccountsByType([AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::ASSET, AccountType::DEFAULT]); @@ -128,7 +127,6 @@ class CategoryController extends Controller 'native_amount' => '0', ]; - // add monies $return[$key]['amount'] = bcadd($return[$key]['amount'], $amount); $return[$key]['native_amount'] = bcadd($return[$key]['native_amount'], $nativeAmount); @@ -139,7 +137,8 @@ class CategoryController extends Controller usort($return, static function (array $a, array $b) { return (float)$a['native_amount'] < (float)$b['native_amount'] ? 1 : -1; }); + $converter->summarize(); + return response()->json($this->clean($return)); } - } diff --git a/app/Api/V2/Controllers/Controller.php b/app/Api/V2/Controllers/Controller.php index 2bf9c8b1e1..ea9a2b28c5 100644 --- a/app/Api/V2/Controllers/Controller.php +++ b/app/Api/V2/Controllers/Controller.php @@ -45,6 +45,9 @@ use Symfony\Component\HttpFoundation\ParameterBag; /** * Class Controller + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.NumberOfChildren) */ class Controller extends BaseController { @@ -53,9 +56,6 @@ class Controller extends BaseController protected const string CONTENT_TYPE = 'application/vnd.api+json'; protected ParameterBag $parameters; - /** - * - */ public function __construct() { $this->middleware( @@ -65,22 +65,59 @@ class Controller extends BaseController return $next($request); } ); + } + final protected function jsonApiList(string $key, LengthAwarePaginator $paginator, AbstractTransformer $transformer): array + { + $manager = new Manager(); + $baseUrl = request()->getSchemeAndHttpHost().'/api/v2'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $objects = $paginator->getCollection(); + + // the transformer, at this point, needs to collect information that ALL items in the collection + // require, like meta-data and stuff like that, and save it for later. + $transformer->collectMetaData($objects); + + $resource = new FractalCollection($objects, $transformer, $key); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return $manager->createData($resource)->toArray(); + } + + /** + * Returns a JSON API object and returns it. + * + * @param array|Model $object + */ + final protected function jsonApiObject(string $key, array|Model $object, AbstractTransformer $transformer): array + { + // create some objects: + $manager = new Manager(); + $baseUrl = request()->getSchemeAndHttpHost().'/api/v2'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $transformer->collectMetaData(new Collection([$object])); + + $resource = new Item($object, $transformer, $key); + + return $manager->createData($resource)->toArray(); } /** * TODO duplicate from V1 controller * Method to grab all parameters from the URL. * - * @return ParameterBag + * @SuppressWarnings(PHPMD.NPathComplexity) */ private function getParameters(): ParameterBag { $bag = new ParameterBag(); $bag->set('limit', 50); + try { $page = (int)request()->get('page'); - } catch (ContainerExceptionInterface | NotFoundExceptionInterface $e) { + } catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) { $page = 1; } @@ -99,6 +136,7 @@ class Controller extends BaseController foreach ($dates as $field) { $date = null; $obj = null; + try { $date = request()->query->get($field); } catch (BadRequestException $e) { @@ -109,7 +147,7 @@ class Controller extends BaseController if (null !== $date) { try { $obj = Carbon::parse((string)$date, config('app.timezone')); - } catch (InvalidDateException | InvalidFormatException $e) { + } catch (InvalidDateException|InvalidFormatException $e) { // don't care app('log')->warning(sprintf('Ignored invalid date "%s" in API v2 controller parameter check: %s', substr((string)$date, 0, 20), $e->getMessage())); } @@ -146,52 +184,4 @@ class Controller extends BaseController return $bag; } - - /** - * @param string $key - * @param LengthAwarePaginator $paginator - * @param AbstractTransformer $transformer - * - * @return array - */ - final protected function jsonApiList(string $key, LengthAwarePaginator $paginator, AbstractTransformer $transformer): array - { - $manager = new Manager(); - $baseUrl = request()->getSchemeAndHttpHost() . '/api/v2'; - $manager->setSerializer(new JsonApiSerializer($baseUrl)); - - $objects = $paginator->getCollection(); - - // the transformer, at this point, needs to collect information that ALL items in the collection - // require, like meta-data and stuff like that, and save it for later. - $transformer->collectMetaData($objects); - - $resource = new FractalCollection($objects, $transformer, $key); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return $manager->createData($resource)->toArray(); - } - - /** - * Returns a JSON API object and returns it. - * - * @param string $key - * @param Model $object - * @param AbstractTransformer $transformer - * - * @return array - */ - final protected function jsonApiObject(string $key, array | Model $object, AbstractTransformer $transformer): array - { - // create some objects: - $manager = new Manager(); - $baseUrl = request()->getSchemeAndHttpHost() . '/api/v2'; - $manager->setSerializer(new JsonApiSerializer($baseUrl)); - - $transformer->collectMetaData(new Collection([$object])); - - $resource = new Item($object, $transformer, $key); - - return $manager->createData($resource)->toArray(); - } } diff --git a/app/Api/V2/Controllers/Data/Bulk/AccountController.php b/app/Api/V2/Controllers/Data/Bulk/AccountController.php index d869b8843f..3dbfd0343d 100644 --- a/app/Api/V2/Controllers/Data/Bulk/AccountController.php +++ b/app/Api/V2/Controllers/Data/Bulk/AccountController.php @@ -29,6 +29,4 @@ use FireflyIII\Api\V2\Controllers\Controller; /** * Class AccountController */ -class AccountController extends Controller -{ -} +class AccountController extends Controller {} diff --git a/app/Api/V2/Controllers/Data/Export/AccountController.php b/app/Api/V2/Controllers/Data/Export/AccountController.php index 3787530d57..4526f0f399 100644 --- a/app/Api/V2/Controllers/Data/Export/AccountController.php +++ b/app/Api/V2/Controllers/Data/Export/AccountController.php @@ -29,6 +29,4 @@ use FireflyIII\Api\V2\Controllers\Controller; /** * Class AccountController */ -class AccountController extends Controller -{ -} +class AccountController extends Controller {} diff --git a/app/Api/V2/Controllers/Data/MassDestroy/AccountController.php b/app/Api/V2/Controllers/Data/MassDestroy/AccountController.php index 51c18b60fe..6d11f3473e 100644 --- a/app/Api/V2/Controllers/Data/MassDestroy/AccountController.php +++ b/app/Api/V2/Controllers/Data/MassDestroy/AccountController.php @@ -29,6 +29,4 @@ use FireflyIII\Api\V2\Controllers\Controller; /** * Class AccountController */ -class AccountController extends Controller -{ -} +class AccountController extends Controller {} diff --git a/app/Api/V2/Controllers/Model/Account/ShowController.php b/app/Api/V2/Controllers/Model/Account/ShowController.php index 0e90b1e3ab..e6c43c7cf1 100644 --- a/app/Api/V2/Controllers/Model/Account/ShowController.php +++ b/app/Api/V2/Controllers/Model/Account/ShowController.php @@ -46,6 +46,7 @@ class ShowController extends Controller return response() ->api($this->jsonApiObject('accounts', $account, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } } diff --git a/app/Api/V2/Controllers/Model/Bill/IndexController.php b/app/Api/V2/Controllers/Model/Bill/IndexController.php index 2d61611348..21e544fff2 100644 --- a/app/Api/V2/Controllers/Model/Bill/IndexController.php +++ b/app/Api/V2/Controllers/Model/Bill/IndexController.php @@ -1,6 +1,5 @@ json($this->jsonApiList('subscriptions', $paginator, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } } diff --git a/app/Api/V2/Controllers/Model/Bill/ShowController.php b/app/Api/V2/Controllers/Model/Bill/ShowController.php index 9576886d7e..863ed81a25 100644 --- a/app/Api/V2/Controllers/Model/Bill/ShowController.php +++ b/app/Api/V2/Controllers/Model/Bill/ShowController.php @@ -1,6 +1,5 @@ api($this->jsonApiObject('subscriptions', $bill, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } } diff --git a/app/Api/V2/Controllers/Model/Bill/SumController.php b/app/Api/V2/Controllers/Model/Bill/SumController.php index 7498025f01..c9e9111ca8 100644 --- a/app/Api/V2/Controllers/Model/Bill/SumController.php +++ b/app/Api/V2/Controllers/Model/Bill/SumController.php @@ -39,9 +39,6 @@ class SumController extends Controller private BillRepositoryInterface $repository; - /** - * - */ public function __construct() { parent::__construct(); @@ -54,7 +51,6 @@ class SumController extends Controller $this->repository->setUserGroup($userGroup); } - return $next($request); } ); @@ -66,9 +62,6 @@ class SumController extends Controller * * TODO see autocomplete/accountcontroller for list. * - * @param DateRequest $request - * - * @return JsonResponse * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function paid(DateRequest $request): JsonResponse @@ -85,10 +78,7 @@ class SumController extends Controller * * TODO see autocomplete/accountcontroller for list. * - * @param DateRequest $request * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * - * @return JsonResponse */ public function unpaid(DateRequest $request): JsonResponse { diff --git a/app/Api/V2/Controllers/Model/Budget/IndexController.php b/app/Api/V2/Controllers/Model/Budget/IndexController.php index 7b25687b55..845c574863 100644 --- a/app/Api/V2/Controllers/Model/Budget/IndexController.php +++ b/app/Api/V2/Controllers/Model/Budget/IndexController.php @@ -52,9 +52,6 @@ class IndexController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v2)#/budgets/listBudgets - * - * - * @return JsonResponse */ public function index(): JsonResponse { @@ -68,6 +65,7 @@ class IndexController extends Controller return response() ->api($this->jsonApiList('budgets', $paginator, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } } diff --git a/app/Api/V2/Controllers/Model/Budget/ShowController.php b/app/Api/V2/Controllers/Model/Budget/ShowController.php index 26c505a401..4a2654b669 100644 --- a/app/Api/V2/Controllers/Model/Budget/ShowController.php +++ b/app/Api/V2/Controllers/Model/Budget/ShowController.php @@ -1,6 +1,5 @@ json($this->jsonApiList('currencies', $paginator, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } - } diff --git a/app/Api/V2/Controllers/Model/PiggyBank/IndexController.php b/app/Api/V2/Controllers/Model/PiggyBank/IndexController.php index b0fa896470..a294633eab 100644 --- a/app/Api/V2/Controllers/Model/PiggyBank/IndexController.php +++ b/app/Api/V2/Controllers/Model/PiggyBank/IndexController.php @@ -1,6 +1,5 @@ json($this->jsonApiList('piggy-banks', $paginator, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } } diff --git a/app/Api/V2/Controllers/Model/Transaction/StoreController.php b/app/Api/V2/Controllers/Model/Transaction/StoreController.php index eb60af6921..476f163cde 100644 --- a/app/Api/V2/Controllers/Model/Transaction/StoreController.php +++ b/app/Api/V2/Controllers/Model/Transaction/StoreController.php @@ -1,6 +1,5 @@ middleware( function ($request, $next) { $this->groupRepository = app(TransactionGroupRepositoryInterface::class); + return $next($request); } ); @@ -63,13 +63,11 @@ class StoreController extends Controller /** * TODO this method is practically the same as the V1 method and borrows as much code as possible. * - * @return JsonResponse * @throws FireflyException * @throws ValidationException */ public function post(StoreRequest $request): JsonResponse { - app('log')->debug('Now in API v2 StoreController::store()'); $data = $request->getAll(); $userGroup = $request->getUserGroup(); @@ -88,12 +86,14 @@ class StoreController extends Controller ['transactions' => [['description' => $e->getMessage()]]], ['transactions.0.description' => new IsDuplicateTransaction()] ); + throw new ValidationException($validator); // @phpstan-ignore-line } catch (FireflyException $e) { // @phpstan-ignore-line app('log')->warning('Caught an exception. Return error message.'); app('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); // @phpstan-ignore-line } app('preferences')->mark(); @@ -103,13 +103,15 @@ class StoreController extends Controller /** @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); + ->setTransactionGroup($transactionGroup) + ; $selectedGroup = $collector->getGroups()->first(); if (null === $selectedGroup) { @@ -121,8 +123,7 @@ class StoreController extends Controller return response() ->api($this->jsonApiObject('transactions', $selectedGroup, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } - - } diff --git a/app/Api/V2/Controllers/Search/AccountController.php b/app/Api/V2/Controllers/Search/AccountController.php index 9c107899cf..af87a73b5a 100644 --- a/app/Api/V2/Controllers/Search/AccountController.php +++ b/app/Api/V2/Controllers/Search/AccountController.php @@ -29,6 +29,4 @@ use FireflyIII\Api\V2\Controllers\Controller; /** * Class AccountController */ -class AccountController extends Controller -{ -} +class AccountController extends Controller {} diff --git a/app/Api/V2/Controllers/Summary/BasicController.php b/app/Api/V2/Controllers/Summary/BasicController.php index 6166b21675..ef267b37a7 100644 --- a/app/Api/V2/Controllers/Summary/BasicController.php +++ b/app/Api/V2/Controllers/Summary/BasicController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V2\Controllers\Summary; use Carbon\Carbon; -use Exception; use FireflyIII\Api\V2\Controllers\Controller; use FireflyIII\Api\V2\Request\Generic\DateRequest; use FireflyIII\Exceptions\FireflyException; @@ -33,7 +32,6 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Report\NetWorthInterface; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionType; use FireflyIII\Models\UserGroup; use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface; @@ -43,6 +41,7 @@ use FireflyIII\Repositories\UserGroups\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\UserGroups\Budget\OperationsRepositoryInterface; use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Api\ExchangeRateConverter; +use FireflyIII\Support\Http\Api\SummaryBalanceGrouped; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\User; use Illuminate\Http\JsonResponse; @@ -63,8 +62,6 @@ class BasicController extends Controller /** * BasicController constructor. - * - */ public function __construct() { @@ -96,10 +93,8 @@ class BasicController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v2)#/summary/getBasicSummary * - * @param DateRequest $request + * @throws \Exception * - * @return JsonResponse - * @throws Exception * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function basic(DateRequest $request): JsonResponse @@ -114,32 +109,37 @@ class BasicController extends Controller $spentData = $this->getLeftToSpendInfo($start, $end); $netWorthData = $this->getNetWorthInfo($start, $end); $total = array_merge($balanceData, $billData, $spentData, $netWorthData); + return response()->json($total); } /** - * @param Carbon $start - * @param Carbon $end - * - * @return array + * Check if date is outside session range. + */ + protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference + { + $result = false; + if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) { + $result = true; + } + // start and end in the past? use $end + if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) { + $result = true; + } + + return $result; + } + + /** * @throws FireflyException */ private function getBalanceInformation(Carbon $start, Carbon $end): array { - // prep some arrays: - $incomes = [ - 'native' => '0', - ]; - $expenses = [ - 'native' => '0', - ]; - $sums = [ - 'native' => '0', - ]; - $return = []; - $currencies = []; - $converter = new ExchangeRateConverter(); - $default = app('amount')->getDefaultCurrency(); + $object = new SummaryBalanceGrouped(); + $default = app('amount')->getDefaultCurrency(); + + $object->setDefault($default); + /** @var User $user */ $user = auth()->user(); @@ -153,33 +153,11 @@ class BasicController extends Controller ->setPage($this->parameters->get('page')) // set types of transactions to return. ->setTypes([TransactionType::DEPOSIT]) - ->setRange($start, $end); + ->setRange($start, $end) + ; $set = $collector->getExtractedJournals(); - /** @var array $transactionJournal */ - foreach ($set as $transactionJournal) { - // transaction info: - $currencyId = (int)$transactionJournal['currency_id']; - $amount = bcmul($transactionJournal['amount'], '-1'); - $currency = $currencies[$currencyId] ?? TransactionCurrency::find($currencyId); - $currencies[$currencyId] = $currency; - $nativeAmount = $converter->convert($currency, $default, $transactionJournal['date'], $amount); - if ((int)$transactionJournal['foreign_currency_id'] === $default->id) { - // use foreign amount instead - $nativeAmount = $transactionJournal['foreign_amount']; - } - // prep the arrays - $incomes[$currencyId] ??= '0'; - $incomes['native'] ??= '0'; - $sums[$currencyId] ??= '0'; - $sums['native'] ??= '0'; - - // add values: - $incomes[$currencyId] = bcadd($incomes[$currencyId], $amount); - $sums[$currencyId] = bcadd($sums[$currencyId], $amount); - $incomes['native'] = bcadd($incomes['native'], $nativeAmount); - $sums['native'] = bcadd($sums['native'], $nativeAmount); - } + $object->groupTransactions('income', $set); // collect expenses of user using the new group collector. /** @var GroupCollectorInterface $collector */ @@ -191,105 +169,14 @@ class BasicController extends Controller ->setPage($this->parameters->get('page')) // set types of transactions to return. ->setTypes([TransactionType::WITHDRAWAL]) - ->setRange($start, $end); + ->setRange($start, $end) + ; $set = $collector->getExtractedJournals(); + $object->groupTransactions('expense', $set); - /** @var array $transactionJournal */ - foreach ($set as $transactionJournal) { - // transaction info - $currencyId = (int)$transactionJournal['currency_id']; - $amount = $transactionJournal['amount']; - $currency = $currencies[$currencyId] ?? $this->currencyRepos->find($currencyId); - $currencies[$currencyId] = $currency; - $nativeAmount = $converter->convert($currency, $default, $transactionJournal['date'], $amount); - if ((int)$transactionJournal['foreign_currency_id'] === $default->id) { - // use foreign amount instead - $nativeAmount = $transactionJournal['foreign_amount']; - } - - // prep arrays - $expenses[$currencyId] ??= '0'; - $expenses['native'] ??= '0'; - $sums[$currencyId] ??= '0'; - $sums['native'] ??= '0'; - - // add values - $expenses[$currencyId] = bcadd($expenses[$currencyId], $amount); - $sums[$currencyId] = bcadd($sums[$currencyId], $amount); - $expenses['native'] = bcadd($expenses['native'], $nativeAmount); - $sums['native'] = bcadd($sums['native'], $nativeAmount); - } - - // create special array for native currency: - $return[] = [ - 'key' => 'balance-in-native', - 'value' => $sums['native'], - 'currency_id' => (string)$default->id, - 'currency_code' => $default->code, - 'currency_symbol' => $default->symbol, - 'currency_decimal_places' => $default->decimal_places, - ]; - $return[] = [ - 'key' => 'spent-in-native', - 'value' => $expenses['native'], - 'currency_id' => (string)$default->id, - 'currency_code' => $default->code, - 'currency_symbol' => $default->symbol, - 'currency_decimal_places' => $default->decimal_places, - ]; - $return[] = [ - 'key' => 'earned-in-native', - 'value' => $incomes['native'], - 'currency_id' => (string)$default->id, - 'currency_code' => $default->code, - 'currency_symbol' => $default->symbol, - 'currency_decimal_places' => $default->decimal_places, - ]; - - // format amounts: - $keys = array_keys($sums); - foreach ($keys as $currencyId) { - if ('native' === $currencyId) { - // skip native entries. - continue; - } - $currency = $currencies[$currencyId] ?? $this->currencyRepos->find($currencyId); - $currencies[$currencyId] = $currency; - // create objects for big array. - $return[] = [ - 'key' => sprintf('balance-in-%s', $currency->code), - 'value' => $sums[$currencyId] ?? '0', - 'currency_id' => (string)$currency->id, - 'currency_code' => $currency->code, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => $currency->decimal_places, - ]; - $return[] = [ - 'key' => sprintf('spent-in-%s', $currency->code), - 'value' => $expenses[$currencyId] ?? '0', - 'currency_id' => (string)$currency->id, - 'currency_code' => $currency->code, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => $currency->decimal_places, - ]; - $return[] = [ - 'key' => sprintf('earned-in-%s', $currency->code), - 'value' => $incomes[$currencyId] ?? '0', - 'currency_id' => (string)$currency->id, - 'currency_code' => $currency->code, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => $currency->decimal_places, - ]; - } - return $return; + return $object->groupData(); } - /** - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ private function getBillInformation(Carbon $start, Carbon $end): array { /* @@ -300,6 +187,7 @@ class BasicController extends Controller $unpaidAmount = $this->billRepository->sumUnpaidInRange($start, $end); $return = []; + /** * @var array $info */ @@ -352,11 +240,7 @@ class BasicController extends Controller } /** - * @param Carbon $start - * @param Carbon $end - * - * @return array - * @throws Exception + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ private function getLeftToSpendInfo(Carbon $start, Carbon $end): array { @@ -394,13 +278,14 @@ class BasicController extends Controller */ foreach ($spent as $currencyId => $row) { app('log')->debug(sprintf('Processing spent array in currency #%d', $currencyId)); - $currencyId = $currencyId; $spent = '0'; $spentNative = '0'; + // get the sum from the array of transactions (double loop but who cares) /** @var array $budget */ foreach ($row['budgets'] as $budget) { app('log')->debug(sprintf('Processing expenses in budget "%s".', $budget['name'])); + /** @var array $journal */ foreach ($budget['transaction_journals'] as $journal) { $journalCurrencyId = $journal['currency_id']; @@ -465,16 +350,11 @@ class BasicController extends Controller } $return[] = $nativeLeft; $return[] = $nativePerDay; + $converter->summarize(); return $return; } - /** - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ private function getNetWorthInfo(Carbon $start, Carbon $end): array { /** @var UserGroup $userGroup */ @@ -529,28 +409,4 @@ class BasicController extends Controller return $return; } - - /** - * Check if date is outside session range. - * - * @param Carbon $date - * - * @param Carbon $start - * @param Carbon $end - * - * @return bool - */ - protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference - { - $result = false; - if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) { - $result = true; - } - // start and end in the past? use $end - if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) { - $result = true; - } - - return $result; - } } diff --git a/app/Api/V2/Controllers/Summary/NetWorthController.php b/app/Api/V2/Controllers/Summary/NetWorthController.php index 90cb1dac90..40ea958419 100644 --- a/app/Api/V2/Controllers/Summary/NetWorthController.php +++ b/app/Api/V2/Controllers/Summary/NetWorthController.php @@ -43,9 +43,6 @@ class NetWorthController extends Controller private NetWorthInterface $netWorth; private AccountRepositoryInterface $repository; - /** - * - */ public function __construct() { parent::__construct(); @@ -68,10 +65,6 @@ class NetWorthController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v2)#/net-worth/getNetWorth - * - * @param SingleDateRequest $request - * - * @return JsonResponse */ public function get(SingleDateRequest $request): JsonResponse { diff --git a/app/Api/V2/Controllers/System/ConfigurationController.php b/app/Api/V2/Controllers/System/ConfigurationController.php index 0002de25a4..e7c9b63ec6 100644 --- a/app/Api/V2/Controllers/System/ConfigurationController.php +++ b/app/Api/V2/Controllers/System/ConfigurationController.php @@ -27,6 +27,4 @@ namespace FireflyIII\Api\V2\Controllers\System; /** * Class ConfigurationController */ -class ConfigurationController -{ -} +class ConfigurationController {} diff --git a/app/Api/V2/Controllers/System/DebugController.php b/app/Api/V2/Controllers/System/DebugController.php index 5f9838c023..d87a51973c 100644 --- a/app/Api/V2/Controllers/System/DebugController.php +++ b/app/Api/V2/Controllers/System/DebugController.php @@ -29,6 +29,4 @@ use FireflyIII\Api\V2\Controllers\Controller; /** * Class DebugController */ -class DebugController extends Controller -{ -} +class DebugController extends Controller {} diff --git a/app/Api/V2/Controllers/System/PreferencesController.php b/app/Api/V2/Controllers/System/PreferencesController.php index 915ab2ce3d..5cc40ea225 100644 --- a/app/Api/V2/Controllers/System/PreferencesController.php +++ b/app/Api/V2/Controllers/System/PreferencesController.php @@ -37,15 +37,12 @@ class PreferencesController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v2)#/preferences/getPreference - * - * @param Preference $preference - * - * @return JsonResponse */ public function get(Preference $preference): JsonResponse { return response() ->json($this->jsonApiObject('preferences', $preference, new PreferenceTransformer())) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } } diff --git a/app/Api/V2/Controllers/System/VersionUpdateController.php b/app/Api/V2/Controllers/System/VersionUpdateController.php index 35d4b3fb3f..979bdb1ece 100644 --- a/app/Api/V2/Controllers/System/VersionUpdateController.php +++ b/app/Api/V2/Controllers/System/VersionUpdateController.php @@ -29,6 +29,4 @@ use FireflyIII\Api\V2\Controllers\Controller; /** * Class VersionUpdateController */ -class VersionUpdateController extends Controller -{ -} +class VersionUpdateController extends Controller {} diff --git a/app/Api/V2/Controllers/Transaction/List/AccountController.php b/app/Api/V2/Controllers/Transaction/List/AccountController.php index ceb6f7802d..d216255e90 100644 --- a/app/Api/V2/Controllers/Transaction/List/AccountController.php +++ b/app/Api/V2/Controllers/Transaction/List/AccountController.php @@ -43,11 +43,6 @@ class AccountController extends Controller /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v2)#/accounts/listTransactionByAccount - * - * @param ListRequest $request - * @param Account $account - * - * @return JsonResponse */ public function list(ListRequest $request, Account $account): JsonResponse { @@ -56,14 +51,14 @@ class AccountController extends Controller $page = max($page, 1); $pageSize = $this->parameters->get('limit'); - /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts(new Collection([$account])) - ->withAPIInformation() - ->setLimit($pageSize) - ->setPage($page) - ->setTypes($request->getTransactionTypes()); + ->withAPIInformation() + ->setLimit($pageSize) + ->setPage($page) + ->setTypes($request->getTransactionTypes()) + ; $start = $request->getStartDate(); $end = $request->getEndDate(); @@ -87,6 +82,7 @@ class AccountController extends Controller return response() ->json($this->jsonApiList('transactions', $paginator, new TransactionGroupTransformer())) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } } diff --git a/app/Api/V2/Controllers/Transaction/List/TransactionController.php b/app/Api/V2/Controllers/Transaction/List/TransactionController.php index 9bcc831a16..db23b4df73 100644 --- a/app/Api/V2/Controllers/Transaction/List/TransactionController.php +++ b/app/Api/V2/Controllers/Transaction/List/TransactionController.php @@ -34,11 +34,6 @@ use Illuminate\Http\JsonResponse; */ class TransactionController extends Controller { - /** - * @param ListRequest $request - * - * @return JsonResponse - */ public function list(ListRequest $request): JsonResponse { // collect transactions: @@ -46,14 +41,14 @@ class TransactionController extends Controller $page = $request->getPage(); $page = max($page, 1); - /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setUserGroup(auth()->user()->userGroup) - ->withAPIInformation() - ->setLimit($pageSize) - ->setPage($page) - ->setTypes($request->getTransactionTypes()); + ->withAPIInformation() + ->setLimit($pageSize) + ->setPage($page) + ->setTypes($request->getTransactionTypes()) + ; $start = $this->parameters->get('start'); $end = $this->parameters->get('end'); @@ -79,8 +74,7 @@ class TransactionController extends Controller return response() ->json($this->jsonApiList('transactions', $paginator, new TransactionGroupTransformer())) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } - - } diff --git a/app/Api/V2/Controllers/Transaction/Sum/BillController.php b/app/Api/V2/Controllers/Transaction/Sum/BillController.php index fc976b62f5..19428e98e2 100644 --- a/app/Api/V2/Controllers/Transaction/Sum/BillController.php +++ b/app/Api/V2/Controllers/Transaction/Sum/BillController.php @@ -29,6 +29,4 @@ use FireflyIII\Api\V2\Controllers\Controller; /** * Class BillController */ -class BillController extends Controller -{ -} +class BillController extends Controller {} diff --git a/app/Api/V2/Controllers/UserGroup/DestroyController.php b/app/Api/V2/Controllers/UserGroup/DestroyController.php index 23e01c0f9a..519b3e1ba1 100644 --- a/app/Api/V2/Controllers/UserGroup/DestroyController.php +++ b/app/Api/V2/Controllers/UserGroup/DestroyController.php @@ -1,6 +1,5 @@ repository->destroy($userGroup); + return response()->json([], 204); } } diff --git a/app/Api/V2/Controllers/UserGroup/ShowController.php b/app/Api/V2/Controllers/UserGroup/ShowController.php index 4b5c492128..903a5eed28 100644 --- a/app/Api/V2/Controllers/UserGroup/ShowController.php +++ b/app/Api/V2/Controllers/UserGroup/ShowController.php @@ -1,6 +1,5 @@ json($this->jsonApiList('user-groups', $paginator, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } - /** - * @param UserGroup $userGroup - * - * @return JsonResponse - */ public function show(UserGroup $userGroup): JsonResponse { $transformer = new UserGroupTransformer(); @@ -93,6 +82,7 @@ class ShowController extends Controller return response() ->api($this->jsonApiObject('user-groups', $userGroup, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } } diff --git a/app/Api/V2/Controllers/UserGroup/StoreController.php b/app/Api/V2/Controllers/UserGroup/StoreController.php index 4e74ab7c75..9b094cb3b9 100644 --- a/app/Api/V2/Controllers/UserGroup/StoreController.php +++ b/app/Api/V2/Controllers/UserGroup/StoreController.php @@ -1,6 +1,5 @@ getAll(); @@ -67,7 +58,7 @@ class StoreController extends Controller return response() ->api($this->jsonApiObject('user-groups', $userGroup, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } - } diff --git a/app/Api/V2/Controllers/UserGroup/UpdateController.php b/app/Api/V2/Controllers/UserGroup/UpdateController.php index 8630952bf6..afbcf1e9ee 100644 --- a/app/Api/V2/Controllers/UserGroup/UpdateController.php +++ b/app/Api/V2/Controllers/UserGroup/UpdateController.php @@ -1,6 +1,5 @@ getAll(); @@ -73,15 +63,10 @@ class UpdateController extends Controller return response() ->api($this->jsonApiObject('user-groups', $userGroup, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } - /** - * @param UpdateMembershipRequest $request - * @param UserGroup $userGroup - * - * @return JsonResponse - */ public function updateMembership(UpdateMembershipRequest $request, UserGroup $userGroup): JsonResponse { $all = $request->getAll(); @@ -91,6 +76,7 @@ class UpdateController extends Controller return response() ->api($this->jsonApiObject('user-groups', $userGroup, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } } diff --git a/app/Api/V2/Request/Autocomplete/AutocompleteRequest.php b/app/Api/V2/Request/Autocomplete/AutocompleteRequest.php index b33161b6ff..8e5daf6b28 100644 --- a/app/Api/V2/Request/Autocomplete/AutocompleteRequest.php +++ b/app/Api/V2/Request/Autocomplete/AutocompleteRequest.php @@ -39,9 +39,6 @@ class AutocompleteRequest extends FormRequest protected array $acceptedRoles = [UserRoleEnum::MANAGE_TRANSACTIONS]; - /** - * @return array - */ public function getData(): array { $types = $this->convertString('types'); @@ -63,9 +60,6 @@ class AutocompleteRequest extends FormRequest ]; } - /** - * @return array - */ public function rules(): array { return [ diff --git a/app/Api/V2/Request/Chart/BalanceChartRequest.php b/app/Api/V2/Request/Chart/BalanceChartRequest.php index c0023bb944..90970c768e 100644 --- a/app/Api/V2/Request/Chart/BalanceChartRequest.php +++ b/app/Api/V2/Request/Chart/BalanceChartRequest.php @@ -1,6 +1,5 @@ after( - static function (Validator $validator) { + static function (Validator $validator): void { // validate transaction query data. $data = $validator->getData(); if (!array_key_exists('accounts', $data)) { $validator->errors()->add('accounts', trans('validation.filled', ['attribute' => 'accounts'])); + return; } if (!is_array($data['accounts'])) { diff --git a/app/Api/V2/Request/Chart/DashboardChartRequest.php b/app/Api/V2/Request/Chart/DashboardChartRequest.php index c82596c799..8620a3a458 100644 --- a/app/Api/V2/Request/Chart/DashboardChartRequest.php +++ b/app/Api/V2/Request/Chart/DashboardChartRequest.php @@ -40,8 +40,6 @@ class DashboardChartRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -53,8 +51,6 @@ class DashboardChartRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -66,19 +62,14 @@ class DashboardChartRequest extends FormRequest ]; } - /** - * @param Validator $validator - * - * @return void - */ public function withValidator(Validator $validator): void { $validator->after( - static function (Validator $validator) { + static function (Validator $validator): void { // validate transaction query data. $data = $validator->getData(); if (!array_key_exists('accounts', $data)) { - //$validator->errors()->add('accounts', trans('validation.filled', ['attribute' => 'accounts'])); + // $validator->errors()->add('accounts', trans('validation.filled', ['attribute' => 'accounts'])); return; } if (!is_array($data['accounts'])) { diff --git a/app/Api/V2/Request/Generic/DateRequest.php b/app/Api/V2/Request/Generic/DateRequest.php index baa7b2c521..73aff18330 100644 --- a/app/Api/V2/Request/Generic/DateRequest.php +++ b/app/Api/V2/Request/Generic/DateRequest.php @@ -40,8 +40,6 @@ class DateRequest extends FormRequest /** * Get all data from the request. - * - * @return array */ public function getAll(): array { @@ -53,8 +51,6 @@ class DateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V2/Request/Generic/SingleDateRequest.php b/app/Api/V2/Request/Generic/SingleDateRequest.php index afd460cdcc..a9a3465b44 100644 --- a/app/Api/V2/Request/Generic/SingleDateRequest.php +++ b/app/Api/V2/Request/Generic/SingleDateRequest.php @@ -41,8 +41,6 @@ class SingleDateRequest extends FormRequest /** * Get all data from the request. - * - * @return Carbon */ public function getDate(): Carbon { @@ -51,8 +49,6 @@ class SingleDateRequest extends FormRequest /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { diff --git a/app/Api/V2/Request/Model/Transaction/ListRequest.php b/app/Api/V2/Request/Model/Transaction/ListRequest.php index 8913feea10..45286d5cbf 100644 --- a/app/Api/V2/Request/Model/Transaction/ListRequest.php +++ b/app/Api/V2/Request/Model/Transaction/ListRequest.php @@ -40,9 +40,6 @@ class ListRequest extends FormRequest use ConvertsDataTypes; use TransactionFilter; - /** - * @return string - */ public function buildParams(int $pageSize): string { $array = [ @@ -56,46 +53,34 @@ class ListRequest extends FormRequest $array['start'] = $start->format('Y-m-d'); $array['end'] = $end->format('Y-m-d'); } + return http_build_query($array); } - /** - * @return int - */ public function getPage(): int { $page = $this->convertInteger('page'); + return 0 === $page || $page > 65536 ? 1 : $page; } - /** - * @return Carbon|null - */ public function getStartDate(): ?Carbon { return $this->getCarbonDate('start'); } - /** - * @return Carbon|null - */ public function getEndDate(): ?Carbon { return $this->getCarbonDate('end'); } - /** - * @return array - */ public function getTransactionTypes(): array { $type = (string)$this->get('type', 'default'); + return $this->mapTransactionTypes($type); } - /** - * @return array - */ public function rules(): array { return [ diff --git a/app/Api/V2/Request/Model/Transaction/StoreRequest.php b/app/Api/V2/Request/Model/Transaction/StoreRequest.php index 0137fb22d2..72af551fb8 100644 --- a/app/Api/V2/Request/Model/Transaction/StoreRequest.php +++ b/app/Api/V2/Request/Model/Transaction/StoreRequest.php @@ -62,8 +62,6 @@ class StoreRequest extends FormRequest /** * Get all data. - * - * @return array */ public function getAll(): array { @@ -79,107 +77,8 @@ class StoreRequest extends FormRequest // TODO include location and ability to process it. } - /** - * Get transaction data. - * - * @return array - */ - private function getTransactionData(): array - { - $return = []; - /** - * @var array $transaction - */ - foreach ($this->get('transactions') as $transaction) { - $object = new NullArrayObject($transaction); - $return[] = [ - 'type' => $this->clearString($object['type']), - 'date' => $this->dateFromValue($object['date']), - 'order' => $this->integerFromValue((string)$object['order']), - - 'currency_id' => $this->integerFromValue((string)$object['currency_id']), - 'currency_code' => $this->clearString((string)$object['currency_code']), - - // foreign currency info: - 'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']), - 'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code']), - - // amount and foreign amount. Cannot be 0. - 'amount' => $this->clearString((string)$object['amount']), - 'foreign_amount' => $this->clearString((string)$object['foreign_amount']), - - // description. - 'description' => $this->clearString($object['description']), - - // source of transaction. If everything is null, assume cash account. - 'source_id' => $this->integerFromValue((string)$object['source_id']), - 'source_name' => $this->clearString((string)$object['source_name']), - 'source_iban' => $this->clearString((string)$object['source_iban']), - 'source_number' => $this->clearString((string)$object['source_number']), - 'source_bic' => $this->clearString((string)$object['source_bic']), - - // destination of transaction. If everything is null, assume cash account. - 'destination_id' => $this->integerFromValue((string)$object['destination_id']), - 'destination_name' => $this->clearString((string)$object['destination_name']), - 'destination_iban' => $this->clearString((string)$object['destination_iban']), - 'destination_number' => $this->clearString((string)$object['destination_number']), - 'destination_bic' => $this->clearString((string)$object['destination_bic']), - - // budget info - 'budget_id' => $this->integerFromValue((string)$object['budget_id']), - 'budget_name' => $this->clearString((string)$object['budget_name']), - - // category info - 'category_id' => $this->integerFromValue((string)$object['category_id']), - 'category_name' => $this->clearString((string)$object['category_name']), - - // journal bill reference. Optional. Will only work for withdrawals - 'bill_id' => $this->integerFromValue((string)$object['bill_id']), - 'bill_name' => $this->clearString((string)$object['bill_name']), - - // piggy bank reference. Optional. Will only work for transfers - 'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']), - 'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name']), - - // some other interesting properties - 'reconciled' => $this->convertBoolean((string)$object['reconciled']), - 'notes' => $this->clearStringKeepNewlines((string)$object['notes']), - 'tags' => $this->arrayFromValue($object['tags']), - - // all custom fields: - 'internal_reference' => $this->clearString((string)$object['internal_reference']), - 'external_id' => $this->clearString((string)$object['external_id']), - 'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')), - 'recurrence_id' => $this->integerFromValue($object['recurrence_id']), - 'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id']), - 'external_url' => $this->clearString((string)$object['external_url']), - - 'sepa_cc' => $this->clearString((string)$object['sepa_cc']), - 'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op']), - 'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id']), - 'sepa_db' => $this->clearString((string)$object['sepa_db']), - 'sepa_country' => $this->clearString((string)$object['sepa_country']), - 'sepa_ep' => $this->clearString((string)$object['sepa_ep']), - 'sepa_ci' => $this->clearString((string)$object['sepa_ci']), - 'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id']), - // custom date fields. Must be Carbon objects. Presence is optional. - 'interest_date' => $this->dateFromValue($object['interest_date']), - 'book_date' => $this->dateFromValue($object['book_date']), - 'process_date' => $this->dateFromValue($object['process_date']), - 'due_date' => $this->dateFromValue($object['due_date']), - 'payment_date' => $this->dateFromValue($object['payment_date']), - 'invoice_date' => $this->dateFromValue($object['invoice_date']), - - ]; - } - - return $return; - } - /** * The rules that the incoming request must be matched against. - * - * @return array */ public function rules(): array { @@ -272,19 +171,16 @@ class StoreRequest extends FormRequest /** * Configure the validator instance. - * - * @param Validator $validator - * - * @return void */ public function withValidator(Validator $validator): void { /** @var User $user */ $user = auth()->user(); + /** @var UserGroup $userGroup */ $userGroup = $this->getUserGroup(); $validator->after( - function (Validator $validator) use ($user, $userGroup) { + function (Validator $validator) use ($user, $userGroup): void { // must be valid array. $this->validateTransactionArray($validator); // does not need group validation. @@ -314,5 +210,98 @@ class StoreRequest extends FormRequest ); } + /** + * Get transaction data. + */ + private function getTransactionData(): array + { + $return = []; + /** + * @var array $transaction + */ + foreach ($this->get('transactions') as $transaction) { + $object = new NullArrayObject($transaction); + $return[] = [ + 'type' => $this->clearString($object['type']), + 'date' => $this->dateFromValue($object['date']), + 'order' => $this->integerFromValue((string)$object['order']), + + 'currency_id' => $this->integerFromValue((string)$object['currency_id']), + 'currency_code' => $this->clearString((string)$object['currency_code']), + + // foreign currency info: + 'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']), + 'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code']), + + // amount and foreign amount. Cannot be 0. + 'amount' => $this->clearString((string)$object['amount']), + 'foreign_amount' => $this->clearString((string)$object['foreign_amount']), + + // description. + 'description' => $this->clearString($object['description']), + + // source of transaction. If everything is null, assume cash account. + 'source_id' => $this->integerFromValue((string)$object['source_id']), + 'source_name' => $this->clearString((string)$object['source_name']), + 'source_iban' => $this->clearString((string)$object['source_iban']), + 'source_number' => $this->clearString((string)$object['source_number']), + 'source_bic' => $this->clearString((string)$object['source_bic']), + + // destination of transaction. If everything is null, assume cash account. + 'destination_id' => $this->integerFromValue((string)$object['destination_id']), + 'destination_name' => $this->clearString((string)$object['destination_name']), + 'destination_iban' => $this->clearString((string)$object['destination_iban']), + 'destination_number' => $this->clearString((string)$object['destination_number']), + 'destination_bic' => $this->clearString((string)$object['destination_bic']), + + // budget info + 'budget_id' => $this->integerFromValue((string)$object['budget_id']), + 'budget_name' => $this->clearString((string)$object['budget_name']), + + // category info + 'category_id' => $this->integerFromValue((string)$object['category_id']), + 'category_name' => $this->clearString((string)$object['category_name']), + + // journal bill reference. Optional. Will only work for withdrawals + 'bill_id' => $this->integerFromValue((string)$object['bill_id']), + 'bill_name' => $this->clearString((string)$object['bill_name']), + + // piggy bank reference. Optional. Will only work for transfers + 'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']), + 'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name']), + + // some other interesting properties + 'reconciled' => $this->convertBoolean((string)$object['reconciled']), + 'notes' => $this->clearStringKeepNewlines((string)$object['notes']), + 'tags' => $this->arrayFromValue($object['tags']), + + // all custom fields: + 'internal_reference' => $this->clearString((string)$object['internal_reference']), + 'external_id' => $this->clearString((string)$object['external_id']), + 'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')), + 'recurrence_id' => $this->integerFromValue($object['recurrence_id']), + 'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id']), + 'external_url' => $this->clearString((string)$object['external_url']), + + 'sepa_cc' => $this->clearString((string)$object['sepa_cc']), + 'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op']), + 'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id']), + 'sepa_db' => $this->clearString((string)$object['sepa_db']), + 'sepa_country' => $this->clearString((string)$object['sepa_country']), + 'sepa_ep' => $this->clearString((string)$object['sepa_ep']), + 'sepa_ci' => $this->clearString((string)$object['sepa_ci']), + 'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id']), + // custom date fields. Must be Carbon objects. Presence is optional. + 'interest_date' => $this->dateFromValue($object['interest_date']), + 'book_date' => $this->dateFromValue($object['book_date']), + 'process_date' => $this->dateFromValue($object['process_date']), + 'due_date' => $this->dateFromValue($object['due_date']), + 'payment_date' => $this->dateFromValue($object['payment_date']), + 'invoice_date' => $this->dateFromValue($object['invoice_date']), + ]; + } + + return $return; + } } diff --git a/app/Api/V2/Request/UserGroup/StoreRequest.php b/app/Api/V2/Request/UserGroup/StoreRequest.php index d90f71cf3c..376f8e9919 100644 --- a/app/Api/V2/Request/UserGroup/StoreRequest.php +++ b/app/Api/V2/Request/UserGroup/StoreRequest.php @@ -1,6 +1,5 @@ value; } + return [ 'id' => 'exists:users,id|required_without:email', 'email' => 'exists:users,email|required_without:id', - 'roles.*' => 'required|in:' . implode(',', $validRoles), + 'roles.*' => 'required|in:'.implode(',', $validRoles), ]; } } diff --git a/app/Api/V2/Request/UserGroup/UpdateRequest.php b/app/Api/V2/Request/UserGroup/UpdateRequest.php index 3ab965f776..fa21e6b5d4 100644 --- a/app/Api/V2/Request/UserGroup/UpdateRequest.php +++ b/app/Api/V2/Request/UserGroup/UpdateRequest.php @@ -1,6 +1,5 @@ route()->parameter('userGroup'); + return [ 'title' => sprintf('required|min:2|max:255|unique:user_groups,title,%d', $userGroup->id), ]; diff --git a/app/Api/V2/Response/Sum/AutoSum.php b/app/Api/V2/Response/Sum/AutoSum.php index bb1364839d..6a0903e466 100644 --- a/app/Api/V2/Response/Sum/AutoSum.php +++ b/app/Api/V2/Response/Sum/AutoSum.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V2\Response\Sum; -use Closure; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; use Illuminate\Database\Eloquent\Model; @@ -38,20 +37,17 @@ use Illuminate\Support\Collection; class AutoSum { /** - * @param Collection $objects - * @param Closure $getCurrency - * @param Closure $getSum - * - * @return array * @throws FireflyException */ - public function autoSum(Collection $objects, Closure $getCurrency, Closure $getSum): array + public function autoSum(Collection $objects, \Closure $getCurrency, \Closure $getSum): array { $return = []; + /** @var Model $object */ foreach ($objects as $object) { /** @var TransactionCurrency $currency */ $currency = $getCurrency($object); + /** @var string $amount */ $amount = $getSum($object); @@ -68,6 +64,7 @@ class AutoSum } var_dump(array_values($return)); + throw new FireflyException('Not implemented'); } } diff --git a/app/Console/Commands/Correction/CorrectAmounts.php b/app/Console/Commands/Correction/CorrectAmounts.php index 147fb0eafd..cc7d11042b 100644 --- a/app/Console/Commands/Correction/CorrectAmounts.php +++ b/app/Console/Commands/Correction/CorrectAmounts.php @@ -34,7 +34,6 @@ use FireflyIII\Models\PiggyBankRepetition; use FireflyIII\Models\RecurrenceTransaction; use FireflyIII\Models\RuleTrigger; use Illuminate\Console\Command; -use ValueError; /** * Class ReportSkeleton @@ -46,9 +45,6 @@ class CorrectAmounts extends Command protected $description = 'This command makes sure positive and negative amounts are recorded correctly.'; protected $signature = 'firefly-iii:fix-amount-pos-neg'; - /** - * @return int - */ public function handle(): int { // auto budgets must be positive @@ -70,13 +66,9 @@ class CorrectAmounts extends Command // rule_triggers must be positive or zero (amount_less, amount_more, amount_is) $this->fixRuleTriggers(); - return 0; } - /** - * @return void - */ private function fixAutoBudgets(): void { $set = AutoBudget::where('amount', '<', 0)->get(); @@ -86,6 +78,7 @@ class CorrectAmounts extends Command return; } + /** @var AutoBudget $item */ foreach ($set as $item) { $item->amount = app('steam')->positive($item->amount); @@ -94,9 +87,6 @@ class CorrectAmounts extends Command $this->friendlyInfo(sprintf('Corrected %d auto budget amount(s).', $count)); } - /** - * @return void - */ private function fixAvailableBudgets(): void { $set = AvailableBudget::where('amount', '<', 0)->get(); @@ -106,6 +96,7 @@ class CorrectAmounts extends Command return; } + /** @var AvailableBudget $item */ foreach ($set as $item) { $item->amount = app('steam')->positive($item->amount); @@ -114,9 +105,6 @@ class CorrectAmounts extends Command $this->friendlyInfo(sprintf('Corrected %d available budget amount(s).', $count)); } - /** - * @return void - */ private function fixBills(): void { $set = Bill::where('amount_min', '<', 0)->orWhere('amount_max', '<', 0)->get(); @@ -126,6 +114,7 @@ class CorrectAmounts extends Command return; } + /** @var Bill $item */ foreach ($set as $item) { $item->amount_min = app('steam')->positive($item->amount_min); @@ -135,9 +124,6 @@ class CorrectAmounts extends Command $this->friendlyInfo(sprintf('Corrected %d bill amount(s).', $count)); } - /** - * @return void - */ private function fixBudgetLimits(): void { $set = BudgetLimit::where('amount', '<', 0)->get(); @@ -147,6 +133,7 @@ class CorrectAmounts extends Command return; } + /** @var BudgetLimit $item */ foreach ($set as $item) { $item->amount = app('steam')->positive($item->amount); @@ -155,9 +142,6 @@ class CorrectAmounts extends Command $this->friendlyInfo(sprintf('Corrected %d budget limit amount(s).', $count)); } - /** - * @return void - */ private function fixExchangeRates(): void { $set = CurrencyExchangeRate::where('rate', '<', 0)->get(); @@ -167,6 +151,7 @@ class CorrectAmounts extends Command return; } + /** @var CurrencyExchangeRate $item */ foreach ($set as $item) { $item->rate = app('steam')->positive($item->rate); @@ -175,9 +160,6 @@ class CorrectAmounts extends Command $this->friendlyInfo(sprintf('Corrected %d currency exchange rate(s).', $count)); } - /** - * @return void - */ private function fixRepetitions(): void { $set = PiggyBankRepetition::where('currentamount', '<', 0)->get(); @@ -187,6 +169,7 @@ class CorrectAmounts extends Command return; } + /** @var PiggyBankRepetition $item */ foreach ($set as $item) { $item->currentamount = app('steam')->positive($item->currentamount); @@ -195,9 +178,6 @@ class CorrectAmounts extends Command $this->friendlyInfo(sprintf('Corrected %d piggy bank repetition amount(s).', $count)); } - /** - * @return void - */ private function fixPiggyBanks(): void { $set = PiggyBank::where('targetamount', '<', 0)->get(); @@ -207,6 +187,7 @@ class CorrectAmounts extends Command return; } + /** @var PiggyBank $item */ foreach ($set as $item) { $item->targetamount = app('steam')->positive($item->targetamount); @@ -215,20 +196,19 @@ class CorrectAmounts extends Command $this->friendlyInfo(sprintf('Corrected %d piggy bank amount(s).', $count)); } - /** - * @return void - */ private function fixRecurrences(): void { $set = RecurrenceTransaction::where('amount', '<', 0) - ->orWhere('foreign_amount', '<', 0) - ->get(); + ->orWhere('foreign_amount', '<', 0) + ->get() + ; $count = $set->count(); if (0 === $count) { $this->friendlyPositive('All recurring transaction amounts are positive.'); return; } + /** @var RecurrenceTransaction $item */ foreach ($set as $item) { $item->amount = app('steam')->positive($item->amount); @@ -238,27 +218,26 @@ class CorrectAmounts extends Command $this->friendlyInfo(sprintf('Corrected %d recurring transaction amount(s).', $count)); } - /** - * @return void - */ private function fixRuleTriggers(): void { $set = RuleTrigger::whereIn('trigger_type', ['amount_less', 'amount_more', 'amount_is'])->get(); $fixed = 0; + /** @var RuleTrigger $item */ foreach ($set as $item) { // basic check: $check = 0; + try { $check = bccomp((string)$item->trigger_value, '0'); - } catch (ValueError $e) { + } catch (\ValueError $e) { $this->friendlyError(sprintf('Rule #%d contained invalid %s-trigger "%s". The trigger has been removed, and the rule is disabled.', $item->rule_id, $item->trigger_type, $item->trigger_value)); $item->rule->active = false; $item->rule->save(); $item->forceDelete(); } if (-1 === $check) { - $fixed++; + ++$fixed; $item->trigger_value = app('steam')->positive($item->trigger_value); $item->save(); } @@ -270,5 +249,4 @@ class CorrectAmounts extends Command } $this->friendlyInfo(sprintf('Corrected %d rule trigger amount(s).', $fixed)); } - } diff --git a/app/Console/Commands/Correction/CorrectDatabase.php b/app/Console/Commands/Correction/CorrectDatabase.php index 73620febd8..8c22aaeab5 100644 --- a/app/Console/Commands/Correction/CorrectDatabase.php +++ b/app/Console/Commands/Correction/CorrectDatabase.php @@ -25,12 +25,9 @@ namespace FireflyIII\Console\Commands\Correction; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use Illuminate\Console\Command; -use Schema; /** * Class CorrectDatabase - * - */ class CorrectDatabase extends Command { @@ -45,7 +42,7 @@ class CorrectDatabase extends Command public function handle(): int { // if table does not exist, return false - if (!Schema::hasTable('users')) { + if (!\Schema::hasTable('users')) { $this->friendlyError('No "users"-table, will not continue.'); return 1; diff --git a/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php b/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php index 5a6ac44727..18f6d6a8e8 100644 --- a/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php +++ b/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php @@ -47,13 +47,12 @@ class CorrectOpeningBalanceCurrencies extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { $journals = $this->getJournals(); $count = 0; + /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $count += $this->correctJournal($journal); @@ -71,22 +70,15 @@ class CorrectOpeningBalanceCurrencies extends Command return 0; } - /** - * @return Collection - */ private function getJournals(): Collection { - /** @var Collection */ + // @var Collection return TransactionJournal::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') - ->whereNull('transaction_journals.deleted_at') - ->where('transaction_types.type', TransactionType::OPENING_BALANCE)->get(['transaction_journals.*']); + ->whereNull('transaction_journals.deleted_at') + ->where('transaction_types.type', TransactionType::OPENING_BALANCE)->get(['transaction_journals.*']) + ; } - /** - * @param TransactionJournal $journal - * - * @return int - */ private function correctJournal(TransactionJournal $journal): int { // get the asset account for this opening balance: @@ -103,17 +95,13 @@ class CorrectOpeningBalanceCurrencies extends Command return $this->setCorrectCurrency($account, $journal); } - /** - * @param TransactionJournal $journal - * - * @return Account|null - */ private function getAccount(TransactionJournal $journal): ?Account { $transactions = $journal->transactions()->get(); + /** @var Transaction $transaction */ foreach ($transactions as $transaction) { - /** @var Account|null $account */ + /** @var null|Account $account */ $account = $transaction->account()->first(); if (null !== $account && AccountType::INITIAL_BALANCE !== $account->accountType()->first()->type) { return $account; @@ -123,12 +111,6 @@ class CorrectOpeningBalanceCurrencies extends Command return null; } - /** - * @param Account $account - * @param TransactionJournal $journal - * - * @return int - */ private function setCorrectCurrency(Account $account, TransactionJournal $journal): int { $currency = $this->getCurrency($account); @@ -151,11 +133,6 @@ class CorrectOpeningBalanceCurrencies extends Command return $count; } - /** - * @param Account $account - * - * @return TransactionCurrency - */ private function getCurrency(Account $account): TransactionCurrency { /** @var AccountRepositoryInterface $repos */ diff --git a/app/Console/Commands/Correction/CreateAccessTokens.php b/app/Console/Commands/Correction/CreateAccessTokens.php index ec97a99e17..3b8692e04f 100644 --- a/app/Console/Commands/Correction/CreateAccessTokens.php +++ b/app/Console/Commands/Correction/CreateAccessTokens.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Correction; -use Exception; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; @@ -43,8 +42,7 @@ class CreateAccessTokens extends Command /** * Execute the console command. * - * @return int - * @throws Exception + * @throws \Exception */ public function handle(): int { @@ -54,6 +52,7 @@ class CreateAccessTokens extends Command $count = 0; $users = $repository->all(); + /** @var User $user */ foreach ($users as $user) { $pref = app('preferences')->getForUser($user, 'access_token'); diff --git a/app/Console/Commands/Correction/CreateLinkTypes.php b/app/Console/Commands/Correction/CreateLinkTypes.php index cc9700e0d6..38dbd4ce6a 100644 --- a/app/Console/Commands/Correction/CreateLinkTypes.php +++ b/app/Console/Commands/Correction/CreateLinkTypes.php @@ -40,8 +40,6 @@ class CreateLinkTypes extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { @@ -54,7 +52,8 @@ class CreateLinkTypes extends Command ]; foreach ($set as $name => $values) { $link = LinkType::where('name', $name) - ->first(); + ->first() + ; if (null === $link) { $link = new LinkType(); $link->name = $name; @@ -69,6 +68,7 @@ class CreateLinkTypes extends Command if (0 === $count) { $this->friendlyPositive('All link types are OK'); } + return 0; } } diff --git a/app/Console/Commands/Correction/DeleteEmptyGroups.php b/app/Console/Commands/Correction/DeleteEmptyGroups.php index 6948cfaa8f..fb75c35b80 100644 --- a/app/Console/Commands/Correction/DeleteEmptyGroups.php +++ b/app/Console/Commands/Correction/DeleteEmptyGroups.php @@ -41,15 +41,14 @@ class DeleteEmptyGroups extends Command /** * Execute the console command. * - * @return int * @throws Exception; - * */ public function handle(): int { $groupIds = TransactionGroup::leftJoin('transaction_journals', 'transaction_groups.id', '=', 'transaction_journals.transaction_group_id') - ->whereNull('transaction_journals.id')->get(['transaction_groups.id'])->pluck('id')->toArray(); + ->whereNull('transaction_journals.id')->get(['transaction_groups.id'])->pluck('id')->toArray() + ; $total = count($groupIds); if ($total > 0) { diff --git a/app/Console/Commands/Correction/DeleteEmptyJournals.php b/app/Console/Commands/Correction/DeleteEmptyJournals.php index ea6dcd2d7d..112c24577f 100644 --- a/app/Console/Commands/Correction/DeleteEmptyJournals.php +++ b/app/Console/Commands/Correction/DeleteEmptyJournals.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Correction; -use DB; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; @@ -43,8 +42,6 @@ class DeleteEmptyJournals extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { @@ -60,9 +57,11 @@ class DeleteEmptyJournals extends Command 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']); // @phpstan-ignore-line + ->groupBy('transactions.transaction_journal_id') + ->get([\DB::raw('COUNT(transactions.transaction_journal_id) as the_count'), 'transaction_journal_id']) // @phpstan-ignore-line + ; $total = 0; + /** @var Transaction $row */ foreach ($set as $row) { $count = (int)$row->the_count; @@ -75,12 +74,11 @@ class DeleteEmptyJournals extends Command app('log')->error($e->getTraceAsString()); } - Transaction::where('transaction_journal_id', $row->transaction_journal_id)->delete(); $this->friendlyWarning( sprintf('Deleted transaction journal #%d because it had an uneven number of transactions.', $row->transaction_journal_id) ); - $total++; + ++$total; } } if (0 === $total) { @@ -88,16 +86,14 @@ class DeleteEmptyJournals extends Command } } - /** - * @return void - */ private function deleteEmptyJournals(): void { $count = 0; $set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->groupBy('transaction_journals.id') - ->whereNull('transactions.transaction_journal_id') - ->get(['transaction_journals.id']); + ->groupBy('transaction_journals.id') + ->whereNull('transactions.transaction_journal_id') + ->get(['transaction_journals.id']) + ; foreach ($set as $entry) { try { @@ -107,7 +103,6 @@ class DeleteEmptyJournals extends Command app('log')->error($e->getTraceAsString()); } - $this->friendlyInfo(sprintf('Deleted empty transaction journal #%d', $entry->id)); ++$count; } diff --git a/app/Console/Commands/Correction/DeleteOrphanedTransactions.php b/app/Console/Commands/Correction/DeleteOrphanedTransactions.php index bc26535586..f039eb0ea6 100644 --- a/app/Console/Commands/Correction/DeleteOrphanedTransactions.php +++ b/app/Console/Commands/Correction/DeleteOrphanedTransactions.php @@ -23,12 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Correction; -use Exception; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use Illuminate\Console\Command; -use stdClass; /** * Deletes transactions where the journal has been deleted. @@ -44,8 +42,7 @@ class DeleteOrphanedTransactions extends Command /** * Execute the console command. * - * @return int - * @throws Exception + * @throws \Exception */ public function handle(): int { @@ -56,18 +53,17 @@ class DeleteOrphanedTransactions extends Command return 0; } - /** - * @return void - */ private function deleteOrphanedJournals(): void { $set = TransactionJournal::leftJoin('transaction_groups', 'transaction_journals.transaction_group_id', 'transaction_groups.id') - ->whereNotNull('transaction_groups.deleted_at') - ->whereNull('transaction_journals.deleted_at') - ->get(['transaction_journals.id', 'transaction_journals.transaction_group_id']); + ->whereNotNull('transaction_groups.deleted_at') + ->whereNull('transaction_journals.deleted_at') + ->get(['transaction_journals.id', 'transaction_journals.transaction_group_id']) + ; $count = $set->count(); if (0 === $count) { $this->friendlyPositive('No orphaned journals.'); + return; } $this->friendlyInfo(sprintf('Found %d orphaned journal(s).', $count)); @@ -87,22 +83,24 @@ class DeleteOrphanedTransactions extends Command } /** - * @throws Exception + * @throws \Exception */ private function deleteOrphanedTransactions(): void { $count = 0; $set = Transaction::leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->whereNotNull('transaction_journals.deleted_at') - ->whereNull('transactions.deleted_at') - ->whereNotNull('transactions.id') - ->get( - [ - 'transaction_journals.id as journal_id', - 'transactions.id as transaction_id', - ] - ); - /** @var stdClass $entry */ + ->whereNotNull('transaction_journals.deleted_at') + ->whereNull('transactions.deleted_at') + ->whereNotNull('transactions.id') + ->get( + [ + 'transaction_journals.id as journal_id', + 'transactions.id as transaction_id', + ] + ) + ; + + /** @var \stdClass $entry */ foreach ($set as $entry) { $transaction = Transaction::find((int)$entry->transaction_id); if (null !== $transaction) { @@ -122,16 +120,15 @@ class DeleteOrphanedTransactions extends Command } } - /** - * - */ private function deleteFromOrphanedAccounts(): void { $set = Transaction::leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id') - ->whereNotNull('accounts.deleted_at') - ->get(['transactions.*']); + ->whereNotNull('accounts.deleted_at') + ->get(['transactions.*']) + ; $count = 0; + /** @var Transaction $transaction */ foreach ($set as $transaction) { // delete journals @@ -147,7 +144,7 @@ class DeleteOrphanedTransactions extends Command $transaction->account_id ) ); - $count++; + ++$count; } if (0 === $count) { $this->friendlyPositive('No orphaned accounts.'); diff --git a/app/Console/Commands/Correction/DeleteZeroAmount.php b/app/Console/Commands/Correction/DeleteZeroAmount.php index 82774b1dad..819c32a011 100644 --- a/app/Console/Commands/Correction/DeleteZeroAmount.php +++ b/app/Console/Commands/Correction/DeleteZeroAmount.php @@ -41,14 +41,13 @@ class DeleteZeroAmount extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { $set = Transaction::where('amount', 0)->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray(); $set = array_unique($set); $journals = TransactionJournal::whereIn('id', $set)->get(); + /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $this->friendlyWarning(sprintf('Deleted transaction journal #%d because the amount is zero (0.00).', $journal->id)); diff --git a/app/Console/Commands/Correction/EnableCurrencies.php b/app/Console/Commands/Correction/EnableCurrencies.php index 774a0f5646..dc51124b5f 100644 --- a/app/Console/Commands/Correction/EnableCurrencies.php +++ b/app/Console/Commands/Correction/EnableCurrencies.php @@ -48,8 +48,6 @@ class EnableCurrencies extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { @@ -57,14 +55,10 @@ class EnableCurrencies extends Command foreach ($userGroups as $userGroup) { $this->correctCurrencies($userGroup); } + return CommandAlias::SUCCESS; } - /** - * @param UserGroup $userGroup - * - * @return void - */ private function correctCurrencies(UserGroup $userGroup): void { /** @var CurrencyRepositoryInterface $repos */ @@ -75,40 +69,41 @@ class EnableCurrencies extends Command Log::debug(sprintf('Now correcting currencies for user group #%d', $userGroup->id)); $found = [$defaultCurrency->id]; + // get all meta entries /** @var Collection $meta */ - $meta = AccountMeta - ::leftJoin('accounts', 'accounts.id', '=', 'account_meta.account_id') + $meta = AccountMeta::leftJoin('accounts', 'accounts.id', '=', 'account_meta.account_id') ->where('accounts.user_group_id', $userGroup->id) - ->where('account_meta.name', 'currency_id')->groupBy('data')->get(['data']); + ->where('account_meta.name', 'currency_id')->groupBy('data')->get(['data']) + ; foreach ($meta as $entry) { $found[] = (int)$entry->data; } // get all from journals: - $journals = TransactionJournal - ::where('user_group_id', $userGroup->id) - ->groupBy('transaction_currency_id')->get(['transaction_currency_id']); + $journals = TransactionJournal::where('user_group_id', $userGroup->id) + ->groupBy('transaction_currency_id')->get(['transaction_currency_id']) + ; foreach ($journals as $entry) { $found[] = (int)$entry->transaction_currency_id; } // get all from transactions - $transactions = Transaction - ::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + $transactions = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->where('transaction_journals.user_group_id', $userGroup->id) ->groupBy('transactions.transaction_currency_id', 'transactions.foreign_currency_id') - ->get(['transactions.transaction_currency_id', 'transactions.foreign_currency_id']); + ->get(['transactions.transaction_currency_id', 'transactions.foreign_currency_id']) + ; foreach ($transactions as $entry) { $found[] = (int)$entry->transaction_currency_id; $found[] = (int)$entry->foreign_currency_id; } // get all from budget limits - $limits = BudgetLimit - ::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') + $limits = BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') ->groupBy('transaction_currency_id') - ->get(['budget_limits.transaction_currency_id']); + ->get(['budget_limits.transaction_currency_id']) + ; foreach ($limits as $entry) { $found[] = $entry->transaction_currency_id; } @@ -118,12 +113,13 @@ class EnableCurrencies extends Command array_filter( $found, static function (int $currencyId) { - return $currencyId !== 0; + return 0 !== $currencyId; } ) ); $valid = new Collection(); + /** @var int $currencyId */ foreach ($found as $currencyId) { $currency = $repos->find($currencyId); @@ -134,12 +130,11 @@ class EnableCurrencies extends Command $ids = $valid->pluck('id')->toArray(); Log::debug(sprintf('Found currencies for user group #%d: %s', $userGroup->id, implode(', ', $ids))); $userGroup->currencies()->sync($ids); + /** @var GroupMembership $membership */ foreach ($userGroup->groupMemberships()->get() as $membership) { // make sure no individual different preferences. $membership->user->currencies()->sync([]); } - - } } diff --git a/app/Console/Commands/Correction/FixAccountOrder.php b/app/Console/Commands/Correction/FixAccountOrder.php index acc0276934..4928584052 100644 --- a/app/Console/Commands/Correction/FixAccountOrder.php +++ b/app/Console/Commands/Correction/FixAccountOrder.php @@ -42,8 +42,6 @@ class FixAccountOrder extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { @@ -64,8 +62,6 @@ class FixAccountOrder 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. - * - */ private function stupidLaravel(): void { diff --git a/app/Console/Commands/Correction/FixAccountTypes.php b/app/Console/Commands/Correction/FixAccountTypes.php index b7bb806317..e04c73d80d 100644 --- a/app/Console/Commands/Correction/FixAccountTypes.php +++ b/app/Console/Commands/Correction/FixAccountTypes.php @@ -34,7 +34,6 @@ use FireflyIII\Models\TransactionType; use Illuminate\Console\Command; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Query\JoinClause; -use JsonException; /** * Class FixAccountTypes @@ -52,8 +51,7 @@ class FixAccountTypes extends Command /** * Execute the console command. * - * @return int - * @throws FireflyException|JsonException + * @throws FireflyException */ public function handle(): int { @@ -63,29 +61,30 @@ class FixAccountTypes extends Command $expected = config('firefly.source_dests'); $query = TransactionJournal::leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id') - ->leftJoin( - 'transactions as source', - static function (JoinClause $join) { - $join->on('transaction_journals.id', '=', 'source.transaction_journal_id')->where('source.amount', '<', 0); - } - ) - ->leftJoin( - 'transactions as destination', - static function (JoinClause $join) { - $join->on('transaction_journals.id', '=', 'destination.transaction_journal_id')->where('destination.amount', '>', 0); - } - ) - ->leftJoin('accounts as source_account', 'source.account_id', '=', 'source_account.id') - ->leftJoin('accounts as destination_account', 'destination.account_id', '=', 'destination_account.id') - ->leftJoin('account_types as source_account_type', 'source_account.account_type_id', '=', 'source_account_type.id') - ->leftJoin('account_types as destination_account_type', 'destination_account.account_type_id', '=', 'destination_account_type.id'); + ->leftJoin( + 'transactions as source', + static function (JoinClause $join): void { + $join->on('transaction_journals.id', '=', 'source.transaction_journal_id')->where('source.amount', '<', 0); + } + ) + ->leftJoin( + 'transactions as destination', + static function (JoinClause $join): void { + $join->on('transaction_journals.id', '=', 'destination.transaction_journal_id')->where('destination.amount', '>', 0); + } + ) + ->leftJoin('accounts as source_account', 'source.account_id', '=', 'source_account.id') + ->leftJoin('accounts as destination_account', 'destination.account_id', '=', 'destination_account.id') + ->leftJoin('account_types as source_account_type', 'source_account.account_type_id', '=', 'source_account_type.id') + ->leftJoin('account_types as destination_account_type', 'destination_account.account_type_id', '=', 'destination_account_type.id') + ; // list all valid combinations, those are allowed. So we select those which are broken. - $query->where(static function (Builder $q) use ($expected) { + $query->where(static function (Builder $q) use ($expected): void { foreach ($expected as $transactionType => $info) { foreach ($info as $source => $destinations) { foreach ($destinations as $destination) { - $q->whereNot(static function (Builder $q1) use ($transactionType, $source, $destination) { + $q->whereNot(static function (Builder $q1) use ($transactionType, $source, $destination): void { $q1->where('transaction_types.type', $transactionType); $q1->where('source_account_type.type', $source); $q1->where('destination_account_type.type', $destination); @@ -98,15 +97,15 @@ class FixAccountTypes extends Command $resultSet = $query->get( [ 'transaction_journals.id', - //'transaction_type_id as type_id', + // 'transaction_type_id as type_id', 'transaction_types.type as journal_type', - //'source.id as source_transaction_id', - //'source_account.id as source_account_id', - //'source_account_type.id as source_account_type_id', + // 'source.id as source_transaction_id', + // 'source_account.id as source_account_id', + // 'source_account_type.id as source_account_type_id', 'source_account_type.type as source_account_type', - //'destination.id as destination_transaction_id', - //'destination_account.id as destination_account_id', - //'destination_account_type.id as destination_account_type_id', + // 'destination.id as destination_transaction_id', + // 'destination_account.id as destination_account_id', + // 'destination_account_type.id as destination_account_type_id', 'destination_account_type.type as destination_account_type', ] ); @@ -131,20 +130,11 @@ class FixAccountTypes extends Command return 0; } - /** - * @return void - */ private function stupidLaravel(): void { $this->count = 0; } - /** - * @param TransactionJournal $journal - * - * @throws FireflyException - * @throws JsonException - */ private function inspectJournal(TransactionJournal $journal): void { app('log')->debug(sprintf('Now inspecting journal #%d', $journal->id)); @@ -182,161 +172,217 @@ class FixAccountTypes extends Command } } - /** - * @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 - * @throws JsonException - */ - private function fixJournal(TransactionJournal $journal, string $type, Transaction $source, Transaction $dest): void + private function fixJournal(TransactionJournal $journal, string $transactionType, Transaction $source, Transaction $dest): void { app('log')->debug(sprintf('Going to fix journal #%d', $journal->id)); - $this->count++; + ++$this->count; // variables: - $combination = sprintf('%s%s%s', $type, $source->account->accountType->type, $dest->account->accountType->type); + $sourceType = $source->account->accountType->type; + $destinationType = $dest->account->accountType->type; + $combination = sprintf('%s%s%s', $transactionType, $source->account->accountType->type, $dest->account->accountType->type); app('log')->debug(sprintf('Combination is "%s"', $combination)); - switch ($combination) { - case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::LOAN): - case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::DEBT): - case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::MORTGAGE): - // from an asset to a liability should be a withdrawal: - $withdrawal = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); - $journal->transactionType()->associate($withdrawal); - $journal->save(); - $message = sprintf('Converted transaction #%d from a transfer to a withdrawal.', $journal->id); - $this->friendlyInfo($message); - app('log')->debug($message); - // check it again: - $this->inspectJournal($journal); - return; - case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::LOAN, AccountType::ASSET): - case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::DEBT, AccountType::ASSET): - case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::MORTGAGE, AccountType::ASSET): - // from a liability to an asset should be a deposit. - $deposit = TransactionType::whereType(TransactionType::DEPOSIT)->first(); - $journal->transactionType()->associate($deposit); - $journal->save(); - $message = sprintf('Converted transaction #%d from a transfer to a deposit.', $journal->id); - $this->friendlyInfo($message); - app('log')->debug($message); - // check it again: - $this->inspectJournal($journal); - return; - case sprintf('%s%s%s', TransactionType::WITHDRAWAL, AccountType::ASSET, AccountType::REVENUE): - // withdrawals with a revenue account as destination instead of an expense account. - $this->factory->setUser($journal->user); - $oldDest = $dest->account; - $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 - ); - $this->friendlyWarning($message); - app('log')->debug($message); - $this->inspectJournal($journal); - return; - case sprintf('%s%s%s', TransactionType::DEPOSIT, AccountType::EXPENSE, AccountType::ASSET): - // deposits with an expense account as source instead of a revenue account. - // find revenue account. - $this->factory->setUser($journal->user); - $result = $this->factory->findOrCreate($source->account->name, AccountType::REVENUE); - $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 - ); - $this->friendlyWarning($message); - app('log')->debug($message); - $this->inspectJournal($journal); - return; + if ($this->shouldBeTransfer($transactionType, $sourceType, $destinationType)) { + $this->makeTransfer($journal); + + return; + } + if ($this->shouldBeDeposit($transactionType, $sourceType, $destinationType)) { + $this->makeDeposit($journal); + + return; + } + if ($this->shouldGoToExpenseAccount($transactionType, $sourceType, $destinationType)) { + $this->makeExpenseDestination($journal, $dest); + + return; + } + if ($this->shouldComeFromRevenueAccount($transactionType, $sourceType, $destinationType)) { + $this->makeRevenueSource($journal, $source); + + return; } - app('log')->debug(sprintf('Fallback to fix transaction journal #%d of type "%s".', $journal->id, $type)); // transaction has no valid source. - $validSources = array_keys($this->expected[$type]); - if (!in_array($source->account->accountType->type, $validSources, true)) { - app('log')->debug('Journal has no valid source.'); - // perhaps we can create the account of type we need: + $validSources = array_keys($this->expected[$transactionType]); + $canCreateSource = $this->canCreateSource($validSources); + $hasValidSource = $this->hasValidAccountType($validSources, $sourceType); + if (!$hasValidSource && $canCreateSource) { + $this->giveNewRevenue($journal, $source); - if (in_array(AccountTypeEnum::REVENUE->value, $validSources, true)) { - app('log')->debug(sprintf('An account of type "%s" could be a valid source.', AccountTypeEnum::REVENUE->value)); - $this->factory->setUser($journal->user); - $newSource = $this->factory->findOrCreate($source->account->name, AccountTypeEnum::REVENUE->value); - $source->account()->associate($newSource); - $source->save(); - $this->friendlyPositive(sprintf('Firefly III gave transaction #%d a new source %s: #%d ("%s").', $journal->transaction_group_id, AccountTypeEnum::REVENUE->value, $newSource->id, $newSource->name)); - app('log')->debug(sprintf('Associated account #%d with transaction #%d', $newSource->id, $source->id)); - $this->inspectJournal($journal); - return; - } - if (!in_array(AccountTypeEnum::REVENUE->value, $validSources, true)) { - app('log')->debug('This transaction type has no source we can create. Just give error.'); - $message = sprintf('The source account of %s #%d cannot be of type "%s". Firefly III cannot fix this. You may have to remove the transaction yourself.', $type, $journal->id, $source->account->accountType->type); - $this->friendlyError($message); - app('log')->debug($message); - } + return; + } + if (!$canCreateSource && !$hasValidSource) { + app('log')->debug('This transaction type has no source we can create. Just give error.'); + $message = sprintf('The source account of %s #%d cannot be of type "%s". Firefly III cannot fix this. You may have to remove the transaction yourself.', $transactionType, $journal->id, $source->account->accountType->type); + $this->friendlyError($message); + app('log')->debug($message); + + return; } - // transaction has no valid destination: - $sourceType = $source->account->accountType->type; - $validDestinations = $this->expected[$type][$sourceType] ?? []; - if (!in_array($dest->account->accountType->type, $validDestinations, true)) { - app('log')->debug('Journal has no valid destination (perhaps because the source is also broken).'); - // perhaps we can create the account of type we need: - if (in_array(AccountTypeEnum::EXPENSE->value, $validDestinations, true)) { - app('log')->debug(sprintf('An account of type "%s" could be a valid destination.', AccountTypeEnum::EXPENSE->value)); - $this->factory->setUser($journal->user); - $newDestination = $this->factory->findOrCreate($dest->account->name, AccountTypeEnum::EXPENSE->value); - $dest->account()->associate($newDestination); - $dest->save(); - $this->friendlyPositive(sprintf('Firefly III gave transaction #%d a new destination %s: #%d ("%s").', $journal->transaction_group_id, AccountTypeEnum::EXPENSE->value, $newDestination->id, $newDestination->name)); - app('log')->debug(sprintf('Associated account #%d with transaction #%d', $newDestination->id, $source->id)); - $this->inspectJournal($journal); - return; - } - if (!in_array(AccountTypeEnum::EXPENSE->value, $validSources, true)) { - app('log')->debug('This transaction type has no destination we can create. Just give error.'); - $message = sprintf('The destination account of %s #%d cannot be of type "%s". Firefly III cannot fix this. You may have to remove the transaction yourself.', $type, $journal->id, $dest->account->accountType->type); - $this->friendlyError($message); - app('log')->debug($message); - return; - } + /** @var array $validDestinations */ + $validDestinations = $this->expected[$transactionType][$sourceType] ?? []; + $canCreateDestination = $this->canCreateDestination($validDestinations); + $hasValidDestination = $this->hasValidAccountType($validDestinations, $destinationType); + if (!$hasValidDestination && $canCreateDestination) { + $this->giveNewExpense($journal, $dest); + + return; + } + if (!$canCreateDestination && !$hasValidDestination) { + app('log')->debug('This transaction type has no destination we can create. Just give error.'); + $message = sprintf('The destination account of %s #%d cannot be of type "%s". Firefly III cannot fix this. You may have to remove the transaction yourself.', $transactionType, $journal->id, $dest->account->accountType->type); + $this->friendlyError($message); + app('log')->debug($message); } } + + private function isLiability(string $destinationType): bool + { + return AccountType::LOAN === $destinationType || AccountType::DEBT === $destinationType || AccountType::MORTGAGE === $destinationType; + } + + private function shouldBeTransfer(string $transactionType, string $sourceType, string $destinationType): bool + { + return TransactionType::TRANSFER === $transactionType && AccountType::ASSET === $sourceType && $this->isLiability($destinationType); + } + + private function shouldBeDeposit(string $transactionType, string $sourceType, string $destinationType): bool + { + return TransactionType::TRANSFER === $transactionType && $this->isLiability($sourceType) && AccountType::ASSET === $destinationType; + } + + private function shouldGoToExpenseAccount(string $transactionType, string $sourceType, string $destinationType): bool + { + return TransactionType::WITHDRAWAL === $transactionType && AccountType::ASSET === $sourceType && AccountType::REVENUE === $destinationType; + } + + private function shouldComeFromRevenueAccount(string $transactionType, string $sourceType, string $destinationType): bool + { + return TransactionType::DEPOSIT === $transactionType && AccountType::EXPENSE === $sourceType && AccountType::ASSET === $destinationType; + } + + private function makeTransfer(TransactionJournal $journal): void + { + // from an asset to a liability should be a withdrawal: + $withdrawal = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); + $journal->transactionType()->associate($withdrawal); + $journal->save(); + $message = sprintf('Converted transaction #%d from a transfer to a withdrawal.', $journal->id); + $this->friendlyInfo($message); + app('log')->debug($message); + // check it again: + $this->inspectJournal($journal); + } + + private function makeDeposit(TransactionJournal $journal): void + { + // from a liability to an asset should be a deposit. + $deposit = TransactionType::whereType(TransactionType::DEPOSIT)->first(); + $journal->transactionType()->associate($deposit); + $journal->save(); + $message = sprintf('Converted transaction #%d from a transfer to a deposit.', $journal->id); + $this->friendlyInfo($message); + app('log')->debug($message); + // check it again: + $this->inspectJournal($journal); + } + + private function makeExpenseDestination(TransactionJournal $journal, Transaction $destination): void + { + // withdrawals with a revenue account as destination instead of an expense account. + $this->factory->setUser($journal->user); + $oldDest = $destination->account; + $result = $this->factory->findOrCreate($destination->account->name, AccountType::EXPENSE); + $destination->account()->associate($result); + $destination->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 + ); + $this->friendlyWarning($message); + app('log')->debug($message); + $this->inspectJournal($journal); + } + + private function makeRevenueSource(TransactionJournal $journal, Transaction $source): void + { + // deposits with an expense account as source instead of a revenue account. + // find revenue account. + $this->factory->setUser($journal->user); + $result = $this->factory->findOrCreate($source->account->name, AccountType::REVENUE); + $oldSource = $source->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 + ); + $this->friendlyWarning($message); + app('log')->debug($message); + $this->inspectJournal($journal); + } + + /** + * Can only create revenue accounts out of the blue. + */ + private function canCreateSource(array $validSources): bool + { + return in_array(AccountTypeEnum::REVENUE->value, $validSources, true); + } + + private function hasValidAccountType(array $validTypes, string $accountType): bool + { + return in_array($accountType, $validTypes, true); + } + + private function canCreateDestination(array $validDestinations): bool + { + return in_array(AccountTypeEnum::EXPENSE->value, $validDestinations, true); + } + + private function giveNewRevenue(TransactionJournal $journal, Transaction $source): void + { + app('log')->debug(sprintf('An account of type "%s" could be a valid source.', AccountTypeEnum::REVENUE->value)); + $this->factory->setUser($journal->user); + $name = $source->account->name; + $newSource = $this->factory->findOrCreate($name, AccountTypeEnum::REVENUE->value); + $source->account()->associate($newSource); + $source->save(); + $this->friendlyPositive(sprintf('Firefly III gave transaction #%d a new source %s: #%d ("%s").', $journal->transaction_group_id, AccountTypeEnum::REVENUE->value, $newSource->id, $newSource->name)); + app('log')->debug(sprintf('Associated account #%d with transaction #%d', $newSource->id, $source->id)); + $this->inspectJournal($journal); + } + + private function giveNewExpense(TransactionJournal $journal, Transaction $destination): void + { + app('log')->debug(sprintf('An account of type "%s" could be a valid destination.', AccountTypeEnum::EXPENSE->value)); + $this->factory->setUser($journal->user); + $name = $destination->account->name; + $newDestination = $this->factory->findOrCreate($name, AccountTypeEnum::EXPENSE->value); + $destination->account()->associate($newDestination); + $destination->save(); + $this->friendlyPositive(sprintf('Firefly III gave transaction #%d a new destination %s: #%d ("%s").', $journal->transaction_group_id, AccountTypeEnum::EXPENSE->value, $newDestination->id, $newDestination->name)); + app('log')->debug(sprintf('Associated account #%d with transaction #%d', $newDestination->id, $destination->id)); + $this->inspectJournal($journal); + } } diff --git a/app/Console/Commands/Correction/FixFrontpageAccounts.php b/app/Console/Commands/Correction/FixFrontpageAccounts.php index e77524e720..9b39416eec 100644 --- a/app/Console/Commands/Correction/FixFrontpageAccounts.php +++ b/app/Console/Commands/Correction/FixFrontpageAccounts.php @@ -43,12 +43,11 @@ class FixFrontpageAccounts extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { $users = User::get(); + /** @var User $user */ foreach ($users as $user) { $preference = app('preferences')->getForUser($user, 'frontPageAccounts'); @@ -61,12 +60,10 @@ class FixFrontpageAccounts extends Command return 0; } - /** - * @param Preference $preference - */ private function fixPreference(Preference $preference): void { $fixed = []; + /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); if (null === $preference->user) { diff --git a/app/Console/Commands/Correction/FixGroupAccounts.php b/app/Console/Commands/Correction/FixGroupAccounts.php index 9575de2807..24c4233e1c 100644 --- a/app/Console/Commands/Correction/FixGroupAccounts.php +++ b/app/Console/Commands/Correction/FixGroupAccounts.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Correction; -use DB; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Events\UpdatedTransactionGroup; use FireflyIII\Handlers\Events\UpdatedGroupEventHandler; @@ -44,14 +43,14 @@ class FixGroupAccounts extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { $groups = []; $res = TransactionJournal::groupBy('transaction_group_id') - ->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]);// @phpstan-ignore-line + ->get(['transaction_group_id', \DB::raw('COUNT(transaction_group_id) as the_count')])// @phpstan-ignore-line + ; + /** @var TransactionJournal $journal */ foreach ($res as $journal) { if ((int)$journal->the_count > 1) { diff --git a/app/Console/Commands/Correction/FixIbans.php b/app/Console/Commands/Correction/FixIbans.php index 0717441a5a..4e8601b2d9 100644 --- a/app/Console/Commands/Correction/FixIbans.php +++ b/app/Console/Commands/Correction/FixIbans.php @@ -43,8 +43,6 @@ class FixIbans extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { @@ -58,11 +56,6 @@ class FixIbans extends Command return 0; } - /** - * @param Collection $accounts - * - * @return void - */ private function filterIbans(Collection $accounts): void { /** @var Account $account */ @@ -74,20 +67,16 @@ class FixIbans extends Command $account->iban = $iban; $account->save(); $this->friendlyInfo(sprintf('Removed spaces from IBAN of account #%d', $account->id)); - $this->count++; + ++$this->count; } } } } - /** - * @param Collection $accounts - * - * @return void - */ private function countAndCorrectIbans(Collection $accounts): void { $set = []; + /** @var Account $account */ foreach ($accounts as $account) { $userId = $account->user_id; @@ -103,9 +92,8 @@ class FixIbans extends Command if (array_key_exists($iban, $set[$userId])) { // iban already in use! two exceptions exist: if ( - !(AccountType::EXPENSE === $set[$userId][$iban] && AccountType::REVENUE === $type) - && // allowed combination - !(AccountType::REVENUE === $set[$userId][$iban] && AccountType::EXPENSE === $type) // also allowed combination. + !(AccountType::EXPENSE === $set[$userId][$iban] && AccountType::REVENUE === $type) // allowed combination + && !(AccountType::REVENUE === $set[$userId][$iban] && AccountType::EXPENSE === $type) // also allowed combination. ) { $this->friendlyWarning( sprintf( @@ -118,7 +106,7 @@ class FixIbans extends Command ); $account->iban = null; $account->save(); - $this->count++; + ++$this->count; } } diff --git a/app/Console/Commands/Correction/FixLongDescriptions.php b/app/Console/Commands/Correction/FixLongDescriptions.php index fe96827961..2b52327156 100644 --- a/app/Console/Commands/Correction/FixLongDescriptions.php +++ b/app/Console/Commands/Correction/FixLongDescriptions.php @@ -42,31 +42,31 @@ class FixLongDescriptions extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { $journals = TransactionJournal::get(['id', 'description']); $count = 0; + /** @var TransactionJournal $journal */ foreach ($journals as $journal) { if (strlen($journal->description) > self::MAX_LENGTH) { $journal->description = substr($journal->description, 0, self::MAX_LENGTH); $journal->save(); $this->friendlyWarning(sprintf('Truncated description of transaction journal #%d', $journal->id)); - $count++; + ++$count; } } $groups = TransactionGroup::get(['id', 'title']); + /** @var TransactionGroup $group */ foreach ($groups as $group) { if (strlen((string)$group->title) > self::MAX_LENGTH) { $group->title = substr($group->title, 0, self::MAX_LENGTH); $group->save(); $this->friendlyWarning(sprintf('Truncated description of transaction group #%d', $group->id)); - $count++; + ++$count; } } if (0 === $count) { diff --git a/app/Console/Commands/Correction/FixPiggies.php b/app/Console/Commands/Correction/FixPiggies.php index 8e15fc700a..7c5779e3b9 100644 --- a/app/Console/Commands/Correction/FixPiggies.php +++ b/app/Console/Commands/Correction/FixPiggies.php @@ -42,8 +42,6 @@ class FixPiggies extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { @@ -55,13 +53,15 @@ class FixPiggies extends Command if (null === $event->transaction_journal_id) { continue; } - /** @var TransactionJournal|null $journal */ + + /** @var null|TransactionJournal $journal */ $journal = $event->transactionJournal; if (null === $journal) { $event->transaction_journal_id = null; $event->save(); - $count++; + ++$count; + continue; } } diff --git a/app/Console/Commands/Correction/FixRecurringTransactions.php b/app/Console/Commands/Correction/FixRecurringTransactions.php index ac2fc02cf8..202d67c9a4 100644 --- a/app/Console/Commands/Correction/FixRecurringTransactions.php +++ b/app/Console/Commands/Correction/FixRecurringTransactions.php @@ -48,8 +48,6 @@ class FixRecurringTransactions extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { @@ -66,8 +64,6 @@ class FixRecurringTransactions 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. - * - */ private function stupidLaravel(): void { @@ -75,34 +71,27 @@ 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 - */ private function processUser(User $user): void { $this->recurringRepos->setUser($user); $recurrences = $this->recurringRepos->get(); + /** @var Recurrence $recurrence */ foreach ($recurrences as $recurrence) { $this->processRecurrence($recurrence); } } - /** - * @param Recurrence $recurrence - */ private function processRecurrence(Recurrence $recurrence): void { /** @var RecurrenceTransaction $transaction */ @@ -111,10 +100,6 @@ class FixRecurringTransactions extends Command } } - /** - * @param Recurrence $recurrence - * @param RecurrenceTransaction $transaction - */ private function processTransaction(Recurrence $recurrence, RecurrenceTransaction $transaction): void { $source = $transaction->sourceAccount; @@ -129,7 +114,7 @@ class FixRecurringTransactions extends Command if (null !== $transactionType) { $recurrence->transaction_type_id = $transactionType->id; $recurrence->save(); - $this->count++; + ++$this->count; } } } diff --git a/app/Console/Commands/Correction/FixTransactionTypes.php b/app/Console/Commands/Correction/FixTransactionTypes.php index c6d083f3a9..107329a141 100644 --- a/app/Console/Commands/Correction/FixTransactionTypes.php +++ b/app/Console/Commands/Correction/FixTransactionTypes.php @@ -45,18 +45,17 @@ class FixTransactionTypes extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { $count = 0; $journals = $this->collectJournals(); + /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $fixed = $this->fixJournal($journal); if (true === $fixed) { - $count++; + ++$count; } } if ($count > 0) { @@ -71,23 +70,18 @@ class FixTransactionTypes extends Command /** * Collect all transaction journals. - * - * @return Collection */ private function collectJournals(): Collection { return TransactionJournal::with(['transactionType', 'transactions', 'transactions.account', 'transactions.account.accountType']) - ->get(); + ->get() + ; } - /** - * @param TransactionJournal $journal - * - * @return bool - */ private function fixJournal(TransactionJournal $journal): bool { $type = $journal->transactionType->type; + try { $source = $this->getSourceAccount($journal); $destination = $this->getDestinationAccount($journal); @@ -117,9 +111,6 @@ class FixTransactionTypes extends Command } /** - * @param TransactionJournal $journal - * - * @return Account * @throws FireflyException */ private function getSourceAccount(TransactionJournal $journal): Account @@ -135,9 +126,11 @@ class FixTransactionTypes extends Command if (1 !== $collection->count()) { throw new FireflyException(sprintf('300002: Journal #%d has multiple source transactions.', $journal->id)); } + /** @var Transaction $transaction */ $transaction = $collection->first(); - /** @var Account|null $account */ + + /** @var null|Account $account */ $account = $transaction->account; if (null === $account) { throw new FireflyException(sprintf('300003: Journal #%d, transaction #%d has no source account.', $journal->id, $transaction->id)); @@ -147,9 +140,6 @@ class FixTransactionTypes extends Command } /** - * @param TransactionJournal $journal - * - * @return Account * @throws FireflyException */ private function getDestinationAccount(TransactionJournal $journal): Account @@ -165,9 +155,11 @@ class FixTransactionTypes extends Command if (1 !== $collection->count()) { throw new FireflyException(sprintf('300005: Journal #%d has multiple destination transactions.', $journal->id)); } + /** @var Transaction $transaction */ $transaction = $collection->first(); - /** @var Account|null $account */ + + /** @var null|Account $account */ $account = $transaction->account; if (null === $account) { throw new FireflyException(sprintf('300006: Journal #%d, transaction #%d has no destination account.', $journal->id, $transaction->id)); @@ -176,10 +168,6 @@ class FixTransactionTypes extends Command return $account; } - /** - * @param TransactionJournal $journal - * @param string $expectedType - */ private function changeJournal(TransactionJournal $journal, string $expectedType): void { $type = TransactionType::whereType($expectedType)->first(); diff --git a/app/Console/Commands/Correction/FixUnevenAmount.php b/app/Console/Commands/Correction/FixUnevenAmount.php index fb532f4ca3..080de0b7d9 100644 --- a/app/Console/Commands/Correction/FixUnevenAmount.php +++ b/app/Console/Commands/Correction/FixUnevenAmount.php @@ -23,14 +23,11 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Correction; -use DB; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use Illuminate\Console\Command; use Illuminate\Support\Facades\Log; -use stdClass; -use ValueError; /** * Class FixUnevenAmount @@ -44,23 +41,23 @@ class FixUnevenAmount extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { $count = 0; - $journals = DB::table('transactions') - ->groupBy('transaction_journal_id') - ->whereNull('deleted_at') - ->get(['transaction_journal_id', DB::raw('SUM(amount) AS the_sum')]); - /** @var stdClass $entry */ + $journals = \DB::table('transactions') + ->groupBy('transaction_journal_id') + ->whereNull('deleted_at') + ->get(['transaction_journal_id', \DB::raw('SUM(amount) AS the_sum')]) + ; + + /** @var \stdClass $entry */ foreach ($journals as $entry) { $sum = (string)$entry->the_sum; - if (!is_numeric($sum) || - '' === $sum || // @phpstan-ignore-line - str_contains($sum, 'e') || - str_contains($sum, ',')) { + if (!is_numeric($sum) + || '' === $sum // @phpstan-ignore-line + || str_contains($sum, 'e') + || str_contains($sum, ',')) { $message = sprintf( 'Journal #%d has an invalid sum ("%s"). No sure what to do.', $entry->transaction_journal_id, @@ -68,13 +65,15 @@ class FixUnevenAmount extends Command ); $this->friendlyWarning($message); app('log')->warning($message); - $count++; + ++$count; + continue; } $res = -1; + try { $res = bccomp($sum, '0'); - } catch (ValueError $e) { + } catch (\ValueError $e) { $this->friendlyError(sprintf('Could not bccomp("%s", "0").', $sum)); Log::error($e->getMessage()); Log::error($e->getTraceAsString()); @@ -88,7 +87,7 @@ class FixUnevenAmount extends Command $this->friendlyWarning($message); app('log')->warning($message); $this->fixJournal($entry->transaction_journal_id); - $count++; + ++$count; } } if (0 === $count) { @@ -98,9 +97,6 @@ class FixUnevenAmount extends Command return 0; } - /** - * @param int $param - */ private function fixJournal(int $param): void { // one of the transactions is bad. @@ -108,7 +104,8 @@ class FixUnevenAmount extends Command if (null === $journal) { return; } - /** @var Transaction|null $source */ + + /** @var null|Transaction $source */ $source = $journal->transactions()->where('amount', '<', 0)->first(); if (null === $source) { @@ -128,7 +125,7 @@ class FixUnevenAmount extends Command $amount = bcmul('-1', $source->amount); // fix amount of destination: - /** @var Transaction|null $destination */ + /** @var null|Transaction $destination */ $destination = $journal->transactions()->where('amount', '>', 0)->first(); if (null === $destination) { diff --git a/app/Console/Commands/Correction/RemoveBills.php b/app/Console/Commands/Correction/RemoveBills.php index a68ad038b4..d3fd0f87f1 100644 --- a/app/Console/Commands/Correction/RemoveBills.php +++ b/app/Console/Commands/Correction/RemoveBills.php @@ -40,17 +40,16 @@ class RemoveBills extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { - /** @var TransactionType|null $withdrawal */ + /** @var null|TransactionType $withdrawal */ $withdrawal = TransactionType::where('type', TransactionType::WITHDRAWAL)->first(); if (null === $withdrawal) { return 0; } $journals = TransactionJournal::whereNotNull('bill_id')->where('transaction_type_id', '!=', $withdrawal->id)->get(); + /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $this->friendlyWarning(sprintf('Transaction journal #%d will be unlinked from bill #%d.', $journal->id, $journal->bill_id)); diff --git a/app/Console/Commands/Correction/RenameMetaFields.php b/app/Console/Commands/Correction/RenameMetaFields.php index c2e1edd1bc..09ee5aa4e6 100644 --- a/app/Console/Commands/Correction/RenameMetaFields.php +++ b/app/Console/Commands/Correction/RenameMetaFields.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Correction; -use DB; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use Illuminate\Console\Command; @@ -41,8 +40,6 @@ class RenameMetaFields extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { @@ -69,18 +66,16 @@ class RenameMetaFields extends Command if (0 !== $this->count) { $this->friendlyInfo(sprintf('Renamed %d meta field(s).', $this->count)); } + return 0; } - /** - * @param string $original - * @param string $update - */ private function rename(string $original, string $update): void { - $total = DB::table('journal_meta') - ->where('name', '=', $original) - ->update(['name' => $update]); + $total = \DB::table('journal_meta') + ->where('name', '=', $original) + ->update(['name' => $update]) + ; $this->count += $total; } } diff --git a/app/Console/Commands/Correction/TransferBudgets.php b/app/Console/Commands/Correction/TransferBudgets.php index caf07f9b1c..e1bb7d3345 100644 --- a/app/Console/Commands/Correction/TransferBudgets.php +++ b/app/Console/Commands/Correction/TransferBudgets.php @@ -40,24 +40,24 @@ class TransferBudgets extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { $set = TransactionJournal::distinct() - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') - ->leftJoin('budget_transaction_journal', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id') - ->whereNotIn('transaction_types.type', [TransactionType::WITHDRAWAL]) - ->whereNotNull('budget_transaction_journal.budget_id')->get(['transaction_journals.*']); + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->leftJoin('budget_transaction_journal', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id') + ->whereNotIn('transaction_types.type', [TransactionType::WITHDRAWAL]) + ->whereNotNull('budget_transaction_journal.budget_id')->get(['transaction_journals.*']) + ; $count = 0; + /** @var TransactionJournal $entry */ foreach ($set as $entry) { $message = sprintf('Transaction journal #%d is a %s, so has no longer a budget.', $entry->id, $entry->transactionType->type); $this->friendlyInfo($message); app('log')->debug($message); $entry->budgets()->sync([]); - $count++; + ++$count; } if (0 === $count) { $message = 'No invalid budget/journal entries.'; @@ -68,6 +68,7 @@ class TransferBudgets extends Command app('log')->debug($message); $this->friendlyInfo($message); } + return 0; } } diff --git a/app/Console/Commands/Correction/TriggerCreditCalculation.php b/app/Console/Commands/Correction/TriggerCreditCalculation.php index 21be5aa4fe..1cefe73a3a 100644 --- a/app/Console/Commands/Correction/TriggerCreditCalculation.php +++ b/app/Console/Commands/Correction/TriggerCreditCalculation.php @@ -1,6 +1,5 @@ whereIn('account_types.type', config('firefly.valid_liabilities')) - ->get(['accounts.*']); + ->whereIn('account_types.type', config('firefly.valid_liabilities')) + ->get(['accounts.*']) + ; foreach ($accounts as $account) { $this->processAccount($account); } } - /** - * @param Account $account - * - * @return void - */ private function processAccount(Account $account): void { /** @var CreditRecalculateService $object */ diff --git a/app/Console/Commands/Export/ExportData.php b/app/Console/Commands/Export/ExportData.php index 8589dcb336..0e12413de4 100644 --- a/app/Console/Commands/Export/ExportData.php +++ b/app/Console/Commands/Export/ExportData.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Export; use Carbon\Carbon; -use Exception; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\VerifiesAccessToken; use FireflyIII\Exceptions\FireflyException; @@ -36,9 +35,6 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\Export\ExportDataGenerator; use Illuminate\Console\Command; use Illuminate\Support\Collection; -use InvalidArgumentException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class ExportData @@ -48,7 +44,6 @@ class ExportData extends Command use ShowsFriendlyMessages; use VerifiesAccessToken; - protected $description = 'Command to export data from Firefly III.'; protected $signature = 'firefly-iii:export-data @@ -74,10 +69,7 @@ class ExportData extends Command /** * Execute the console command. * - * @return int * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -92,6 +84,7 @@ class ExportData extends Command $user = $this->getUser(); $this->journalRepository->setUser($user); $this->accountRepository->setUser($user); + // get the options. try { $options = $this->parseOptions(); @@ -100,6 +93,7 @@ class ExportData extends Command return 1; } + // make export object and configure it. /** @var ExportDataGenerator $exporter */ $exporter = app(ExportDataGenerator::class); @@ -139,8 +133,6 @@ 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. - * - */ private function stupidLaravel(): void { @@ -149,9 +141,8 @@ class ExportData extends Command } /** - * @return array * @throws FireflyException - * @throws Exception + * @throws \Exception */ private function parseOptions(): array { @@ -181,10 +172,7 @@ class ExportData extends Command } /** - * @param string $field - * - * @return Carbon - * @throws Exception + * @throws \Exception */ private function getDateParameter(string $field): Carbon { @@ -198,13 +186,14 @@ class ExportData extends Command if (is_string($this->option($field))) { try { $date = Carbon::createFromFormat('!Y-m-d', $this->option($field)); - } catch (InvalidArgumentException $e) { + } catch (\InvalidArgumentException $e) { app('log')->error($e->getMessage()); $this->friendlyError(sprintf('%s date "%s" must be formatted YYYY-MM-DD. Field will be ignored.', $field, $this->option('start'))); $error = true; } if (false === $date) { $this->friendlyError(sprintf('%s date "%s" must be formatted YYYY-MM-DD.', $field, $this->option('start'))); + throw new FireflyException(sprintf('%s date "%s" must be formatted YYYY-MM-DD.', $field, $this->option('start'))); } } @@ -217,12 +206,14 @@ class ExportData extends Command $journal = $this->journalRepository->firstNull(); $date = null === $journal ? today(config('app.timezone'))->subYear() : $journal->date; $date->startOfDay(); + return $date; } // field can only be 'end' at this point, so no need to include it in the check. if (true === $error) { $date = today(config('app.timezone')); $date->endOfDay(); + return $date; } @@ -234,7 +225,6 @@ class ExportData extends Command } /** - * @return Collection * @throws FireflyException */ private function getAccountsParameter(): Collection @@ -250,6 +240,7 @@ class ExportData extends Command if ('' === $accountList) { $accounts = $this->accountRepository->getAccountsByType($types); } + // filter accounts, /** @var Account $account */ foreach ($accounts as $account) { @@ -265,9 +256,7 @@ class ExportData extends Command } /** - * @return string * @throws FireflyException - * */ private function getExportDirectory(): string { @@ -283,9 +272,6 @@ class ExportData extends Command } /** - * @param array $options - * @param array $data - * * @throws FireflyException */ private function exportData(array $options, array $data): void diff --git a/app/Console/Commands/Integrity/CreateGroupMemberships.php b/app/Console/Commands/Integrity/CreateGroupMemberships.php index 5f9f3d0bc5..cee66c6ce2 100644 --- a/app/Console/Commands/Integrity/CreateGroupMemberships.php +++ b/app/Console/Commands/Integrity/CreateGroupMemberships.php @@ -47,7 +47,6 @@ class CreateGroupMemberships extends Command /** * Execute the console command. * - * @return int * @throws FireflyException */ public function handle(): int @@ -58,24 +57,9 @@ class CreateGroupMemberships extends Command return 0; } - /** - * - * @throws FireflyException - */ - private function createGroupMemberships(): void - { - $users = User::get(); - /** @var User $user */ - foreach ($users as $user) { - self::createGroupMembership($user); - } - } - /** * TODO move to helper. * - * @param User $user - * * @throws FireflyException */ public static function createGroupMembership(User $user): void @@ -92,8 +76,9 @@ class CreateGroupMemberships extends Command throw new FireflyException('Firefly III could not find a user role. Please make sure all migrations have run.'); } $membership = GroupMembership::where('user_id', $user->id) - ->where('user_group_id', $userGroup->id) - ->where('user_role_id', $userRole->id)->first(); + ->where('user_group_id', $userGroup->id) + ->where('user_role_id', $userRole->id)->first() + ; if (null === $membership) { GroupMembership::create( [ @@ -108,4 +93,17 @@ class CreateGroupMemberships extends Command $user->save(); } } + + /** + * @throws FireflyException + */ + private function createGroupMemberships(): void + { + $users = User::get(); + + /** @var User $user */ + foreach ($users as $user) { + self::createGroupMembership($user); + } + } } diff --git a/app/Console/Commands/Integrity/ReportEmptyObjects.php b/app/Console/Commands/Integrity/ReportEmptyObjects.php index b9a396cbe0..7f7524f4de 100644 --- a/app/Console/Commands/Integrity/ReportEmptyObjects.php +++ b/app/Console/Commands/Integrity/ReportEmptyObjects.php @@ -29,7 +29,6 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\Category; use FireflyIII\Models\Tag; use Illuminate\Console\Command; -use stdClass; /** * Class ReportEmptyObjects @@ -38,15 +37,12 @@ class ReportEmptyObjects extends Command { use ShowsFriendlyMessages; - protected $description = 'Reports on empty database objects.'; protected $signature = 'firefly-iii:report-empty-objects'; /** * Execute the console command. - * - * @return int */ public function handle(): int { @@ -65,13 +61,14 @@ class ReportEmptyObjects extends Command private function reportEmptyBudgets(): void { $set = Budget::leftJoin('budget_transaction_journal', 'budgets.id', '=', 'budget_transaction_journal.budget_id') - ->leftJoin('users', 'budgets.user_id', '=', 'users.id') - ->distinct() - ->whereNull('budget_transaction_journal.budget_id') - ->whereNull('budgets.deleted_at') - ->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email']); + ->leftJoin('users', 'budgets.user_id', '=', 'users.id') + ->distinct() + ->whereNull('budget_transaction_journal.budget_id') + ->whereNull('budgets.deleted_at') + ->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email']) + ; - /** @var stdClass $entry */ + /** @var \stdClass $entry */ foreach ($set as $entry) { $line = sprintf( 'User #%d (%s) has budget #%d ("%s") which has no transaction journals.', @@ -90,13 +87,14 @@ class ReportEmptyObjects extends Command private function reportEmptyCategories(): void { $set = Category::leftJoin('category_transaction_journal', 'categories.id', '=', 'category_transaction_journal.category_id') - ->leftJoin('users', 'categories.user_id', '=', 'users.id') - ->distinct() - ->whereNull('category_transaction_journal.category_id') - ->whereNull('categories.deleted_at') - ->get(['categories.id', 'categories.name', 'categories.user_id', 'users.email']); + ->leftJoin('users', 'categories.user_id', '=', 'users.id') + ->distinct() + ->whereNull('category_transaction_journal.category_id') + ->whereNull('categories.deleted_at') + ->get(['categories.id', 'categories.name', 'categories.user_id', 'users.email']) + ; - /** @var stdClass $entry */ + /** @var \stdClass $entry */ foreach ($set as $entry) { $line = sprintf( 'User #%d (%s) has category #%d ("%s") which has no transaction journals.', @@ -109,19 +107,17 @@ class ReportEmptyObjects extends Command } } - /** - * - */ private function reportEmptyTags(): void { $set = Tag::leftJoin('tag_transaction_journal', 'tags.id', '=', 'tag_transaction_journal.tag_id') - ->leftJoin('users', 'tags.user_id', '=', 'users.id') - ->distinct() - ->whereNull('tag_transaction_journal.tag_id') - ->whereNull('tags.deleted_at') - ->get(['tags.id', 'tags.tag', 'tags.user_id', 'users.email']); + ->leftJoin('users', 'tags.user_id', '=', 'users.id') + ->distinct() + ->whereNull('tag_transaction_journal.tag_id') + ->whereNull('tags.deleted_at') + ->get(['tags.id', 'tags.tag', 'tags.user_id', 'users.email']) + ; - /** @var stdClass $entry */ + /** @var \stdClass $entry */ foreach ($set as $entry) { $line = sprintf( 'User #%d (%s) has tag #%d ("%s") which has no transaction journals.', @@ -140,14 +136,15 @@ class ReportEmptyObjects extends Command 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'] - ); + ->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 */ + /** @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); @@ -161,10 +158,11 @@ class ReportEmptyObjects extends Command 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']); + ->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) { diff --git a/app/Console/Commands/Integrity/ReportIntegrity.php b/app/Console/Commands/Integrity/ReportIntegrity.php index 4ace551c88..9ec1eb8243 100644 --- a/app/Console/Commands/Integrity/ReportIntegrity.php +++ b/app/Console/Commands/Integrity/ReportIntegrity.php @@ -25,18 +25,14 @@ namespace FireflyIII\Console\Commands\Integrity; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use Illuminate\Console\Command; -use Schema; /** * Class ReportIntegrity - * - */ class ReportIntegrity extends Command { use ShowsFriendlyMessages; - protected $description = 'Will report on the integrity of your database.'; protected $signature = 'firefly-iii:report-integrity'; @@ -47,7 +43,7 @@ class ReportIntegrity extends Command public function handle(): int { // if table does not exist, return false - if (!Schema::hasTable('users')) { + if (!\Schema::hasTable('users')) { return 1; } $commands = [ diff --git a/app/Console/Commands/Integrity/ReportSum.php b/app/Console/Commands/Integrity/ReportSum.php index 9dc5192edf..a586b9b507 100644 --- a/app/Console/Commands/Integrity/ReportSum.php +++ b/app/Console/Commands/Integrity/ReportSum.php @@ -40,8 +40,6 @@ class ReportSum extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { @@ -64,6 +62,7 @@ class ReportSum extends Command if (!is_numeric($sum)) { $message = sprintf('Error: Transactions for user #%d (%s) have an invalid sum ("%s").', $user->id, $user->email, $sum); $this->friendlyError($message); + continue; } if (0 !== bccomp($sum, '0')) { diff --git a/app/Console/Commands/Integrity/RestoreOAuthKeys.php b/app/Console/Commands/Integrity/RestoreOAuthKeys.php index 463b270c91..b84a7eec14 100644 --- a/app/Console/Commands/Integrity/RestoreOAuthKeys.php +++ b/app/Console/Commands/Integrity/RestoreOAuthKeys.php @@ -40,8 +40,6 @@ class RestoreOAuthKeys extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { @@ -50,9 +48,6 @@ class RestoreOAuthKeys extends Command return 0; } - /** - * - */ private function restoreOAuthKeys(): void { if (!$this->keysInDatabase() && !$this->keysOnDrive()) { @@ -84,41 +79,26 @@ class RestoreOAuthKeys extends Command $this->friendlyPositive('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(); } - /** - * - */ private function storeKeysInDB(): void { OAuthKeys::storeKeysInDB(); } - /** - * - */ private function restoreKeysFromDB(): bool { return OAuthKeys::restoreKeysFromDB(); diff --git a/app/Console/Commands/Integrity/UpdateGroupInformation.php b/app/Console/Commands/Integrity/UpdateGroupInformation.php index 305026d943..24c11715b7 100644 --- a/app/Console/Commands/Integrity/UpdateGroupInformation.php +++ b/app/Console/Commands/Integrity/UpdateGroupInformation.php @@ -66,6 +66,7 @@ class UpdateGroupInformation extends Command // recurrences, rule groups, rules, tags, transaction groups, transaction journals, webhooks $users = User::get(); + /** @var User $user */ foreach ($users as $user) { $this->updateGroupInfo($user); @@ -74,11 +75,6 @@ class UpdateGroupInformation extends Command return 0; } - /** - * @param User $user - * - * @return void - */ private function updateGroupInfo(User $user): void { $group = $user->userGroup; @@ -109,13 +105,6 @@ class UpdateGroupInformation extends Command } } - /** - * @param User $user - * @param UserGroup $group - * @param string $className - * - * @return void - */ private function updateGroupInfoForObject(User $user, UserGroup $group, string $className): void { try { diff --git a/app/Console/Commands/ShowsFriendlyMessages.php b/app/Console/Commands/ShowsFriendlyMessages.php index bc40b466d1..1046218d0f 100644 --- a/app/Console/Commands/ShowsFriendlyMessages.php +++ b/app/Console/Commands/ShowsFriendlyMessages.php @@ -1,6 +1,5 @@ error(sprintf(' [x] %s', trim($message))); } - /** - * @param string $message - * - * @return void - */ public function friendlyInfo(string $message): void { $this->friendlyNeutral($message); } - /** - * @param string $message - * - * @return void - */ public function friendlyNeutral(string $message): void { $this->line(sprintf(' [i] %s', trim($message))); } - /** - * @param string $message - * - * @return void - */ public function friendlyLine(string $message): void { $this->line(sprintf(' %s', trim($message))); } - /** - * @param string $message - * - * @return void - */ public function friendlyPositive(string $message): void { $this->info(sprintf(' [✓] %s', trim($message))); } - /** - * @param string $message - * - * @return void - */ public function friendlyWarning(string $message): void { $this->warn(sprintf(' [!] %s', trim($message))); } - } diff --git a/app/Console/Commands/System/CreateDatabase.php b/app/Console/Commands/System/CreateDatabase.php index 3f80f6637c..345cf108a7 100644 --- a/app/Console/Commands/System/CreateDatabase.php +++ b/app/Console/Commands/System/CreateDatabase.php @@ -27,7 +27,6 @@ namespace FireflyIII\Console\Commands\System; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use Illuminate\Console\Command; use PDO; -use PDOException; /** * Class CreateDatabase @@ -36,7 +35,6 @@ class CreateDatabase extends Command { use ShowsFriendlyMessages; - protected $description = 'Tries to create the database if it doesn\'t exist yet.'; protected $signature = 'firefly-iii:create-database'; @@ -44,7 +42,7 @@ class CreateDatabase extends Command /** * Execute the console command. * - * @return int + * @suppressWarnings(PHPMD.MissingImport) */ public function handle(): int { @@ -63,16 +61,17 @@ class CreateDatabase extends Command $this->friendlyLine(sprintf('DSN is %s', $dsn)); $options = [ - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_EMULATE_PREPARES => false, + \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, + \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, + \PDO::ATTR_EMULATE_PREPARES => false, ]; // when it fails, display error try { - $pdo = new PDO($dsn, env('DB_USERNAME'), env('DB_PASSWORD'), $options); - } catch (PDOException $e) { + $pdo = new \PDO($dsn, env('DB_USERNAME'), env('DB_PASSWORD'), $options); + } catch (\PDOException $e) { $this->friendlyError(sprintf('Error when connecting to DB: %s', $e->getMessage())); + return 1; } diff --git a/app/Console/Commands/System/CreateFirstUser.php b/app/Console/Commands/System/CreateFirstUser.php index 0cdc5370e6..7f7a0e32b7 100644 --- a/app/Console/Commands/System/CreateFirstUser.php +++ b/app/Console/Commands/System/CreateFirstUser.php @@ -28,18 +28,14 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Repositories\User\UserRepositoryInterface; use Illuminate\Console\Command; use Illuminate\Support\Facades\Hash; -use Str; /** * Class CreateFirstUser - * - * @package FireflyIII\Console\Commands */ class CreateFirstUser extends Command { use ShowsFriendlyMessages; - protected $description = 'Creates a new user and gives admin rights. Outputs the password on the command line. Strictly for testing.'; protected $signature = 'firefly-iii:create-first-user {email}'; @@ -47,8 +43,6 @@ class CreateFirstUser extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { @@ -70,11 +64,11 @@ class CreateFirstUser extends Command 'email' => $this->argument('email'), 'role' => 'owner', ]; - $password = Str::random(24); + $password = \Str::random(24); $user = $this->repository->store($data); $user->password = Hash::make($password); $user->save(); - $user->setRememberToken(Str::random(60)); + $user->setRememberToken(\Str::random(60)); $this->friendlyInfo(sprintf('Created new admin user (ID #%d) with email address "%s" and password "%s".', $user->id, $user->email, $password)); $this->friendlyWarning('Change this password.'); @@ -86,8 +80,6 @@ class CreateFirstUser 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. - * - */ private function stupidLaravel(): void { diff --git a/app/Console/Commands/System/ForceDecimalSize.php b/app/Console/Commands/System/ForceDecimalSize.php index 08e86cc706..c7be65a7a7 100644 --- a/app/Console/Commands/System/ForceDecimalSize.php +++ b/app/Console/Commands/System/ForceDecimalSize.php @@ -58,17 +58,17 @@ class ForceDecimalSize extends Command private string $cast; private array $classes = [ - 'accounts' => Account::class, - 'auto_budgets' => AutoBudget::class, - 'available_budgets' => AvailableBudget::class, - 'bills' => Bill::class, - 'budget_limits' => BudgetLimit::class, - 'piggy_bank_events' => PiggyBankEvent::class, - 'piggy_bank_repetitions' => PiggyBankRepetition::class, - 'piggy_banks' => PiggyBank::class, - 'recurrences_transactions' => RecurrenceTransaction::class, - 'transactions' => Transaction::class, - ]; + 'accounts' => Account::class, + 'auto_budgets' => AutoBudget::class, + 'available_budgets' => AvailableBudget::class, + 'bills' => Bill::class, + 'budget_limits' => BudgetLimit::class, + 'piggy_bank_events' => PiggyBankEvent::class, + 'piggy_bank_repetitions' => PiggyBankRepetition::class, + 'piggy_banks' => PiggyBank::class, + 'recurrences_transactions' => RecurrenceTransaction::class, + 'transactions' => Transaction::class, + ]; private string $operator; private string $regularExpression; @@ -90,7 +90,6 @@ class ForceDecimalSize extends Command /** * Execute the console command. - * */ public function handle(): int { @@ -108,9 +107,6 @@ class ForceDecimalSize extends Command return 0; } - /** - * @return void - */ private function determineDatabaseType(): void { // switch stuff based on database connection: @@ -129,8 +125,6 @@ class ForceDecimalSize extends Command /** * This method checks if a basic check can be done or if it needs to be complicated. - * - * @return void */ private function correctAmounts(): void { @@ -155,13 +149,13 @@ class ForceDecimalSize extends Command /** * This method loops all enabled currencies and then calls the method that will fix all objects in this currency. * - * @return void * @throws FireflyException */ private function correctAmountsByCurrency(): void { /** @var Collection $enabled */ $enabled = TransactionCurrency::whereEnabled(1)->get(); + /** @var TransactionCurrency $currency */ foreach ($enabled as $currency) { $this->correctByCurrency($currency); @@ -171,9 +165,6 @@ class ForceDecimalSize extends Command /** * This method loops the available tables that may need fixing, and calls for the right method that can fix them. * - * @param TransactionCurrency $currency - * - * @return void * @throws FireflyException */ private function correctByCurrency(TransactionCurrency $currency): void @@ -187,32 +178,46 @@ class ForceDecimalSize extends Command default: $message = sprintf('Cannot handle table "%s"', $name); $this->friendlyError($message); + throw new FireflyException($message); + case 'accounts': $this->correctAccountAmounts($currency, $fields); + break; + case 'auto_budgets': case 'available_budgets': case 'bills': case 'budget_limits': case 'recurrences_transactions': $this->correctGeneric($currency, $name); + break; + case 'currency_exchange_rates': case 'limit_repetitions': // do nothing break; + case 'piggy_bank_events': $this->correctPiggyEventAmounts($currency, $fields); + break; + case 'piggy_bank_repetitions': $this->correctPiggyRepetitionAmounts($currency, $fields); + break; + case 'piggy_banks': $this->correctPiggyAmounts($currency, $fields); + break; + case 'transactions': $this->correctTransactionAmounts($currency); + break; } } @@ -220,11 +225,6 @@ class ForceDecimalSize extends Command /** * This method loops over all accounts and validates the amounts. - * - * @param TransactionCurrency $currency - * @param array $fields - * - * @return void */ private function correctAccountAmounts(TransactionCurrency $currency, array $fields): void { @@ -234,9 +234,10 @@ class ForceDecimalSize extends Command /** @var Builder $query */ $query = Account::leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') - ->where('account_meta.name', 'currency_id') - ->where('account_meta.data', json_encode((string)$currency->id)); - $query->where(static function (Builder $q) use ($fields, $currency, $operator, $cast, $regularExpression) { + ->where('account_meta.name', 'currency_id') + ->where('account_meta.data', json_encode((string)$currency->id)) + ; + $query->where(static function (Builder $q) use ($fields, $currency, $operator, $cast, $regularExpression): void { foreach ($fields as $field) { $q->orWhere( DB::raw(sprintf('CAST(accounts.%s AS %s)', $field, $cast)), // @phpstan-ignore-line @@ -251,11 +252,12 @@ class ForceDecimalSize extends Command return; } + /** @var Account $account */ foreach ($result as $account) { /** @var string $field */ foreach ($fields as $field) { - $value = $account->$field; + $value = $account->{$field}; if (null === $value) { continue; } @@ -270,11 +272,6 @@ class ForceDecimalSize extends Command /** * This method fixes all auto budgets in currency $currency. - * - * @param TransactionCurrency $currency - * @param string $table - * - * @return void */ private function correctGeneric(TransactionCurrency $currency, string $table): void { @@ -286,7 +283,7 @@ class ForceDecimalSize extends Command /** @var Builder $query */ $query = $class::where('transaction_currency_id', $currency->id)->where( - static function (Builder $q) use ($fields, $currency, $operator, $cast, $regularExpression) { + static function (Builder $q) use ($fields, $currency, $operator, $cast, $regularExpression): void { /** @var string $field */ foreach ($fields as $field) { $q->orWhere( @@ -304,11 +301,12 @@ class ForceDecimalSize extends Command return; } + /** @var Model $item */ foreach ($result as $item) { /** @var string $field */ foreach ($fields as $field) { - $value = $item->$field; + $value = $item->{$field}; if (null === $value) { continue; } @@ -323,11 +321,6 @@ class ForceDecimalSize extends Command /** * This method fixes all piggy bank events in currency $currency. - * - * @param TransactionCurrency $currency - * @param array $fields - * - * @return void */ private function correctPiggyEventAmounts(TransactionCurrency $currency, array $fields): void { @@ -337,19 +330,20 @@ class ForceDecimalSize extends Command /** @var Builder $query */ $query = PiggyBankEvent::leftJoin('piggy_banks', 'piggy_bank_events.piggy_bank_id', '=', 'piggy_banks.id') - ->leftJoin('accounts', 'piggy_banks.account_id', '=', 'accounts.id') - ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') - ->where('account_meta.name', 'currency_id') - ->where('account_meta.data', json_encode((string)$currency->id)) - ->where(static function (Builder $q) use ($fields, $currency, $cast, $operator, $regularExpression) { - foreach ($fields as $field) { - $q->orWhere( - DB::raw(sprintf('CAST(piggy_bank_events.%s AS %s)', $field, $cast)), // @phpstan-ignore-line - $operator, - DB::raw(sprintf($regularExpression, $currency->decimal_places)) - ); - } - }); + ->leftJoin('accounts', 'piggy_banks.account_id', '=', 'accounts.id') + ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') + ->where('account_meta.name', 'currency_id') + ->where('account_meta.data', json_encode((string)$currency->id)) + ->where(static function (Builder $q) use ($fields, $currency, $cast, $operator, $regularExpression): void { + foreach ($fields as $field) { + $q->orWhere( + DB::raw(sprintf('CAST(piggy_bank_events.%s AS %s)', $field, $cast)), // @phpstan-ignore-line + $operator, + DB::raw(sprintf($regularExpression, $currency->decimal_places)) + ); + } + }) + ; $result = $query->get(['piggy_bank_events.*']); if (0 === $result->count()) { @@ -357,11 +351,12 @@ class ForceDecimalSize extends Command return; } + /** @var PiggyBankEvent $item */ foreach ($result as $item) { /** @var string $field */ foreach ($fields as $field) { - $value = $item->$field; + $value = $item->{$field}; if (null === $value) { continue; } @@ -378,33 +373,30 @@ class ForceDecimalSize extends Command /** * This method fixes all piggy bank repetitions in currency $currency. - * - * @param TransactionCurrency $currency - * @param array $fields - * - * @return void */ - private function correctPiggyRepetitionAmounts(TransactionCurrency $currency, array $fields) + private function correctPiggyRepetitionAmounts(TransactionCurrency $currency, array $fields): void { $operator = $this->operator; $cast = $this->cast; $regularExpression = $this->regularExpression; + // select all piggy bank repetitions with this currency and issue. /** @var Builder $query */ $query = PiggyBankRepetition::leftJoin('piggy_banks', 'piggy_bank_repetitions.piggy_bank_id', '=', 'piggy_banks.id') - ->leftJoin('accounts', 'piggy_banks.account_id', '=', 'accounts.id') - ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') - ->where('account_meta.name', 'currency_id') - ->where('account_meta.data', json_encode((string)$currency->id)) - ->where(static function (Builder $q) use ($fields, $currency, $operator, $cast, $regularExpression) { - foreach ($fields as $field) { - $q->orWhere( - DB::raw(sprintf('CAST(piggy_bank_repetitions.%s AS %s)', $field, $cast)), // @phpstan-ignore-line - $operator, - DB::raw(sprintf($regularExpression, $currency->decimal_places)) - ); - } - }); + ->leftJoin('accounts', 'piggy_banks.account_id', '=', 'accounts.id') + ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') + ->where('account_meta.name', 'currency_id') + ->where('account_meta.data', json_encode((string)$currency->id)) + ->where(static function (Builder $q) use ($fields, $currency, $operator, $cast, $regularExpression): void { + foreach ($fields as $field) { + $q->orWhere( + DB::raw(sprintf('CAST(piggy_bank_repetitions.%s AS %s)', $field, $cast)), // @phpstan-ignore-line + $operator, + DB::raw(sprintf($regularExpression, $currency->decimal_places)) + ); + } + }) + ; $result = $query->get(['piggy_bank_repetitions.*']); if (0 === $result->count()) { @@ -412,11 +404,12 @@ class ForceDecimalSize extends Command return; } + /** @var PiggyBankRepetition $item */ foreach ($result as $item) { /** @var string $field */ foreach ($fields as $field) { - $value = $item->$field; + $value = $item->{$field}; if (null === $value) { continue; } @@ -433,11 +426,6 @@ class ForceDecimalSize extends Command /** * This method fixes all piggy banks in currency $currency. - * - * @param TransactionCurrency $currency - * @param array $fields - * - * @return void */ private function correctPiggyAmounts(TransactionCurrency $currency, array $fields): void { @@ -447,18 +435,19 @@ class ForceDecimalSize extends Command /** @var Builder $query */ $query = PiggyBank::leftJoin('accounts', 'piggy_banks.account_id', '=', 'accounts.id') - ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') - ->where('account_meta.name', 'currency_id') - ->where('account_meta.data', json_encode((string)$currency->id)) - ->where(static function (Builder $q) use ($fields, $currency, $operator, $cast, $regularExpression) { - foreach ($fields as $field) { - $q->orWhere( - DB::raw(sprintf('CAST(piggy_banks.%s AS %s)', $field, $cast)), // @phpstan-ignore-line - $operator, - DB::raw(sprintf($regularExpression, $currency->decimal_places)) - ); - } - }); + ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') + ->where('account_meta.name', 'currency_id') + ->where('account_meta.data', json_encode((string)$currency->id)) + ->where(static function (Builder $q) use ($fields, $currency, $operator, $cast, $regularExpression): void { + foreach ($fields as $field) { + $q->orWhere( + DB::raw(sprintf('CAST(piggy_banks.%s AS %s)', $field, $cast)), // @phpstan-ignore-line + $operator, + DB::raw(sprintf($regularExpression, $currency->decimal_places)) + ); + } + }) + ; $result = $query->get(['piggy_banks.*']); if (0 === $result->count()) { @@ -466,11 +455,12 @@ class ForceDecimalSize extends Command return; } + /** @var PiggyBank $item */ foreach ($result as $item) { /** @var string $field */ foreach ($fields as $field) { - $value = $item->$field; + $value = $item->{$field}; if (null === $value) { continue; } @@ -485,10 +475,6 @@ class ForceDecimalSize extends Command /** * This method fixes all transactions in currency $currency. - * - * @param TransactionCurrency $currency - * - * @return void */ private function correctTransactionAmounts(TransactionCurrency $currency): void { @@ -532,6 +518,7 @@ class ForceDecimalSize extends Command return; } + /** @var Transaction $item */ foreach ($result as $item) { $value = $item->foreign_amount; @@ -548,9 +535,6 @@ class ForceDecimalSize extends Command } } - /** - * @return void - */ private function updateDecimals(): void { $this->friendlyInfo('Going to force the size of DECIMAL columns. Please hold.'); @@ -567,15 +551,16 @@ class ForceDecimalSize extends Command if ('pgsql' === $type) { DB::select(sprintf('ALTER TABLE %s ALTER COLUMN %s TYPE DECIMAL(32,12);', $name, $field)); sleep(1); + return; } if ('mysql' === $type) { DB::select(sprintf('ALTER TABLE %s CHANGE COLUMN %s %s DECIMAL(32, 12);', $name, $field, $field)); sleep(1); + return; } $this->friendlyError(sprintf('Cannot handle database type "%s".', $type)); - } } } diff --git a/app/Console/Commands/System/ForceMigration.php b/app/Console/Commands/System/ForceMigration.php index bd2df4e03a..26a3b0043b 100644 --- a/app/Console/Commands/System/ForceMigration.php +++ b/app/Console/Commands/System/ForceMigration.php @@ -40,7 +40,6 @@ class ForceMigration extends Command use ShowsFriendlyMessages; use VerifiesAccessToken; - protected $description = 'This command will force-run all database migrations.'; protected $signature = 'firefly-iii:force-migrations @@ -74,9 +73,6 @@ class ForceMigration extends Command return 0; } - /** - * @return void - */ private function forceMigration(): void { DB::commit(); diff --git a/app/Console/Commands/System/OutputVersion.php b/app/Console/Commands/System/OutputVersion.php index e11923746c..3e225bcf08 100644 --- a/app/Console/Commands/System/OutputVersion.php +++ b/app/Console/Commands/System/OutputVersion.php @@ -1,6 +1,5 @@ fileName(); $encryptedContent = $disk->get($fileName); if (null === $encryptedContent) { app('log')->error(sprintf('No content for attachment #%d under filename "%s"', $attachment->id, $fileName)); + continue; } + try { - $decryptedContent = Crypt::decrypt($encryptedContent); // verified + $decryptedContent = \Crypt::decrypt($encryptedContent); // verified } catch (DecryptException $e) { app('log')->error(sprintf('Could not decrypt data of attachment #%d: %s', $attachment->id, $e->getMessage())); $decryptedContent = $encryptedContent; @@ -69,6 +66,7 @@ class ScanAttachments extends Command $tempFileName = tempnam(sys_get_temp_dir(), 'FireflyIII'); if (false === $tempFileName) { app('log')->error(sprintf('Could not create temporary file for attachment #%d', $attachment->id)); + exit(1); } file_put_contents($tempFileName, $decryptedContent); diff --git a/app/Console/Commands/System/SetLatestVersion.php b/app/Console/Commands/System/SetLatestVersion.php index 7ef7d9d953..cefca2427c 100644 --- a/app/Console/Commands/System/SetLatestVersion.php +++ b/app/Console/Commands/System/SetLatestVersion.php @@ -34,15 +34,12 @@ class SetLatestVersion extends Command { use ShowsFriendlyMessages; - protected $description = 'Set latest version in DB.'; protected $signature = 'firefly-iii:set-latest-version {--james-is-cool}'; /** * Execute the console command. - * - * @return int */ public function handle(): int { diff --git a/app/Console/Commands/System/UpgradeFireflyInstructions.php b/app/Console/Commands/System/UpgradeFireflyInstructions.php index e9fd95b354..0b209f4407 100644 --- a/app/Console/Commands/System/UpgradeFireflyInstructions.php +++ b/app/Console/Commands/System/UpgradeFireflyInstructions.php @@ -28,14 +28,11 @@ use Illuminate\Console\Command; /** * Class UpgradeFireflyInstructions. - * - */ class UpgradeFireflyInstructions extends Command { use GeneratesInstallationId; - protected $description = 'Instructions in case of upgrade trouble.'; protected $signature = 'firefly:instructions {task}'; @@ -62,9 +59,11 @@ class UpgradeFireflyInstructions extends Command private function updateInstructions(): void { $version = (string)config('firefly.version'); + /** @var array $config */ $config = config('upgrade.text.upgrade'); $text = ''; + /** @var string $compare */ foreach (array_keys($config) as $compare) { // if string starts with: @@ -97,8 +96,6 @@ class UpgradeFireflyInstructions extends Command /** * The logo takes up 8 lines of code. So 8 colors can be used. - * - * @return void */ private function showLogo(): void { @@ -146,27 +143,23 @@ class UpgradeFireflyInstructions extends Command /** * Show a nice box. - * - * @param string $text */ private function boxed(string $text): void { $parts = explode("\n", wordwrap($text)); foreach ($parts as $string) { - $this->line('| ' . sprintf('%-77s', $string) . '|'); + $this->line('| '.sprintf('%-77s', $string).'|'); } } /** * Show a nice info box. - * - * @param string $text */ private function boxedInfo(string $text): void { $parts = explode("\n", wordwrap($text)); foreach ($parts as $string) { - $this->info('| ' . sprintf('%-77s', $string) . '|'); + $this->info('| '.sprintf('%-77s', $string).'|'); } } @@ -176,9 +169,11 @@ class UpgradeFireflyInstructions extends Command private function installInstructions(): void { $version = (string)config('firefly.version'); + /** @var array $config */ $config = config('upgrade.text.install'); $text = ''; + /** @var string $compare */ foreach (array_keys($config) as $compare) { // if string starts with: diff --git a/app/Console/Commands/System/VerifySecurityAlerts.php b/app/Console/Commands/System/VerifySecurityAlerts.php index dd8c59feac..cbfdab3512 100644 --- a/app/Console/Commands/System/VerifySecurityAlerts.php +++ b/app/Console/Commands/System/VerifySecurityAlerts.php @@ -28,7 +28,6 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages; use Illuminate\Console\Command; use Illuminate\Database\QueryException; use League\Flysystem\FilesystemException; -use Storage; /** * Class VerifySecurityAlerts @@ -37,7 +36,6 @@ class VerifySecurityAlerts extends Command { use ShowsFriendlyMessages; - protected $description = 'Verify security alerts'; protected $signature = 'firefly-iii:verify-security-alerts'; @@ -45,7 +43,6 @@ class VerifySecurityAlerts extends Command /** * Execute the console command. * - * @return int * @throws FilesystemException */ public function handle(): int @@ -54,7 +51,7 @@ class VerifySecurityAlerts extends Command // check for security advisories. $version = config('firefly.version'); - $disk = Storage::disk('resources'); + $disk = \Storage::disk('resources'); // Next line is ignored because it's a Laravel Facade. if (!$disk->has('alerts.json')) { // @phpstan-ignore-line app('log')->debug('No alerts.json file present.'); @@ -100,12 +97,10 @@ class VerifySecurityAlerts extends Command } app('log')->debug(sprintf('No security alerts for version %s', $version)); $this->friendlyPositive(sprintf('No security alerts for version %s', $version)); + return 0; } - /** - * @return void - */ private function removeOldAdvisory(): void { try { @@ -116,11 +111,6 @@ class VerifySecurityAlerts extends Command } } - /** - * @param array $array - * - * @return void - */ private function saveSecurityAdvisory(array $array): void { try { diff --git a/app/Console/Commands/Tools/ApplyRules.php b/app/Console/Commands/Tools/ApplyRules.php index eaa50b94bd..3f78585b94 100644 --- a/app/Console/Commands/Tools/ApplyRules.php +++ b/app/Console/Commands/Tools/ApplyRules.php @@ -47,7 +47,6 @@ class ApplyRules extends Command use ShowsFriendlyMessages; use VerifiesAccessToken; - protected $description = 'This command will apply your rules and rule groups on a selection of your transactions.'; protected $signature @@ -74,7 +73,6 @@ class ApplyRules extends Command /** * Execute the console command. * - * @return int * @throws FireflyException */ public function handle(): int @@ -135,7 +133,7 @@ class ApplyRules extends Command // start running rules. $this->friendlyLine(sprintf('Will apply %d rule(s) to your transaction(s).', $count)); - // file the rule(s) + // fire the rule(s) $ruleEngine->fire(); $this->friendlyLine(''); @@ -149,8 +147,6 @@ class ApplyRules 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. - * - */ private function stupidLaravel(): void { @@ -165,7 +161,6 @@ class ApplyRules extends Command } /** - * @return bool * @throws FireflyException */ private function verifyInput(): bool @@ -188,7 +183,6 @@ class ApplyRules extends Command } /** - * @return bool * @throws FireflyException */ private function verifyInputAccounts(): bool @@ -223,9 +217,6 @@ class ApplyRules extends Command return true; } - /** - * @return bool - */ private function verifyInputRuleGroups(): bool { $ruleGroupString = $this->option('rule_groups'); @@ -248,9 +239,6 @@ class ApplyRules extends Command return true; } - /** - * @return bool - */ private function verifyInputRules(): bool { $ruleString = $this->option('rules'); @@ -299,6 +287,7 @@ class ApplyRules extends Command } if (false === $inputEnd || false === $inputStart) { Log::error('Could not parse start or end date in verifyInputDate().'); + return; } @@ -310,22 +299,19 @@ class ApplyRules extends Command $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. @@ -340,12 +326,6 @@ class ApplyRules extends Command 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) diff --git a/app/Console/Commands/Tools/Cron.php b/app/Console/Commands/Tools/Cron.php index a7b2a7920e..58bbfdb671 100644 --- a/app/Console/Commands/Tools/Cron.php +++ b/app/Console/Commands/Tools/Cron.php @@ -32,20 +32,14 @@ use FireflyIII\Support\Cronjobs\BillWarningCronjob; use FireflyIII\Support\Cronjobs\ExchangeRatesCronjob; use FireflyIII\Support\Cronjobs\RecurringCronjob; use Illuminate\Console\Command; -use InvalidArgumentException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class Cron - * - */ class Cron extends Command { use ShowsFriendlyMessages; - protected $description = 'Runs all Firefly III cron-job related commands. Configure a cron job according to the official Firefly III documentation.'; protected $signature = 'firefly-iii:cron @@ -53,24 +47,18 @@ class Cron extends Command {--date= : Set the date in YYYY-MM-DD to make Firefly III think that\'s the current date.} '; - /** - * @return int - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ public function handle(): int { $date = null; + try { $date = new Carbon($this->option('date')); - } catch (InvalidArgumentException $e) { + } catch (\InvalidArgumentException $e) { $this->friendlyError(sprintf('"%s" is not a valid date', $this->option('date'))); } $force = (bool)$this->option('force'); // @phpstan-ignore-line - /* - * Fire exchange rates cron job. - */ + // Fire exchange rates cron job. if (true === config('cer.download_enabled')) { try { $this->exchangeRatesCronJob($force, $date); @@ -81,9 +69,7 @@ class Cron extends Command } } - /* - * Fire recurring transaction cron job. - */ + // Fire recurring transaction cron job. try { $this->recurringCronJob($force, $date); } catch (FireflyException $e) { @@ -92,9 +78,7 @@ class Cron extends Command $this->friendlyError($e->getMessage()); } - /* - * Fire auto-budget cron job: - */ + // Fire auto-budget cron job: try { $this->autoBudgetCronJob($force, $date); } catch (FireflyException $e) { @@ -103,9 +87,7 @@ class Cron extends Command $this->friendlyError($e->getMessage()); } - /* - * Fire bill warning cron job - */ + // Fire bill warning cron job try { $this->billWarningCronJob($force, $date); } catch (FireflyException $e) { @@ -119,10 +101,6 @@ class Cron extends Command return 0; } - /** - * @param bool $force - * @param Carbon|null $date - */ private function exchangeRatesCronJob(bool $force, ?Carbon $date): void { $exchangeRates = new ExchangeRatesCronjob(); @@ -146,12 +124,7 @@ class Cron extends Command } /** - * @param bool $force - * @param Carbon|null $date - * - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface */ private function recurringCronJob(bool $force, ?Carbon $date): void { @@ -175,11 +148,6 @@ class Cron extends Command } } - /** - * @param bool $force - * @param Carbon|null $date - * - */ private function autoBudgetCronJob(bool $force, ?Carbon $date): void { $autoBudget = new AutoBudgetCronjob(); @@ -203,12 +171,7 @@ class Cron extends Command } /** - * @param bool $force - * @param Carbon|null $date - * * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ private function billWarningCronJob(bool $force, ?Carbon $date): void { diff --git a/app/Console/Commands/Upgrade/AccountCurrencies.php b/app/Console/Commands/Upgrade/AccountCurrencies.php index 37305ff4e8..2732c5c43f 100644 --- a/app/Console/Commands/Upgrade/AccountCurrencies.php +++ b/app/Console/Commands/Upgrade/AccountCurrencies.php @@ -34,8 +34,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Console\Command; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class AccountCurrencies @@ -55,8 +53,6 @@ class AccountCurrencies extends Command /** * Each (asset) account must have a reference to a preferred currency. If the account does not have one, it's * forced upon the account. - * - * @return int */ public function handle(): int { @@ -84,7 +80,6 @@ class AccountCurrencies 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. - * */ private function stupidLaravel(): void { @@ -93,20 +88,13 @@ class AccountCurrencies extends Command $this->count = 0; } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } - /** - * - */ private function updateAccountCurrencies(): void { $users = $this->userRepos->all(); @@ -116,8 +104,6 @@ class AccountCurrencies extends Command } /** - * @param User $user - * * @throws FireflyException */ private function updateCurrenciesForUser(User $user): void @@ -134,10 +120,6 @@ class AccountCurrencies extends Command } } - /** - * @param Account $account - * @param TransactionCurrency $currency - */ private function updateAccount(Account $account, TransactionCurrency $currency): void { $this->accountRepos->setUser($account->user); @@ -150,7 +132,7 @@ class AccountCurrencies extends Command AccountMeta::where('account_id', $account->id)->where('name', 'currency_id')->forceDelete(); AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $currency->id]); $this->friendlyInfo(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $currency->code)); - $this->count++; + ++$this->count; return; } @@ -159,7 +141,7 @@ class AccountCurrencies extends Command if (0 === $accountCurrency && $obCurrency > 0) { AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $obCurrency]); $this->friendlyInfo(sprintf('Account #%d ("%s") now has a currency setting (#%d).', $account->id, $account->name, $obCurrency)); - $this->count++; + ++$this->count; return; } @@ -169,19 +151,16 @@ class AccountCurrencies extends Command $openingBalance->transaction_currency_id = $accountCurrency; $openingBalance->save(); $openingBalance->transactions->each( - static function (Transaction $transaction) use ($accountCurrency) { + static function (Transaction $transaction) use ($accountCurrency): void { $transaction->transaction_currency_id = $accountCurrency; $transaction->save(); } ); $this->friendlyInfo(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name)); - $this->count++; + ++$this->count; } } - /** - * - */ private function markAsExecuted(): void { app('fireflyconfig')->set(self::CONFIG_NAME, true); diff --git a/app/Console/Commands/Upgrade/AppendBudgetLimitPeriods.php b/app/Console/Commands/Upgrade/AppendBudgetLimitPeriods.php index 83125ac478..b167042cc4 100644 --- a/app/Console/Commands/Upgrade/AppendBudgetLimitPeriods.php +++ b/app/Console/Commands/Upgrade/AppendBudgetLimitPeriods.php @@ -26,8 +26,6 @@ namespace FireflyIII\Console\Commands\Upgrade; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\BudgetLimit; use Illuminate\Console\Command; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class AppendBudgetLimitPeriods @@ -44,10 +42,6 @@ class AppendBudgetLimitPeriods extends Command /** * Execute the console command. - * - * @return int - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -63,11 +57,6 @@ class AppendBudgetLimitPeriods extends Command return 0; } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); @@ -75,21 +64,16 @@ class AppendBudgetLimitPeriods extends Command return (bool)$configVar->data; } - /** - * - */ 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): void { $period = $this->getLimitPeriod($limit); @@ -119,11 +103,6 @@ class AppendBudgetLimitPeriods extends Command app('log')->debug($msg); } - /** - * @param BudgetLimit $limit - * - * @return string|null - */ private function getLimitPeriod(BudgetLimit $limit): ?string { // is daily @@ -172,9 +151,6 @@ class AppendBudgetLimitPeriods extends Command return null; } - /** - * - */ private function markAsExecuted(): void { app('fireflyconfig')->set(self::CONFIG_NAME, true); diff --git a/app/Console/Commands/Upgrade/BackToJournals.php b/app/Console/Commands/Upgrade/BackToJournals.php index a767b0f732..ce72554139 100644 --- a/app/Console/Commands/Upgrade/BackToJournals.php +++ b/app/Console/Commands/Upgrade/BackToJournals.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Upgrade; -use DB; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\Budget; use FireflyIII\Models\Category; @@ -31,8 +30,6 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use Illuminate\Console\Command; use Illuminate\Support\Collection; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class BackToJournals @@ -49,10 +46,6 @@ class BackToJournals extends Command /** * Execute the console command. - * - * @return int - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -68,7 +61,6 @@ class BackToJournals extends Command $this->friendlyWarning('Forcing the command.'); } - $this->migrateAll(); $this->friendlyInfo('Updated category and budget info for all transaction journals'); $this->markAsExecuted(); @@ -76,11 +68,6 @@ class BackToJournals extends Command return 0; } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isMigrated(): bool { $configVar = app('fireflyconfig')->get(MigrateToGroups::CONFIG_NAME, false); @@ -88,11 +75,6 @@ class BackToJournals extends Command return (bool)$configVar->data; } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); @@ -100,22 +82,16 @@ class BackToJournals extends Command return (bool)$configVar->data; } - /** - * - */ private function migrateAll(): void { $this->migrateBudgets(); $this->migrateCategories(); // empty tables - DB::table('budget_transaction')->delete(); - DB::table('category_transaction')->delete(); + \DB::table('budget_transaction')->delete(); + \DB::table('category_transaction')->delete(); } - /** - * - */ private function migrateBudgets(): void { $journals = new Collection(); @@ -125,45 +101,42 @@ class BackToJournals extends Command $collected = TransactionJournal::whereIn('id', $journalIds)->with(['transactions', 'budgets', 'transactions.budgets'])->get(); $journals = $journals->merge($collected); } + /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $this->migrateBudgetsForJournal($journal); } } - /** - * @return array - */ private function getIdsForBudgets(): array { - $transactions = DB::table('budget_transaction')->distinct()->pluck('transaction_id')->toArray(); + $transactions = \DB::table('budget_transaction')->distinct()->pluck('transaction_id')->toArray(); $array = []; $chunks = array_chunk($transactions, 500); foreach ($chunks as $chunk) { - $set = DB::table('transactions')->whereIn('transactions.id', $chunk)->pluck('transaction_journal_id')->toArray(); + $set = \DB::table('transactions')->whereIn('transactions.id', $chunk)->pluck('transaction_journal_id')->toArray(); $array = array_merge($array, $set); } return $array; } - /** - * @param TransactionJournal $journal - */ private function migrateBudgetsForJournal(TransactionJournal $journal): void { // grab category from first transaction - /** @var Transaction|null $transaction */ + /** @var null|Transaction $transaction */ $transaction = $journal->transactions->first(); if (null === $transaction) { $this->friendlyInfo(sprintf('Transaction journal #%d has no transactions. Will be fixed later.', $journal->id)); return; } - /** @var Budget|null $budget */ + + /** @var null|Budget $budget */ $budget = $transaction->budgets->first(); - /** @var Budget|null $journalBudget */ + + /** @var null|Budget $journalBudget */ $journalBudget = $journal->budgets->first(); // both have a budget, but they don't match. @@ -181,61 +154,55 @@ class BackToJournals extends Command } } - /** - * - */ private function migrateCategories(): void { $journals = new Collection(); $allIds = $this->getIdsForCategories(); - $chunks = array_chunk($allIds, 500); foreach ($chunks as $chunk) { $collected = TransactionJournal::whereIn('id', $chunk)->with(['transactions', 'categories', 'transactions.categories'])->get(); $journals = $journals->merge($collected); } + /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $this->migrateCategoriesForJournal($journal); } } - /** - * @return array - */ private function getIdsForCategories(): array { - $transactions = DB::table('category_transaction')->distinct()->pluck('transaction_id')->toArray(); + $transactions = \DB::table('category_transaction')->distinct()->pluck('transaction_id')->toArray(); $array = []; $chunks = array_chunk($transactions, 500); foreach ($chunks as $chunk) { - $set = DB::table('transactions') - ->whereIn('transactions.id', $chunk) - ->pluck('transaction_journal_id')->toArray(); + $set = \DB::table('transactions') + ->whereIn('transactions.id', $chunk) + ->pluck('transaction_journal_id')->toArray() + ; $array = array_merge($array, $set); } return $array; } - /** - * @param TransactionJournal $journal - */ private function migrateCategoriesForJournal(TransactionJournal $journal): void { // grab category from first transaction - /** @var Transaction|null $transaction */ + /** @var null|Transaction $transaction */ $transaction = $journal->transactions->first(); if (null === $transaction) { $this->friendlyInfo(sprintf('Transaction journal #%d has no transactions. Will be fixed later.', $journal->id)); return; } - /** @var Category|null $category */ + + /** @var null|Category $category */ $category = $transaction->categories->first(); - /** @var Category|null $journalCategory */ + + /** @var null|Category $journalCategory */ $journalCategory = $journal->categories->first(); // both have a category, but they don't match. @@ -250,9 +217,6 @@ class BackToJournals extends Command } } - /** - * - */ 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 e0244bcce4..6b2121f49d 100644 --- a/app/Console/Commands/Upgrade/BudgetLimitCurrency.php +++ b/app/Console/Commands/Upgrade/BudgetLimitCurrency.php @@ -29,8 +29,6 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\User; use Illuminate\Console\Command; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class BudgetLimitCurrency @@ -48,10 +46,7 @@ class BudgetLimitCurrency extends Command /** * Execute the console command. * - * @return int - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -61,16 +56,16 @@ class BudgetLimitCurrency extends Command return 0; } - $count = 0; $budgetLimits = BudgetLimit::get(); + /** @var BudgetLimit $budgetLimit */ foreach ($budgetLimits as $budgetLimit) { if (null === $budgetLimit->transaction_currency_id) { - /** @var Budget|null $budget */ + /** @var null|Budget $budget */ $budget = $budgetLimit->budget; if (null !== $budget) { - /** @var User|null $user */ + /** @var null|User $user */ $user = $budget->user; if (null !== $user) { $currency = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); @@ -79,7 +74,7 @@ class BudgetLimitCurrency extends Command $this->friendlyInfo( sprintf('Budget limit #%d (part of budget "%s") now has a currency setting (%s).', $budgetLimit->id, $budget->name, $currency->name) ); - $count++; + ++$count; } } } @@ -92,11 +87,6 @@ class BudgetLimitCurrency extends Command return 0; } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); @@ -107,9 +97,6 @@ class BudgetLimitCurrency extends Command return false; } - /** - * - */ private function markAsExecuted(): void { app('fireflyconfig')->set(self::CONFIG_NAME, true); diff --git a/app/Console/Commands/Upgrade/CCLiabilities.php b/app/Console/Commands/Upgrade/CCLiabilities.php index 6eb373c67a..df3cb3856a 100644 --- a/app/Console/Commands/Upgrade/CCLiabilities.php +++ b/app/Console/Commands/Upgrade/CCLiabilities.php @@ -29,8 +29,6 @@ use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use Illuminate\Console\Command; use Illuminate\Support\Collection; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class CCLiabilities @@ -46,10 +44,7 @@ class CCLiabilities extends Command /** * Execute the console command. * - * @return int - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -59,7 +54,6 @@ class CCLiabilities extends Command return 0; } - $ccType = AccountType::where('type', AccountType::CREDITCARD)->first(); $debtType = AccountType::where('type', AccountType::DEBT)->first(); if (null === $ccType || null === $debtType) { @@ -68,6 +62,7 @@ class CCLiabilities extends Command return 0; } + /** @var Collection $accounts */ $accounts = Account::where('account_type_id', $ccType->id)->get(); foreach ($accounts as $account) { @@ -88,20 +83,13 @@ class CCLiabilities extends Command return 0; } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } - /** - * - */ private function markAsExecuted(): void { app('fireflyconfig')->set(self::CONFIG_NAME, true); diff --git a/app/Console/Commands/Upgrade/DecryptDatabase.php b/app/Console/Commands/Upgrade/DecryptDatabase.php index 6ce6473b84..411c29c4cc 100644 --- a/app/Console/Commands/Upgrade/DecryptDatabase.php +++ b/app/Console/Commands/Upgrade/DecryptDatabase.php @@ -24,17 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Upgrade; -use Crypt; -use DB; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Preference; use Illuminate\Console\Command; use Illuminate\Contracts\Encryption\DecryptException; -use JsonException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; -use stdClass; /** * Class DecryptDatabase @@ -48,10 +42,6 @@ class DecryptDatabase extends Command /** * Execute the console command. - * - * @return int - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -68,6 +58,7 @@ class DecryptDatabase extends Command 'transactions' => ['description'], 'journal_links' => ['comment'], ]; + /** * @var string $table * @var array $fields @@ -75,16 +66,10 @@ class DecryptDatabase extends Command foreach ($tables as $table => $fields) { $this->decryptTable($table, $fields); } + return 0; } - /** - * @param string $table - * @param array $fields - * - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function decryptTable(string $table, array $fields): void { if ($this->isDecrypted($table)) { @@ -101,17 +86,11 @@ class DecryptDatabase extends Command app('fireflyconfig')->set($configName, true); } - /** - * @param string $table - * - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isDecrypted(string $table): bool { $configName = sprintf('is_decrypted_%s', $table); $configVar = null; + try { $configVar = app('fireflyconfig')->get($configName, false); } catch (FireflyException $e) { @@ -124,27 +103,19 @@ class DecryptDatabase extends Command return false; } - /** - * @param string $table - * @param string $field - */ private function decryptField(string $table, string $field): void { - $rows = DB::table($table)->get(['id', $field]); - /** @var stdClass $row */ + $rows = \DB::table($table)->get(['id', $field]); + + /** @var \stdClass $row */ foreach ($rows as $row) { $this->decryptRow($table, $field, $row); } } - /** - * @param string $table - * @param string $field - * @param stdClass $row - */ - private function decryptRow(string $table, string $field, stdClass $row): void + private function decryptRow(string $table, string $field, \stdClass $row): void { - $original = $row->$field; + $original = $row->{$field}; if (null === $original) { return; } @@ -168,7 +139,7 @@ class DecryptDatabase extends Command } if ($value !== $original) { - DB::table($table)->where('id', $id)->update([$field => $value]); + \DB::table($table)->where('id', $id)->update([$field => $value]); } } @@ -178,12 +149,13 @@ class DecryptDatabase extends Command * @param mixed $value * * @return string + * * @throws FireflyException */ private function tryDecrypt($value) { try { - $value = Crypt::decrypt($value); + $value = \Crypt::decrypt($value); } catch (DecryptException $e) { if ('The MAC is invalid.' === $e->getMessage()) { throw new FireflyException($e->getMessage(), 0, $e); @@ -193,16 +165,12 @@ class DecryptDatabase extends Command return $value; } - /** - * @param int $id - * @param string $value - */ private function decryptPreferencesRow(int $id, string $value): void { // try to json_decrypt the value. try { $newValue = json_decode($value, true, 512, JSON_THROW_ON_ERROR) ?? $value; - } catch (JsonException $e) { + } catch (\JsonException $e) { $message = sprintf('Could not JSON decode preference row #%d: %s. This does not have to be a problem.', $id, $e->getMessage()); $this->friendlyError($message); app('log')->warning($message); @@ -212,7 +180,7 @@ class DecryptDatabase extends Command return; } - /** @var Preference|null $object */ + /** @var null|Preference $object */ $object = Preference::find($id); if (null !== $object) { $object->data = $newValue; diff --git a/app/Console/Commands/Upgrade/FixPostgresSequences.php b/app/Console/Commands/Upgrade/FixPostgresSequences.php index 520cc36963..e97426a7ad 100644 --- a/app/Console/Commands/Upgrade/FixPostgresSequences.php +++ b/app/Console/Commands/Upgrade/FixPostgresSequences.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Upgrade; -use DB; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use Illuminate\Console\Command; @@ -35,95 +34,36 @@ class FixPostgresSequences extends Command { use ShowsFriendlyMessages; - protected $description = 'Fixes issues with PostgreSQL sequences.'; protected $signature = 'firefly-iii:fix-pgsql-sequences'; /** * Execute the console command. - * - * @return int */ public function handle(): int { - if (DB::connection()->getName() !== 'pgsql') { + if ('pgsql' !== \DB::connection()->getName()) { return 0; } $this->friendlyLine('Going to verify PostgreSQL table sequences.'); - $tablesToCheck = [ - '2fa_tokens', - 'account_meta', - 'account_types', - 'accounts', - 'attachments', - 'auto_budgets', - 'available_budgets', - 'bills', - 'budget_limits', - 'budget_transaction', - 'budget_transaction_journal', - 'budgets', - 'categories', - 'category_transaction', - 'category_transaction_journal', - 'configuration', - 'currency_exchange_rates', - 'failed_jobs', - 'group_journals', - 'jobs', - 'journal_links', - 'journal_meta', - 'limit_repetitions', - 'link_types', - 'locations', - 'migrations', - 'notes', - 'oauth_clients', - 'oauth_personal_access_clients', - 'object_groups', - 'permissions', - 'piggy_bank_events', - 'piggy_bank_repetitions', - 'piggy_banks', - 'preferences', - 'recurrences', - 'recurrences_meta', - 'recurrences_repetitions', - 'recurrences_transactions', - 'roles', - 'rt_meta', - 'rule_actions', - 'rule_groups', - 'rule_triggers', - 'rules', - 'tag_transaction_journal', - 'tags', - 'transaction_currencies', - 'transaction_groups', - 'transaction_journals', - 'transaction_types', - 'transactions', - 'users', - 'webhook_attempts', - 'webhook_messages', - 'webhooks', - ]; + $tablesToCheck = ['2fa_tokens', 'account_meta', 'account_types', 'accounts', 'attachments', 'auto_budgets', 'available_budgets', 'bills', 'budget_limits', 'budget_transaction', 'budget_transaction_journal', 'budgets', 'categories', 'category_transaction', 'category_transaction_journal', 'configuration', 'currency_exchange_rates', 'failed_jobs', 'group_journals', 'jobs', 'journal_links', 'journal_meta', 'limit_repetitions', 'link_types', 'locations', 'migrations', 'notes', 'oauth_clients', 'oauth_personal_access_clients', 'object_groups', 'permissions', 'piggy_bank_events', 'piggy_bank_repetitions', 'piggy_banks', 'preferences', 'recurrences', 'recurrences_meta', 'recurrences_repetitions', 'recurrences_transactions', 'roles', 'rt_meta', 'rule_actions', 'rule_groups', 'rule_triggers', 'rules', 'tag_transaction_journal', 'tags', 'transaction_currencies', 'transaction_groups', 'transaction_journals', 'transaction_types', 'transactions', 'users', 'webhook_attempts', 'webhook_messages', 'webhooks']; foreach ($tablesToCheck as $tableToCheck) { $this->friendlyLine(sprintf('Checking the next id sequence for table "%s".', $tableToCheck)); - $highestId = DB::table($tableToCheck)->select(DB::raw('MAX(id)'))->first(); - $nextId = DB::table($tableToCheck)->select(DB::raw(sprintf('nextval(\'%s_id_seq\')', $tableToCheck)))->first(); + $highestId = \DB::table($tableToCheck)->select(\DB::raw('MAX(id)'))->first(); + $nextId = \DB::table($tableToCheck)->select(\DB::raw(sprintf('nextval(\'%s_id_seq\')', $tableToCheck)))->first(); if (null === $nextId) { $this->friendlyInfo(sprintf('nextval is NULL for table "%s", go to next table.', $tableToCheck)); + continue; } if ($nextId->nextval < $highestId->max) { // @phpstan-ignore-line - DB::select(sprintf('SELECT setval(\'%s_id_seq\', %d)', $tableToCheck, $highestId->max)); - $highestId = DB::table($tableToCheck)->select(DB::raw('MAX(id)'))->first(); - $nextId = DB::table($tableToCheck)->select(DB::raw(sprintf('nextval(\'%s_id_seq\')', $tableToCheck)))->first(); + \DB::select(sprintf('SELECT setval(\'%s_id_seq\', %d)', $tableToCheck, $highestId->max)); + $highestId = \DB::table($tableToCheck)->select(\DB::raw('MAX(id)'))->first(); + $nextId = \DB::table($tableToCheck)->select(\DB::raw(sprintf('nextval(\'%s_id_seq\')', $tableToCheck)))->first(); if ($nextId->nextval > $highestId->max) { // @phpstan-ignore-line $this->friendlyInfo(sprintf('Table "%s" autoincrement corrected.', $tableToCheck)); } @@ -136,7 +76,6 @@ class FixPostgresSequences extends Command } } - return 0; } } diff --git a/app/Console/Commands/Upgrade/MigrateAttachments.php b/app/Console/Commands/Upgrade/MigrateAttachments.php index 3d2e638666..1aa487e213 100644 --- a/app/Console/Commands/Upgrade/MigrateAttachments.php +++ b/app/Console/Commands/Upgrade/MigrateAttachments.php @@ -28,8 +28,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Attachment; use FireflyIII\Models\Note; use Illuminate\Console\Command; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class MigrateAttachments @@ -47,10 +45,7 @@ class MigrateAttachments extends Command /** * Execute the console command. * - * @return int - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -61,7 +56,6 @@ class MigrateAttachments extends Command return 0; } - $attachments = Attachment::get(); $count = 0; @@ -84,7 +78,7 @@ class MigrateAttachments extends Command $att->save(); app('log')->debug(sprintf('Migrated attachment #%s description to note #%d.', $att->id, $note->id)); - $count++; + ++$count; } } if (0 === $count) { @@ -100,11 +94,6 @@ class MigrateAttachments extends Command return 0; } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); @@ -115,9 +104,6 @@ class MigrateAttachments extends Command return false; } - /** - * - */ private function markAsExecuted(): void { app('fireflyconfig')->set(self::CONFIG_NAME, true); diff --git a/app/Console/Commands/Upgrade/MigrateJournalNotes.php b/app/Console/Commands/Upgrade/MigrateJournalNotes.php index 1e4fb924ac..ab71126391 100644 --- a/app/Console/Commands/Upgrade/MigrateJournalNotes.php +++ b/app/Console/Commands/Upgrade/MigrateJournalNotes.php @@ -27,8 +27,6 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\Note; use FireflyIII\Models\TransactionJournalMeta; use Illuminate\Console\Command; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class MigrateJournalNotes @@ -45,10 +43,6 @@ class MigrateJournalNotes extends Command /** * Execute the console command. - * - * @return int - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -62,6 +56,7 @@ class MigrateJournalNotes extends Command $count = 0; $set = TransactionJournalMeta::whereName('notes')->get(); + /** @var TransactionJournalMeta $meta */ foreach ($set as $meta) { $journal = $meta->transactionJournal; @@ -76,7 +71,7 @@ class MigrateJournalNotes extends Command app('log')->debug(sprintf('Migrated meta note #%d to Note #%d', $meta->id, $note->id)); $meta->delete(); - $count++; + ++$count; } if (0 === $count) { @@ -93,11 +88,6 @@ class MigrateJournalNotes extends Command return 0; } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); @@ -108,9 +98,6 @@ class MigrateJournalNotes extends Command return false; } - /** - * - */ private function markAsExecuted(): void { app('fireflyconfig')->set(self::CONFIG_NAME, true); diff --git a/app/Console/Commands/Upgrade/MigrateRecurrenceMeta.php b/app/Console/Commands/Upgrade/MigrateRecurrenceMeta.php index fda6b8e70b..0a7e4204fc 100644 --- a/app/Console/Commands/Upgrade/MigrateRecurrenceMeta.php +++ b/app/Console/Commands/Upgrade/MigrateRecurrenceMeta.php @@ -29,9 +29,6 @@ use FireflyIII\Models\Recurrence; use FireflyIII\Models\RecurrenceMeta; use FireflyIII\Models\RecurrenceTransactionMeta; use Illuminate\Console\Command; -use JsonException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class MigrateRecurrenceMeta @@ -48,11 +45,6 @@ class MigrateRecurrenceMeta extends Command /** * Execute the console command. - * - * @return int - * @throws ContainerExceptionInterface - * @throws JsonException - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -75,30 +67,22 @@ class MigrateRecurrenceMeta extends Command return 0; } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool)$configVar->data; + return (bool) $configVar->data; } return false; } - /** - * @return int - * @throws JsonException - */ private function migrateMetaData(): int { $count = 0; // get all recurrence meta data: $collection = RecurrenceMeta::with('recurrence')->get(); + /** @var RecurrenceMeta $meta */ foreach ($collection as $meta) { $count += $this->migrateEntry($meta); @@ -107,15 +91,9 @@ class MigrateRecurrenceMeta extends Command return $count; } - /** - * @param RecurrenceMeta $meta - * - * @return int - * @throws JsonException - */ private function migrateEntry(RecurrenceMeta $meta): int { - /** @var Recurrence|null $recurrence */ + /** @var null|Recurrence $recurrence */ $recurrence = $meta->recurrence; if (null === $recurrence) { return 0; @@ -143,9 +121,6 @@ class MigrateRecurrenceMeta extends Command return 1; } - /** - * - */ private function markAsExecuted(): void { app('fireflyconfig')->set(self::CONFIG_NAME, true); diff --git a/app/Console/Commands/Upgrade/MigrateRecurrenceType.php b/app/Console/Commands/Upgrade/MigrateRecurrenceType.php index c722547621..7c5393a167 100644 --- a/app/Console/Commands/Upgrade/MigrateRecurrenceType.php +++ b/app/Console/Commands/Upgrade/MigrateRecurrenceType.php @@ -29,8 +29,6 @@ use FireflyIII\Models\Recurrence; use FireflyIII\Models\RecurrenceTransaction; use FireflyIII\Models\TransactionType; use Illuminate\Console\Command; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class MigrateRecurrenceType @@ -47,10 +45,6 @@ class MigrateRecurrenceType extends Command /** * Execute the console command. - * - * @return int - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -63,46 +57,35 @@ class MigrateRecurrenceType extends Command $this->migrateTypes(); $this->markAsExecuted(); - return 0; } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + return (bool)$configVar?->data; } - /** - * - */ private function migrateTypes(): void { $set = Recurrence::get(); + /** @var Recurrence $recurrence */ foreach ($set as $recurrence) { - if ($recurrence->transactionType->type !== TransactionType::INVALID) { + if (TransactionType::INVALID !== $recurrence->transactionType->type) { $this->migrateRecurrence($recurrence); } } } - /** - * @param Recurrence $recurrence - * - * @return void - */ private function migrateRecurrence(Recurrence $recurrence): void { $originalType = $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; @@ -111,17 +94,11 @@ class MigrateRecurrenceType extends Command $this->friendlyInfo(sprintf('Updated recurrence #%d to new transaction type model.', $recurrence->id)); } - /** - * - */ private function getInvalidType(): TransactionType { return TransactionType::whereType(TransactionType::INVALID)->firstOrCreate(['type' => TransactionType::INVALID]); } - /** - * - */ private function markAsExecuted(): void { app('fireflyconfig')->set(self::CONFIG_NAME, true); diff --git a/app/Console/Commands/Upgrade/MigrateTagLocations.php b/app/Console/Commands/Upgrade/MigrateTagLocations.php index afcb1a60c8..a09c0cc0ac 100644 --- a/app/Console/Commands/Upgrade/MigrateTagLocations.php +++ b/app/Console/Commands/Upgrade/MigrateTagLocations.php @@ -28,8 +28,6 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\Location; use FireflyIII\Models\Tag; use Illuminate\Console\Command; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class MigrateTagLocations @@ -46,10 +44,6 @@ class MigrateTagLocations extends Command /** * Execute the console command. - * - * @return int - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -64,11 +58,6 @@ class MigrateTagLocations extends Command return 0; } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); @@ -79,12 +68,10 @@ class MigrateTagLocations extends Command return false; } - /** - * @return void - */ private function migrateTagLocations(): void { $tags = Tag::get(); + /** @var Tag $tag */ foreach ($tags as $tag) { if ($this->hasLocationDetails($tag)) { @@ -93,19 +80,11 @@ class MigrateTagLocations extends Command } } - /** - * @param Tag $tag - * - * @return bool - */ private function hasLocationDetails(Tag $tag): bool { return null !== $tag->latitude && null !== $tag->longitude && null !== $tag->zoomLevel; } - /** - * @param Tag $tag - */ private function migrateLocationDetails(Tag $tag): void { $location = new Location(); @@ -121,9 +100,6 @@ class MigrateTagLocations extends Command $tag->save(); } - /** - * - */ private function markAsExecuted(): void { app('fireflyconfig')->set(self::CONFIG_NAME, true); diff --git a/app/Console/Commands/Upgrade/MigrateToGroups.php b/app/Console/Commands/Upgrade/MigrateToGroups.php index 8da1102421..8424f7055a 100644 --- a/app/Console/Commands/Upgrade/MigrateToGroups.php +++ b/app/Console/Commands/Upgrade/MigrateToGroups.php @@ -23,8 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Upgrade; -use DB; -use Exception; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Factory\TransactionGroupFactory; use FireflyIII\Models\Budget; @@ -36,8 +34,6 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Services\Internal\Destroy\JournalDestroyService; use Illuminate\Console\Command; use Illuminate\Support\Collection; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * This command will take split transactions and migrate them to "transaction groups". @@ -61,10 +57,6 @@ class MigrateToGroups extends Command /** * Execute the console command. - * - * @return int - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -80,7 +72,6 @@ class MigrateToGroups extends Command $this->friendlyWarning('Forcing the migration.'); } - $this->makeGroupsFromSplitJournals(); $this->makeGroupsFromAll(); @@ -99,8 +90,6 @@ class MigrateToGroups 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. - * - */ private function stupidLaravel(): void { @@ -111,29 +100,25 @@ class MigrateToGroups extends Command $this->cliRepository = app(JournalCLIRepositoryInterface::class); } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isMigrated(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool)$configVar->data; + return (bool) $configVar->data; } return false; } /** - * @throws Exception + * @throws \Exception */ private function makeGroupsFromSplitJournals(): void { $splitJournals = $this->cliRepository->getSplitJournals(); if ($splitJournals->count() > 0) { $this->friendlyLine(sprintf('Going to convert %d split transaction(s). Please hold..', $splitJournals->count())); + /** @var TransactionJournal $journal */ foreach ($splitJournals as $journal) { $this->makeMultiGroup($journal); @@ -142,15 +127,13 @@ class MigrateToGroups extends Command } /** - * @param TransactionJournal $journal - * - * @throws Exception + * @throws \Exception */ private function makeMultiGroup(TransactionJournal $journal): void { // double check transaction count. if ($journal->transactions->count() <= 2) { - app('log')->debug(sprintf('Will not try to convert journal #%d because it has 2 or less transactions.', $journal->id)); + app('log')->debug(sprintf('Will not try to convert journal #%d because it has 2 or fewer transactions.', $journal->id)); return; } @@ -166,94 +149,12 @@ class MigrateToGroups extends Command 'transactions' => [], ]; $destTransactions = $this->getDestinationTransactions($journal); - $budgetId = $this->cliRepository->getJournalBudgetId($journal); - $categoryId = $this->cliRepository->getJournalCategoryId($journal); - $notes = $this->cliRepository->getNoteText($journal); - $tags = $this->cliRepository->getTags($journal); - $internalRef = $this->cliRepository->getMetaField($journal, 'internal-reference'); - $sepaCC = $this->cliRepository->getMetaField($journal, 'sepa_cc'); - $sepaCtOp = $this->cliRepository->getMetaField($journal, 'sepa_ct_op'); - $sepaCtId = $this->cliRepository->getMetaField($journal, 'sepa_ct_id'); - $sepaDb = $this->cliRepository->getMetaField($journal, 'sepa_db'); - $sepaCountry = $this->cliRepository->getMetaField($journal, 'sepa_country'); - $sepaEp = $this->cliRepository->getMetaField($journal, 'sepa_ep'); - $sepaCi = $this->cliRepository->getMetaField($journal, 'sepa_ci'); - $sepaBatchId = $this->cliRepository->getMetaField($journal, 'sepa_batch_id'); - $externalId = $this->cliRepository->getMetaField($journal, 'external-id'); - $originalSource = $this->cliRepository->getMetaField($journal, 'original-source'); - $recurrenceId = $this->cliRepository->getMetaField($journal, 'recurrence_id'); - $bunq = $this->cliRepository->getMetaField($journal, 'bunq_payment_id'); - $hash = $this->cliRepository->getMetaField($journal, 'import_hash'); - $hashTwo = $this->cliRepository->getMetaField($journal, 'import_hash_v2'); - $interestDate = $this->cliRepository->getMetaDate($journal, 'interest_date'); - $bookDate = $this->cliRepository->getMetaDate($journal, 'book_date'); - $processDate = $this->cliRepository->getMetaDate($journal, 'process_date'); - $dueDate = $this->cliRepository->getMetaDate($journal, 'due_date'); - $paymentDate = $this->cliRepository->getMetaDate($journal, 'payment_date'); - $invoiceDate = $this->cliRepository->getMetaDate($journal, 'invoice_date'); app('log')->debug(sprintf('Will use %d positive transactions to create a new group.', $destTransactions->count())); /** @var Transaction $transaction */ foreach ($destTransactions as $transaction) { - app('log')->debug(sprintf('Now going to add transaction #%d to the array.', $transaction->id)); - $opposingTr = $this->findOpposingTransaction($journal, $transaction); - - if (null === $opposingTr) { - $this->friendlyError( - sprintf( - 'Journal #%d has no opposing transaction for transaction #%d. Cannot upgrade this entry.', - $journal->id, - $transaction->id - ) - ); - continue; - } - - // overrule journal category with transaction category. - $budgetId = $this->getTransactionBudget($transaction, $opposingTr) ?? $budgetId; - $categoryId = $this->getTransactionCategory($transaction, $opposingTr) ?? $categoryId; - - $tArray = [ - 'type' => strtolower($journal->transactionType->type), - 'date' => $journal->date, - 'user' => $journal->user_id, - 'currency_id' => $transaction->transaction_currency_id, - 'foreign_currency_id' => $transaction->foreign_currency_id, - 'amount' => $transaction->amount, - 'foreign_amount' => $transaction->foreign_amount, - 'description' => $transaction->description ?? $journal->description, - 'source_id' => $opposingTr->account_id, - 'destination_id' => $transaction->account_id, - 'budget_id' => $budgetId, - 'category_id' => $categoryId, - 'bill_id' => $journal->bill_id, - 'notes' => $notes, - 'tags' => $tags, - 'internal_reference' => $internalRef, - 'sepa_cc' => $sepaCC, - 'sepa_ct_op' => $sepaCtOp, - 'sepa_ct_id' => $sepaCtId, - 'sepa_db' => $sepaDb, - 'sepa_country' => $sepaCountry, - 'sepa_ep' => $sepaEp, - 'sepa_ci' => $sepaCi, - 'sepa_batch_id' => $sepaBatchId, - 'external_id' => $externalId, - 'original-source' => $originalSource, - 'recurrence_id' => $recurrenceId, - 'bunq_payment_id' => $bunq, - 'import_hash' => $hash, - 'import_hash_v2' => $hashTwo, - 'interest_date' => $interestDate, - 'book_date' => $bookDate, - 'process_date' => $processDate, - 'due_date' => $dueDate, - 'payment_date' => $paymentDate, - 'invoice_date' => $invoiceDate, - ]; - - $data['transactions'][] = $tArray; + $data['transactions'][] = $this->generateTransaction($journal, $transaction); } app('log')->debug(sprintf('Now calling transaction journal factory (%d transactions in array)', count($data['transactions']))); $group = $this->groupFactory->create($data); @@ -262,7 +163,7 @@ class MigrateToGroups extends Command // delete the old transaction journal. $this->service->destroy($journal); - $this->count++; + ++$this->count; // report on result: app('log')->debug( @@ -283,11 +184,6 @@ class MigrateToGroups extends Command ); } - /** - * @param TransactionJournal $journal - * - * @return Collection - */ private function getDestinationTransactions(TransactionJournal $journal): Collection { return $journal->transactions->filter( @@ -297,17 +193,11 @@ class MigrateToGroups extends Command ); } - /** - * @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; // intentional float + $amount = (float) $transaction->amount * -1 === (float) $subject->amount; // intentional float $identifier = $transaction->identifier === $subject->identifier; app('log')->debug(sprintf('Amount the same? %s', var_export($amount, true))); app('log')->debug(sprintf('ID the same? %s', var_export($identifier, true))); @@ -320,17 +210,101 @@ class MigrateToGroups extends Command } /** - * @param Transaction $left - * @param Transaction $right - * - * @return int|null + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ + private function generateTransaction(TransactionJournal $journal, Transaction $transaction): array + { + app('log')->debug(sprintf('Now going to add transaction #%d to the array.', $transaction->id)); + $opposingTr = $this->findOpposingTransaction($journal, $transaction); + + if (null === $opposingTr) { + $this->friendlyError( + sprintf( + 'Journal #%d has no opposing transaction for transaction #%d. Cannot upgrade this entry.', + $journal->id, + $transaction->id + ) + ); + + return []; + } + + $budgetId = $this->cliRepository->getJournalBudgetId($journal); + $categoryId = $this->cliRepository->getJournalCategoryId($journal); + $notes = $this->cliRepository->getNoteText($journal); + $tags = $this->cliRepository->getTags($journal); + $internalRef = $this->cliRepository->getMetaField($journal, 'internal-reference'); + $sepaCC = $this->cliRepository->getMetaField($journal, 'sepa_cc'); + $sepaCtOp = $this->cliRepository->getMetaField($journal, 'sepa_ct_op'); + $sepaCtId = $this->cliRepository->getMetaField($journal, 'sepa_ct_id'); + $sepaDb = $this->cliRepository->getMetaField($journal, 'sepa_db'); + $sepaCountry = $this->cliRepository->getMetaField($journal, 'sepa_country'); + $sepaEp = $this->cliRepository->getMetaField($journal, 'sepa_ep'); + $sepaCi = $this->cliRepository->getMetaField($journal, 'sepa_ci'); + $sepaBatchId = $this->cliRepository->getMetaField($journal, 'sepa_batch_id'); + $externalId = $this->cliRepository->getMetaField($journal, 'external-id'); + $originalSource = $this->cliRepository->getMetaField($journal, 'original-source'); + $recurrenceId = $this->cliRepository->getMetaField($journal, 'recurrence_id'); + $bunq = $this->cliRepository->getMetaField($journal, 'bunq_payment_id'); + $hash = $this->cliRepository->getMetaField($journal, 'import_hash'); + $hashTwo = $this->cliRepository->getMetaField($journal, 'import_hash_v2'); + $interestDate = $this->cliRepository->getMetaDate($journal, 'interest_date'); + $bookDate = $this->cliRepository->getMetaDate($journal, 'book_date'); + $processDate = $this->cliRepository->getMetaDate($journal, 'process_date'); + $dueDate = $this->cliRepository->getMetaDate($journal, 'due_date'); + $paymentDate = $this->cliRepository->getMetaDate($journal, 'payment_date'); + $invoiceDate = $this->cliRepository->getMetaDate($journal, 'invoice_date'); + + // overrule journal category with transaction category. + $budgetId = $this->getTransactionBudget($transaction, $opposingTr) ?? $budgetId; + $categoryId = $this->getTransactionCategory($transaction, $opposingTr) ?? $categoryId; + + return [ + 'type' => strtolower($journal->transactionType->type), + 'date' => $journal->date, + 'user' => $journal->user_id, + 'currency_id' => $transaction->transaction_currency_id, + 'foreign_currency_id' => $transaction->foreign_currency_id, + 'amount' => $transaction->amount, + 'foreign_amount' => $transaction->foreign_amount, + 'description' => $transaction->description ?? $journal->description, + 'source_id' => $opposingTr->account_id, + 'destination_id' => $transaction->account_id, + 'budget_id' => $budgetId, + 'category_id' => $categoryId, + 'bill_id' => $journal->bill_id, + 'notes' => $notes, + 'tags' => $tags, + 'internal_reference' => $internalRef, + 'sepa_cc' => $sepaCC, + 'sepa_ct_op' => $sepaCtOp, + 'sepa_ct_id' => $sepaCtId, + 'sepa_db' => $sepaDb, + 'sepa_country' => $sepaCountry, + 'sepa_ep' => $sepaEp, + 'sepa_ci' => $sepaCi, + 'sepa_batch_id' => $sepaBatchId, + 'external_id' => $externalId, + 'original-source' => $originalSource, + 'recurrence_id' => $recurrenceId, + 'bunq_payment_id' => $bunq, + 'import_hash' => $hash, + 'import_hash_v2' => $hashTwo, + 'interest_date' => $interestDate, + 'book_date' => $bookDate, + 'process_date' => $processDate, + 'due_date' => $dueDate, + 'payment_date' => $paymentDate, + 'invoice_date' => $invoiceDate, + ]; + } + private function getTransactionBudget(Transaction $left, Transaction $right): ?int { app('log')->debug('Now in getTransactionBudget()'); // try to get a budget ID from the left transaction: - /** @var Budget|null $budget */ + /** @var null|Budget $budget */ $budget = $left->budgets()->first(); if (null !== $budget) { app('log')->debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $left->id)); @@ -339,7 +313,7 @@ class MigrateToGroups extends Command } // try to get a budget ID from the right transaction: - /** @var Budget|null $budget */ + /** @var null|Budget $budget */ $budget = $right->budgets()->first(); if (null !== $budget) { app('log')->debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $right->id)); @@ -352,18 +326,12 @@ class MigrateToGroups extends Command return null; } - /** - * @param Transaction $left - * @param Transaction $right - * - * @return int|null - */ private function getTransactionCategory(Transaction $left, Transaction $right): ?int { app('log')->debug('Now in getTransactionCategory()'); // try to get a category ID from the left transaction: - /** @var Category|null $category */ + /** @var null|Category $category */ $category = $left->categories()->first(); if (null !== $category) { app('log')->debug(sprintf('Return category #%d, from transaction #%d', $category->id, $left->id)); @@ -372,7 +340,7 @@ class MigrateToGroups extends Command } // try to get a category ID from the left transaction: - /** @var Category|null $category */ + /** @var null|Category $category */ $category = $right->categories()->first(); if (null !== $category) { app('log')->debug(sprintf('Return category #%d, from transaction #%d', $category->id, $category->id)); @@ -395,6 +363,7 @@ class MigrateToGroups extends Command if ($total > 0) { app('log')->debug(sprintf('Going to convert %d transaction journals. Please hold..', $total)); $this->friendlyInfo(sprintf('Going to convert %d transaction journals. Please hold..', $total)); + /** @var array $array */ foreach ($orphanedJournals as $array) { $this->giveGroup($array); @@ -405,12 +374,9 @@ class MigrateToGroups extends Command } } - /** - * @param array $array - */ private function giveGroup(array $array): void { - $groupId = DB::table('transaction_groups')->insertGetId( + $groupId = \DB::table('transaction_groups')->insertGetId( [ 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), @@ -418,13 +384,10 @@ class MigrateToGroups extends Command 'user_id' => $array['user_id'], ] ); - DB::table('transaction_journals')->where('id', $array['id'])->update(['transaction_group_id' => $groupId]); - $this->count++; + \DB::table('transaction_journals')->where('id', $array['id'])->update(['transaction_group_id' => $groupId]); + ++$this->count; } - /** - * - */ private function markAsMigrated(): void { app('fireflyconfig')->set(self::CONFIG_NAME, true); diff --git a/app/Console/Commands/Upgrade/MigrateToRules.php b/app/Console/Commands/Upgrade/MigrateToRules.php index 94ec5bd1e0..6937e3755d 100644 --- a/app/Console/Commands/Upgrade/MigrateToRules.php +++ b/app/Console/Commands/Upgrade/MigrateToRules.php @@ -34,8 +34,6 @@ use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Console\Command; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class MigrateToRules @@ -58,10 +56,7 @@ class MigrateToRules extends Command /** * Execute the console command. * - * @return int - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -73,8 +68,8 @@ class MigrateToRules extends Command return 0; } - $users = $this->userRepository->all(); + /** @var User $user */ foreach ($users as $user) { $this->migrateUser($user); @@ -96,8 +91,6 @@ class MigrateToRules 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. - * - */ private function stupidLaravel(): void { @@ -108,11 +101,6 @@ class MigrateToRules extends Command $this->ruleRepository = app(RuleRepositoryInterface::class); } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); @@ -126,8 +114,6 @@ 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 @@ -159,11 +145,6 @@ class MigrateToRules extends Command } } - /** - * @param RuleGroup $ruleGroup - * @param Bill $bill - * @param Preference $language - */ private function migrateBill(RuleGroup $ruleGroup, Bill $bill, Preference $language): void { if ('MIGRATED_TO_RULES' === $bill->match) { @@ -228,12 +209,9 @@ class MigrateToRules extends Command 'active' => $bill->active, ]; $this->billRepository->update($bill, $newBillData); - $this->count++; + ++$this->count; } - /** - * - */ private function markAsExecuted(): void { app('fireflyconfig')->set(self::CONFIG_NAME, true); diff --git a/app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php b/app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php index 18e01b6119..32a64b7131 100644 --- a/app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php +++ b/app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php @@ -34,8 +34,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Journal\JournalCLIRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use Illuminate\Console\Command; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class OtherCurrenciesCorrections @@ -55,10 +53,6 @@ class OtherCurrenciesCorrections extends Command /** * Execute the console command. - * - * @return int - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -70,7 +64,6 @@ class OtherCurrenciesCorrections extends Command return 0; } - $this->updateOtherJournalsCurrencies(); $this->markAsExecuted(); @@ -83,8 +76,6 @@ class OtherCurrenciesCorrections 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. - * - */ private function stupidLaravel(): void { @@ -95,11 +86,6 @@ class OtherCurrenciesCorrections extends Command $this->cliRepos = app(JournalCLIRepositoryInterface::class); } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); @@ -119,7 +105,7 @@ class OtherCurrenciesCorrections extends Command private function updateOtherJournalsCurrencies(): void { $set = $this->cliRepos->getAllJournals( - [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,] + [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION] ); /** @var TransactionJournal $journal */ @@ -128,9 +114,6 @@ class OtherCurrenciesCorrections extends Command } } - /** - * @param TransactionJournal $journal - */ private function updateJournalCurrency(TransactionJournal $journal): void { $this->accountRepos->setUser($journal->user); @@ -156,13 +139,13 @@ class OtherCurrenciesCorrections extends Command $journal->id ) ); - $this->count++; + ++$this->count; return; } // fix each transaction: $journal->transactions->each( - static function (Transaction $transaction) use ($currency) { + static function (Transaction $transaction) use ($currency): void { if (null === $transaction->transaction_currency_id) { $transaction->transaction_currency_id = $currency->id; $transaction->save(); @@ -179,31 +162,33 @@ class OtherCurrenciesCorrections extends Command ); // also update the journal, of course: $journal->transaction_currency_id = $currency->id; - $this->count++; + ++$this->count; $journal->save(); } /** * 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( @@ -212,7 +197,9 @@ class OtherCurrenciesCorrections extends Command '=', '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( @@ -221,17 +208,13 @@ class OtherCurrenciesCorrections extends Command '=', '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; @@ -252,9 +235,6 @@ class OtherCurrenciesCorrections extends Command 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 58697b522d..6d22bb7bb2 100644 --- a/app/Console/Commands/Upgrade/RenameAccountMeta.php +++ b/app/Console/Commands/Upgrade/RenameAccountMeta.php @@ -27,8 +27,6 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\AccountMeta; use Illuminate\Console\Command; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class RenameAccountMeta @@ -46,10 +44,7 @@ class RenameAccountMeta extends Command /** * Execute the console command. * - * @return int - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -90,11 +85,6 @@ class RenameAccountMeta extends Command return 0; } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); @@ -105,9 +95,6 @@ class RenameAccountMeta extends Command return false; } - /** - * - */ private function markAsExecuted(): void { app('fireflyconfig')->set(self::CONFIG_NAME, true); diff --git a/app/Console/Commands/Upgrade/TransactionIdentifier.php b/app/Console/Commands/Upgrade/TransactionIdentifier.php index eb21a358b5..3ed8fbab02 100644 --- a/app/Console/Commands/Upgrade/TransactionIdentifier.php +++ b/app/Console/Commands/Upgrade/TransactionIdentifier.php @@ -30,9 +30,6 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalCLIRepositoryInterface; use Illuminate\Console\Command; use Illuminate\Database\QueryException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; -use Schema; /** * Class TransactionIdentifier @@ -57,10 +54,7 @@ class TransactionIdentifier extends Command * When either of these are the same amount, FF3 can't keep them apart: +3/-3, +3/-3, +3/-3. This happens more * often than you would think. So each set gets a number (1,2,3) to keep them apart. * - * @return int - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -73,11 +67,12 @@ class TransactionIdentifier extends Command } // if table does not exist, return false - if (!Schema::hasTable('transaction_journals')) { + if (!\Schema::hasTable('transaction_journals')) { return 0; } $journals = $this->cliRepository->getSplitJournals(); + /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $this->updateJournalIdentifiers($journal); @@ -99,8 +94,6 @@ class TransactionIdentifier 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. - * - */ private function stupidLaravel(): void { @@ -108,11 +101,6 @@ class TransactionIdentifier extends Command $this->count = 0; } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); @@ -126,8 +114,6 @@ class TransactionIdentifier extends Command /** * 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. - * - * @param TransactionJournal $transactionJournal */ private function updateJournalIdentifiers(TransactionJournal $transactionJournal): void { @@ -146,18 +132,12 @@ class TransactionIdentifier extends Command $opposing->save(); $exclude[] = $transaction->id; $exclude[] = $opposing->id; - $this->count++; + ++$this->count; } ++$identifier; } } - /** - * @param Transaction $transaction - * @param array $exclude - * - * @return Transaction|null - */ private function findOpposing(Transaction $transaction, array $exclude): ?Transaction { // find opposing: @@ -166,9 +146,10 @@ class TransactionIdentifier extends Command try { /** @var Transaction $opposing */ $opposing = Transaction::where('transaction_journal_id', $transaction->transaction_journal_id) - ->where('amount', $amount)->where('identifier', '=', 0) - ->whereNotIn('id', $exclude) - ->first(); + ->where('amount', $amount)->where('identifier', '=', 0) + ->whereNotIn('id', $exclude) + ->first() + ; } catch (QueryException $e) { app('log')->error($e->getMessage()); $this->friendlyError('Firefly III could not find the "identifier" field in the "transactions" table.'); @@ -182,9 +163,6 @@ class TransactionIdentifier extends Command 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 a87f658ff8..ab5e731c1b 100644 --- a/app/Console/Commands/Upgrade/TransferCurrenciesCorrections.php +++ b/app/Console/Commands/Upgrade/TransferCurrenciesCorrections.php @@ -32,8 +32,6 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Journal\JournalCLIRepositoryInterface; use Illuminate\Console\Command; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class TransferCurrenciesCorrections @@ -59,10 +57,6 @@ class TransferCurrenciesCorrections extends Command /** * Execute the console command. - * - * @return int - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -74,16 +68,17 @@ class TransferCurrenciesCorrections extends Command return 0; } - $this->startUpdateRoutine(); $this->markAsExecuted(); if (0 === $this->count) { $this->friendlyPositive('All transfers have correct currency information.'); + return 0; } $this->friendlyInfo(sprintf('Verified currency information of %d transfer(s).', $this->count)); + return 0; } @@ -91,8 +86,6 @@ class TransferCurrenciesCorrections 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. - * - */ private function stupidLaravel(): void { @@ -105,8 +98,6 @@ class TransferCurrenciesCorrections extends Command /** * Reset all the class fields for the current transfer. - * - */ private function resetInformation(): void { @@ -118,11 +109,6 @@ class TransferCurrenciesCorrections extends Command $this->destinationCurrency = null; } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); @@ -144,27 +130,23 @@ class TransferCurrenciesCorrections extends Command 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(); - if ($this->isSplitJournal($transfer)) { $this->friendlyWarning(sprintf('Transaction journal #%d is a split journal. Cannot continue.', $transfer->id)); return; } - $this->getSourceInformation($transfer); $this->getDestinationInformation($transfer); @@ -186,7 +168,6 @@ class TransferCurrenciesCorrections extends Command return; } - // fix source transaction having no currency. $this->fixSourceNoCurrency(); @@ -215,10 +196,6 @@ class TransferCurrenciesCorrections extends Command /** * Is this a split transaction journal? - * - * @param TransactionJournal $transfer - * - * @return bool */ private function isSplitJournal(TransactionJournal $transfer): bool { @@ -227,10 +204,6 @@ class TransferCurrenciesCorrections extends Command /** * Extract source transaction, source account + source account currency from the journal. - * - * @param TransactionJournal $journal - * - */ private function getSourceInformation(TransactionJournal $journal): void { @@ -239,21 +212,11 @@ class TransferCurrenciesCorrections extends Command $this->sourceCurrency = null === $this->sourceAccount ? null : $this->getCurrency($this->sourceAccount); } - /** - * @param TransactionJournal $transfer - * - * @return Transaction|null - */ 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; @@ -276,10 +239,6 @@ class TransferCurrenciesCorrections extends Command /** * Extract destination transaction, destination account + destination account currency from the journal. - * - * @param TransactionJournal $journal - * - */ private function getDestinationInformation(TransactionJournal $journal): void { @@ -288,11 +247,6 @@ class TransferCurrenciesCorrections extends Command $this->destinationCurrency = null === $this->destinationAccount ? null : $this->getCurrency($this->destinationAccount); } - /** - * @param TransactionJournal $transfer - * - * @return Transaction|null - */ private function getDestinationTransaction(TransactionJournal $transfer): ?Transaction { return $transfer->transactions()->where('amount', '>', 0)->first(); @@ -300,8 +254,6 @@ class TransferCurrenciesCorrections extends Command /** * Is either the source or destination transaction NULL? - * - * @return bool */ private function isEmptyTransactions(): bool { @@ -310,9 +262,6 @@ class TransferCurrenciesCorrections extends Command || null === $this->destinationAccount; } - /** - * @return bool - */ private function isNoCurrencyPresent(): bool { // source account must have a currency preference. @@ -349,14 +298,15 @@ class TransferCurrenciesCorrections extends Command if (null === $this->sourceTransaction->transaction_currency_id && null !== $this->sourceCurrency) { $this->sourceTransaction ->transaction_currency_id - = $this->sourceCurrency->id; + = $this->sourceCurrency->id + ; $message = sprintf( 'Transaction #%d has no currency setting, now set to %s.', $this->sourceTransaction->id, $this->sourceCurrency->code ); $this->friendlyInfo($message); - $this->count++; + ++$this->count; $this->sourceTransaction->save(); } } @@ -379,7 +329,7 @@ class TransferCurrenciesCorrections extends Command $this->sourceTransaction->amount ); $this->friendlyWarning($message); - $this->count++; + ++$this->count; $this->sourceTransaction->transaction_currency_id = $this->sourceCurrency->id; $this->sourceTransaction->save(); } @@ -394,14 +344,15 @@ class TransferCurrenciesCorrections extends Command if (null === $this->destinationTransaction->transaction_currency_id && null !== $this->destinationCurrency) { $this->destinationTransaction ->transaction_currency_id - = $this->destinationCurrency->id; + = $this->destinationCurrency->id + ; $message = sprintf( 'Transaction #%d has no currency setting, now set to %s.', $this->destinationTransaction->id, $this->destinationCurrency->code ); $this->friendlyInfo($message); - $this->count++; + ++$this->count; $this->destinationTransaction->save(); } } @@ -424,7 +375,7 @@ class TransferCurrenciesCorrections extends Command $this->destinationTransaction->amount ); $this->friendlyWarning($message); - $this->count++; + ++$this->count; $this->destinationTransaction->transaction_currency_id = $this->destinationCurrency->id; $this->destinationTransaction->save(); } @@ -465,7 +416,7 @@ class TransferCurrenciesCorrections extends Command $this->sourceTransaction->save(); $this->destinationTransaction->save(); - $this->count++; + ++$this->count; $this->friendlyInfo( sprintf('Verified foreign currency ID of transaction #%d and #%d', $this->sourceTransaction->id, $this->destinationTransaction->id) ); @@ -481,7 +432,7 @@ class TransferCurrenciesCorrections extends Command if (null === $this->sourceTransaction->foreign_amount && null !== $this->destinationTransaction->foreign_amount) { $this->sourceTransaction->foreign_amount = bcmul($this->destinationTransaction->foreign_amount, '-1'); $this->sourceTransaction->save(); - $this->count++; + ++$this->count; $this->friendlyInfo( sprintf( 'Restored foreign amount of source transaction #%d to %s', @@ -501,7 +452,7 @@ class TransferCurrenciesCorrections extends Command if (null === $this->destinationTransaction->foreign_amount && null !== $this->sourceTransaction->foreign_amount) { $this->destinationTransaction->foreign_amount = bcmul($this->sourceTransaction->foreign_amount, '-1'); $this->destinationTransaction->save(); - $this->count++; + ++$this->count; $this->friendlyInfo( sprintf( 'Restored foreign amount of destination transaction #%d to %s', @@ -514,8 +465,6 @@ class TransferCurrenciesCorrections extends Command /** * This method makes sure that the transaction journal uses the currency given in the source transaction. - * - * @param TransactionJournal $journal */ private function fixTransactionJournalCurrency(TransactionJournal $journal): void { @@ -529,15 +478,12 @@ class TransferCurrenciesCorrections extends Command $this->sourceCurrency->code, $oldCurrencyCode ); - $this->count++; + ++$this->count; $this->friendlyInfo($message); $journal->save(); } } - /** - * - */ private function markAsExecuted(): void { app('fireflyconfig')->set(self::CONFIG_NAME, true); diff --git a/app/Console/Commands/Upgrade/UpgradeCurrencyPreferences.php b/app/Console/Commands/Upgrade/UpgradeCurrencyPreferences.php index c0f0b965fa..5e8b6d48be 100644 --- a/app/Console/Commands/Upgrade/UpgradeCurrencyPreferences.php +++ b/app/Console/Commands/Upgrade/UpgradeCurrencyPreferences.php @@ -1,6 +1,5 @@ get(self::CONFIG_NAME, false); @@ -83,27 +77,25 @@ class UpgradeCurrencyPreferences extends Command private function runUpgrade(): void { $groups = UserGroup::get(); + /** @var UserGroup $group */ foreach ($groups as $group) { $this->upgradeGroupPreferences($group); } $users = User::get(); + /** @var User $user */ foreach ($users as $user) { $this->upgradeUserPreferences($user); } } - /** - * @param UserGroup $group - * - * @return void - */ - private function upgradeGroupPreferences(UserGroup $group) + private function upgradeGroupPreferences(UserGroup $group): void { $currencies = TransactionCurrency::get(); $enabled = new Collection(); + /** @var TransactionCurrency $currency */ foreach ($currencies as $currency) { if ($currency->enabled) { @@ -113,15 +105,11 @@ class UpgradeCurrencyPreferences extends Command $group->currencies()->sync($enabled->pluck('id')->toArray()); } - /** - * @param User $user - * - * @return void - */ private function upgradeUserPreferences(User $user): void { $currencies = TransactionCurrency::get(); $enabled = new Collection(); + /** @var TransactionCurrency $currency */ foreach ($currencies as $currency) { if ($currency->enabled) { @@ -141,11 +129,6 @@ class UpgradeCurrencyPreferences extends Command $user->userGroup->currencies()->updateExistingPivot($defaultCurrency->id, ['group_default' => true]); } - /** - * @param User $user - * - * @return string - */ private function getPreference(User $user): string { $preference = Preference::where('user_id', $user->id)->where('name', 'currencyPreference')->first(['id', 'user_id', 'name', 'data', 'updated_at', 'created_at']); @@ -157,12 +140,10 @@ class UpgradeCurrencyPreferences extends Command if (null !== $preference->data && !is_array($preference->data)) { return (string)$preference->data; } + return 'EUR'; } - /** - * - */ private function markAsExecuted(): void { app('fireflyconfig')->set(self::CONFIG_NAME, true); diff --git a/app/Console/Commands/Upgrade/UpgradeDatabase.php b/app/Console/Commands/Upgrade/UpgradeDatabase.php index 3f48f1f1b8..bf701b585b 100644 --- a/app/Console/Commands/Upgrade/UpgradeDatabase.php +++ b/app/Console/Commands/Upgrade/UpgradeDatabase.php @@ -30,8 +30,6 @@ use Illuminate\Console\Command; /** * Class UpgradeDatabase - * - */ class UpgradeDatabase extends Command { @@ -42,8 +40,6 @@ class UpgradeDatabase extends Command /** * Execute the console command. - * - * @return int */ public function handle(): int { @@ -89,9 +85,6 @@ class UpgradeDatabase extends Command return 0; } - /** - * @return void - */ private function callInitialCommands(): void { $this->call('migrate', ['--seed' => true, '--force' => true, '--no-interaction' => true]); diff --git a/app/Console/Commands/Upgrade/UpgradeLiabilities.php b/app/Console/Commands/Upgrade/UpgradeLiabilities.php index f7c84ad070..0cda08cdc8 100644 --- a/app/Console/Commands/Upgrade/UpgradeLiabilities.php +++ b/app/Console/Commands/Upgrade/UpgradeLiabilities.php @@ -33,8 +33,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Services\Internal\Support\CreditRecalculateService; use FireflyIII\User; use Illuminate\Console\Command; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class UpgradeLiabilities @@ -49,10 +47,6 @@ class UpgradeLiabilities extends Command /** * Execute the console command. - * - * @return int - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -64,14 +58,10 @@ class UpgradeLiabilities extends Command $this->upgradeLiabilities(); $this->markAsExecuted(); + return 0; } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); @@ -82,27 +72,24 @@ class UpgradeLiabilities extends Command return false; } - /** - * - */ private function upgradeLiabilities(): void { $users = User::get(); + /** @var User $user */ foreach ($users as $user) { $this->upgradeForUser($user); } } - /** - * @param User $user - */ private function upgradeForUser(User $user): void { $accounts = $user->accounts() - ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') - ->whereIn('account_types.type', config('firefly.valid_liabilities')) - ->get(['accounts.*']); + ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') + ->whereIn('account_types.type', config('firefly.valid_liabilities')) + ->get(['accounts.*']) + ; + /** @var Account $account */ foreach ($accounts as $account) { $this->upgradeLiability($account); @@ -112,9 +99,6 @@ class UpgradeLiabilities extends Command } } - /** - * @param Account $account - */ private function upgradeLiability(Account $account): void { /** @var AccountRepositoryInterface $repository */ @@ -137,10 +121,6 @@ class UpgradeLiabilities extends Command } } - /** - * @param Account $account - * @param TransactionJournal $openingBalance - */ private function correctOpeningBalance(Account $account, TransactionJournal $openingBalance): void { $source = $this->getSourceTransaction($openingBalance); @@ -159,29 +139,16 @@ class UpgradeLiabilities extends Command } } - /** - * @param TransactionJournal $journal - * - * @return Transaction|null - */ private function getSourceTransaction(TransactionJournal $journal): ?Transaction { return $journal->transactions()->where('amount', '<', 0)->first(); } - /** - * @param TransactionJournal $journal - * - * @return Transaction|null - */ private function getDestinationTransaction(TransactionJournal $journal): ?Transaction { return $journal->transactions()->where('amount', '>', 0)->first(); } - /** - * - */ private function markAsExecuted(): void { app('fireflyconfig')->set(self::CONFIG_NAME, true); diff --git a/app/Console/Commands/Upgrade/UpgradeLiabilitiesEight.php b/app/Console/Commands/Upgrade/UpgradeLiabilitiesEight.php index b6e8f8069d..445e59533e 100644 --- a/app/Console/Commands/Upgrade/UpgradeLiabilitiesEight.php +++ b/app/Console/Commands/Upgrade/UpgradeLiabilitiesEight.php @@ -34,8 +34,6 @@ use FireflyIII\Services\Internal\Destroy\TransactionGroupDestroyService; use FireflyIII\Services\Internal\Support\CreditRecalculateService; use FireflyIII\User; use Illuminate\Console\Command; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class UpgradeLiabilitiesEight @@ -50,10 +48,6 @@ class UpgradeLiabilitiesEight extends Command /** * Execute the console command. - * - * @return int - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function handle(): int { @@ -68,11 +62,6 @@ class UpgradeLiabilitiesEight extends Command return 0; } - /** - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function isExecuted(): bool { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); @@ -83,27 +72,24 @@ class UpgradeLiabilitiesEight extends Command return false; } - /** - * - */ private function upgradeLiabilities(): void { $users = User::get(); + /** @var User $user */ foreach ($users as $user) { $this->upgradeForUser($user); } } - /** - * @param User $user - */ private function upgradeForUser(User $user): void { $accounts = $user->accounts() - ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') - ->whereIn('account_types.type', config('firefly.valid_liabilities')) - ->get(['accounts.*']); + ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') + ->whereIn('account_types.type', config('firefly.valid_liabilities')) + ->get(['accounts.*']) + ; + /** @var Account $account */ foreach ($accounts as $account) { $this->upgradeLiability($account); @@ -113,9 +99,6 @@ class UpgradeLiabilitiesEight extends Command } } - /** - * @param Account $account - */ private function upgradeLiability(Account $account): void { /** @var AccountRepositoryInterface $repository */ @@ -136,26 +119,23 @@ class UpgradeLiabilitiesEight extends Command } } - /** - * @param Account $account - * - * @return bool - */ private function hasBadOpening(Account $account): bool { $openingBalanceType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first(); $liabilityType = TransactionType::whereType(TransactionType::LIABILITY_CREDIT)->first(); $openingJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transactions.account_id', $account->id) - ->where('transaction_journals.transaction_type_id', $openingBalanceType->id) - ->first(['transaction_journals.*']); + ->where('transactions.account_id', $account->id) + ->where('transaction_journals.transaction_type_id', $openingBalanceType->id) + ->first(['transaction_journals.*']) + ; if (null === $openingJournal) { return false; } $liabilityJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transactions.account_id', $account->id) - ->where('transaction_journals.transaction_type_id', $liabilityType->id) - ->first(['transaction_journals.*']); + ->where('transactions.account_id', $account->id) + ->where('transaction_journals.transaction_type_id', $liabilityType->id) + ->first(['transaction_journals.*']) + ; if (null === $liabilityJournal) { return false; } @@ -166,18 +146,14 @@ class UpgradeLiabilitiesEight extends Command return true; } - /** - * @param Account $account - * - * @return void - */ private function deleteCreditTransaction(Account $account): void { $liabilityType = TransactionType::whereType(TransactionType::LIABILITY_CREDIT)->first(); $liabilityJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transactions.account_id', $account->id) - ->where('transaction_journals.transaction_type_id', $liabilityType->id) - ->first(['transaction_journals.*']); + ->where('transactions.account_id', $account->id) + ->where('transaction_journals.transaction_type_id', $liabilityType->id) + ->first(['transaction_journals.*']) + ; if (null !== $liabilityJournal) { $group = $liabilityJournal->transactionGroup; $service = new TransactionGroupDestroyService(); @@ -187,22 +163,21 @@ class UpgradeLiabilitiesEight extends Command } } - /** - * @param Account $account - * - * @return void - */ private function reverseOpeningBalance(Account $account): void { $openingBalanceType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first(); + /** @var TransactionJournal $openingJournal */ $openingJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transactions.account_id', $account->id) - ->where('transaction_journals.transaction_type_id', $openingBalanceType->id) - ->first(['transaction_journals.*']); - /** @var Transaction|null $source */ + ->where('transactions.account_id', $account->id) + ->where('transaction_journals.transaction_type_id', $openingBalanceType->id) + ->first(['transaction_journals.*']) + ; + + /** @var null|Transaction $source */ $source = $openingJournal->transactions()->where('amount', '<', 0)->first(); - /** @var Transaction|null $dest */ + + /** @var null|Transaction $dest */ $dest = $openingJournal->transactions()->where('amount', '>', 0)->first(); if (null !== $source && null !== $dest) { $sourceId = $source->account_id; @@ -217,23 +192,20 @@ class UpgradeLiabilitiesEight extends Command app('log')->warning('Did not find opening balance.'); } - /** - * @param Account $account - * - * @return int - */ private function deleteTransactions(Account $account): int { $count = 0; $journals = TransactionJournal::leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->where('transactions.account_id', $account->id)->get(['transaction_journals.*']); + ->where('transactions.account_id', $account->id)->get(['transaction_journals.*']) + ; + /** @var TransactionJournal $journal */ foreach ($journals as $journal) { -// $delete = false; -// /** @var Transaction $source */ -// $source = $journal->transactions()->where('amount', '<', 0)->first(); -// /** @var Transaction $dest */ -// $dest = $journal->transactions()->where('amount', '>', 0)->first(); + // $delete = false; + // /** @var Transaction $source */ + // $source = $journal->transactions()->where('amount', '<', 0)->first(); + // /** @var Transaction $dest */ + // $dest = $journal->transactions()->where('amount', '>', 0)->first(); /** * // if source is this liability and destination is expense, remove transaction. @@ -253,16 +225,13 @@ class UpgradeLiabilitiesEight extends Command // if ($delete) { $service = app(TransactionGroupDestroyService::class); $service->destroy($journal->transactionGroup); - $count++; + ++$count; // } } return $count; } - /** - * - */ private function markAsExecuted(): void { app('fireflyconfig')->set(self::CONFIG_NAME, true); diff --git a/app/Console/Commands/VerifiesAccessToken.php b/app/Console/Commands/VerifiesAccessToken.php index 38c1809551..5691f03910 100644 --- a/app/Console/Commands/VerifiesAccessToken.php +++ b/app/Console/Commands/VerifiesAccessToken.php @@ -31,18 +31,16 @@ use FireflyIII\User; * Trait VerifiesAccessToken. * * Verifies user access token for sensitive commands. - * - */ trait VerifiesAccessToken { /** - * @return User * @throws FireflyException */ public function getUser(): User { $userId = (int)$this->option('user'); + /** @var UserRepositoryInterface $repository */ $repository = app(UserRepositoryInterface::class); $user = $repository->find($userId); @@ -56,7 +54,7 @@ trait VerifiesAccessToken /** * Abstract method to make sure trait knows about method "option". * - * @param string|null $key + * @param null|string $key * * @return mixed */ @@ -65,13 +63,13 @@ trait VerifiesAccessToken /** * Returns false when given token does not match given user token. * - * @return bool * @throws FireflyException */ protected function verifyAccessToken(): bool { $userId = (int)$this->option('user'); $token = (string)$this->option('token'); + /** @var UserRepositoryInterface $repository */ $repository = app(UserRepositoryInterface::class); $user = $repository->find($userId); diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 7ca1d8903a..8e2776a54e 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -29,8 +29,6 @@ use Illuminate\Foundation\Console\Kernel as ConsoleKernel; /** * File to make sure commands work. - * - */ class Kernel extends ConsoleKernel { @@ -39,20 +37,18 @@ class Kernel extends ConsoleKernel */ protected function commands(): void { - $this->load(__DIR__ . '/Commands'); + $this->load(__DIR__.'/Commands'); require base_path('routes/console.php'); } /** * Define the application's command schedule. - * - * @param Schedule $schedule */ protected function schedule(Schedule $schedule): void { $schedule->call( - static function () { + static function (): void { app('log')->error( 'Firefly III no longer users the Laravel scheduler to do cron jobs! Please read the instructions at https://docs.firefly-iii.org/' ); diff --git a/app/Enums/SearchDirection.php b/app/Enums/SearchDirection.php index 5cc5f17825..ea87e1d481 100644 --- a/app/Enums/SearchDirection.php +++ b/app/Enums/SearchDirection.php @@ -28,5 +28,4 @@ enum SearchDirection case SOURCE; case DESTINATION; case BOTH; - } diff --git a/app/Enums/WebhookDelivery.php b/app/Enums/WebhookDelivery.php index c09f3b1b07..3322d761e8 100644 --- a/app/Enums/WebhookDelivery.php +++ b/app/Enums/WebhookDelivery.php @@ -29,6 +29,6 @@ namespace FireflyIII\Enums; */ enum WebhookDelivery: int { - //case XML = 200; + // case XML = 200; case JSON = 300; } diff --git a/app/Enums/WebhookTrigger.php b/app/Enums/WebhookTrigger.php index 4e48851ba1..c3bf60a4c1 100644 --- a/app/Enums/WebhookTrigger.php +++ b/app/Enums/WebhookTrigger.php @@ -30,9 +30,9 @@ namespace FireflyIII\Enums; enum WebhookTrigger: int { case STORE_TRANSACTION = 100; - //case BEFORE_STORE_TRANSACTION = 101; + // case BEFORE_STORE_TRANSACTION = 101; case UPDATE_TRANSACTION = 110; - //case BEFORE_UPDATE_TRANSACTION = 111; + // case BEFORE_UPDATE_TRANSACTION = 111; case DESTROY_TRANSACTION = 120; - //case BEFORE_DESTROY_TRANSACTION = 121; + // case BEFORE_DESTROY_TRANSACTION = 121; } diff --git a/app/Events/ActuallyLoggedIn.php b/app/Events/ActuallyLoggedIn.php index 598077fe0a..6bb21a5a04 100644 --- a/app/Events/ActuallyLoggedIn.php +++ b/app/Events/ActuallyLoggedIn.php @@ -37,14 +37,10 @@ class ActuallyLoggedIn extends Event public User $user; - /** - * @param User|Authenticatable|null $user - */ - public function __construct(User | Authenticatable | null $user) + public function __construct(null|Authenticatable|User $user) { if ($user instanceof User) { $this->user = $user; } - } } diff --git a/app/Events/Admin/InvitationCreated.php b/app/Events/Admin/InvitationCreated.php index 3df959e956..c057b35b06 100644 --- a/app/Events/Admin/InvitationCreated.php +++ b/app/Events/Admin/InvitationCreated.php @@ -42,8 +42,6 @@ class InvitationCreated extends Event /** * Create a new event instance. - * - * @param InvitedUser $invitee */ public function __construct(InvitedUser $invitee) { diff --git a/app/Events/AdminRequestedTestMessage.php b/app/Events/AdminRequestedTestMessage.php index 09334089bb..4f32c7841c 100644 --- a/app/Events/AdminRequestedTestMessage.php +++ b/app/Events/AdminRequestedTestMessage.php @@ -29,8 +29,6 @@ use Illuminate\Queue\SerializesModels; /** * Class AdminRequestedTestMessage. - * - */ class AdminRequestedTestMessage extends Event { @@ -40,8 +38,6 @@ class AdminRequestedTestMessage extends Event /** * Create a new event instance. - * - * @param User $user */ public function __construct(User $user) { diff --git a/app/Events/DestroyedTransactionGroup.php b/app/Events/DestroyedTransactionGroup.php index fa4aea8b1e..9911864c1e 100644 --- a/app/Events/DestroyedTransactionGroup.php +++ b/app/Events/DestroyedTransactionGroup.php @@ -29,8 +29,6 @@ use Illuminate\Queue\SerializesModels; /** * Class DestroyedTransactionGroup. - * - */ class DestroyedTransactionGroup extends Event { @@ -40,8 +38,6 @@ class DestroyedTransactionGroup extends Event /** * Create a new event instance. - * - * @param TransactionGroup $transactionGroup */ public function __construct(TransactionGroup $transactionGroup) { diff --git a/app/Events/DestroyedTransactionLink.php b/app/Events/DestroyedTransactionLink.php index 9353e95883..c4b748c83b 100644 --- a/app/Events/DestroyedTransactionLink.php +++ b/app/Events/DestroyedTransactionLink.php @@ -37,8 +37,6 @@ class DestroyedTransactionLink extends Event /** * DestroyedTransactionLink constructor. - * - * @param TransactionJournalLink $link */ public function __construct(TransactionJournalLink $link) { diff --git a/app/Events/DetectedNewIPAddress.php b/app/Events/DetectedNewIPAddress.php index 567bba32cc..3b2ba133be 100644 --- a/app/Events/DetectedNewIPAddress.php +++ b/app/Events/DetectedNewIPAddress.php @@ -38,9 +38,6 @@ class DetectedNewIPAddress extends Event /** * Create a new event instance. This event is triggered when a new user registers. - * - * @param User $user - * @param string $ipAddress */ public function __construct(User $user, string $ipAddress) { diff --git a/app/Events/Event.php b/app/Events/Event.php index ff329a8c3a..12c302afdb 100644 --- a/app/Events/Event.php +++ b/app/Events/Event.php @@ -26,9 +26,5 @@ namespace FireflyIII\Events; /** * Class Event. - * - */ -abstract class Event -{ -} +abstract class Event {} diff --git a/app/Events/Model/BudgetLimit/Created.php b/app/Events/Model/BudgetLimit/Created.php index e0133a3f21..2600c41632 100644 --- a/app/Events/Model/BudgetLimit/Created.php +++ b/app/Events/Model/BudgetLimit/Created.php @@ -1,6 +1,5 @@ budgetLimit = $budgetLimit; diff --git a/app/Events/Model/BudgetLimit/Deleted.php b/app/Events/Model/BudgetLimit/Deleted.php index 059d594ab5..06aab6e0ca 100644 --- a/app/Events/Model/BudgetLimit/Deleted.php +++ b/app/Events/Model/BudgetLimit/Deleted.php @@ -1,6 +1,5 @@ budgetLimit = $budgetLimit; diff --git a/app/Events/Model/BudgetLimit/Updated.php b/app/Events/Model/BudgetLimit/Updated.php index 828ae98a07..6429635ea0 100644 --- a/app/Events/Model/BudgetLimit/Updated.php +++ b/app/Events/Model/BudgetLimit/Updated.php @@ -1,6 +1,5 @@ budgetLimit = $budgetLimit; diff --git a/app/Events/Model/PiggyBank/ChangedAmount.php b/app/Events/Model/PiggyBank/ChangedAmount.php index 83fe12b350..9521641ab3 100644 --- a/app/Events/Model/PiggyBank/ChangedAmount.php +++ b/app/Events/Model/PiggyBank/ChangedAmount.php @@ -44,11 +44,6 @@ class ChangedAmount extends Event /** * Create a new event instance. - * - * @param PiggyBank $piggyBank - * @param string $amount - * @param TransactionJournal|null $transactionJournal - * @param TransactionGroup|null $transactionGroup */ public function __construct(PiggyBank $piggyBank, string $amount, ?TransactionJournal $transactionJournal, ?TransactionGroup $transactionGroup) { diff --git a/app/Events/Model/Rule/RuleActionFailedOnArray.php b/app/Events/Model/Rule/RuleActionFailedOnArray.php index fe11bf9a0b..8c6ca6aa9d 100644 --- a/app/Events/Model/Rule/RuleActionFailedOnArray.php +++ b/app/Events/Model/Rule/RuleActionFailedOnArray.php @@ -1,6 +1,5 @@ debug('Created new RuleActionFailedOnArray'); diff --git a/app/Events/Model/Rule/RuleActionFailedOnObject.php b/app/Events/Model/Rule/RuleActionFailedOnObject.php index a9df1869ee..bfc14bd78d 100644 --- a/app/Events/Model/Rule/RuleActionFailedOnObject.php +++ b/app/Events/Model/Rule/RuleActionFailedOnObject.php @@ -1,6 +1,5 @@ debug('Created new RuleActionFailedOnObject'); diff --git a/app/Events/NewVersionAvailable.php b/app/Events/NewVersionAvailable.php index 9051f76e9c..db3626b490 100644 --- a/app/Events/NewVersionAvailable.php +++ b/app/Events/NewVersionAvailable.php @@ -37,8 +37,6 @@ class NewVersionAvailable extends Event /** * Create a new event instance. This event is triggered when a new version is available. - * - * @param string $message */ public function __construct(string $message) { diff --git a/app/Events/RegisteredUser.php b/app/Events/RegisteredUser.php index 16e29abfd2..0a4d116d8f 100644 --- a/app/Events/RegisteredUser.php +++ b/app/Events/RegisteredUser.php @@ -29,8 +29,6 @@ use Illuminate\Queue\SerializesModels; /** * Class RegisteredUser. - * - */ class RegisteredUser extends Event { @@ -40,8 +38,6 @@ class RegisteredUser extends Event /** * Create a new event instance. This event is triggered when a new user registers. - * - * @param User $user */ public function __construct(User $user) { diff --git a/app/Events/RequestedNewPassword.php b/app/Events/RequestedNewPassword.php index 5967426dcd..71634ce001 100644 --- a/app/Events/RequestedNewPassword.php +++ b/app/Events/RequestedNewPassword.php @@ -29,8 +29,6 @@ use Illuminate\Queue\SerializesModels; /** * Class RequestedNewPassword. - * - */ class RequestedNewPassword extends Event { @@ -38,17 +36,15 @@ class RequestedNewPassword extends Event /** @var string The users IP address */ public $ipAddress; + /** @var string The token */ public $token; + /** @var User The user */ public $user; /** * Create a new event instance. This event is triggered when a users tries to reset his or her password. - * - * @param User $user - * @param string $token - * @param string $ipAddress */ public function __construct(User $user, string $token, string $ipAddress) { diff --git a/app/Events/RequestedReportOnJournals.php b/app/Events/RequestedReportOnJournals.php index 5719c25a8f..01ed141159 100644 --- a/app/Events/RequestedReportOnJournals.php +++ b/app/Events/RequestedReportOnJournals.php @@ -32,8 +32,6 @@ use Illuminate\Support\Collection; /** * Class RequestedReportOnJournals - * - */ class RequestedReportOnJournals { @@ -46,9 +44,6 @@ class RequestedReportOnJournals /** * Create a new event instance. - * - * @param int $userId - * @param Collection $groups */ public function __construct(int $userId, Collection $groups) { diff --git a/app/Events/RequestedVersionCheckStatus.php b/app/Events/RequestedVersionCheckStatus.php index 9379beacb3..d574884f28 100644 --- a/app/Events/RequestedVersionCheckStatus.php +++ b/app/Events/RequestedVersionCheckStatus.php @@ -29,8 +29,6 @@ use Illuminate\Queue\SerializesModels; /** * Class RequestedVersionCheckStatus - * - */ class RequestedVersionCheckStatus extends Event { @@ -42,8 +40,6 @@ class RequestedVersionCheckStatus extends Event /** * Create a new event instance. This event is triggered when Firefly III wants to know * what the deal is with the version checker. - * - * @param User $user */ public function __construct(User $user) { diff --git a/app/Events/StoredAccount.php b/app/Events/StoredAccount.php index d126dd3468..441afed64a 100644 --- a/app/Events/StoredAccount.php +++ b/app/Events/StoredAccount.php @@ -38,8 +38,6 @@ class StoredAccount extends Event /** * Create a new event instance. - * - * @param Account $account */ public function __construct(Account $account) { diff --git a/app/Events/StoredTransactionGroup.php b/app/Events/StoredTransactionGroup.php index 40d65bf776..268525635f 100644 --- a/app/Events/StoredTransactionGroup.php +++ b/app/Events/StoredTransactionGroup.php @@ -29,8 +29,6 @@ use Illuminate\Queue\SerializesModels; /** * Class StoredTransactionGroup. - * - */ class StoredTransactionGroup extends Event { @@ -42,10 +40,6 @@ class StoredTransactionGroup extends Event /** * Create a new event instance. - * - * @param TransactionGroup $transactionGroup - * @param bool $applyRules - * @param bool $fireWebhooks */ public function __construct(TransactionGroup $transactionGroup, bool $applyRules, bool $fireWebhooks) { diff --git a/app/Events/TriggeredAuditLog.php b/app/Events/TriggeredAuditLog.php index a09bf11b84..19d66b07da 100644 --- a/app/Events/TriggeredAuditLog.php +++ b/app/Events/TriggeredAuditLog.php @@ -42,6 +42,7 @@ class TriggeredAuditLog extends Event /** * Create a new event instance. + * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct(Model $changer, Model $auditable, string $field, mixed $before, mixed $after) diff --git a/app/Events/UpdatedAccount.php b/app/Events/UpdatedAccount.php index 66640720b2..3deceba692 100644 --- a/app/Events/UpdatedAccount.php +++ b/app/Events/UpdatedAccount.php @@ -38,8 +38,6 @@ class UpdatedAccount extends Event /** * Create a new event instance. - * - * @param Account $account */ public function __construct(Account $account) { diff --git a/app/Events/UpdatedTransactionGroup.php b/app/Events/UpdatedTransactionGroup.php index b3a79a56f2..9827d81d06 100644 --- a/app/Events/UpdatedTransactionGroup.php +++ b/app/Events/UpdatedTransactionGroup.php @@ -29,8 +29,6 @@ use Illuminate\Queue\SerializesModels; /** * Class UpdatedTransactionGroup. - * - */ class UpdatedTransactionGroup extends Event { @@ -42,10 +40,6 @@ class UpdatedTransactionGroup extends Event /** * Create a new event instance. - * - * @param TransactionGroup $transactionGroup - * @param bool $applyRules - * @param bool $fireWebhooks */ public function __construct(TransactionGroup $transactionGroup, bool $applyRules, bool $fireWebhooks) { diff --git a/app/Events/UserChangedEmail.php b/app/Events/UserChangedEmail.php index a71a4b7580..1d2e11fcf7 100644 --- a/app/Events/UserChangedEmail.php +++ b/app/Events/UserChangedEmail.php @@ -29,8 +29,6 @@ use Illuminate\Queue\SerializesModels; /** * Class UserChangedEmail. - * - */ class UserChangedEmail extends Event { @@ -42,10 +40,6 @@ class UserChangedEmail extends Event /** * UserChangedEmail constructor. - * - * @param User $user - * @param string $newEmail - * @param string $oldEmail */ public function __construct(User $user, string $newEmail, string $oldEmail) { diff --git a/app/Events/WarnUserAboutBill.php b/app/Events/WarnUserAboutBill.php index d0673f5755..98b7d5c449 100644 --- a/app/Events/WarnUserAboutBill.php +++ b/app/Events/WarnUserAboutBill.php @@ -29,8 +29,6 @@ use Illuminate\Queue\SerializesModels; /** * Class WarnUserAboutBill. - * - */ class WarnUserAboutBill extends Event { @@ -40,11 +38,6 @@ class WarnUserAboutBill extends Event public int $diff; public string $field; - /** - * @param Bill $bill - * @param string $field - * @param int $diff - */ public function __construct(Bill $bill, string $field, int $diff) { $this->bill = $bill; diff --git a/app/Exceptions/BadHttpHeaderException.php b/app/Exceptions/BadHttpHeaderException.php index 1ac5a5359c..19e1e5a7af 100644 --- a/app/Exceptions/BadHttpHeaderException.php +++ b/app/Exceptions/BadHttpHeaderException.php @@ -24,12 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Exceptions; -use Exception; - -/** - * - */ -class BadHttpHeaderException extends Exception +class BadHttpHeaderException extends \Exception { public int $statusCode = 406; } diff --git a/app/Exceptions/DuplicateTransactionException.php b/app/Exceptions/DuplicateTransactionException.php index 28c614f53c..b8d6ece025 100644 --- a/app/Exceptions/DuplicateTransactionException.php +++ b/app/Exceptions/DuplicateTransactionException.php @@ -24,11 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Exceptions; -use Exception; - /** * Class DuplicateTransactionException */ -class DuplicateTransactionException extends Exception -{ -} +class DuplicateTransactionException extends \Exception {} diff --git a/app/Exceptions/FireflyException.php b/app/Exceptions/FireflyException.php index 9d57980b0e..18b3fb0a35 100644 --- a/app/Exceptions/FireflyException.php +++ b/app/Exceptions/FireflyException.php @@ -24,13 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Exceptions; -use Exception; - /** * Class FireflyException. - * - */ -class FireflyException extends Exception -{ -} +class FireflyException extends \Exception {} diff --git a/app/Exceptions/GracefulNotFoundHandler.php b/app/Exceptions/GracefulNotFoundHandler.php index 1b27f0d67b..101abb5cfa 100644 --- a/app/Exceptions/GracefulNotFoundHandler.php +++ b/app/Exceptions/GracefulNotFoundHandler.php @@ -33,7 +33,6 @@ use FireflyIII\User; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; -use Throwable; /** * Class GracefulNotFoundHandler @@ -43,13 +42,13 @@ class GracefulNotFoundHandler extends ExceptionHandler /** * Render an exception into an HTTP response. * - * @param Request $request - * @param Throwable $e + * @param Request $request * - * @return Response - * @throws Throwable + * @throws \Throwable + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - public function render($request, Throwable $e): Response + public function render($request, \Throwable $e): Response { $route = $request->route(); if (null === $route) { @@ -65,57 +64,69 @@ class GracefulNotFoundHandler extends ExceptionHandler app('log')->warning(sprintf('GracefulNotFoundHandler cannot handle route with name "%s"', $name)); return parent::render($request, $e); + case 'accounts.show': case 'accounts.edit': case 'accounts.show.all': return $this->handleAccount($request, $e); + case 'transactions.show': case 'transactions.edit': return $this->handleGroup($request, $e); + case 'attachments.show': case 'attachments.edit': case 'attachments.download': case 'attachments.view': // redirect to original attachment holder. return $this->handleAttachment($request, $e); + case 'bills.show': $request->session()->reflash(); return redirect(route('bills.index')); + case 'currencies.show': $request->session()->reflash(); return redirect(route('currencies.index')); + case 'budgets.show': case 'budgets.edit': case 'budgets.show.limit': $request->session()->reflash(); return redirect(route('budgets.index')); + case 'piggy-banks.show': $request->session()->reflash(); return redirect(route('piggy-banks.index')); + case 'recurring.show': case 'recurring.edit': $request->session()->reflash(); return redirect(route('recurring.index')); + case 'tags.show.all': case 'tags.show': case 'tags.edit': $request->session()->reflash(); return redirect(route('tags.index')); + case 'categories.show': case 'categories.show.all': $request->session()->reflash(); return redirect(route('categories.index')); + case 'rules.edit': $request->session()->reflash(); return redirect(route('rules.index')); + case 'transactions.mass.edit': case 'transactions.mass.delete': case 'transactions.bulk.edit': @@ -130,15 +141,12 @@ class GracefulNotFoundHandler extends ExceptionHandler } /** - * @param Request $request - * @param Throwable $exception - * - * @return Response - * @throws Throwable + * @throws \Throwable */ - private function handleAccount(Request $request, Throwable $exception): Response + private function handleAccount(Request $request, \Throwable $exception): Response { app('log')->debug('404 page is probably a deleted account. Redirect to overview of account types.'); + /** @var User $user */ $user = auth()->user(); $route = $request->route(); @@ -150,7 +158,8 @@ class GracefulNotFoundHandler extends ExceptionHandler if (!($param instanceof Account) && !is_object($param)) { $accountId = (int)$param; } - /** @var Account|null $account */ + + /** @var null|Account $account */ $account = $user->accounts()->with(['accountType'])->withTrashed()->find($accountId); if (null === $account) { app('log')->error(sprintf('Could not find account %d, so give big fat error.', $accountId)); @@ -165,29 +174,29 @@ class GracefulNotFoundHandler extends ExceptionHandler } /** - * @param Request $request - * @param Throwable $exception - * * @return Response - * @throws Throwable + * + * @throws \Throwable */ - private function handleGroup(Request $request, Throwable $exception) + private function handleGroup(Request $request, \Throwable $exception) { app('log')->debug('404 page is probably a deleted group. Redirect to overview of group types.'); + /** @var User $user */ $user = auth()->user(); $route = $request->route(); $param = $route->parameter('transactionGroup'); $groupId = !is_object($param) ? (int)$param : 0; - /** @var TransactionGroup|null $group */ + /** @var null|TransactionGroup $group */ $group = $user->transactionGroups()->withTrashed()->find($groupId); if (null === $group) { app('log')->error(sprintf('Could not find group %d, so give big fat error.', $groupId)); return parent::render($request, $exception); } - /** @var TransactionJournal|null $journal */ + + /** @var null|TransactionJournal $journal */ $journal = $group->transactionJournals()->withTrashed()->first(); if (null === $journal) { app('log')->error(sprintf('Could not find journal for group %d, so give big fat error.', $groupId)); @@ -205,21 +214,21 @@ class GracefulNotFoundHandler extends ExceptionHandler } /** - * @param Request $request - * @param Throwable $exception - * * @return Response - * @throws Throwable + * + * @throws \Throwable */ - private function handleAttachment(Request $request, Throwable $exception) + private function handleAttachment(Request $request, \Throwable $exception) { app('log')->debug('404 page is probably a deleted attachment. Redirect to parent object.'); + /** @var User $user */ $user = auth()->user(); $route = $request->route(); $param = $route->parameter('attachment'); $attachmentId = is_object($param) ? 0 : (int)$param; - /** @var Attachment|null $attachment */ + + /** @var null|Attachment $attachment */ $attachment = $user->attachments()->withTrashed()->find($attachmentId); if (null === $attachment) { app('log')->error(sprintf('Could not find attachment %d, so give big fat error.', $attachmentId)); @@ -229,7 +238,7 @@ class GracefulNotFoundHandler extends ExceptionHandler // get bindable. if (TransactionJournal::class === $attachment->attachable_type) { // is linked to journal, get group of journal (if not also deleted) - /** @var TransactionJournal|null $journal */ + /** @var null|TransactionJournal $journal */ $journal = $user->transactionJournals()->withTrashed()->find($attachment->attachable_id); if (null !== $journal) { return redirect(route('transactions.show', [$journal->transaction_group_id])); @@ -237,7 +246,7 @@ class GracefulNotFoundHandler extends ExceptionHandler } if (Bill::class === $attachment->attachable_type) { // is linked to bill. - /** @var Bill|null $bill */ + /** @var null|Bill $bill */ $bill = $user->bills()->withTrashed()->find($attachment->attachable_id); if (null !== $bill) { return redirect(route('bills.show', [$bill->id])); diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index d62e81247f..d4b2867672 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Exceptions; -use ErrorException; use FireflyIII\Jobs\MailError; use Illuminate\Auth\AuthenticationException; use Illuminate\Database\QueryException; @@ -43,17 +42,14 @@ use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Throwable; /** * Class Handler - * - */ class Handler extends ExceptionHandler { /** - * @var array> + * @var array> */ protected $dontReport = [ @@ -69,15 +65,17 @@ class Handler extends ExceptionHandler ]; /** - * Render an exception into an HTTP response. + * Render an exception into an HTTP response. It's complex but lucky for us, we never use it because + * Firefly III never crashes. * - * @param Request $request - * @param Throwable $e + * @param Request $request * - * @return Response - * @throws Throwable + * @throws \Throwable + * + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - public function render($request, Throwable $e): Response + public function render($request, \Throwable $e): Response { $expectsJson = $request->expectsJson(); // if the user requests anything /api/, assume the user wants to see JSON. @@ -90,33 +88,39 @@ class Handler extends ExceptionHandler if ($e instanceof LaravelValidationException && $expectsJson) { // ignore it: controller will handle it. app('log')->debug(sprintf('Return to parent to handle LaravelValidationException(%d)', $e->status)); + return parent::render($request, $e); } if ($e instanceof NotFoundHttpException && $expectsJson) { // JSON error: app('log')->debug('Return JSON not found error.'); + return response()->json(['message' => 'Resource not found', 'exception' => 'NotFoundHttpException'], 404); } if ($e instanceof AuthenticationException && $expectsJson) { // somehow Laravel handler does not catch this: app('log')->debug('Return JSON unauthenticated error.'); + return response()->json(['message' => 'Unauthenticated', 'exception' => 'AuthenticationException'], 401); } if ($e instanceof OAuthServerException && $expectsJson) { app('log')->debug('Return JSON OAuthServerException.'); + // somehow Laravel handler does not catch this: return response()->json(['message' => $e->getMessage(), 'exception' => 'OAuthServerException'], 401); } if ($e instanceof BadRequestHttpException) { app('log')->debug('Return JSON BadRequestHttpException.'); + return response()->json(['message' => $e->getMessage(), 'exception' => 'BadRequestHttpException'], 400); } if ($e instanceof BadHttpHeaderException) { // is always API exception. app('log')->debug('Return JSON BadHttpHeaderException.'); + return response()->json(['message' => $e->getMessage(), 'exception' => 'BadHttpHeaderException'], $e->statusCode); } @@ -124,9 +128,10 @@ class Handler extends ExceptionHandler $errorCode = 500; $errorCode = $e instanceof MethodNotAllowedHttpException ? 405 : $errorCode; - $isDebug = (bool)config('app.debug', false); + $isDebug = (bool) config('app.debug', false); if ($isDebug) { app('log')->debug(sprintf('Return JSON %s with debug.', get_class($e))); + return response()->json( [ 'message' => $e->getMessage(), @@ -139,6 +144,7 @@ class Handler extends ExceptionHandler ); } app('log')->debug(sprintf('Return JSON %s.', get_class($e))); + return response()->json( ['message' => sprintf('Internal Firefly III Exception: %s', $e->getMessage()), 'exception' => get_class($e)], $errorCode @@ -160,7 +166,7 @@ class Handler extends ExceptionHandler return response()->view('errors.DatabaseException', ['exception' => $e, 'debug' => $isDebug], 500); } - if ($e instanceof FireflyException || $e instanceof ErrorException || $e instanceof OAuthServerException) { + if ($e instanceof FireflyException || $e instanceof \ErrorException || $e instanceof OAuthServerException) { app('log')->debug('Return Firefly III error view.'); $isDebug = config('app.debug'); @@ -175,15 +181,11 @@ class Handler extends ExceptionHandler /** * Report or log an exception. * - * @param Throwable $e - * - * @return void - * @throws Throwable - * + * @throws \Throwable */ - public function report(Throwable $e) + public function report(\Throwable $e): void { - $doMailError = (bool)config('firefly.send_error_message'); + $doMailError = (bool) config('firefly.send_error_message'); if ($this->shouldntReportLocal($e) || !$doMailError) { parent::report($e); @@ -218,36 +220,18 @@ class Handler extends ExceptionHandler // create job that will mail. $ipAddress = request()->ip() ?? '0.0.0.0'; - $job = new MailError($userData, (string)config('firefly.site_owner'), $ipAddress, $data); + $job = new MailError($userData, (string) config('firefly.site_owner'), $ipAddress, $data); dispatch($job); parent::report($e); } - /** - * @param Throwable $e - * - * @return bool - */ - private function shouldntReportLocal(Throwable $e): bool - { - return null !== Arr::first( - $this->dontReport, - static function ($type) use ($e) { - return $e instanceof $type; - } - ); - } - /** * Convert a validation exception into a response. * - * @param Request $request - * @param LaravelValidationException $exception - * - * @return JsonResponse| RedirectResponse |\Illuminate\Http\Response + * @param Request $request */ - protected function invalid($request, LaravelValidationException $exception): JsonResponse | RedirectResponse | \Illuminate\Http\Response + protected function invalid($request, LaravelValidationException $exception): \Illuminate\Http\Response|JsonResponse|RedirectResponse { // protect against open redirect when submitting invalid forms. $previous = app('steam')->getSafePreviousUrl(); @@ -255,15 +239,22 @@ class Handler extends ExceptionHandler return redirect($redirect ?? $previous) ->withInput(Arr::except($request->input(), $this->dontFlash)) - ->withErrors($exception->errors(), $request->input('_error_bag', $exception->errorBag)); + ->withErrors($exception->errors(), $request->input('_error_bag', $exception->errorBag)) + ; + } + + private function shouldntReportLocal(\Throwable $e): bool + { + return null !== Arr::first( + $this->dontReport, + static function ($type) use ($e) { + return $e instanceof $type; + } + ); } /** * Only return the redirectTo property from the exception if it is a valid URL. Return NULL otherwise. - * - * @param LaravelValidationException $exception - * - * @return string|null */ private function getRedirectUrl(LaravelValidationException $exception): ?string { diff --git a/app/Exceptions/IntervalException.php b/app/Exceptions/IntervalException.php index 417fedd182..2eef5cc811 100644 --- a/app/Exceptions/IntervalException.php +++ b/app/Exceptions/IntervalException.php @@ -1,6 +1,5 @@ availableIntervals = []; $this->periodicity = Periodicity::Monthly; } - /** - * @param Periodicity $periodicity - * @param array $intervals - * @param int $code - * @param Throwable|null $previous - * - * @return IntervalException - */ public static function unavailable( Periodicity $periodicity, array $intervals, int $code = 0, - ?Throwable $previous = null - ): self - { + ?\Throwable $previous = null + ): self { $message = sprintf( 'The periodicity %s is unknown. Choose one of available periodicity: %s', $periodicity->name, @@ -70,6 +59,7 @@ final class IntervalException extends Exception $exception = new self($message, $code, $previous); $exception->periodicity = $periodicity; $exception->availableIntervals = $intervals; + return $exception; } } diff --git a/app/Exceptions/NotImplementedException.php b/app/Exceptions/NotImplementedException.php index 31ce17a23f..37bc63c0bb 100644 --- a/app/Exceptions/NotImplementedException.php +++ b/app/Exceptions/NotImplementedException.php @@ -24,13 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Exceptions; -use Exception; - /** * Class NotImplementedException. - * - */ -class NotImplementedException extends Exception -{ -} +class NotImplementedException extends \Exception {} diff --git a/app/Exceptions/ValidationException.php b/app/Exceptions/ValidationException.php index f8f3db344f..1927df03ef 100644 --- a/app/Exceptions/ValidationException.php +++ b/app/Exceptions/ValidationException.php @@ -24,13 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Exceptions; -use Exception; - /** * Class ValidationExceptions. - * - */ -class ValidationException extends Exception -{ -} +class ValidationException extends \Exception {} diff --git a/app/Factory/AccountFactory.php b/app/Factory/AccountFactory.php index 09165897fd..2be08d186c 100644 --- a/app/Factory/AccountFactory.php +++ b/app/Factory/AccountFactory.php @@ -34,7 +34,6 @@ use FireflyIII\Services\Internal\Support\LocationServiceTrait; use FireflyIII\Services\Internal\Update\AccountUpdateService; use FireflyIII\User; use Illuminate\Support\Facades\Log; -use JsonException; /** * Factory to create or return accounts. @@ -56,8 +55,6 @@ class AccountFactory /** * AccountFactory constructor. - * - */ public function __construct() { @@ -70,12 +67,7 @@ class AccountFactory } /** - * @param string $accountName - * @param string $accountType - * - * @return Account * @throws FireflyException - * @throws JsonException */ public function findOrCreate(string $accountName, string $accountType): Account { @@ -107,11 +99,7 @@ class AccountFactory } /** - * @param array $data - * - * @return Account * @throws FireflyException - * @throws JsonException */ public function create(array $data): Account { @@ -133,10 +121,22 @@ class AccountFactory return $return; } + public function find(string $accountName, string $accountType): ?Account + { + app('log')->debug(sprintf('Now in AccountFactory::find("%s", "%s")', $accountName, $accountType)); + $type = AccountType::whereType($accountType)->first(); + + // @var Account|null + return $this->user->accounts()->where('account_type_id', $type->id)->where('name', $accountName)->first(); + } + + public function setUser(User $user): void + { + $this->user = $user; + $this->accountRepository->setUser($user); + } + /** - * @param array $data - * - * @return AccountType|null * @throws FireflyException */ protected function getAccountType(array $data): ?AccountType @@ -161,6 +161,7 @@ class AccountFactory } if (null === $result) { app('log')->warning(sprintf('Found NO account type based on %d and "%s"', $accountTypeId, $accountTypeName)); + throw new FireflyException(sprintf('AccountFactory::create() was unable to find account type #%d ("%s").', $accountTypeId, $accountTypeName)); } app('log')->debug(sprintf('Found account type based on %d and "%s": "%s"', $accountTypeId, $accountTypeName, $result->type)); @@ -169,27 +170,7 @@ class AccountFactory } /** - * @param string $accountName - * @param string $accountType - * - * @return Account|null - */ - public function find(string $accountName, string $accountType): ?Account - { - app('log')->debug(sprintf('Now in AccountFactory::find("%s", "%s")', $accountName, $accountType)); - $type = AccountType::whereType($accountType)->first(); - - /** @var Account|null */ - return $this->user->accounts()->where('account_type_id', $type->id)->where('name', $accountName)->first(); - } - - /** - * @param AccountType $type - * @param array $data - * - * @return Account * @throws FireflyException - * @throws JsonException */ private function createAccount(AccountType $type, array $data): Account { @@ -257,12 +238,7 @@ class AccountFactory } /** - * @param Account $account - * @param array $data - * - * @return array * @throws FireflyException - * @throws JsonException */ private function cleanMetaDataArray(Account $account, array $data): array { @@ -272,7 +248,7 @@ class AccountFactory $currency = $this->getCurrency($currencyId, $currencyCode); // only asset account may have a role: - if ($account->accountType->type !== AccountType::ASSET) { + if (AccountType::ASSET !== $account->accountType->type) { $accountRole = ''; } // only liability may have direction: @@ -285,17 +261,13 @@ class AccountFactory return $data; } - /** - * @param Account $account - * @param array $data - */ private function storeMetaData(Account $account, array $data): void { $fields = $this->validFields; - if ($account->accountType->type === AccountType::ASSET) { + if (AccountType::ASSET === $account->accountType->type) { $fields = $this->validAssetFields; } - if ($account->accountType->type === AccountType::ASSET && 'ccAsset' === $data['account_role']) { + if (AccountType::ASSET === $account->accountType->type && 'ccAsset' === $data['account_role']) { $fields = $this->validCCFields; } @@ -304,7 +276,7 @@ class AccountFactory $list = config('firefly.valid_currency_account_types'); if (!in_array($type, $list, true)) { $pos = array_search('currency_id', $fields, true); - if ($pos !== false) { + if (false !== $pos) { unset($fields[$pos]); } } @@ -329,9 +301,6 @@ class AccountFactory } /** - * @param Account $account - * @param array $data - * * @throws FireflyException */ private function storeOpeningBalance(Account $account, array $data): void @@ -351,9 +320,6 @@ class AccountFactory } /** - * @param Account $account - * @param array $data - * * @throws FireflyException */ private function storeCreditLiability(Account $account, array $data): void @@ -380,9 +346,6 @@ class AccountFactory } /** - * @param Account $account - * @param array $data - * * @throws FireflyException */ private function storeOrder(Account $account, array $data): void @@ -402,13 +365,4 @@ class AccountFactory $updateService->setUser($account->user); $updateService->update($account, ['order' => $order]); } - - /** - * @param User $user - */ - public function setUser(User $user): void - { - $this->user = $user; - $this->accountRepository->setUser($user); - } } diff --git a/app/Factory/AccountMetaFactory.php b/app/Factory/AccountMetaFactory.php index 46f1d013b1..cad3bdddb6 100644 --- a/app/Factory/AccountMetaFactory.php +++ b/app/Factory/AccountMetaFactory.php @@ -34,16 +34,10 @@ class AccountMetaFactory { /** * Create update or delete meta data. - * - * @param Account $account - * @param string $field - * @param string $value - * - * @return AccountMeta|null */ public function crud(Account $account, string $field, string $value): ?AccountMeta { - /** @var AccountMeta|null $entry */ + /** @var null|AccountMeta $entry */ $entry = $account->accountMeta()->where('name', $field)->first(); // must not be an empty string: if ('' !== $value) { @@ -65,11 +59,6 @@ class AccountMetaFactory return $entry; } - /** - * @param array $data - * - * @return AccountMeta|null - */ public function create(array $data): ?AccountMeta { return AccountMeta::create($data); diff --git a/app/Factory/AttachmentFactory.php b/app/Factory/AttachmentFactory.php index bf4f63da5a..41038b67d6 100644 --- a/app/Factory/AttachmentFactory.php +++ b/app/Factory/AttachmentFactory.php @@ -38,9 +38,6 @@ class AttachmentFactory private User $user; /** - * @param array $data - * - * @return Attachment|null * @throws FireflyException */ public function create(array $data): ?Attachment @@ -51,7 +48,7 @@ class AttachmentFactory // get journal instead of transaction. if (Transaction::class === $model) { - /** @var Transaction|null $transaction */ + /** @var null|Transaction $transaction */ $transaction = $this->user->transactions()->find((int)$data['attachable_id']); if (null === $transaction) { throw new FireflyException('Unexpectedly could not find transaction'); @@ -86,9 +83,6 @@ class AttachmentFactory return $attachment; } - /** - * @param User $user - */ public function setUser(User $user): void { $this->user = $user; diff --git a/app/Factory/BillFactory.php b/app/Factory/BillFactory.php index d4b81d6eab..3a0db90c22 100644 --- a/app/Factory/BillFactory.php +++ b/app/Factory/BillFactory.php @@ -30,7 +30,6 @@ use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; use FireflyIII\Services\Internal\Support\BillServiceTrait; use FireflyIII\User; use Illuminate\Database\QueryException; -use JsonException; /** * Class BillFactory @@ -43,11 +42,7 @@ class BillFactory private User $user; /** - * @param array $data - * - * @return Bill|null * @throws FireflyException - * @throws JsonException */ public function create(array $data): ?Bill { @@ -59,6 +54,7 @@ class BillFactory 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( [ @@ -81,6 +77,7 @@ class BillFactory } catch (QueryException $e) { app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); + throw new FireflyException('400000: Could not store bill.', 0, $e); } @@ -108,12 +105,6 @@ class BillFactory return $bill; } - /** - * @param int|null $billId - * @param null|string $billName - * - * @return Bill|null - */ public function find(?int $billId, ?string $billName): ?Bill { $billId = (int)$billId; @@ -133,19 +124,11 @@ class BillFactory return $bill; } - /** - * @param string $name - * - * @return Bill|null - */ public function findByName(string $name): ?Bill { return $this->user->bills()->where('name', 'LIKE', sprintf('%%%s%%', $name))->first(); } - /** - * @param User $user - */ public function setUser(User $user): void { $this->user = $user; diff --git a/app/Factory/BudgetFactory.php b/app/Factory/BudgetFactory.php index 9c284b91f6..f5b438b714 100644 --- a/app/Factory/BudgetFactory.php +++ b/app/Factory/BudgetFactory.php @@ -33,12 +33,6 @@ class BudgetFactory { private User $user; - /** - * @param int|null $budgetId - * @param null|string $budgetName - * - * @return Budget|null - */ public function find(?int $budgetId, ?string $budgetName): ?Budget { $budgetId = (int)$budgetId; @@ -50,7 +44,7 @@ class BudgetFactory // first by ID: if ($budgetId > 0) { - /** @var Budget|null $budget */ + /** @var null|Budget $budget */ $budget = $this->user->budgets()->find($budgetId); if (null !== $budget) { return $budget; @@ -67,19 +61,11 @@ class BudgetFactory return null; } - /** - * @param string $name - * - * @return Budget|null - */ public function findByName(string $name): ?Budget { return $this->user->budgets()->where('name', $name)->first(); } - /** - * @param User $user - */ public function setUser(User $user): void { $this->user = $user; diff --git a/app/Factory/CategoryFactory.php b/app/Factory/CategoryFactory.php index 799740b3a1..b5adc0d7e6 100644 --- a/app/Factory/CategoryFactory.php +++ b/app/Factory/CategoryFactory.php @@ -36,10 +36,6 @@ class CategoryFactory private User $user; /** - * @param int|null $categoryId - * @param null|string $categoryName - * - * @return Category|null * @throws FireflyException */ public function findOrCreate(?int $categoryId, ?string $categoryName): ?Category @@ -54,7 +50,7 @@ class CategoryFactory } // first by ID: if ($categoryId > 0) { - /** @var Category|null $category */ + /** @var null|Category $category */ $category = $this->user->categories()->find($categoryId); if (null !== $category) { return $category; @@ -66,6 +62,7 @@ class CategoryFactory if (null !== $category) { return $category; } + try { return Category::create( [ @@ -77,6 +74,7 @@ class CategoryFactory } catch (QueryException $e) { app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); + throw new FireflyException('400003: Could not store new category.', 0, $e); } } @@ -84,19 +82,11 @@ class CategoryFactory return null; } - /** - * @param string $name - * - * @return Category|null - */ public function findByName(string $name): ?Category { return $this->user->categories()->where('name', $name)->first(); } - /** - * @param User $user - */ public function setUser(User $user): void { $this->user = $user; diff --git a/app/Factory/PiggyBankEventFactory.php b/app/Factory/PiggyBankEventFactory.php index 38c395c5e0..34ce47c4b5 100644 --- a/app/Factory/PiggyBankEventFactory.php +++ b/app/Factory/PiggyBankEventFactory.php @@ -34,10 +34,6 @@ use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; */ class PiggyBankEventFactory { - /** - * @param TransactionJournal $journal - * @param PiggyBank|null $piggyBank - */ public function create(TransactionJournal $journal, ?PiggyBank $piggyBank): void { app('log')->debug(sprintf('Now in PiggyBankEventCreate for a %s', $journal->transactionType->type)); diff --git a/app/Factory/PiggyBankFactory.php b/app/Factory/PiggyBankFactory.php index ec53f757b5..505c92d0da 100644 --- a/app/Factory/PiggyBankFactory.php +++ b/app/Factory/PiggyBankFactory.php @@ -33,12 +33,6 @@ class PiggyBankFactory { private User $user; - /** - * @param int|null $piggyBankId - * @param null|string $piggyBankName - * - * @return PiggyBank|null - */ public function find(?int $piggyBankId, ?string $piggyBankName): ?PiggyBank { $piggyBankId = (int)$piggyBankId; @@ -48,7 +42,7 @@ class PiggyBankFactory } // first find by ID: if ($piggyBankId > 0) { - /** @var PiggyBank|null $piggyBank */ + /** @var null|PiggyBank $piggyBank */ $piggyBank = $this->user->piggyBanks()->find($piggyBankId); if (null !== $piggyBank) { return $piggyBank; @@ -57,7 +51,7 @@ class PiggyBankFactory // then find by name: if ('' !== $piggyBankName) { - /** @var PiggyBank|null $piggyBank */ + /** @var null|PiggyBank $piggyBank */ $piggyBank = $this->findByName($piggyBankName); if (null !== $piggyBank) { return $piggyBank; @@ -67,19 +61,11 @@ class PiggyBankFactory return null; } - /** - * @param string $name - * - * @return PiggyBank|null - */ public function findByName(string $name): ?PiggyBank { return $this->user->piggyBanks()->where('piggy_banks.name', $name)->first(); } - /** - * @param User $user - */ public function setUser(User $user): void { $this->user = $user; diff --git a/app/Factory/RecurrenceFactory.php b/app/Factory/RecurrenceFactory.php index 3eab740fbd..364894a661 100644 --- a/app/Factory/RecurrenceFactory.php +++ b/app/Factory/RecurrenceFactory.php @@ -30,7 +30,6 @@ use FireflyIII\Services\Internal\Support\RecurringTransactionTrait; use FireflyIII\Services\Internal\Support\TransactionTypeTrait; use FireflyIII\User; use Illuminate\Support\MessageBag; -use JsonException; /** * Class RecurrenceFactory @@ -45,8 +44,6 @@ class RecurrenceFactory /** * Constructor. - * - */ public function __construct() { @@ -54,11 +51,9 @@ class RecurrenceFactory } /** - * @param array $data - * - * @return Recurrence * @throws FireflyException - * @throws JsonException + * + * @SuppressWarnings(PHPMD.NPathComplexity) */ public function create(array $data): Recurrence { @@ -84,7 +79,7 @@ class RecurrenceFactory $firstDate = $data['recurrence']['first_date']; } if (array_key_exists('nr_of_repetitions', $data['recurrence'])) { - $repetitions = (int)$data['recurrence']['nr_of_repetitions']; + $repetitions = (int) $data['recurrence']['nr_of_repetitions']; } if (array_key_exists('repeat_until', $data['recurrence'])) { $repeatUntil = $data['recurrence']['repeat_until']; @@ -121,10 +116,11 @@ class RecurrenceFactory $recurrence->save(); if (array_key_exists('notes', $data['recurrence'])) { - $this->updateNote($recurrence, (string)$data['recurrence']['notes']); + $this->updateNote($recurrence, (string) $data['recurrence']['notes']); } $this->createRepetitions($recurrence, $data['repetitions'] ?? []); + try { $this->createTransactions($recurrence, $data['transactions'] ?? []); } catch (FireflyException $e) { @@ -133,24 +129,18 @@ class RecurrenceFactory $recurrence->forceDelete(); $message = sprintf('Could not create recurring transaction: %s', $e->getMessage()); $this->errors->add('store', $message); + throw new FireflyException($message, 0, $e); } - return $recurrence; } - /** - * @return MessageBag - */ public function getErrors(): MessageBag { return $this->errors; } - /** - * @param User $user - */ public function setUser(User $user): void { $this->user = $user; diff --git a/app/Factory/TagFactory.php b/app/Factory/TagFactory.php index 0f396d30e0..0af4b51bd0 100644 --- a/app/Factory/TagFactory.php +++ b/app/Factory/TagFactory.php @@ -34,17 +34,12 @@ class TagFactory { private User $user; - /** - * @param string $tag - * - * @return Tag|null - */ public function findOrCreate(string $tag): ?Tag { $tag = trim($tag); app('log')->debug(sprintf('Now in TagFactory::findOrCreate("%s")', $tag)); - /** @var Tag|null $dbTag */ + /** @var null|Tag $dbTag */ $dbTag = $this->user->tags()->where('tag', $tag)->first(); if (null !== $dbTag) { app('log')->debug(sprintf('Tag exists (#%d), return it.', $dbTag->id)); @@ -71,11 +66,6 @@ class TagFactory return $newTag; } - /** - * @param array $data - * - * @return Tag|null - */ public function create(array $data): ?Tag { $zoomLevel = 0 === (int)$data['zoom_level'] ? null : (int)$data['zoom_level']; @@ -92,7 +82,8 @@ class TagFactory 'longitude' => null, 'zoomLevel' => null, ]; - /** @var Tag|null $tag */ + + /** @var null|Tag $tag */ $tag = Tag::create($array); if (null !== $tag && null !== $latitude && null !== $longitude) { // create location object. @@ -107,9 +98,6 @@ class TagFactory return $tag; } - /** - * @param User $user - */ public function setUser(User $user): void { $this->user = $user; diff --git a/app/Factory/TransactionCurrencyFactory.php b/app/Factory/TransactionCurrencyFactory.php index 946f3f63b5..6c305c066b 100644 --- a/app/Factory/TransactionCurrencyFactory.php +++ b/app/Factory/TransactionCurrencyFactory.php @@ -33,9 +33,6 @@ use Illuminate\Database\QueryException; class TransactionCurrencyFactory { /** - * @param array $data - * - * @return TransactionCurrency * @throws FireflyException */ public function create(array $data): TransactionCurrency @@ -68,18 +65,13 @@ class TransactionCurrencyFactory $result = null; app('log')->error(sprintf('Could not create new currency: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); + throw new FireflyException('400004: Could not store new currency.', 0, $e); } return $result; } - /** - * @param int|null $currencyId - * @param null|string $currencyCode - * - * @return TransactionCurrency|null - */ public function find(?int $currencyId, ?string $currencyCode): ?TransactionCurrency { $currencyCode = e($currencyCode); diff --git a/app/Factory/TransactionFactory.php b/app/Factory/TransactionFactory.php index a5f8835984..b28e5715d5 100644 --- a/app/Factory/TransactionFactory.php +++ b/app/Factory/TransactionFactory.php @@ -33,7 +33,6 @@ use FireflyIII\Rules\UniqueIban; use FireflyIII\Services\Internal\Update\AccountUpdateService; use FireflyIII\User; use Illuminate\Database\QueryException; -use Validator; /** * Class TransactionFactory @@ -49,8 +48,6 @@ class TransactionFactory /** * Constructor. - * - */ public function __construct() { @@ -61,10 +58,6 @@ class TransactionFactory /** * Create transaction with negative amount (for source accounts). * - * @param string $amount - * @param string|null $foreignAmount - * - * @return Transaction * @throws FireflyException */ public function createNegative(string $amount, ?string $foreignAmount): Transaction @@ -80,10 +73,64 @@ class TransactionFactory } /** - * @param string $amount - * @param string|null $foreignAmount + * Create transaction with positive amount (for destination accounts). * - * @return Transaction + * @throws FireflyException + */ + public function createPositive(string $amount, ?string $foreignAmount): Transaction + { + if ('' === $foreignAmount) { + $foreignAmount = null; + } + if (null !== $foreignAmount) { + $foreignAmount = app('steam')->positive($foreignAmount); + } + + return $this->create(app('steam')->positive($amount), $foreignAmount); + } + + public function setAccount(Account $account): void + { + $this->account = $account; + } + + public function setAccountInformation(array $accountInformation): void + { + $this->accountInformation = $accountInformation; + } + + public function setCurrency(TransactionCurrency $currency): void + { + $this->currency = $currency; + } + + /** + * @param null|TransactionCurrency $foreignCurrency |null + */ + public function setForeignCurrency(?TransactionCurrency $foreignCurrency): void + { + $this->foreignCurrency = $foreignCurrency; + } + + public function setJournal(TransactionJournal $journal): void + { + $this->journal = $journal; + } + + public function setReconciled(bool $reconciled): void + { + $this->reconciled = $reconciled; + } + + /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function setUser(User $user): void + { + // empty function. + } + + /** * @throws FireflyException */ private function create(string $amount, ?string $foreignAmount): Transaction @@ -102,13 +149,15 @@ class TransactionFactory 'foreign_currency_id' => null, 'identifier' => 0, ]; + try { - /** @var Transaction|null $result */ + /** @var null|Transaction $result */ $result = Transaction::create($data); } catch (QueryException $e) { app('log')->error(sprintf('Could not create transaction: %s', $e->getMessage()), $data); app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); + throw new FireflyException(sprintf('Query exception when creating transaction: %s', $e->getMessage()), 0, $e); } if (null === $result) { @@ -127,9 +176,9 @@ class TransactionFactory ); // do foreign currency thing: add foreign currency info to $one and $two if necessary. - if (null !== $this->foreignCurrency && - null !== $foreignAmount && - $this->foreignCurrency->id !== $this->currency->id) { + if (null !== $this->foreignCurrency + && null !== $foreignAmount + && $this->foreignCurrency->id !== $this->currency->id) { $result->foreign_currency_id = $this->foreignCurrency->id; $result->foreign_amount = $foreignAmount; } @@ -142,29 +191,32 @@ class TransactionFactory } /** - * @return void * @throws FireflyException */ private function updateAccountInformation(): void { if (!array_key_exists('iban', $this->accountInformation)) { app('log')->debug('No IBAN information in array, will not update.'); + return; } if ('' !== (string)$this->account->iban) { app('log')->debug('Account already has IBAN information, will not update.'); + return; } if ($this->account->iban === $this->accountInformation['iban']) { app('log')->debug('Account already has this IBAN, will not update.'); + return; } // validate info: - $validator = Validator::make(['iban' => $this->accountInformation['iban']], [ + $validator = \Validator::make(['iban' => $this->accountInformation['iban']], [ 'iban' => ['required', new UniqueIban($this->account, $this->account->accountType->type)], ]); if ($validator->fails()) { app('log')->debug('Invalid or non-unique IBAN, will not update.'); + return; } @@ -172,92 +224,4 @@ class TransactionFactory $service = app(AccountUpdateService::class); $service->update($this->account, ['iban' => $this->accountInformation['iban']]); } - - /** - * Create transaction with positive amount (for destination accounts). - * - * @param string $amount - * @param string|null $foreignAmount - * - * @return Transaction - * @throws FireflyException - */ - public function createPositive(string $amount, ?string $foreignAmount): Transaction - { - if ('' === $foreignAmount) { - $foreignAmount = null; - } - if (null !== $foreignAmount) { - $foreignAmount = app('steam')->positive($foreignAmount); - } - - return $this->create(app('steam')->positive($amount), $foreignAmount); - } - - /** - * @param Account $account - * - - */ - public function setAccount(Account $account): void - { - $this->account = $account; - } - - /** - * @param array $accountInformation - */ - public function setAccountInformation(array $accountInformation): void - { - $this->accountInformation = $accountInformation; - } - - /** - * @param TransactionCurrency $currency - * - - */ - public function setCurrency(TransactionCurrency $currency): void - { - $this->currency = $currency; - } - - /** - * @param TransactionCurrency|null $foreignCurrency |null - * - - */ - public function setForeignCurrency(?TransactionCurrency $foreignCurrency): void - { - $this->foreignCurrency = $foreignCurrency; - } - - /** - * @param TransactionJournal $journal - * - - */ - public function setJournal(TransactionJournal $journal): void - { - $this->journal = $journal; - } - - /** - * @param bool $reconciled - * - - */ - public function setReconciled(bool $reconciled): void - { - $this->reconciled = $reconciled; - } - - /** - * @param User $user - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function setUser(User $user): void - { - // empty function. - } } diff --git a/app/Factory/TransactionGroupFactory.php b/app/Factory/TransactionGroupFactory.php index ccc1b275ae..87573bb933 100644 --- a/app/Factory/TransactionGroupFactory.php +++ b/app/Factory/TransactionGroupFactory.php @@ -27,12 +27,9 @@ use FireflyIII\Exceptions\DuplicateTransactionException; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionGroup; use FireflyIII\User; -use JsonException; /** * Class TransactionGroupFactory - * - */ class TransactionGroupFactory { @@ -50,22 +47,20 @@ class TransactionGroupFactory /** * Store a new transaction journal. * - * @param array $data - * - * @return TransactionGroup * @throws DuplicateTransactionException * @throws FireflyException - * @throws JsonException */ public function create(array $data): TransactionGroup { app('log')->debug('Now in TransactionGroupFactory::create()'); $this->journalFactory->setUser($this->user); $this->journalFactory->setErrorOnHash($data['error_if_duplicate_hash'] ?? false); + try { $collection = $this->journalFactory->create($data); } catch (DuplicateTransactionException $e) { app('log')->warning('GroupFactory::create() caught journalFactory::create() with a duplicate!'); + throw new DuplicateTransactionException($e->getMessage(), 0, $e); } $title = $data['group_title'] ?? null; @@ -91,8 +86,6 @@ class TransactionGroupFactory /** * Set the user. - * - * @param User $user */ public function setUser(User $user): void { diff --git a/app/Factory/TransactionJournalFactory.php b/app/Factory/TransactionJournalFactory.php index e839fcdc12..3678081ccf 100644 --- a/app/Factory/TransactionJournalFactory.php +++ b/app/Factory/TransactionJournalFactory.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Factory; use Carbon\Carbon; -use Exception; use FireflyIII\Exceptions\DuplicateTransactionException; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; @@ -47,7 +46,6 @@ use FireflyIII\Support\NullArrayObject; use FireflyIII\User; use FireflyIII\Validation\AccountValidator; use Illuminate\Support\Collection; -use JsonException; /** * Class TransactionJournalFactory @@ -70,7 +68,7 @@ class TransactionJournalFactory /** * Constructor. * - * @throws Exception + * @throws \Exception */ public function __construct() { @@ -91,12 +89,8 @@ class TransactionJournalFactory /** * Store a new (set of) transaction journals. * - * @param array $data - * - * @return Collection * @throws DuplicateTransactionException * @throws FireflyException - * @throws JsonException */ public function create(array $data): Collection { @@ -112,6 +106,7 @@ class TransactionJournalFactory return new Collection(); } + try { /** @var array $row */ foreach ($transactions as $index => $row) { @@ -129,12 +124,14 @@ class TransactionJournalFactory app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); $this->forceDeleteOnError($collection); + throw new DuplicateTransactionException($e->getMessage(), 0, $e); } catch (FireflyException $e) { app('log')->warning('TransactionJournalFactory::create() caught an exception.'); app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); $this->forceDeleteOnError($collection); + throw new FireflyException($e->getMessage(), 0, $e); } @@ -142,12 +139,56 @@ class TransactionJournalFactory } /** - * @param NullArrayObject $row + * Set the user. + */ + public function setUser(User $user): void + { + $this->user = $user; + $this->currencyRepository->setUser($this->user); + $this->tagFactory->setUser($user); + $this->billRepository->setUser($this->user); + $this->budgetRepository->setUser($this->user); + $this->categoryRepository->setUser($this->user); + $this->piggyRepository->setUser($this->user); + $this->accountRepository->setUser($this->user); + } + + public function setErrorOnHash(bool $errorOnHash): void + { + $this->errorOnHash = $errorOnHash; + if (true === $errorOnHash) { + app('log')->info('Will trigger duplication alert for this journal.'); + } + } + + protected function storeMeta(TransactionJournal $journal, NullArrayObject $data, string $field): void + { + $set = [ + 'journal' => $journal, + 'name' => $field, + 'data' => (string) ($data[$field] ?? ''), + ]; + if ($data[$field] instanceof Carbon) { + $data[$field]->setTimezone(config('app.timezone')); + app('log')->debug(sprintf('%s Date: %s (%s)', $field, $data[$field], $data[$field]->timezone->getName())); + $set['data'] = $data[$field]->format('Y-m-d H:i:s'); + } + + app('log')->debug(sprintf('Going to store meta-field "%s", with value "%s".', $set['name'], $set['data'])); + + /** @var TransactionJournalMetaFactory $factory */ + $factory = app(TransactionJournalMetaFactory::class); + $factory->updateOrCreate($set); + } + + /** + * TODO typeOverrule: the account validator may have another opinion on the transaction type. not sure what to do + * with this. * - * @return TransactionJournal|null * @throws DuplicateTransactionException * @throws FireflyException - * @throws JsonException + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ private function createJournal(NullArrayObject $row): ?TransactionJournal { @@ -159,13 +200,13 @@ class TransactionJournalFactory $type = $this->typeRepository->findTransactionType(null, $row['type']); $carbon = $row['date'] ?? today(config('app.timezone')); $order = $row['order'] ?? 0; - $currency = $this->currencyRepository->findCurrency((int)$row['currency_id'], $row['currency_code']); + $currency = $this->currencyRepository->findCurrency((int) $row['currency_id'], $row['currency_code']); $foreignCurrency = $this->currencyRepository->findCurrencyNull($row['foreign_currency_id'], $row['foreign_currency_code']); - $bill = $this->billRepository->findBill((int)$row['bill_id'], $row['bill_name']); + $bill = $this->billRepository->findBill((int) $row['bill_id'], $row['bill_name']); $billId = TransactionType::WITHDRAWAL === $type->type && null !== $bill ? $bill->id : null; - $description = (string)$row['description']; + $description = (string) $row['description']; - /** Manipulate basic fields */ + // Manipulate basic fields $carbon->setTimezone(config('app.timezone')); try { @@ -178,9 +219,6 @@ class TransactionJournalFactory return null; } - // typeOverrule: the account validator may have another opinion on the transaction type. - // not sure what to do with this. - /** create or get source and destination accounts */ $sourceInfo = [ 'id' => $row['source_id'], @@ -201,9 +239,7 @@ class TransactionJournalFactory ]; app('log')->debug('Source info:', $sourceInfo); app('log')->debug('Destination info:', $destInfo); - app('log')->debug('Now calling getAccount for the source.'); - $sourceAccount = $this->getAccount($type->type, 'source', $sourceInfo); - app('log')->debug('Now calling getAccount for the destination.'); + $sourceAccount = $this->getAccount($type->type, 'source', $sourceInfo); $destinationAccount = $this->getAccount($type->type, 'destination', $destInfo); app('log')->debug('Done with getAccount(2x)'); @@ -245,17 +281,16 @@ class TransactionJournalFactory $transactionFactory->setAccountInformation($sourceInfo); $transactionFactory->setForeignCurrency($foreignCurrency); $transactionFactory->setReconciled($row['reconciled'] ?? false); + try { - $negative = $transactionFactory->createNegative((string)$row['amount'], (string)$row['foreign_amount']); + $negative = $transactionFactory->createNegative((string) $row['amount'], (string) $row['foreign_amount']); } catch (FireflyException $e) { - app('log')->error('Exception creating negative transaction.'); - app('log')->error($e->getMessage()); - app('log')->error($e->getTraceAsString()); + app('log')->error(sprintf('Exception creating negative transaction: %s', $e->getMessage())); $this->forceDeleteOnError(new Collection([$journal])); + throw new FireflyException($e->getMessage(), 0, $e); } - // and the destination one: /** @var TransactionFactory $transactionFactory */ $transactionFactory = app(TransactionFactory::class); $transactionFactory->setUser($this->user); @@ -265,56 +300,40 @@ class TransactionJournalFactory $transactionFactory->setCurrency($currency); $transactionFactory->setForeignCurrency($foreignCurrency); $transactionFactory->setReconciled($row['reconciled'] ?? false); + try { - $transactionFactory->createPositive((string)$row['amount'], (string)$row['foreign_amount']); + $transactionFactory->createPositive((string) $row['amount'], (string) $row['foreign_amount']); } catch (FireflyException $e) { - app('log')->error('Exception creating positive transaction.'); - app('log')->error($e->getMessage()); - app('log')->error($e->getTraceAsString()); - app('log')->warning('Delete negative transaction.'); + app('log')->error(sprintf('Exception creating positive transaction: %s', $e->getMessage())); $this->forceTrDelete($negative); $this->forceDeleteOnError(new Collection([$journal])); + throw new FireflyException($e->getMessage(), 0, $e); } - // verify that journal has two transactions. Otherwise, delete and cancel. $journal->completed = true; $journal->save(); - - /** Link all other data to the journal. */ - - /** Link budget */ $this->storeBudget($journal, $row); - - /** Link category */ $this->storeCategory($journal, $row); - - /** Set notes */ $this->storeNotes($journal, $row['notes']); - - /** Set piggy bank */ $this->storePiggyEvent($journal, $row); - - /** Set tags */ $this->storeTags($journal, $row['tags']); - - /** set all meta fields */ $this->storeMetaFields($journal, $row); return $journal; } - /** - * @param NullArrayObject $row - * - * @return string - * @throws JsonException - */ private function hashArray(NullArrayObject $row): string { $dataRow = $row->getArrayCopy(); unset($dataRow['import_hash_v2'], $dataRow['original_source']); - $json = json_encode($dataRow, JSON_THROW_ON_ERROR); + + try { + $json = json_encode($dataRow, JSON_THROW_ON_ERROR); + } catch (\JsonException $e) { + app('log')->error(sprintf('Could not encode dataRow: %s', $e->getMessage())); + $json = microtime(); + } $hash = hash('sha256', $json); app('log')->debug(sprintf('The hash is: %s', $hash), $dataRow); @@ -324,10 +343,7 @@ class TransactionJournalFactory /** * If this transaction already exists, throw an error. * - * @param string $hash - * * @throws DuplicateTransactionException - * @throws JsonException */ private function errorIfDuplicate(string $hash): void { @@ -336,11 +352,13 @@ class TransactionJournalFactory return; } app('log')->debug('Will verify duplicate!'); - /** @var TransactionJournalMeta|null $result */ + + /** @var null|TransactionJournalMeta $result */ $result = TransactionJournalMeta::withTrashed() - ->where('data', json_encode($hash, JSON_THROW_ON_ERROR)) - ->with(['transactionJournal', 'transactionJournal.transactionGroup']) - ->first(); + ->where('data', json_encode($hash, JSON_THROW_ON_ERROR)) + ->with(['transactionJournal', 'transactionJournal.transactionGroup']) + ->first() + ; if (null !== $result) { app('log')->warning(sprintf('Found a duplicate in errorIfDuplicate because hash %s is not unique!', $hash)); $journal = $result->transactionJournal()->withTrashed()->first(); @@ -349,13 +367,12 @@ class TransactionJournalFactory if (null === $group) { $groupId = 0; } + throw new DuplicateTransactionException(sprintf('Duplicate of transaction #%d.', $groupId)); } } /** - * @param NullArrayObject $data - * * @throws FireflyException */ private function validateAccounts(NullArrayObject $data): void @@ -367,10 +384,10 @@ class TransactionJournalFactory // validate source account. $array = [ - 'id' => null !== $data['source_id'] ? (int)$data['source_id'] : null, - 'name' => null !== $data['source_name'] ? (string)$data['source_name'] : null, - 'iban' => null !== $data['source_iban'] ? (string)$data['source_iban'] : null, - 'number' => null !== $data['source_number'] ? (string)$data['source_number'] : null, + 'id' => null !== $data['source_id'] ? (int) $data['source_id'] : null, + 'name' => null !== $data['source_name'] ? (string) $data['source_name'] : null, + 'iban' => null !== $data['source_iban'] ? (string) $data['source_iban'] : null, + 'number' => null !== $data['source_number'] ? (string) $data['source_number'] : null, ]; $validSource = $this->accountValidator->validateSource($array); @@ -382,10 +399,10 @@ class TransactionJournalFactory // validate destination account $array = [ - 'id' => null !== $data['destination_id'] ? (int)$data['destination_id'] : null, - 'name' => null !== $data['destination_name'] ? (string)$data['destination_name'] : null, - 'iban' => null !== $data['destination_iban'] ? (string)$data['destination_iban'] : null, - 'number' => null !== $data['destination_number'] ? (string)$data['destination_number'] : null, + 'id' => null !== $data['destination_id'] ? (int) $data['destination_id'] : null, + 'name' => null !== $data['destination_name'] ? (string) $data['destination_name'] : null, + 'iban' => null !== $data['destination_iban'] ? (string) $data['destination_iban'] : null, + 'number' => null !== $data['destination_number'] ? (string) $data['destination_number'] : null, ]; $validDestination = $this->accountValidator->validateDestination($array); @@ -395,40 +412,19 @@ class TransactionJournalFactory } } - /** - * Set the user. - * - * @param User $user - */ - public function setUser(User $user): void - { - $this->user = $user; - $this->currencyRepository->setUser($this->user); - $this->tagFactory->setUser($user); - $this->billRepository->setUser($this->user); - $this->budgetRepository->setUser($this->user); - $this->categoryRepository->setUser($this->user); - $this->piggyRepository->setUser($this->user); - $this->accountRepository->setUser($this->user); - } - - /** - * @param Account|null $sourceAccount - * @param Account|null $destinationAccount - * - * @return array - */ private function reconciliationSanityCheck(?Account $sourceAccount, ?Account $destinationAccount): array { app('log')->debug(sprintf('Now in %s', __METHOD__)); if (null !== $sourceAccount && null !== $destinationAccount) { app('log')->debug('Both accounts exist, simply return them.'); + return [$sourceAccount, $destinationAccount]; } if (null === $destinationAccount) { // @phpstan-ignore-line app('log')->debug('Destination account is NULL, source account is not.'); $account = $this->accountRepository->getReconciliation($sourceAccount); app('log')->debug(sprintf('Will return account #%d ("%s") of type "%s"', $account->id, $account->name, $account->accountType->type)); + return [$sourceAccount, $account]; } @@ -436,21 +432,16 @@ class TransactionJournalFactory app('log')->debug('Source account is NULL, destination account is not.'); $account = $this->accountRepository->getReconciliation($destinationAccount); app('log')->debug(sprintf('Will return account #%d ("%s") of type "%s"', $account->id, $account->name, $account->accountType->type)); + return [$account, $destinationAccount]; } app('log')->debug('Unused fallback'); // @phpstan-ignore-line + return [$sourceAccount, $destinationAccount]; } /** - * @param string $type - * @param TransactionCurrency|null $currency - * @param Account $source - * @param Account $destination - * - * @return TransactionCurrency * @throws FireflyException - * @throws JsonException */ private function getCurrencyByAccount(string $type, ?TransactionCurrency $currency, Account $source, Account $destination): TransactionCurrency { @@ -463,17 +454,13 @@ class TransactionJournalFactory } /** - * @param TransactionCurrency|null $currency - * @param Account $account - * - * @return TransactionCurrency * @throws FireflyException - * @throws JsonException */ private function getCurrency(?TransactionCurrency $currency, Account $account): TransactionCurrency { app('log')->debug('Now in getCurrency()'); - /** @var TransactionCurrency|null $preference */ + + /** @var null|TransactionCurrency $preference */ $preference = $this->accountRepository->getAccountCurrency($account); if (null === $preference && null === $currency) { // return user's default: @@ -487,11 +474,6 @@ class TransactionJournalFactory /** * Set foreign currency to NULL if it's the same as the normal currency: - * - * @param TransactionCurrency|null $currency - * @param TransactionCurrency|null $foreignCurrency - * - * @return TransactionCurrency|null */ private function compareCurrencies(?TransactionCurrency $currency, ?TransactionCurrency $foreignCurrency): ?TransactionCurrency { @@ -506,13 +488,7 @@ class TransactionJournalFactory } /** - * @param string $type - * @param TransactionCurrency|null $foreignCurrency - * @param Account $destination - * - * @return TransactionCurrency|null * @throws FireflyException - * @throws JsonException */ private function getForeignByAccount(string $type, ?TransactionCurrency $foreignCurrency, Account $destination): ?TransactionCurrency { @@ -523,11 +499,6 @@ class TransactionJournalFactory return $foreignCurrency; } - /** - * @param string $description - * - * @return string - */ private function getDescription(string $description): string { $description = '' === $description ? '(empty description)' : $description; @@ -538,13 +509,12 @@ class TransactionJournalFactory /** * Force the deletion of an entire set of transaction journals and their meta object in case of * an error creating a group. - * - * @param Collection $collection */ private function forceDeleteOnError(Collection $collection): void { app('log')->debug(sprintf('forceDeleteOnError on collection size %d item(s)', $collection->count())); $service = app(JournalDestroyService::class); + /** @var TransactionJournal $journal */ foreach ($collection as $journal) { app('log')->debug(sprintf('forceDeleteOnError on journal #%d', $journal->id)); @@ -552,9 +522,6 @@ class TransactionJournalFactory } } - /** - * @param Transaction $transaction - */ private function forceTrDelete(Transaction $transaction): void { $transaction->delete(); @@ -562,15 +529,12 @@ class TransactionJournalFactory /** * Link a piggy bank to this journal. - * - * @param TransactionJournal $journal - * @param NullArrayObject $data */ private function storePiggyEvent(TransactionJournal $journal, NullArrayObject $data): void { app('log')->debug('Will now store piggy event.'); - $piggyBank = $this->piggyRepository->findPiggyBank((int)$data['piggy_bank_id'], $data['piggy_bank_name']); + $piggyBank = $this->piggyRepository->findPiggyBank((int) $data['piggy_bank_id'], $data['piggy_bank_name']); if (null !== $piggyBank) { $this->piggyEventFactory->create($journal, $piggyBank); @@ -581,50 +545,10 @@ class TransactionJournalFactory app('log')->debug('Create no piggy event'); } - /** - * @param TransactionJournal $journal - * @param NullArrayObject $transaction - */ private function storeMetaFields(TransactionJournal $journal, NullArrayObject $transaction): void { foreach ($this->fields as $field) { $this->storeMeta($journal, $transaction, $field); } } - - /** - * @param TransactionJournal $journal - * @param NullArrayObject $data - * @param string $field - */ - protected function storeMeta(TransactionJournal $journal, NullArrayObject $data, string $field): void - { - $set = [ - 'journal' => $journal, - 'name' => $field, - 'data' => (string)($data[$field] ?? ''), - ]; - if ($data[$field] instanceof Carbon) { - $data[$field]->setTimezone(config('app.timezone')); - app('log')->debug(sprintf('%s Date: %s (%s)', $field, $data[$field], $data[$field]->timezone->getName())); - $set['data'] = $data[$field]->format('Y-m-d H:i:s'); - } - - app('log')->debug(sprintf('Going to store meta-field "%s", with value "%s".', $set['name'], $set['data'])); - - /** @var TransactionJournalMetaFactory $factory */ - $factory = app(TransactionJournalMetaFactory::class); - $factory->updateOrCreate($set); - } - - /** - * @param bool $errorOnHash - */ - public function setErrorOnHash(bool $errorOnHash): void - { - $this->errorOnHash = $errorOnHash; - if (true === $errorOnHash) { - app('log')->info('Will trigger duplication alert for this journal.'); - } - } } diff --git a/app/Factory/TransactionJournalMetaFactory.php b/app/Factory/TransactionJournalMetaFactory.php index 0a27e92b5f..56febdfa77 100644 --- a/app/Factory/TransactionJournalMetaFactory.php +++ b/app/Factory/TransactionJournalMetaFactory.php @@ -31,19 +31,15 @@ use FireflyIII\Models\TransactionJournalMeta; */ class TransactionJournalMetaFactory { - /** - * @param array $data - * - * @return TransactionJournalMeta|null - */ public function updateOrCreate(array $data): ?TransactionJournalMeta { - //app('log')->debug('In updateOrCreate()'); + // app('log')->debug('In updateOrCreate()'); $value = $data['data']; - /** @var TransactionJournalMeta|null $entry */ + + /** @var null|TransactionJournalMeta $entry */ $entry = $data['journal']->transactionJournalMeta()->where('name', $data['name'])->first(); if (null === $value && null !== $entry) { - //app('log')->debug('Value is empty, delete meta value.'); + // app('log')->debug('Value is empty, delete meta value.'); $entry->delete(); return null; @@ -65,7 +61,7 @@ class TransactionJournalMetaFactory } if (null === $entry) { - //app('log')->debug('Will create new object.'); + // app('log')->debug('Will create new object.'); app('log')->debug(sprintf('Going to create new meta-data entry to store "%s".', $data['name'])); $entry = new TransactionJournalMeta(); $entry->transactionJournal()->associate($data['journal']); diff --git a/app/Factory/TransactionTypeFactory.php b/app/Factory/TransactionTypeFactory.php index 64294ef1ee..b7bde85c90 100644 --- a/app/Factory/TransactionTypeFactory.php +++ b/app/Factory/TransactionTypeFactory.php @@ -31,11 +31,6 @@ use FireflyIII\Models\TransactionType; */ class TransactionTypeFactory { - /** - * @param string $type - * - * @return TransactionType|null - */ public function find(string $type): ?TransactionType { return TransactionType::whereType(ucfirst($type))->first(); diff --git a/app/Factory/UserGroupFactory.php b/app/Factory/UserGroupFactory.php index f4d3169772..86cf7ccd88 100644 --- a/app/Factory/UserGroupFactory.php +++ b/app/Factory/UserGroupFactory.php @@ -1,6 +1,5 @@ [value => x, 'currency_symbol' => 'x'] - * - * @param array $data - * - * @return array */ public function multiCurrencyPieChart(array $data): array { @@ -99,10 +95,6 @@ class ChartJsGenerator implements GeneratorInterface * ] * * // it's five. - * - * @param array $data - * - * @return array */ public function multiSet(array $data): array { @@ -148,10 +140,6 @@ class ChartJsGenerator implements GeneratorInterface * Expects data as:. * * key => value - * - * @param array $data - * - * @return array */ public function pieChart(array $data): array { @@ -189,11 +177,6 @@ class ChartJsGenerator implements GeneratorInterface * Will generate a (ChartJS) compatible array from the given input. Expects this format:. * * 'label-of-entry' => value - * - * @param string $setLabel - * @param array $data - * - * @return array */ public function singleSet(string $setLabel, array $data): array { diff --git a/app/Generator/Chart/Basic/GeneratorInterface.php b/app/Generator/Chart/Basic/GeneratorInterface.php index a43c85bba9..f55193b96c 100644 --- a/app/Generator/Chart/Basic/GeneratorInterface.php +++ b/app/Generator/Chart/Basic/GeneratorInterface.php @@ -28,11 +28,6 @@ namespace FireflyIII\Generator\Chart\Basic; */ interface GeneratorInterface { - /** - * @param array $data - * - * @return array - */ public function multiCurrencyPieChart(array $data): array; /** @@ -62,10 +57,6 @@ interface GeneratorInterface * ] * * // it's five. - * - * @param array $data - * - * @return array */ public function multiSet(array $data): array; @@ -73,10 +64,6 @@ interface GeneratorInterface * Expects data as:. * * key => value - * - * @param array $data - * - * @return array */ public function pieChart(array $data): array; @@ -84,11 +71,6 @@ interface GeneratorInterface * Will generate a (ChartJS) compatible array from the given input. Expects this format:. * * 'label-of-entry' => value - * - * @param string $setLabel - * @param array $data - * - * @return array */ public function singleSet(string $setLabel, array $data): array; } diff --git a/app/Generator/Report/Account/MonthReportGenerator.php b/app/Generator/Report/Account/MonthReportGenerator.php index 8c6fc02a19..5562b01f2d 100644 --- a/app/Generator/Report/Account/MonthReportGenerator.php +++ b/app/Generator/Report/Account/MonthReportGenerator.php @@ -27,12 +27,9 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Report\ReportGeneratorInterface; use Illuminate\Support\Collection; -use Throwable; /** * Class MonthReportGenerator. - * - */ class MonthReportGenerator implements ReportGeneratorInterface { @@ -44,7 +41,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Generate the report. * - * @return string * @throws FireflyException */ public function generate(): string @@ -53,37 +49,26 @@ class MonthReportGenerator implements ReportGeneratorInterface $doubleIds = implode(',', $this->expense->pluck('id')->toArray()); $reportType = 'account'; $preferredPeriod = $this->preferredPeriod(); + try { $result = view('reports.double.report', compact('accountIds', 'reportType', 'doubleIds', 'preferredPeriod')) ->with('start', $this->start)->with('end', $this->end) ->with('doubles', $this->expense) - ->render(); - } catch (Throwable $e) { + ->render() + ; + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render reports.double.report: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $result = sprintf('Could not render report view: %s', $e->getMessage()); + throw new FireflyException($result, 0, $e); } return $result; } - /** - * Return the preferred period. - * - * @return string - */ - protected function preferredPeriod(): string - { - return 'day'; - } - /** * Set accounts. - * - * @param Collection $accounts - * - * @return ReportGeneratorInterface */ public function setAccounts(Collection $accounts): ReportGeneratorInterface { @@ -94,10 +79,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set budgets. - * - * @param Collection $budgets - * - * @return ReportGeneratorInterface */ public function setBudgets(Collection $budgets): ReportGeneratorInterface { @@ -106,10 +87,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set categories. - * - * @param Collection $categories - * - * @return ReportGeneratorInterface */ public function setCategories(Collection $categories): ReportGeneratorInterface { @@ -118,10 +95,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set end date. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setEndDate(Carbon $date): ReportGeneratorInterface { @@ -132,10 +105,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set expense collection. - * - * @param Collection $expense - * - * @return ReportGeneratorInterface */ public function setExpense(Collection $expense): ReportGeneratorInterface { @@ -146,10 +115,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set start date. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setStartDate(Carbon $date): ReportGeneratorInterface { @@ -160,13 +125,17 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set collection of tags. - * - * @param Collection $tags - * - * @return ReportGeneratorInterface */ public function setTags(Collection $tags): ReportGeneratorInterface { return $this; } + + /** + * Return the preferred period. + */ + protected function preferredPeriod(): string + { + return 'day'; + } } diff --git a/app/Generator/Report/Account/MultiYearReportGenerator.php b/app/Generator/Report/Account/MultiYearReportGenerator.php index 8ac4322348..06619ce900 100644 --- a/app/Generator/Report/Account/MultiYearReportGenerator.php +++ b/app/Generator/Report/Account/MultiYearReportGenerator.php @@ -25,15 +25,11 @@ namespace FireflyIII\Generator\Report\Account; /** * Class MultiYearReportGenerator. - * - */ class MultiYearReportGenerator extends MonthReportGenerator { /** * Returns the preferred period. - * - * @return string */ protected function preferredPeriod(): string { diff --git a/app/Generator/Report/Account/YearReportGenerator.php b/app/Generator/Report/Account/YearReportGenerator.php index 0f73b14d13..e911928c85 100644 --- a/app/Generator/Report/Account/YearReportGenerator.php +++ b/app/Generator/Report/Account/YearReportGenerator.php @@ -25,15 +25,11 @@ namespace FireflyIII\Generator\Report\Account; /** * Class YearReportGenerator. - * - */ class YearReportGenerator extends MonthReportGenerator { /** * Returns the preferred period. - * - * @return string */ protected function preferredPeriod(): string { diff --git a/app/Generator/Report/Audit/MonthReportGenerator.php b/app/Generator/Report/Audit/MonthReportGenerator.php index 8cc4204b61..8db365247d 100644 --- a/app/Generator/Report/Audit/MonthReportGenerator.php +++ b/app/Generator/Report/Audit/MonthReportGenerator.php @@ -31,8 +31,6 @@ use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use Illuminate\Support\Collection; -use JsonException; -use Throwable; /** * Class MonthReportGenerator. @@ -46,15 +44,14 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Generates the report. * - * @return string * @throws FireflyException - * @throws JsonException */ public function generate(): string { $auditData = []; $dayBefore = clone $this->start; $dayBefore->subDay(); + /** @var Account $account */ foreach ($this->accounts as $account) { // balance the day before: @@ -92,14 +89,17 @@ class MonthReportGenerator implements ReportGeneratorInterface 'payment_date', 'invoice_date', ]; + try { $result = view('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) { + ->render() + ; + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render reports.audit.report: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $result = sprintf('Could not render report view: %s', $e->getMessage()); + throw new FireflyException($result, 0, $e); } @@ -109,12 +109,7 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Get the audit report. * - * @param Account $account - * @param Carbon $date - * - * @return array * @throws FireflyException - * @throws JsonException */ public function getAuditReport(Account $account, Carbon $date): array { @@ -129,7 +124,8 @@ class MonthReportGenerator implements ReportGeneratorInterface /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts(new Collection([$account]))->setRange($this->start, $this->end)->withAccountInformation() - ->withBudgetInformation()->withCategoryInformation()->withBillInformation(); + ->withBudgetInformation()->withCategoryInformation()->withBillInformation() + ; $journals = $collector->getExtractedJournals(); $journals = array_reverse($journals, true); $dayBeforeBalance = app('steam')->balance($account, $date); @@ -180,10 +176,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Account collection setter. - * - * @param Collection $accounts - * - * @return ReportGeneratorInterface */ public function setAccounts(Collection $accounts): ReportGeneratorInterface { @@ -194,10 +186,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Budget collection setter. - * - * @param Collection $budgets - * - * @return ReportGeneratorInterface */ public function setBudgets(Collection $budgets): ReportGeneratorInterface { @@ -206,10 +194,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Category collection setter. - * - * @param Collection $categories - * - * @return ReportGeneratorInterface */ public function setCategories(Collection $categories): ReportGeneratorInterface { @@ -218,10 +202,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * End date setter. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setEndDate(Carbon $date): ReportGeneratorInterface { @@ -232,10 +212,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Expenses collection setter. - * - * @param Collection $expense - * - * @return ReportGeneratorInterface */ public function setExpense(Collection $expense): ReportGeneratorInterface { @@ -245,10 +221,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Start date collection setter. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setStartDate(Carbon $date): ReportGeneratorInterface { @@ -259,10 +231,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Tags collection setter. - * - * @param Collection $tags - * - * @return ReportGeneratorInterface */ public function setTags(Collection $tags): ReportGeneratorInterface { diff --git a/app/Generator/Report/Audit/MultiYearReportGenerator.php b/app/Generator/Report/Audit/MultiYearReportGenerator.php index 1677320748..2dde9586a0 100644 --- a/app/Generator/Report/Audit/MultiYearReportGenerator.php +++ b/app/Generator/Report/Audit/MultiYearReportGenerator.php @@ -25,9 +25,5 @@ namespace FireflyIII\Generator\Report\Audit; /** * Class MultiYearReportGenerator. - * - */ -class MultiYearReportGenerator extends MonthReportGenerator -{ -} +class MultiYearReportGenerator extends MonthReportGenerator {} diff --git a/app/Generator/Report/Audit/YearReportGenerator.php b/app/Generator/Report/Audit/YearReportGenerator.php index df7144b367..6357823820 100644 --- a/app/Generator/Report/Audit/YearReportGenerator.php +++ b/app/Generator/Report/Audit/YearReportGenerator.php @@ -25,9 +25,5 @@ namespace FireflyIII\Generator\Report\Audit; /** * Class YearReportGenerator. - * - */ -class YearReportGenerator extends MonthReportGenerator -{ -} +class YearReportGenerator extends MonthReportGenerator {} diff --git a/app/Generator/Report/Budget/MonthReportGenerator.php b/app/Generator/Report/Budget/MonthReportGenerator.php index 3f80611c1f..ca30523f1b 100644 --- a/app/Generator/Report/Budget/MonthReportGenerator.php +++ b/app/Generator/Report/Budget/MonthReportGenerator.php @@ -29,13 +29,10 @@ use FireflyIII\Generator\Report\ReportGeneratorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionType; use Illuminate\Support\Collection; -use Throwable; /** * Class MonthReportGenerator. * TODO include info about tags. - * - */ class MonthReportGenerator implements ReportGeneratorInterface { @@ -56,13 +53,13 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Generates the report. * - * @return string * @throws FireflyException */ public function generate(): string { $accountIds = implode(',', $this->accounts->pluck('id')->toArray()); $budgetIds = implode(',', $this->budgets->pluck('id')->toArray()); + try { $result = view( 'reports.budget.month', @@ -71,11 +68,13 @@ class MonthReportGenerator implements ReportGeneratorInterface ->with('start', $this->start)->with('end', $this->end) ->with('budgets', $this->budgets) ->with('accounts', $this->accounts) - ->render(); - } catch (Throwable $e) { + ->render() + ; + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render reports.account.report: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $result = sprintf('Could not render report view: %s', $e->getMessage()); + throw new FireflyException($result, 0, $e); } @@ -84,10 +83,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Unused category setter. - * - * @param Collection $categories - * - * @return ReportGeneratorInterface */ public function setCategories(Collection $categories): ReportGeneratorInterface { @@ -96,10 +91,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set the end date of the report. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setEndDate(Carbon $date): ReportGeneratorInterface { @@ -110,10 +101,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Unused expense setter. - * - * @param Collection $expense - * - * @return ReportGeneratorInterface */ public function setExpense(Collection $expense): ReportGeneratorInterface { @@ -122,10 +109,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set the start date of the report. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setStartDate(Carbon $date): ReportGeneratorInterface { @@ -136,20 +119,34 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Unused tags setter. - * - * @param Collection $tags - * - * @return ReportGeneratorInterface */ public function setTags(Collection $tags): ReportGeneratorInterface { return $this; } + /** + * Set the involved budgets. + */ + public function setBudgets(Collection $budgets): ReportGeneratorInterface + { + $this->budgets = $budgets; + + return $this; + } + + /** + * Set the involved accounts. + */ + public function setAccounts(Collection $accounts): ReportGeneratorInterface + { + $this->accounts = $accounts; + + return $this; + } + /** * Get the expenses. - * - * @return array */ protected function getExpenses(): array { @@ -162,42 +159,15 @@ class MonthReportGenerator implements ReportGeneratorInterface /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) - ->setTypes([TransactionType::WITHDRAWAL]) - ->withAccountInformation() - ->withBudgetInformation() - ->setBudgets($this->budgets); + ->setTypes([TransactionType::WITHDRAWAL]) + ->withAccountInformation() + ->withBudgetInformation() + ->setBudgets($this->budgets) + ; $journals = $collector->getExtractedJournals(); $this->expenses = $journals; return $journals; } - - /** - * Set the involved budgets. - * - * @param Collection $budgets - * - * @return ReportGeneratorInterface - */ - public function setBudgets(Collection $budgets): ReportGeneratorInterface - { - $this->budgets = $budgets; - - return $this; - } - - /** - * Set the involved accounts. - * - * @param Collection $accounts - * - * @return ReportGeneratorInterface - */ - public function setAccounts(Collection $accounts): ReportGeneratorInterface - { - $this->accounts = $accounts; - - return $this; - } } diff --git a/app/Generator/Report/Budget/MultiYearReportGenerator.php b/app/Generator/Report/Budget/MultiYearReportGenerator.php index a470db9a99..77a8579374 100644 --- a/app/Generator/Report/Budget/MultiYearReportGenerator.php +++ b/app/Generator/Report/Budget/MultiYearReportGenerator.php @@ -25,9 +25,5 @@ namespace FireflyIII\Generator\Report\Budget; /** * Class MultiYearReportGenerator. - * - */ -class MultiYearReportGenerator extends MonthReportGenerator -{ -} +class MultiYearReportGenerator extends MonthReportGenerator {} diff --git a/app/Generator/Report/Budget/YearReportGenerator.php b/app/Generator/Report/Budget/YearReportGenerator.php index 8b0cd930b7..4251a99e49 100644 --- a/app/Generator/Report/Budget/YearReportGenerator.php +++ b/app/Generator/Report/Budget/YearReportGenerator.php @@ -25,9 +25,5 @@ namespace FireflyIII\Generator\Report\Budget; /** * Class YearReportGenerator. - * - */ -class YearReportGenerator extends MonthReportGenerator -{ -} +class YearReportGenerator extends MonthReportGenerator {} diff --git a/app/Generator/Report/Category/MonthReportGenerator.php b/app/Generator/Report/Category/MonthReportGenerator.php index aeca99aa80..53559d6aa9 100644 --- a/app/Generator/Report/Category/MonthReportGenerator.php +++ b/app/Generator/Report/Category/MonthReportGenerator.php @@ -29,13 +29,10 @@ use FireflyIII\Generator\Report\ReportGeneratorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionType; use Illuminate\Support\Collection; -use Throwable; /** * Class MonthReportGenerator. * TODO include info about tags - * - */ class MonthReportGenerator implements ReportGeneratorInterface { @@ -58,7 +55,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Generates the report. * - * @return string * @throws FireflyException */ public function generate(): string @@ -73,21 +69,19 @@ class MonthReportGenerator implements ReportGeneratorInterface ->with('start', $this->start)->with('end', $this->end) ->with('categories', $this->categories) ->with('accounts', $this->accounts) - ->render(); - } catch (Throwable $e) { + ->render() + ; + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render reports.category.month: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $result = sprintf('Could not render report view: %s', $e->getMessage()); + throw new FireflyException($result, 0, $e); } } /** * Empty budget setter. - * - * @param Collection $budgets - * - * @return ReportGeneratorInterface */ public function setBudgets(Collection $budgets): ReportGeneratorInterface { @@ -96,10 +90,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set the end date for this report. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setEndDate(Carbon $date): ReportGeneratorInterface { @@ -110,10 +100,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set the expenses involved in this report. - * - * @param Collection $expense - * - * @return ReportGeneratorInterface */ public function setExpense(Collection $expense): ReportGeneratorInterface { @@ -122,10 +108,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set the start date for this report. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setStartDate(Carbon $date): ReportGeneratorInterface { @@ -136,20 +118,34 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Unused tag setter. - * - * @param Collection $tags - * - * @return ReportGeneratorInterface */ public function setTags(Collection $tags): ReportGeneratorInterface { return $this; } + /** + * Set the categories involved in this report. + */ + public function setCategories(Collection $categories): ReportGeneratorInterface + { + $this->categories = $categories; + + return $this; + } + + /** + * Set the involved accounts. + */ + public function setAccounts(Collection $accounts): ReportGeneratorInterface + { + $this->accounts = $accounts; + + return $this; + } + /** * Get the expenses for this report. - * - * @return array */ protected function getExpenses(): array { @@ -162,8 +158,9 @@ class MonthReportGenerator implements ReportGeneratorInterface /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) - ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) - ->setCategories($this->categories)->withAccountInformation(); + ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) + ->setCategories($this->categories)->withAccountInformation() + ; $transactions = $collector->getExtractedJournals(); $this->expenses = $transactions; @@ -171,38 +168,8 @@ class MonthReportGenerator implements ReportGeneratorInterface return $transactions; } - /** - * Set the categories involved in this report. - * - * @param Collection $categories - * - * @return ReportGeneratorInterface - */ - public function setCategories(Collection $categories): ReportGeneratorInterface - { - $this->categories = $categories; - - return $this; - } - - /** - * Set the involved accounts. - * - * @param Collection $accounts - * - * @return ReportGeneratorInterface - */ - public function setAccounts(Collection $accounts): ReportGeneratorInterface - { - $this->accounts = $accounts; - - return $this; - } - /** * Get the income for this report. - * - * @return array */ protected function getIncome(): array { @@ -214,8 +181,9 @@ class MonthReportGenerator implements ReportGeneratorInterface $collector = app(GroupCollectorInterface::class); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) - ->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) - ->setCategories($this->categories)->withAccountInformation(); + ->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) + ->setCategories($this->categories)->withAccountInformation() + ; $transactions = $collector->getExtractedJournals(); $this->income = $transactions; diff --git a/app/Generator/Report/Category/MultiYearReportGenerator.php b/app/Generator/Report/Category/MultiYearReportGenerator.php index f8525587c0..c7fb20662a 100644 --- a/app/Generator/Report/Category/MultiYearReportGenerator.php +++ b/app/Generator/Report/Category/MultiYearReportGenerator.php @@ -25,9 +25,5 @@ namespace FireflyIII\Generator\Report\Category; /** * Class MultiYearReportGenerator. - * - */ -class MultiYearReportGenerator extends MonthReportGenerator -{ -} +class MultiYearReportGenerator extends MonthReportGenerator {} diff --git a/app/Generator/Report/Category/YearReportGenerator.php b/app/Generator/Report/Category/YearReportGenerator.php index 982ef37fe5..49f3d2f84f 100644 --- a/app/Generator/Report/Category/YearReportGenerator.php +++ b/app/Generator/Report/Category/YearReportGenerator.php @@ -25,9 +25,5 @@ namespace FireflyIII\Generator\Report\Category; /** * Class YearReportGenerator. - * - */ -class YearReportGenerator extends MonthReportGenerator -{ -} +class YearReportGenerator extends MonthReportGenerator {} diff --git a/app/Generator/Report/ReportGeneratorFactory.php b/app/Generator/Report/ReportGeneratorFactory.php index a577f8f076..9c49c9e5f8 100644 --- a/app/Generator/Report/ReportGeneratorFactory.php +++ b/app/Generator/Report/ReportGeneratorFactory.php @@ -28,20 +28,12 @@ use FireflyIII\Exceptions\FireflyException; /** * Class ReportGeneratorFactory. - * - */ class ReportGeneratorFactory { /** * Static report generator class. * - * @param string $type - * @param Carbon $start - * @param Carbon $end - * - * @return ReportGeneratorInterface - * * @throws FireflyException */ public static function reportGenerator(string $type, Carbon $start, Carbon $end): ReportGeneratorInterface @@ -66,6 +58,7 @@ class ReportGeneratorFactory return $obj; } + throw new FireflyException(sprintf('Cannot generate report. There is no "%s"-report for period "%s".', $type, $period)); } } diff --git a/app/Generator/Report/ReportGeneratorInterface.php b/app/Generator/Report/ReportGeneratorInterface.php index 1040e3c4c2..67a0991e4e 100644 --- a/app/Generator/Report/ReportGeneratorInterface.php +++ b/app/Generator/Report/ReportGeneratorInterface.php @@ -33,71 +33,41 @@ interface ReportGeneratorInterface { /** * Generate the report. - * - * @return string */ public function generate(): string; /** * Set the involved accounts. - * - * @param Collection $accounts - * - * @return ReportGeneratorInterface */ public function setAccounts(Collection $accounts): self; /** * Set the involved budgets. - * - * @param Collection $budgets - * - * @return ReportGeneratorInterface */ public function setBudgets(Collection $budgets): self; /** * Set the involved categories. - * - * @param Collection $categories - * - * @return ReportGeneratorInterface */ public function setCategories(Collection $categories): self; /** * Set the end date. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setEndDate(Carbon $date): self; /** * Set the expense accounts. - * - * @param Collection $expense - * - * @return ReportGeneratorInterface */ public function setExpense(Collection $expense): self; /** * Set the start date. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setStartDate(Carbon $date): self; /** * Set the tags. - * - * @param Collection $tags - * - * @return ReportGeneratorInterface */ public function setTags(Collection $tags): self; } diff --git a/app/Generator/Report/Standard/MonthReportGenerator.php b/app/Generator/Report/Standard/MonthReportGenerator.php index 7e1296833a..fe70dc3041 100644 --- a/app/Generator/Report/Standard/MonthReportGenerator.php +++ b/app/Generator/Report/Standard/MonthReportGenerator.php @@ -27,26 +27,24 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Report\ReportGeneratorInterface; use Illuminate\Support\Collection; -use Throwable; /** * Class MonthReportGenerator. - * - */ class MonthReportGenerator implements ReportGeneratorInterface { /** @var Collection The accounts involved in the report. */ private $accounts; + /** @var Carbon The end date. */ private $end; + /** @var Carbon The start date. */ private $start; /** * Generates the report. * - * @return string * @throws FireflyException */ public function generate(): string @@ -56,20 +54,17 @@ class MonthReportGenerator implements ReportGeneratorInterface try { return view('reports.default.month', compact('accountIds', 'reportType'))->with('start', $this->start)->with('end', $this->end)->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render reports.default.month: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $result = 'Could not render report view.'; + throw new FireflyException($result, 0, $e); } } /** * Sets the accounts involved in the report. - * - * @param Collection $accounts - * - * @return ReportGeneratorInterface */ public function setAccounts(Collection $accounts): ReportGeneratorInterface { @@ -80,10 +75,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Unused budget setter. - * - * @param Collection $budgets - * - * @return ReportGeneratorInterface */ public function setBudgets(Collection $budgets): ReportGeneratorInterface { @@ -92,10 +83,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Unused category setter. - * - * @param Collection $categories - * - * @return ReportGeneratorInterface */ public function setCategories(Collection $categories): ReportGeneratorInterface { @@ -104,10 +91,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set the end date of the report. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setEndDate(Carbon $date): ReportGeneratorInterface { @@ -118,10 +101,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set the expenses used in this report. - * - * @param Collection $expense - * - * @return ReportGeneratorInterface */ public function setExpense(Collection $expense): ReportGeneratorInterface { @@ -130,10 +109,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set the start date of this report. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setStartDate(Carbon $date): ReportGeneratorInterface { @@ -144,10 +119,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set the tags used in this report. - * - * @param Collection $tags - * - * @return ReportGeneratorInterface */ public function setTags(Collection $tags): ReportGeneratorInterface { diff --git a/app/Generator/Report/Standard/MultiYearReportGenerator.php b/app/Generator/Report/Standard/MultiYearReportGenerator.php index e358dc5a82..f40dcfe8eb 100644 --- a/app/Generator/Report/Standard/MultiYearReportGenerator.php +++ b/app/Generator/Report/Standard/MultiYearReportGenerator.php @@ -27,26 +27,24 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Report\ReportGeneratorInterface; use Illuminate\Support\Collection; -use Throwable; /** * Class MonthReportGenerator. - * - */ class MultiYearReportGenerator implements ReportGeneratorInterface { /** @var Collection The accounts involved. */ private $accounts; + /** @var Carbon The end date. */ private $end; + /** @var Carbon The start date. */ private $start; /** * Generates the report. * - * @return string * @throws FireflyException */ public function generate(): string @@ -60,20 +58,17 @@ class MultiYearReportGenerator implements ReportGeneratorInterface 'reports.default.multi-year', compact('accountIds', 'reportType') )->with('start', $this->start)->with('end', $this->end)->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render reports.default.multi-year: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $result = sprintf('Could not render report view: %s', $e->getMessage()); + throw new FireflyException($result, 0, $e); } } /** * Sets the accounts used in the report. - * - * @param Collection $accounts - * - * @return ReportGeneratorInterface */ public function setAccounts(Collection $accounts): ReportGeneratorInterface { @@ -84,10 +79,6 @@ class MultiYearReportGenerator implements ReportGeneratorInterface /** * Sets the budgets used in the report. - * - * @param Collection $budgets - * - * @return ReportGeneratorInterface */ public function setBudgets(Collection $budgets): ReportGeneratorInterface { @@ -96,10 +87,6 @@ class MultiYearReportGenerator implements ReportGeneratorInterface /** * Sets the categories used in the report. - * - * @param Collection $categories - * - * @return ReportGeneratorInterface */ public function setCategories(Collection $categories): ReportGeneratorInterface { @@ -108,10 +95,6 @@ class MultiYearReportGenerator implements ReportGeneratorInterface /** * Sets the end date used in the report. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setEndDate(Carbon $date): ReportGeneratorInterface { @@ -122,10 +105,6 @@ class MultiYearReportGenerator implements ReportGeneratorInterface /** * Unused setter for expenses. - * - * @param Collection $expense - * - * @return ReportGeneratorInterface */ public function setExpense(Collection $expense): ReportGeneratorInterface { @@ -134,10 +113,6 @@ class MultiYearReportGenerator implements ReportGeneratorInterface /** * Set the start date of the report. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setStartDate(Carbon $date): ReportGeneratorInterface { @@ -148,10 +123,6 @@ class MultiYearReportGenerator implements ReportGeneratorInterface /** * Set the tags for the report. - * - * @param Collection $tags - * - * @return ReportGeneratorInterface */ public function setTags(Collection $tags): ReportGeneratorInterface { diff --git a/app/Generator/Report/Standard/YearReportGenerator.php b/app/Generator/Report/Standard/YearReportGenerator.php index d2fcb1c80d..74b3b57ea9 100644 --- a/app/Generator/Report/Standard/YearReportGenerator.php +++ b/app/Generator/Report/Standard/YearReportGenerator.php @@ -27,26 +27,24 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Report\ReportGeneratorInterface; use Illuminate\Support\Collection; -use Throwable; /** * Class MonthReportGenerator. - * - */ class YearReportGenerator implements ReportGeneratorInterface { /** @var Collection The accounts involved. */ private $accounts; + /** @var Carbon The end date. */ private $end; + /** @var Carbon The start date. */ private $start; /** * Generates the report. * - * @return string * @throws FireflyException */ public function generate(): string @@ -60,10 +58,11 @@ class YearReportGenerator implements ReportGeneratorInterface 'reports.default.year', compact('accountIds', 'reportType') )->with('start', $this->start)->with('end', $this->end)->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render reports.account.report: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $result = 'Could not render report view.'; + throw new FireflyException($result, 0, $e); } @@ -72,10 +71,6 @@ class YearReportGenerator implements ReportGeneratorInterface /** * Set the accounts. - * - * @param Collection $accounts - * - * @return ReportGeneratorInterface */ public function setAccounts(Collection $accounts): ReportGeneratorInterface { @@ -86,10 +81,6 @@ class YearReportGenerator implements ReportGeneratorInterface /** * Unused budget setter. - * - * @param Collection $budgets - * - * @return ReportGeneratorInterface */ public function setBudgets(Collection $budgets): ReportGeneratorInterface { @@ -98,10 +89,6 @@ class YearReportGenerator implements ReportGeneratorInterface /** * Unused categories setter. - * - * @param Collection $categories - * - * @return ReportGeneratorInterface */ public function setCategories(Collection $categories): ReportGeneratorInterface { @@ -110,10 +97,6 @@ class YearReportGenerator implements ReportGeneratorInterface /** * Set the end date. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setEndDate(Carbon $date): ReportGeneratorInterface { @@ -124,10 +107,6 @@ class YearReportGenerator implements ReportGeneratorInterface /** * Set the expenses used. - * - * @param Collection $expense - * - * @return ReportGeneratorInterface */ public function setExpense(Collection $expense): ReportGeneratorInterface { @@ -136,10 +115,6 @@ class YearReportGenerator implements ReportGeneratorInterface /** * Set the start date. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setStartDate(Carbon $date): ReportGeneratorInterface { @@ -150,10 +125,6 @@ class YearReportGenerator implements ReportGeneratorInterface /** * Unused tags setter. - * - * @param Collection $tags - * - * @return ReportGeneratorInterface */ public function setTags(Collection $tags): ReportGeneratorInterface { diff --git a/app/Generator/Report/Tag/MonthReportGenerator.php b/app/Generator/Report/Tag/MonthReportGenerator.php index 8398176550..23149855f0 100644 --- a/app/Generator/Report/Tag/MonthReportGenerator.php +++ b/app/Generator/Report/Tag/MonthReportGenerator.php @@ -27,12 +27,9 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Report\ReportGeneratorInterface; use Illuminate\Support\Collection; -use Throwable; /** * Class MonthReportGenerator. - * - */ class MonthReportGenerator implements ReportGeneratorInterface { @@ -53,7 +50,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Generate the report. * - * @return string * @throws FireflyException */ public function generate(): string @@ -68,10 +64,11 @@ class MonthReportGenerator implements ReportGeneratorInterface 'reports.tag.month', compact('accountIds', 'reportType', 'tagIds') )->with('start', $this->start)->with('end', $this->end)->with('tags', $this->tags)->with('accounts', $this->accounts)->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render reports.tag.month: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $result = sprintf('Could not render report view: %s', $e->getMessage()); + throw new FireflyException($result, 0, $e); } @@ -80,10 +77,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set the accounts. - * - * @param Collection $accounts - * - * @return ReportGeneratorInterface */ public function setAccounts(Collection $accounts): ReportGeneratorInterface { @@ -94,10 +87,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Unused budget setter. - * - * @param Collection $budgets - * - * @return ReportGeneratorInterface */ public function setBudgets(Collection $budgets): ReportGeneratorInterface { @@ -106,10 +95,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Unused category setter. - * - * @param Collection $categories - * - * @return ReportGeneratorInterface */ public function setCategories(Collection $categories): ReportGeneratorInterface { @@ -118,10 +103,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set the end date of the report. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setEndDate(Carbon $date): ReportGeneratorInterface { @@ -132,10 +113,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set the expenses in this report. - * - * @param Collection $expense - * - * @return ReportGeneratorInterface */ public function setExpense(Collection $expense): ReportGeneratorInterface { @@ -144,10 +121,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set the start date. - * - * @param Carbon $date - * - * @return ReportGeneratorInterface */ public function setStartDate(Carbon $date): ReportGeneratorInterface { @@ -158,10 +131,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * Set the tags used in this report. - * - * @param Collection $tags - * - * @return ReportGeneratorInterface */ public function setTags(Collection $tags): ReportGeneratorInterface { diff --git a/app/Generator/Report/Tag/MultiYearReportGenerator.php b/app/Generator/Report/Tag/MultiYearReportGenerator.php index 6830d8aea2..f4f4b3eca3 100644 --- a/app/Generator/Report/Tag/MultiYearReportGenerator.php +++ b/app/Generator/Report/Tag/MultiYearReportGenerator.php @@ -25,9 +25,5 @@ namespace FireflyIII\Generator\Report\Tag; /** * Class MultiYearReportGenerator. - * - */ -class MultiYearReportGenerator extends MonthReportGenerator -{ -} +class MultiYearReportGenerator extends MonthReportGenerator {} diff --git a/app/Generator/Report/Tag/YearReportGenerator.php b/app/Generator/Report/Tag/YearReportGenerator.php index 0dd2aaa36f..91b94cdd7d 100644 --- a/app/Generator/Report/Tag/YearReportGenerator.php +++ b/app/Generator/Report/Tag/YearReportGenerator.php @@ -25,9 +25,5 @@ namespace FireflyIII\Generator\Report\Tag; /** * Class YearReportGenerator. - * - */ -class YearReportGenerator extends MonthReportGenerator -{ -} +class YearReportGenerator extends MonthReportGenerator {} diff --git a/app/Generator/Webhook/MessageGeneratorInterface.php b/app/Generator/Webhook/MessageGeneratorInterface.php index 1437122341..fbe441bad4 100644 --- a/app/Generator/Webhook/MessageGeneratorInterface.php +++ b/app/Generator/Webhook/MessageGeneratorInterface.php @@ -31,35 +31,15 @@ use Illuminate\Support\Collection; */ interface MessageGeneratorInterface { - /** - * - */ public function generateMessages(): void; - /** - * @return int - */ public function getVersion(): int; - /** - * @param Collection $objects - */ public function setObjects(Collection $objects): void; - /** - * @param int $trigger - */ public function setTrigger(int $trigger): void; - /** - * @param User $user - */ public function setUser(User $user): void; - /** - * @param Collection $webhooks - * - * @return void - */ public function setWebhooks(Collection $webhooks): void; } diff --git a/app/Generator/Webhook/StandardMessageGenerator.php b/app/Generator/Webhook/StandardMessageGenerator.php index c6bcd6c82f..762cbc0af0 100644 --- a/app/Generator/Webhook/StandardMessageGenerator.php +++ b/app/Generator/Webhook/StandardMessageGenerator.php @@ -37,7 +37,6 @@ use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; -use JsonException; use Ramsey\Uuid\Uuid; use Symfony\Component\HttpFoundation\ParameterBag; @@ -52,18 +51,12 @@ class StandardMessageGenerator implements MessageGeneratorInterface private int $version = 0; private Collection $webhooks; - /** - * - */ public function __construct() { $this->objects = new Collection(); $this->webhooks = new Collection(); } - /** - * - */ public function generateMessages(): void { app('log')->debug(__METHOD__); @@ -79,20 +72,40 @@ class StandardMessageGenerator implements MessageGeneratorInterface $this->run(); } - /** - * @return Collection - */ + public function getVersion(): int + { + return $this->version; + } + + public function setObjects(Collection $objects): void + { + $this->objects = $objects; + } + + public function setTrigger(int $trigger): void + { + $this->trigger = $trigger; + } + + public function setUser(User $user): void + { + $this->user = $user; + } + + public function setWebhooks(Collection $webhooks): void + { + $this->webhooks = $webhooks; + } + private function getWebhooks(): Collection { return $this->user->webhooks()->where('active', true)->where('trigger', $this->trigger)->get(['webhooks.*']); } - /** - * - */ private function run(): void { app('log')->debug('Now in StandardMessageGenerator::run'); + /** @var Webhook $webhook */ foreach ($this->webhooks as $webhook) { $this->runWebhook($webhook); @@ -101,14 +114,12 @@ class StandardMessageGenerator implements MessageGeneratorInterface } /** - * @param Webhook $webhook - * * @throws FireflyException - * @throws JsonException */ private function runWebhook(Webhook $webhook): void { app('log')->debug(sprintf('Now in runWebhook(#%d)', $webhook->id)); + /** @var Model $object */ foreach ($this->objects as $object) { $this->generateMessage($webhook, $object); @@ -116,11 +127,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface } /** - * @param Webhook $webhook - * @param Model $model - * * @throws FireflyException - * @throws JsonException */ private function generateMessage(Webhook $webhook, Model $model): void { @@ -148,9 +155,11 @@ class StandardMessageGenerator implements MessageGeneratorInterface ); return; + case TransactionGroup::class: - /** @var TransactionGroup $model */ + // @var TransactionGroup $model $basicMessage['user_id'] = $model->user->id; + break; } @@ -162,11 +171,16 @@ class StandardMessageGenerator implements MessageGeneratorInterface ); return; + case WebhookResponse::NONE->value: $basicMessage['content'] = []; + break; + case WebhookResponse::TRANSACTIONS->value: + /** @var TransactionGroup $model */ $transformer = new TransactionGroupTransformer(); + try { $basicMessage['content'] = $transformer->transformObject($model); } catch (FireflyException $e) { @@ -177,8 +191,11 @@ class StandardMessageGenerator implements MessageGeneratorInterface return; } + break; + case WebhookResponse::ACCOUNTS->value: + /** @var TransactionGroup $model */ $accounts = $this->collectAccounts($model); foreach ($accounts as $account) { $transformer = new AccountTransformer(); @@ -189,22 +206,10 @@ class StandardMessageGenerator implements MessageGeneratorInterface $this->storeMessage($webhook, $basicMessage); } - /** - * @inheritDoc - */ - public function getVersion(): int - { - return $this->version; - } - - /** - * @param TransactionGroup $transactionGroup - * - * @return Collection - */ private function collectAccounts(TransactionGroup $transactionGroup): Collection { $accounts = new Collection(); + /** @var TransactionJournal $journal */ foreach ($transactionGroup->transactionJournals as $journal) { /** @var Transaction $transaction */ @@ -216,12 +221,6 @@ class StandardMessageGenerator implements MessageGeneratorInterface return $accounts->unique(); } - /** - * @param Webhook $webhook - * @param array $message - * - * @return void - */ private function storeMessage(Webhook $webhook, array $message): void { $webhookMessage = new WebhookMessage(); @@ -233,36 +232,4 @@ class StandardMessageGenerator implements MessageGeneratorInterface $webhookMessage->save(); app('log')->debug(sprintf('Stored new webhook message #%d', $webhookMessage->id)); } - - /** - * @param Collection $objects - */ - public function setObjects(Collection $objects): void - { - $this->objects = $objects; - } - - /** - * @param int $trigger - */ - public function setTrigger(int $trigger): void - { - $this->trigger = $trigger; - } - - /** - * @param User $user - */ - public function setUser(User $user): void - { - $this->user = $user; - } - - /** - * @inheritDoc - */ - public function setWebhooks(Collection $webhooks): void - { - $this->webhooks = $webhooks; - } } diff --git a/app/Handlers/Events/APIEventHandler.php b/app/Handlers/Events/APIEventHandler.php index 510d90b764..bbe989922c 100644 --- a/app/Handlers/Events/APIEventHandler.php +++ b/app/Handlers/Events/APIEventHandler.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Handlers\Events; -use Exception; use FireflyIII\Notifications\User\NewAccessToken; use FireflyIII\Repositories\User\UserRepositoryInterface; use Illuminate\Support\Facades\Notification; @@ -36,13 +35,11 @@ class APIEventHandler { /** * Respond to the creation of an access token. - * - * @param AccessTokenCreated $event - * */ public function accessTokenCreated(AccessTokenCreated $event): void { app('log')->debug(__METHOD__); + /** @var UserRepositoryInterface $repository */ $repository = app(UserRepositoryInterface::class); $user = $repository->find((int)$event->userId); @@ -50,14 +47,16 @@ class APIEventHandler if (null !== $user) { try { Notification::send($user, new NewAccessToken()); - } catch (Exception $e) { // @phpstan-ignore-line + } catch (\Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } if (str_contains($message, 'RFC 2822')) { app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } app('log')->error($e->getMessage()); diff --git a/app/Handlers/Events/AdminEventHandler.php b/app/Handlers/Events/AdminEventHandler.php index e4f7c5e36b..be927cf0d4 100644 --- a/app/Handlers/Events/AdminEventHandler.php +++ b/app/Handlers/Events/AdminEventHandler.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Handlers\Events; -use Exception; use FireflyIII\Events\Admin\InvitationCreated; use FireflyIII\Events\AdminRequestedTestMessage; use FireflyIII\Events\NewVersionAvailable; @@ -38,11 +37,6 @@ use Illuminate\Support\Facades\Notification; */ class AdminEventHandler { - /** - * @param InvitationCreated $event - * - * @return void - */ public function sendInvitationNotification(InvitationCreated $event): void { $sendMail = app('fireflyconfig')->get('notification_invite_created', true)->data; @@ -57,14 +51,16 @@ class AdminEventHandler if ($repository->hasRole($user, 'owner')) { try { Notification::send($user, new UserInvitation($event->invitee)); - } catch (Exception $e) { // @phpstan-ignore-line + } catch (\Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } if (str_contains($message, 'RFC 2822')) { app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } app('log')->error($e->getMessage()); @@ -76,10 +72,6 @@ class AdminEventHandler /** * Send new version message to admin. - * - * @param NewVersionAvailable $event - * - * @return void */ public function sendNewVersion(NewVersionAvailable $event): void { @@ -95,14 +87,16 @@ class AdminEventHandler if ($repository->hasRole($user, 'owner')) { try { Notification::send($user, new VersionCheckResult($event->message)); - } catch (Exception $e) {// @phpstan-ignore-line + } catch (\Exception $e) {// @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } if (str_contains($message, 'RFC 2822')) { app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } app('log')->error($e->getMessage()); @@ -114,10 +108,6 @@ class AdminEventHandler /** * Sends a test message to an administrator. - * - * @param AdminRequestedTestMessage $event - * - * @return void */ public function sendTestMessage(AdminRequestedTestMessage $event): void { @@ -127,16 +117,19 @@ class AdminEventHandler if (!$repository->hasRole($event->user, 'owner')) { return; } + try { Notification::send($event->user, new TestNotification($event->user->email)); - } catch (Exception $e) { // @phpstan-ignore-line + } catch (\Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } if (str_contains($message, 'RFC 2822')) { app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } app('log')->error($e->getMessage()); diff --git a/app/Handlers/Events/AuditEventHandler.php b/app/Handlers/Events/AuditEventHandler.php index a0edd222bd..9ae3dd5bfd 100644 --- a/app/Handlers/Events/AuditEventHandler.php +++ b/app/Handlers/Events/AuditEventHandler.php @@ -33,11 +33,6 @@ use FireflyIII\Repositories\AuditLogEntry\ALERepositoryInterface; */ class AuditEventHandler { - /** - * @param TriggeredAuditLog $event - * - * @return void - */ public function storeAuditEvent(TriggeredAuditLog $event): void { $array = [ diff --git a/app/Handlers/Events/AutomationHandler.php b/app/Handlers/Events/AutomationHandler.php index e7e8dc68b8..61857d33c9 100644 --- a/app/Handlers/Events/AutomationHandler.php +++ b/app/Handlers/Events/AutomationHandler.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Handlers\Events; -use Exception; use FireflyIII\Events\RequestedReportOnJournals; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionGroup; @@ -40,27 +39,28 @@ class AutomationHandler /** * Respond to the creation of X journals. * - * @param RequestedReportOnJournals $event - * * @throws FireflyException */ public function reportJournals(RequestedReportOnJournals $event): void { app('log')->debug('In reportJournals.'); + /** @var UserRepositoryInterface $repository */ $repository = app(UserRepositoryInterface::class); $user = $repository->find($event->userId); + /** @var bool $sendReport */ $sendReport = app('preferences')->getForUser($user, 'notification_transaction_creation', false)->data; if (false === $sendReport) { app('log')->debug('Not sending report, because config says so.'); + return; } - if (null === $user || 0 === $event->groups->count()) { app('log')->debug('No transaction groups in event, nothing to email about.'); + return; } app('log')->debug('Continue with message!'); @@ -69,20 +69,24 @@ class AutomationHandler /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); $groups = []; + /** @var TransactionGroup $group */ foreach ($event->groups as $group) { $groups[] = $transformer->transformObject($group); } + try { Notification::send($user, new TransactionCreation($groups)); - } catch (Exception $e) { // @phpstan-ignore-line + } catch (\Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } if (str_contains($message, 'RFC 2822')) { app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } app('log')->error($e->getMessage()); diff --git a/app/Handlers/Events/BillEventHandler.php b/app/Handlers/Events/BillEventHandler.php index d6864d095d..81092b6a2b 100644 --- a/app/Handlers/Events/BillEventHandler.php +++ b/app/Handlers/Events/BillEventHandler.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Handlers\Events; -use Exception; use FireflyIII\Events\WarnUserAboutBill; use FireflyIII\Notifications\User\BillReminder; use Illuminate\Support\Facades\Notification; @@ -34,31 +33,30 @@ use Illuminate\Support\Facades\Notification; */ class BillEventHandler { - /** - * @param WarnUserAboutBill $event - * - * @return void - */ public function warnAboutBill(WarnUserAboutBill $event): void { app('log')->debug(sprintf('Now in %s', __METHOD__)); $bill = $event->bill; + /** @var bool $preference */ $preference = app('preferences')->getForUser($bill->user, 'notification_bill_reminder', true)->data; if (true === $preference) { app('log')->debug('Bill reminder is true!'); + try { Notification::send($bill->user, new BillReminder($bill, $event->field, $event->diff)); - } catch (Exception $e) { // @phpstan-ignore-line + } catch (\Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } if (str_contains($message, 'RFC 2822')) { app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } app('log')->error($e->getMessage()); diff --git a/app/Handlers/Events/DestroyedGroupEventHandler.php b/app/Handlers/Events/DestroyedGroupEventHandler.php index 72baf3f8b1..c615735a8d 100644 --- a/app/Handlers/Events/DestroyedGroupEventHandler.php +++ b/app/Handlers/Events/DestroyedGroupEventHandler.php @@ -34,14 +34,12 @@ use Illuminate\Support\Collection; */ class DestroyedGroupEventHandler { - /** - * @param DestroyedTransactionGroup $destroyedGroupEvent - */ public function triggerWebhooks(DestroyedTransactionGroup $destroyedGroupEvent): void { app('log')->debug('DestroyedTransactionGroup:triggerWebhooks'); $group = $destroyedGroupEvent->transactionGroup; $user = $group->user; + /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); $engine->setUser($user); diff --git a/app/Handlers/Events/Model/BudgetLimitHandler.php b/app/Handlers/Events/Model/BudgetLimitHandler.php index 52d9a8f7d3..dcf1b022e5 100644 --- a/app/Handlers/Events/Model/BudgetLimitHandler.php +++ b/app/Handlers/Events/Model/BudgetLimitHandler.php @@ -1,6 +1,5 @@ debug(sprintf('BudgetLimitHandler::created(#%s)', $event->budgetLimit->id)); $this->updateAvailableBudget($event->budgetLimit); } - /** - * @param BudgetLimit $budgetLimit - * - * @return void - */ + public function deleted(Deleted $event): void + { + app('log')->debug(sprintf('BudgetLimitHandler::deleted(#%s)', $event->budgetLimit->id)); + $budgetLimit = $event->budgetLimit; + $budgetLimit->id = 0; + $this->updateAvailableBudget($event->budgetLimit); + } + + public function updated(Updated $event): void + { + app('log')->debug(sprintf('BudgetLimitHandler::updated(#%s)', $event->budgetLimit->id)); + $this->updateAvailableBudget($event->budgetLimit); + } + private function updateAvailableBudget(BudgetLimit $budgetLimit): void { app('log')->debug(sprintf('Now in updateAvailableBudget(#%d)', $budgetLimit->id)); @@ -71,15 +74,18 @@ class BudgetLimitHandler if (null === $budget) { app('log')->warning('Budget is still null, cannot continue, will delete budget limit.'); $budgetLimit->forceDelete(); + return; } - /** @var User|null $user */ + + /** @var null|User $user */ $user = $budget->user; // sanity check. It happens when the budget has been deleted so the original user is unknown. if (null === $user) { app('log')->warning('User is null, cannot continue.'); $budgetLimit->forceDelete(); + return; } @@ -88,7 +94,7 @@ class BudgetLimitHandler // all have to be created or updated. try { $viewRange = app('preferences')->getForUser($user, 'viewRange', '1M')->data; - } catch (ContainerExceptionInterface | NotFoundExceptionInterface $e) { + } catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) { app('log')->error($e->getMessage()); $viewRange = '1M'; } @@ -112,7 +118,7 @@ class BudgetLimitHandler $currentEnd = app('navigation')->endOfPeriod($current, $viewRange); // create or find AB for this particular period, and set the amount accordingly. - /** @var AvailableBudget|null $availableBudget */ + /** @var null|AvailableBudget $availableBudget */ $availableBudget = $user->availableBudgets()->where('start_date', $current->format('Y-m-d'))->where( 'end_date', $currentEnd->format('Y-m-d') @@ -156,11 +162,6 @@ class BudgetLimitHandler } } - /** - * @param AvailableBudget $availableBudget - * - * @return void - */ private function calculateAmount(AvailableBudget $availableBudget): void { $repository = app(BudgetLimitRepositoryInterface::class); @@ -178,6 +179,7 @@ class BudgetLimitHandler // have to recalculate everything just in case. $set = $repository->getAllBudgetLimitsByCurrency($availableBudget->transactionCurrency, $availableBudget->start_date, $availableBudget->end_date); app('log')->debug(sprintf('Found %d interesting budget limit(s).', $set->count())); + /** @var BudgetLimit $budgetLimit */ foreach ($set as $budgetLimit) { app('log')->debug( @@ -218,6 +220,7 @@ class BudgetLimitHandler if (0 === bccomp('0', $newAmount)) { app('log')->debug('New amount is zero, deleting AB.'); $availableBudget->delete(); + return; } app('log')->debug(sprintf('Concluded new amount for this AB must be %s', $newAmount)); @@ -225,11 +228,6 @@ class BudgetLimitHandler $availableBudget->save(); } - /** - * @param BudgetLimit $budgetLimit - * - * @return string - */ private function getDailyAmount(BudgetLimit $budgetLimit): string { if (0 === $budgetLimit->id) { @@ -246,31 +244,7 @@ class BudgetLimitHandler app('log')->debug( sprintf('Total amount for budget limit #%d is %s. Nr. of days is %d. Amount per day is %s', $budgetLimit->id, $budgetLimit->amount, $days, $amount) ); + return $amount; } - - /** - * @param Deleted $event - * - * @return void - */ - public function deleted(Deleted $event): void - { - app('log')->debug(sprintf('BudgetLimitHandler::deleted(#%s)', $event->budgetLimit->id)); - $budgetLimit = $event->budgetLimit; - $budgetLimit->id = 0; - $this->updateAvailableBudget($event->budgetLimit); - } - - /** - * @param Updated $event - * - * @return void - */ - public function updated(Updated $event): void - { - app('log')->debug(sprintf('BudgetLimitHandler::updated(#%s)', $event->budgetLimit->id)); - $this->updateAvailableBudget($event->budgetLimit); - } - } diff --git a/app/Handlers/Events/Model/PiggyBankEventHandler.php b/app/Handlers/Events/Model/PiggyBankEventHandler.php index b7ac122910..a0c8208146 100644 --- a/app/Handlers/Events/Model/PiggyBankEventHandler.php +++ b/app/Handlers/Events/Model/PiggyBankEventHandler.php @@ -32,11 +32,6 @@ use FireflyIII\Models\PiggyBankEvent; */ class PiggyBankEventHandler { - /** - * @param ChangedAmount $event - * - * @return void - */ public function changePiggyAmount(ChangedAmount $event): void { // find journal if group is present. @@ -48,10 +43,12 @@ class PiggyBankEventHandler // sanity check: event must not already exist for this journal and piggy bank. if (null !== $journal) { $exists = PiggyBankEvent::where('piggy_bank_id', $event->piggyBank->id) - ->where('transaction_journal_id', $journal->id) - ->exists(); + ->where('transaction_journal_id', $journal->id) + ->exists() + ; if ($exists) { app('log')->warning('Already have event for this journal and piggy, will not create another.'); + return; } } diff --git a/app/Handlers/Events/Model/RuleHandler.php b/app/Handlers/Events/Model/RuleHandler.php index c9a9985bf2..8ae02c8260 100644 --- a/app/Handlers/Events/Model/RuleHandler.php +++ b/app/Handlers/Events/Model/RuleHandler.php @@ -1,6 +1,5 @@ ruleAction; $rule = $ruleAction->rule; + /** @var bool $preference */ $preference = app('preferences')->getForUser($rule->user, 'notification_rule_action_failures', true)->data; if (false === $preference) { @@ -61,19 +56,14 @@ class RuleHandler $ruleLink = route('rules.edit', [$rule->id]); $params = [$mainMessage, $groupTitle, $groupLink, $ruleTitle, $ruleLink]; - Notification::send($user, new RuleActionFailed($params)); } - /** - * @param RuleActionFailedOnObject $event - * - * @return void - */ public function ruleActionFailedOnObject(RuleActionFailedOnObject $event): void { $ruleAction = $event->ruleAction; $rule = $ruleAction->rule; + /** @var bool $preference */ $preference = app('preferences')->getForUser($rule->user, 'notification_rule_action_failures', true)->data; if (false === $preference) { @@ -91,8 +81,6 @@ class RuleHandler $ruleLink = route('rules.edit', [$rule->id]); $params = [$mainMessage, $groupTitle, $groupLink, $ruleTitle, $ruleLink]; - Notification::send($user, new RuleActionFailed($params)); } - } diff --git a/app/Handlers/Events/StoredAccountEventHandler.php b/app/Handlers/Events/StoredAccountEventHandler.php index a4a48f4cbf..bb81b22cec 100644 --- a/app/Handlers/Events/StoredAccountEventHandler.php +++ b/app/Handlers/Events/StoredAccountEventHandler.php @@ -32,12 +32,10 @@ use FireflyIII\Services\Internal\Support\CreditRecalculateService; */ class StoredAccountEventHandler { - /** - * @param StoredAccount $event - */ public function recalculateCredit(StoredAccount $event): void { $account = $event->account; + /** @var CreditRecalculateService $object */ $object = app(CreditRecalculateService::class); $object->setAccount($account); diff --git a/app/Handlers/Events/StoredGroupEventHandler.php b/app/Handlers/Events/StoredGroupEventHandler.php index 71f3e6752a..a057774587 100644 --- a/app/Handlers/Events/StoredGroupEventHandler.php +++ b/app/Handlers/Events/StoredGroupEventHandler.php @@ -40,8 +40,6 @@ class StoredGroupEventHandler { /** * This method grabs all the users rules and processes them. - * - * @param StoredTransactionGroup $storedGroupEvent */ public function processRules(StoredTransactionGroup $storedGroupEvent): void { @@ -54,6 +52,7 @@ class StoredGroupEventHandler $journals = $storedGroupEvent->transactionGroup->transactionJournals; $array = []; + /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $array[] = $journal->id; @@ -77,12 +76,10 @@ class StoredGroupEventHandler $newRuleEngine->fire(); } - /** - * @param StoredTransactionGroup $event - */ public function recalculateCredit(StoredTransactionGroup $event): void { $group = $event->transactionGroup; + /** @var CreditRecalculateService $object */ $object = app(CreditRecalculateService::class); $object->setGroup($group); @@ -91,8 +88,6 @@ class StoredGroupEventHandler /** * This method processes all webhooks that respond to the "stored transaction group" trigger (100) - * - * @param StoredTransactionGroup $storedGroupEvent */ public function triggerWebhooks(StoredTransactionGroup $storedGroupEvent): void { @@ -105,6 +100,7 @@ class StoredGroupEventHandler } $user = $group->user; + /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); $engine->setUser($user); diff --git a/app/Handlers/Events/UpdatedAccountEventHandler.php b/app/Handlers/Events/UpdatedAccountEventHandler.php index 6874fcbba4..e949c02e6b 100644 --- a/app/Handlers/Events/UpdatedAccountEventHandler.php +++ b/app/Handlers/Events/UpdatedAccountEventHandler.php @@ -32,12 +32,10 @@ use FireflyIII\Services\Internal\Support\CreditRecalculateService; */ class UpdatedAccountEventHandler { - /** - * @param UpdatedAccount $event - */ public function recalculateCredit(UpdatedAccount $event): void { $account = $event->account; + /** @var CreditRecalculateService $object */ $object = app(CreditRecalculateService::class); $object->setAccount($account); diff --git a/app/Handlers/Events/UpdatedGroupEventHandler.php b/app/Handlers/Events/UpdatedGroupEventHandler.php index a1312828d6..7ff319630a 100644 --- a/app/Handlers/Events/UpdatedGroupEventHandler.php +++ b/app/Handlers/Events/UpdatedGroupEventHandler.php @@ -43,8 +43,6 @@ class UpdatedGroupEventHandler { /** * This method will check all the rules when a journal is updated. - * - * @param UpdatedTransactionGroup $updatedGroupEvent */ public function processRules(UpdatedTransactionGroup $updatedGroupEvent): void { @@ -56,6 +54,7 @@ class UpdatedGroupEventHandler $journals = $updatedGroupEvent->transactionGroup->transactionJournals; $array = []; + /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $array[] = $journal->id; @@ -77,21 +76,16 @@ class UpdatedGroupEventHandler $newRuleEngine->fire(); } - /** - * @param UpdatedTransactionGroup $event - */ public function recalculateCredit(UpdatedTransactionGroup $event): void { $group = $event->transactionGroup; + /** @var CreditRecalculateService $object */ $object = app(CreditRecalculateService::class); $object->setGroup($group); $object->recalculate(); } - /** - * @param UpdatedTransactionGroup $updatedGroupEvent - */ public function triggerWebhooks(UpdatedTransactionGroup $updatedGroupEvent): void { app('log')->debug(__METHOD__); @@ -102,6 +96,7 @@ class UpdatedGroupEventHandler return; } $user = $group->user; + /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); $engine->setUser($user); @@ -114,8 +109,6 @@ class UpdatedGroupEventHandler /** * This method will make sure all source / destination accounts are the same. - * - * @param UpdatedTransactionGroup $updatedGroupEvent */ public function unifyAccounts(UpdatedTransactionGroup $updatedGroupEvent): void { @@ -123,23 +116,28 @@ class UpdatedGroupEventHandler if (1 === $group->transactionJournals->count()) { return; } + // first journal: - /** @var TransactionJournal|null $first */ + /** @var null|TransactionJournal $first */ $first = $group->transactionJournals() - ->orderBy('transaction_journals.date', 'DESC') - ->orderBy('transaction_journals.order', 'ASC') - ->orderBy('transaction_journals.id', 'DESC') - ->orderBy('transaction_journals.description', 'DESC') - ->first(); + ->orderBy('transaction_journals.date', 'DESC') + ->orderBy('transaction_journals.order', 'ASC') + ->orderBy('transaction_journals.id', 'DESC') + ->orderBy('transaction_journals.description', 'DESC') + ->first() + ; if (null === $first) { app('log')->warning(sprintf('Group #%d has no transaction journals.', $group->id)); + return; } $all = $group->transactionJournals()->get()->pluck('id')->toArray(); + /** @var Account $sourceAccount */ $sourceAccount = $first->transactions()->where('amount', '<', '0')->first()->account; + /** @var Account $destAccount */ $destAccount = $first->transactions()->where('amount', '>', '0')->first()->account; @@ -147,12 +145,14 @@ class UpdatedGroupEventHandler if (TransactionType::TRANSFER === $type || TransactionType::WITHDRAWAL === $type) { // set all source transactions to source account: Transaction::whereIn('transaction_journal_id', $all) - ->where('amount', '<', 0)->update(['account_id' => $sourceAccount->id]); + ->where('amount', '<', 0)->update(['account_id' => $sourceAccount->id]) + ; } if (TransactionType::TRANSFER === $type || TransactionType::DEPOSIT === $type) { // set all destination transactions to destination account: Transaction::whereIn('transaction_journal_id', $all) - ->where('amount', '>', 0)->update(['account_id' => $destAccount->id]); + ->where('amount', '>', 0)->update(['account_id' => $destAccount->id]) + ; } } } diff --git a/app/Handlers/Events/UserEventHandler.php b/app/Handlers/Events/UserEventHandler.php index 9cf9b577fe..e4952b960e 100644 --- a/app/Handlers/Events/UserEventHandler.php +++ b/app/Handlers/Events/UserEventHandler.php @@ -25,7 +25,6 @@ namespace FireflyIII\Handlers\Events; use Carbon\Carbon; use Database\Seeders\ExchangeRateSeeder; -use Exception; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Events\ActuallyLoggedIn; use FireflyIII\Events\Admin\InvitationCreated; @@ -61,8 +60,6 @@ class UserEventHandler { /** * This method will bestow upon a user the "owner" role if he is the first user in the system. - * - * @param RegisteredUser $event */ public function attachUserRole(RegisteredUser $event): void { @@ -78,8 +75,6 @@ class UserEventHandler /** * Fires to see if a user is admin. - * - * @param Login $event */ public function checkSingleUserIsAdmin(Login $event): void { @@ -107,7 +102,6 @@ class UserEventHandler } /** - * @param RegisteredUser $event * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function createExchangeRates(RegisteredUser $event): void @@ -117,8 +111,6 @@ class UserEventHandler } /** - * @param RegisteredUser $event - * * @throws FireflyException */ public function createGroupMembership(RegisteredUser $event): void @@ -127,6 +119,7 @@ class UserEventHandler $groupExists = true; $groupTitle = $user->email; $index = 1; + /** @var UserGroup $group */ $group = null; @@ -135,15 +128,17 @@ class UserEventHandler $groupExists = UserGroup::where('title', $groupTitle)->count() > 0; if (false === $groupExists) { $group = UserGroup::create(['title' => $groupTitle]); + break; } $groupTitle = sprintf('%s-%d', $user->email, $index); - $index++; + ++$index; if ($index > 99) { throw new FireflyException('Email address can no longer be used for registrations.'); } } - /** @var UserRole|null $role */ + + /** @var null|UserRole $role */ $role = UserRole::where('title', UserRoleEnum::OWNER->value)->first(); if (null === $role) { throw new FireflyException('The user role is unexpectedly empty. Did you run all migrations?'); @@ -162,8 +157,6 @@ class UserEventHandler /** * Set the demo user back to English. * - * @param Login $event - * * @throws FireflyException */ public function demoUserBackToEnglish(Login $event): void @@ -182,8 +175,6 @@ class UserEventHandler } /** - * @param DetectedNewIPAddress $event - * * @throws FireflyException */ public function notifyNewIPAddress(DetectedNewIPAddress $event): void @@ -205,14 +196,16 @@ class UserEventHandler if (false === $entry['notified']) { try { Notification::send($user, new UserLogin($ipAddress)); - } catch (Exception $e) { // @phpstan-ignore-line + } catch (\Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } if (str_contains($message, 'RFC 2822')) { app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } app('log')->error($e->getMessage()); @@ -225,9 +218,6 @@ class UserEventHandler app('preferences')->setForUser($user, 'login_ip_history', $list); } - /** - * @param RegisteredUser $event - */ public function sendAdminRegistrationNotification(RegisteredUser $event): void { $sendMail = (bool)app('fireflyconfig')->get('notification_admin_new_reg', true)->data; @@ -239,14 +229,16 @@ class UserEventHandler if ($repository->hasRole($user, 'owner')) { try { Notification::send($user, new AdminRegistrationNotification($event->user)); - } catch (Exception $e) { // @phpstan-ignore-line + } catch (\Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } if (str_contains($message, 'RFC 2822')) { app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } app('log')->error($e->getMessage()); @@ -261,8 +253,6 @@ class UserEventHandler * Send email to confirm email change. Will not be made into a notification, because * this requires some custom fields from the user and not just the "user" object. * - * @param UserChangedEmail $event - * * @throws FireflyException */ public function sendEmailChangeConfirmMail(UserChangedEmail $event): void @@ -274,10 +264,11 @@ class UserEventHandler $url = route('profile.confirm-email-change', [$token->data]); try { - Mail::to($newEmail)->send(new ConfirmEmailChangeMail($newEmail, $oldEmail, $url)); - } catch (Exception $e) { + \Mail::to($newEmail)->send(new ConfirmEmailChangeMail($newEmail, $oldEmail, $url)); + } catch (\Exception $e) { app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); + throw new FireflyException($e->getMessage(), 0, $e); } } @@ -286,8 +277,6 @@ class UserEventHandler * Send email to be able to undo email change. Will not be made into a notification, because * this requires some custom fields from the user and not just the "user" object. * - * @param UserChangedEmail $event - * * @throws FireflyException */ public function sendEmailChangeUndoMail(UserChangedEmail $event): void @@ -298,32 +287,34 @@ class UserEventHandler $token = app('preferences')->getForUser($user, 'email_change_undo_token', 'invalid'); $hashed = hash('sha256', sprintf('%s%s', (string)config('app.key'), $oldEmail)); $url = route('profile.undo-email-change', [$token->data, $hashed]); + try { - Mail::to($oldEmail)->send(new UndoEmailChangeMail($newEmail, $oldEmail, $url)); - } catch (Exception $e) { + \Mail::to($oldEmail)->send(new UndoEmailChangeMail($newEmail, $oldEmail, $url)); + } catch (\Exception $e) { app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); + throw new FireflyException($e->getMessage(), 0, $e); } } /** * Send a new password to the user. - * - * @param RequestedNewPassword $event */ public function sendNewPassword(RequestedNewPassword $event): void { try { Notification::send($event->user, new UserNewPassword(route('password.reset', [$event->token]))); - } catch (Exception $e) { // @phpstan-ignore-line + } catch (\Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } if (str_contains($message, 'RFC 2822')) { app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } app('log')->error($e->getMessage()); @@ -332,9 +323,6 @@ class UserEventHandler } /** - * @param InvitationCreated $event - * - * @return void * @throws FireflyException */ public function sendRegistrationInvite(InvitationCreated $event): void @@ -342,11 +330,13 @@ class UserEventHandler $invitee = $event->invitee->email; $admin = $event->invitee->user->email; $url = route('invite', [$event->invitee->invite_code]); + try { - Mail::to($invitee)->send(new InvitationMail($invitee, $admin, $url)); - } catch (Exception $e) { + \Mail::to($invitee)->send(new InvitationMail($invitee, $admin, $url)); + } catch (\Exception $e) { app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); + throw new FireflyException($e->getMessage(), 0, $e); } } @@ -354,9 +344,6 @@ class UserEventHandler /** * This method will send the user a registration mail, welcoming him or her to Firefly III. * This message is only sent when the configuration of Firefly III says so. - * - * @param RegisteredUser $event - * */ public function sendRegistrationMail(RegisteredUser $event): void { @@ -364,14 +351,16 @@ class UserEventHandler if ($sendMail) { try { Notification::send($event->user, new UserRegistrationNotification()); - } catch (Exception $e) { // @phpstan-ignore-line + } catch (\Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } if (str_contains($message, 'RFC 2822')) { app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + return; } app('log')->error($e->getMessage()); @@ -381,8 +370,6 @@ class UserEventHandler } /** - * @param ActuallyLoggedIn $event - * * @throws FireflyException */ public function storeUserIPAddress(ActuallyLoggedIn $event): void @@ -392,6 +379,7 @@ class UserEventHandler if ($user->hasRole('demo')) { app('log')->debug('Do not log demo user logins'); + return; } @@ -431,6 +419,7 @@ class UserEventHandler ]; } $preference = array_values($preference); + /** @var bool $send */ $send = app('preferences')->getForUser($user, 'notification_user_login', true)->data; app('preferences')->setForUser($user, 'login_ip_history', $preference); diff --git a/app/Handlers/Events/VersionCheckEventHandler.php b/app/Handlers/Events/VersionCheckEventHandler.php index 256e4f4f89..56f060d678 100644 --- a/app/Handlers/Events/VersionCheckEventHandler.php +++ b/app/Handlers/Events/VersionCheckEventHandler.php @@ -28,8 +28,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Update\UpdateTrait; use FireflyIII\Models\Configuration; use FireflyIII\Repositories\User\UserRepositoryInterface; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class VersionCheckEventHandler @@ -41,11 +39,8 @@ class VersionCheckEventHandler /** * Checks with GitHub to see if there is a new version. * - * @param RequestedVersionCheckStatus $event - * * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface + * * @deprecated ? */ public function checkForUpdates(RequestedVersionCheckStatus $event): void @@ -90,11 +85,7 @@ class VersionCheckEventHandler } /** - * @param RequestedVersionCheckStatus $event - * * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ protected function warnToCheckForUpdates(RequestedVersionCheckStatus $event): void { diff --git a/app/Handlers/Events/WebhookEventHandler.php b/app/Handlers/Events/WebhookEventHandler.php index ea9236e191..863800c594 100644 --- a/app/Handlers/Events/WebhookEventHandler.php +++ b/app/Handlers/Events/WebhookEventHandler.php @@ -39,12 +39,13 @@ class WebhookEventHandler app('log')->debug(sprintf('Now in %s', __METHOD__)); // kick off the job! $messages = WebhookMessage::where('webhook_messages.sent', false) - ->get(['webhook_messages.*']) - ->filter( - static function (WebhookMessage $message) { - return $message->webhookAttempts()->count() <= 2; - } - )->splice(0, 5); + ->get(['webhook_messages.*']) + ->filter( + static function (WebhookMessage $message) { + return $message->webhookAttempts()->count() <= 2; + } + )->splice(0, 5) + ; app('log')->debug(sprintf('Found %d webhook message(s) ready to be send.', $messages->count())); foreach ($messages as $message) { if (false === $message->sent) { diff --git a/app/Handlers/Observer/AccountObserver.php b/app/Handlers/Observer/AccountObserver.php index b5ad9971a7..9d8cb9de9e 100644 --- a/app/Handlers/Observer/AccountObserver.php +++ b/app/Handlers/Observer/AccountObserver.php @@ -32,10 +32,6 @@ class AccountObserver { /** * Also delete related objects. - * - * @param Account $account - * - * @return void */ public function deleting(Account $account): void { @@ -52,7 +48,5 @@ class AccountObserver } $account->notes()->delete(); $account->locations()->delete(); - } - } diff --git a/app/Handlers/Observer/BillObserver.php b/app/Handlers/Observer/BillObserver.php index 42206dc7f0..5d93e8609f 100644 --- a/app/Handlers/Observer/BillObserver.php +++ b/app/Handlers/Observer/BillObserver.php @@ -30,11 +30,6 @@ use FireflyIII\Models\Bill; */ class BillObserver { - /** - * @param Bill $bill - * - * @return void - */ public function deleting(Bill $bill): void { app('log')->debug('Observe "deleting" of a bill.'); @@ -43,5 +38,4 @@ class BillObserver } $bill->notes()->delete(); } - } diff --git a/app/Handlers/Observer/BudgetObserver.php b/app/Handlers/Observer/BudgetObserver.php index a3ef976b40..e928b1497a 100644 --- a/app/Handlers/Observer/BudgetObserver.php +++ b/app/Handlers/Observer/BudgetObserver.php @@ -31,11 +31,6 @@ use FireflyIII\Models\BudgetLimit; */ class BudgetObserver { - /** - * @param Budget $budget - * - * @return void - */ public function deleting(Budget $budget): void { app('log')->debug('Observe "deleting" of a budget.'); @@ -43,6 +38,7 @@ class BudgetObserver $attachment->delete(); } $budgetLimits = $budget->budgetlimits()->get(); + /** @var BudgetLimit $budgetLimit */ foreach($budgetLimits as $budgetLimit) { // this loop exists so several events are fired. @@ -53,7 +49,5 @@ class BudgetObserver $budget->autoBudgets()->delete(); // recalculate available budgets. - } - } diff --git a/app/Handlers/Observer/CategoryObserver.php b/app/Handlers/Observer/CategoryObserver.php index d1029ef2cf..0cc866761a 100644 --- a/app/Handlers/Observer/CategoryObserver.php +++ b/app/Handlers/Observer/CategoryObserver.php @@ -30,11 +30,6 @@ use FireflyIII\Models\Category; */ class CategoryObserver { - /** - * @param Category $category - * - * @return void - */ public function deleting(Category $category): void { app('log')->debug('Observe "deleting" of a category.'); @@ -43,5 +38,4 @@ class CategoryObserver } $category->notes()->delete(); } - } diff --git a/app/Handlers/Observer/PiggyBankObserver.php b/app/Handlers/Observer/PiggyBankObserver.php index 999aa831f8..7f56a3fefb 100644 --- a/app/Handlers/Observer/PiggyBankObserver.php +++ b/app/Handlers/Observer/PiggyBankObserver.php @@ -31,11 +31,6 @@ use FireflyIII\Models\PiggyBankRepetition; */ class PiggyBankObserver { - /** - * @param PiggyBank $piggyBank - * - * @return void - */ public function created(PiggyBank $piggyBank): void { app('log')->debug('Observe "created" of a piggy bank.'); @@ -49,10 +44,6 @@ class PiggyBankObserver /** * Also delete related objects. - * - * @param PiggyBank $piggyBank - * - * @return void */ public function deleting(PiggyBank $piggyBank): void { @@ -67,5 +58,4 @@ class PiggyBankObserver $piggyBank->notes()->delete(); } - } diff --git a/app/Handlers/Observer/RecurrenceObserver.php b/app/Handlers/Observer/RecurrenceObserver.php index 086a79966a..c892315f60 100644 --- a/app/Handlers/Observer/RecurrenceObserver.php +++ b/app/Handlers/Observer/RecurrenceObserver.php @@ -30,11 +30,6 @@ use FireflyIII\Models\Recurrence; */ class RecurrenceObserver { - /** - * @param Recurrence $recurrence - * - * @return void - */ public function deleting(Recurrence $recurrence): void { app('log')->debug('Observe "deleting" of a recurrence.'); @@ -48,7 +43,5 @@ class RecurrenceObserver $transaction->delete(); } $recurrence->notes()->delete(); - } - } diff --git a/app/Handlers/Observer/RecurrenceTransactionObserver.php b/app/Handlers/Observer/RecurrenceTransactionObserver.php index 45cb092f63..f88cb38b5d 100644 --- a/app/Handlers/Observer/RecurrenceTransactionObserver.php +++ b/app/Handlers/Observer/RecurrenceTransactionObserver.php @@ -30,15 +30,9 @@ use FireflyIII\Models\RecurrenceTransaction; */ class RecurrenceTransactionObserver { - /** - * @param RecurrenceTransaction $transaction - * - * @return void - */ public function deleting(RecurrenceTransaction $transaction): void { app('log')->debug('Observe "deleting" of a recurrence transaction.'); $transaction->recurrenceTransactionMeta()->delete(); } - } diff --git a/app/Handlers/Observer/RuleGroupObserver.php b/app/Handlers/Observer/RuleGroupObserver.php index 1b14d7276a..c7e1c223bf 100644 --- a/app/Handlers/Observer/RuleGroupObserver.php +++ b/app/Handlers/Observer/RuleGroupObserver.php @@ -30,18 +30,11 @@ use FireflyIII\Models\RuleGroup; */ class RuleGroupObserver { - /** - * @param RuleGroup $ruleGroup - * - * @return void - */ public function deleting(RuleGroup $ruleGroup): void { app('log')->debug('Observe "deleting" of a rule group.'); foreach ($ruleGroup->rules()->get() as $rule) { $rule->delete(); } - } - } diff --git a/app/Handlers/Observer/RuleObserver.php b/app/Handlers/Observer/RuleObserver.php index 098b7037d1..3121a6b62e 100644 --- a/app/Handlers/Observer/RuleObserver.php +++ b/app/Handlers/Observer/RuleObserver.php @@ -30,16 +30,10 @@ use FireflyIII\Models\Rule; */ class RuleObserver { - /** - * @param Rule $rule - * - * @return void - */ public function deleting(Rule $rule): void { app('log')->debug('Observe "deleting" of a rule.'); $rule->ruleActions()->delete(); $rule->ruleTriggers()->delete(); } - } diff --git a/app/Handlers/Observer/TagObserver.php b/app/Handlers/Observer/TagObserver.php index 85c6c16c63..1994a30aa1 100644 --- a/app/Handlers/Observer/TagObserver.php +++ b/app/Handlers/Observer/TagObserver.php @@ -30,11 +30,6 @@ use FireflyIII\Models\Tag; */ class TagObserver { - /** - * @param Tag $tag - * - * @return void - */ public function deleting(Tag $tag): void { app('log')->debug('Observe "deleting" of a tag.'); @@ -44,7 +39,5 @@ class TagObserver } $tag->locations()->delete(); - } - } diff --git a/app/Handlers/Observer/TransactionGroupObserver.php b/app/Handlers/Observer/TransactionGroupObserver.php index 5b351cec06..418356668c 100644 --- a/app/Handlers/Observer/TransactionGroupObserver.php +++ b/app/Handlers/Observer/TransactionGroupObserver.php @@ -37,5 +37,4 @@ class TransactionGroupObserver $journal->delete(); } } - } diff --git a/app/Handlers/Observer/TransactionJournalObserver.php b/app/Handlers/Observer/TransactionJournalObserver.php index bab42543d0..3bb106783e 100644 --- a/app/Handlers/Observer/TransactionJournalObserver.php +++ b/app/Handlers/Observer/TransactionJournalObserver.php @@ -30,17 +30,12 @@ use FireflyIII\Models\TransactionJournal; */ class TransactionJournalObserver { - /** - * @param TransactionJournal $transactionJournal - * - * @return void - */ public function deleting(TransactionJournal $transactionJournal): void { app('log')->debug('Observe "deleting" of a transaction journal.'); // to make sure the listener doesn't get back to use and loop - TransactionJournal::withoutEvents(static function () use ($transactionJournal) { + TransactionJournal::withoutEvents(static function () use ($transactionJournal): void { foreach ($transactionJournal->transactions()->get() as $transaction) { $transaction->delete(); } @@ -53,5 +48,4 @@ class TransactionJournalObserver $transactionJournal->destJournalLinks()->delete(); $transactionJournal->auditLogEntries()->delete(); } - } diff --git a/app/Handlers/Observer/TransactionObserver.php b/app/Handlers/Observer/TransactionObserver.php index 8b20b95d65..1c39335bc1 100644 --- a/app/Handlers/Observer/TransactionObserver.php +++ b/app/Handlers/Observer/TransactionObserver.php @@ -30,15 +30,9 @@ use FireflyIII\Models\Transaction; */ class TransactionObserver { - /** - * @param Transaction|null $transaction - * - * @return void - */ public function deleting(?Transaction $transaction): void { app('log')->debug('Observe "deleting" of a transaction.'); $transaction?->transactionJournal?->delete(); } - } diff --git a/app/Handlers/Observer/WebhookMessageObserver.php b/app/Handlers/Observer/WebhookMessageObserver.php index 5744e3e5f8..6248dc4d3c 100644 --- a/app/Handlers/Observer/WebhookMessageObserver.php +++ b/app/Handlers/Observer/WebhookMessageObserver.php @@ -30,11 +30,6 @@ use FireflyIII\Models\WebhookMessage; */ class WebhookMessageObserver { - /** - * @param WebhookMessage $webhookMessage - * - * @return void - */ public function deleting(WebhookMessage $webhookMessage): void { app('log')->debug('Observe "deleting" of a webhook message.'); diff --git a/app/Handlers/Observer/WebhookObserver.php b/app/Handlers/Observer/WebhookObserver.php index d7dd0dbca9..6a0799db99 100644 --- a/app/Handlers/Observer/WebhookObserver.php +++ b/app/Handlers/Observer/WebhookObserver.php @@ -30,11 +30,6 @@ use FireflyIII\Models\Webhook; */ class WebhookObserver { - /** - * @param Webhook $webhook - * - * @return void - */ public function deleting(Webhook $webhook): void { app('log')->debug('Observe "deleting" of a webhook.'); @@ -42,5 +37,4 @@ class WebhookObserver $message->delete(); } } - } diff --git a/app/Helpers/Attachments/AttachmentHelper.php b/app/Helpers/Attachments/AttachmentHelper.php index 380e437666..b357c64626 100644 --- a/app/Helpers/Attachments/AttachmentHelper.php +++ b/app/Helpers/Attachments/AttachmentHelper.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Helpers\Attachments; -use Crypt; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Attachment; use FireflyIII\Models\PiggyBank; @@ -36,7 +35,6 @@ use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use Illuminate\Support\MessageBag; use Symfony\Component\HttpFoundation\File\UploadedFile; -use const DIRECTORY_SEPARATOR; /** * Class AttachmentHelper. @@ -53,8 +51,6 @@ class AttachmentHelper implements AttachmentHelperInterface /** * AttachmentHelper constructor. - * - */ public function __construct() { @@ -68,17 +64,13 @@ class AttachmentHelper implements AttachmentHelperInterface /** * Returns the content of an attachment. - * - * - * @param Attachment $attachment - * - * @return string */ public function getAttachmentContent(Attachment $attachment): string { $encryptedData = (string)$this->uploadDisk->get(sprintf('at-%d.data', $attachment->id)); + try { - $unencryptedData = Crypt::decrypt($encryptedData); // verified + $unencryptedData = \Crypt::decrypt($encryptedData); // verified } catch (DecryptException $e) { Log::error(sprintf('Could not decrypt data of attachment #%d: %s', $attachment->id, $e->getMessage())); $unencryptedData = $encryptedData; @@ -89,20 +81,14 @@ class AttachmentHelper implements AttachmentHelperInterface /** * Returns the file path relative to upload disk for an attachment, - * - * @param Attachment $attachment - * - * @return string */ public function getAttachmentLocation(Attachment $attachment): string { - return sprintf('%sat-%d.data', DIRECTORY_SEPARATOR, $attachment->id); + return sprintf('%sat-%d.data', \DIRECTORY_SEPARATOR, $attachment->id); } /** * Get all attachments. - * - * @return Collection */ public function getAttachments(): Collection { @@ -111,8 +97,6 @@ class AttachmentHelper implements AttachmentHelperInterface /** * Get all errors. - * - * @return MessageBag */ public function getErrors(): MessageBag { @@ -121,8 +105,6 @@ class AttachmentHelper implements AttachmentHelperInterface /** * Get all messages. - * - * @return MessageBag */ public function getMessages(): MessageBag { @@ -131,11 +113,6 @@ class AttachmentHelper implements AttachmentHelperInterface /** * Uploads a file as a string. - * - * @param Attachment $attachment - * @param string $content - * - * @return bool */ public function saveAttachmentFromApi(Attachment $attachment, string $content): bool { @@ -158,6 +135,7 @@ class AttachmentHelper implements AttachmentHelperInterface $result = fwrite($resource, $content); if (false === $result) { Log::error('Could not write temp file.'); + return false; } Log::debug(sprintf('Wrote %d bytes to temp file.', $result)); @@ -165,6 +143,7 @@ class AttachmentHelper implements AttachmentHelperInterface if (false === $finfo) { Log::error('Could not open finfo.'); fclose($resource); + return false; } $mime = (string)finfo_file($finfo, $path); @@ -197,22 +176,19 @@ class AttachmentHelper implements AttachmentHelperInterface /** * Save attachments that get uploaded with models, through the app. * - * @param object $model - * @param array|null $files - * - * @return bool * @throws FireflyException */ public function saveAttachmentsForModel(object $model, ?array $files): bool { - if (!($model instanceof Model)) { + if (!$model instanceof Model) { return false; } Log::debug(sprintf('Now in saveAttachmentsForModel for model %s', get_class($model))); if (is_array($files)) { Log::debug('$files is an array.'); - /** @var UploadedFile|null $entry */ + + /** @var null|UploadedFile $entry */ foreach ($files as $entry) { if (null !== $entry) { $this->processFile($entry, $model); @@ -230,10 +206,6 @@ class AttachmentHelper implements AttachmentHelperInterface /** * Process the upload of a file. * - * @param UploadedFile $file - * @param Model $model - * - * @return Attachment|null * @throws FireflyException * @throws EncryptException */ @@ -286,11 +258,6 @@ class AttachmentHelper implements AttachmentHelperInterface /** * Verify if the file was uploaded correctly. - * - * @param UploadedFile $file - * @param Model $model - * - * @return bool */ protected function validateUpload(UploadedFile $file, Model $model): bool { @@ -304,7 +271,6 @@ class AttachmentHelper implements AttachmentHelperInterface $result = false; } - // can't seem to reach this point. if (true === $result && !$this->validSize($file)) { $result = false; @@ -319,10 +285,6 @@ class AttachmentHelper implements AttachmentHelperInterface /** * Verify if the mime of a file is valid. - * - * @param UploadedFile $file - * - * @return bool */ protected function validMime(UploadedFile $file): bool { @@ -346,11 +308,6 @@ class AttachmentHelper implements AttachmentHelperInterface /** * Verify if the size of a file is valid. - * - * - * @param UploadedFile $file - * - * @return bool */ protected function validSize(UploadedFile $file): bool { @@ -370,11 +327,6 @@ class AttachmentHelper implements AttachmentHelperInterface /** * Check if a model already has this file attached. - * - * @param UploadedFile $file - * @param Model $model - * - * @return bool */ protected function hasFile(UploadedFile $file, Model $model): bool { @@ -386,7 +338,7 @@ class AttachmentHelper implements AttachmentHelperInterface if ($model instanceof PiggyBank) { $count = $model->account->user->attachments()->where('md5', $md5)->where('attachable_id', $model->id)->where('attachable_type', $class)->count(); } - if (!($model instanceof PiggyBank)) { + if (!$model instanceof PiggyBank) { $count = $model->user->attachments()->where('md5', $md5)->where('attachable_id', $model->id)->where('attachable_type', $class)->count(); } $result = false; diff --git a/app/Helpers/Attachments/AttachmentHelperInterface.php b/app/Helpers/Attachments/AttachmentHelperInterface.php index 766193e86b..4640d5deb8 100644 --- a/app/Helpers/Attachments/AttachmentHelperInterface.php +++ b/app/Helpers/Attachments/AttachmentHelperInterface.php @@ -34,60 +34,36 @@ interface AttachmentHelperInterface { /** * Get content of an attachment. - * - * @param Attachment $attachment - * - * @return string */ public function getAttachmentContent(Attachment $attachment): string; /** * Get the location of an attachment. - * - * @param Attachment $attachment - * - * @return string */ public function getAttachmentLocation(Attachment $attachment): string; /** * Get all attachments. - * - * @return Collection */ public function getAttachments(): Collection; /** * Get all errors. - * - * @return MessageBag */ public function getErrors(): MessageBag; /** * Get all messages/ - * - * @return MessageBag */ public function getMessages(): MessageBag; /** * Uploads a file as a string. - * - * @param Attachment $attachment - * @param string $content - * - * @return bool */ public function saveAttachmentFromApi(Attachment $attachment, string $content): bool; /** * Save attachments that got uploaded. - * - * @param object $model - * @param null|array $files - * - * @return bool */ public function saveAttachmentsForModel(object $model, ?array $files): bool; } diff --git a/app/Helpers/Collector/Extensions/AccountCollection.php b/app/Helpers/Collector/Extensions/AccountCollection.php index 2981ae44dc..9ababe58e5 100644 --- a/app/Helpers/Collector/Extensions/AccountCollection.php +++ b/app/Helpers/Collector/Extensions/AccountCollection.php @@ -35,10 +35,6 @@ trait AccountCollection { /** * These accounts must not be included. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function excludeAccounts(Collection $accounts): GroupCollectorInterface { @@ -55,10 +51,6 @@ trait AccountCollection /** * These accounts must not be destination accounts. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function excludeDestinationAccounts(Collection $accounts): GroupCollectorInterface { @@ -74,10 +66,6 @@ trait AccountCollection /** * These accounts must not be source accounts. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function excludeSourceAccounts(Collection $accounts): GroupCollectorInterface { @@ -93,22 +81,18 @@ trait AccountCollection /** * Define which accounts can be part of the source and destination transactions. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function setAccounts(Collection $accounts): GroupCollectorInterface { if ($accounts->count() > 0) { $accountIds = $accounts->pluck('id')->toArray(); $this->query->where( - static function (EloquentBuilder $query) use ($accountIds) { // @phpstan-ignore-line + static function (EloquentBuilder $query) use ($accountIds): void { // @phpstan-ignore-line $query->whereIn('source.account_id', $accountIds); $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; @@ -116,17 +100,13 @@ trait AccountCollection /** * Both source AND destination must be in this list of accounts. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function setBothAccounts(Collection $accounts): GroupCollectorInterface { if ($accounts->count() > 0) { $accountIds = $accounts->pluck('id')->toArray(); $this->query->where( - static function (EloquentBuilder $query) use ($accountIds) { // @phpstan-ignore-line + static function (EloquentBuilder $query) use ($accountIds): void { // @phpstan-ignore-line $query->whereIn('source.account_id', $accountIds); $query->whereIn('destination.account_id', $accountIds); } @@ -139,10 +119,6 @@ trait AccountCollection /** * Define which accounts can be part of the source and destination transactions. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function setDestinationAccounts(Collection $accounts): GroupCollectorInterface { @@ -158,22 +134,18 @@ trait AccountCollection /** * Define which accounts can NOT be part of the source and destination transactions. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function setNotAccounts(Collection $accounts): GroupCollectorInterface { if ($accounts->count() > 0) { $accountIds = $accounts->pluck('id')->toArray(); $this->query->where( - static function (EloquentBuilder $query) use ($accountIds) { // @phpstan-ignore-line + static function (EloquentBuilder $query) use ($accountIds): void { // @phpstan-ignore-line $query->whereNotIn('source.account_id', $accountIds); $query->whereNotIn('destination.account_id', $accountIds); } ); - //app('log')->debug(sprintf('GroupCollector: setAccounts: %s', implode(', ', $accountIds))); + // app('log')->debug(sprintf('GroupCollector: setAccounts: %s', implode(', ', $accountIds))); } return $this; @@ -181,10 +153,6 @@ trait AccountCollection /** * Define which accounts can be part of the source and destination transactions. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function setSourceAccounts(Collection $accounts): GroupCollectorInterface { @@ -200,28 +168,24 @@ trait AccountCollection /** * Either account can be set, but NOT both. This effectively excludes internal transfers. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function setXorAccounts(Collection $accounts): GroupCollectorInterface { if ($accounts->count() > 0) { $accountIds = $accounts->pluck('id')->toArray(); $this->query->where( - static function (EloquentBuilder $q1) use ($accountIds) { // @phpstan-ignore-line + static function (EloquentBuilder $q1) use ($accountIds): void { // @phpstan-ignore-line // sourceAccount is in the set, and destination is NOT. $q1->where( - static function (EloquentBuilder $q2) use ($accountIds) { + static function (EloquentBuilder $q2) use ($accountIds): void { $q2->whereIn('source.account_id', $accountIds); $q2->whereNotIn('destination.account_id', $accountIds); } ); // destination is in the set, and source is NOT $q1->orWhere( - static function (EloquentBuilder $q3) use ($accountIds) { + static function (EloquentBuilder $q3) use ($accountIds): void { $q3->whereNotIn('source.account_id', $accountIds); $q3->whereIn('destination.account_id', $accountIds); } @@ -237,8 +201,6 @@ trait AccountCollection /** * Will include the source and destination account names and types. - * - * @return GroupCollectorInterface */ public function withAccountInformation(): GroupCollectorInterface { diff --git a/app/Helpers/Collector/Extensions/AmountCollection.php b/app/Helpers/Collector/Extensions/AmountCollection.php index 42edbe55fc..6daf0192a7 100644 --- a/app/Helpers/Collector/Extensions/AmountCollection.php +++ b/app/Helpers/Collector/Extensions/AmountCollection.php @@ -34,15 +34,11 @@ trait AmountCollection { /** * Get transactions with a specific amount. - * - * @param string $amount - * - * @return GroupCollectorInterface */ public function amountIs(string $amount): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($amount) { // @phpstan-ignore-line + static function (EloquentBuilder $q) use ($amount): void { // @phpstan-ignore-line $q->where('source.amount', app('steam')->negative($amount)); } ); @@ -50,13 +46,10 @@ trait AmountCollection return $this; } - /** - * @inheritDoc - */ public function amountIsNot(string $amount): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($amount) { // @phpstan-ignore-line + static function (EloquentBuilder $q) use ($amount): void { // @phpstan-ignore-line $q->where('source.amount', '!=', app('steam')->negative($amount)); } ); @@ -66,15 +59,11 @@ trait AmountCollection /** * Get transactions where the amount is less than. - * - * @param string $amount - * - * @return GroupCollectorInterface */ public function amountLess(string $amount): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($amount) { // @phpstan-ignore-line + static function (EloquentBuilder $q) use ($amount): void { // @phpstan-ignore-line $q->where('destination.amount', '<=', app('steam')->positive($amount)); } ); @@ -84,15 +73,11 @@ trait AmountCollection /** * Get transactions where the amount is more than. - * - * @param string $amount - * - * @return GroupCollectorInterface */ public function amountMore(string $amount): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($amount) { // @phpstan-ignore-line + static function (EloquentBuilder $q) use ($amount): void { // @phpstan-ignore-line $q->where('destination.amount', '>=', app('steam')->positive($amount)); } ); @@ -102,15 +87,11 @@ trait AmountCollection /** * Get transactions with a specific foreign amount. - * - * @param string $amount - * - * @return GroupCollectorInterface */ public function foreignAmountIs(string $amount): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($amount) { // @phpstan-ignore-line + static function (EloquentBuilder $q) use ($amount): void { // @phpstan-ignore-line $q->whereNotNull('source.foreign_amount'); $q->where('source.foreign_amount', app('steam')->negative($amount)); } @@ -121,15 +102,11 @@ trait AmountCollection /** * Get transactions with a specific foreign amount. - * - * @param string $amount - * - * @return GroupCollectorInterface */ public function foreignAmountIsNot(string $amount): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($amount) { // @phpstan-ignore-line + static function (EloquentBuilder $q) use ($amount): void { // @phpstan-ignore-line $q->whereNull('source.foreign_amount'); $q->orWhere('source.foreign_amount', '!=', app('steam')->negative($amount)); } @@ -140,15 +117,11 @@ trait AmountCollection /** * Get transactions where the amount is less than. - * - * @param string $amount - * - * @return GroupCollectorInterface */ public function foreignAmountLess(string $amount): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($amount) { // @phpstan-ignore-line + static function (EloquentBuilder $q) use ($amount): void { // @phpstan-ignore-line $q->whereNotNull('destination.foreign_amount'); $q->where('destination.foreign_amount', '<=', app('steam')->positive($amount)); } @@ -159,15 +132,11 @@ trait AmountCollection /** * Get transactions where the amount is more than. - * - * @param string $amount - * - * @return GroupCollectorInterface */ public function foreignAmountMore(string $amount): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($amount) { // @phpstan-ignore-line + static function (EloquentBuilder $q) use ($amount): void { // @phpstan-ignore-line $q->whereNotNull('destination.foreign_amount'); $q->where('destination.foreign_amount', '>=', app('steam')->positive($amount)); } diff --git a/app/Helpers/Collector/Extensions/AttachmentCollection.php b/app/Helpers/Collector/Extensions/AttachmentCollection.php index 4d929fb4db..517d865367 100644 --- a/app/Helpers/Collector/Extensions/AttachmentCollection.php +++ b/app/Helpers/Collector/Extensions/AttachmentCollection.php @@ -35,15 +35,11 @@ use Illuminate\Database\Eloquent\Builder as EloquentBuilder; */ trait AttachmentCollection { - /** - * @param string $name - * - * @return GroupCollectorInterface - */ public function attachmentNameContains(string $name): GroupCollectorInterface { $this->hasAttachments(); $this->withAttachmentInformation(); + /** * @param int $index * @param array $object @@ -64,6 +60,7 @@ trait AttachmentCollection } } } + return false; }; $this->postFilters[] = $filter; @@ -73,8 +70,6 @@ trait AttachmentCollection /** * Has attachments - * - * @return GroupCollectorInterface */ public function hasAttachments(): GroupCollectorInterface { @@ -86,29 +81,6 @@ trait AttachmentCollection return $this; } - /** - * Join table to get attachment information. - */ - private function joinAttachmentTables(): void - { - if (false === $this->hasJoinedAttTables) { - // join some extra tables: - $this->hasJoinedAttTables = true; - $this->query->leftJoin('attachments', 'attachments.attachable_id', '=', 'transaction_journals.id') - ->where( - static function (EloquentBuilder $q1) { // @phpstan-ignore-line - $q1->where('attachments.attachable_type', TransactionJournal::class); - $q1->where('attachments.uploaded', true); - $q1->whereNull('attachments.deleted_at'); - $q1->orWhereNull('attachments.attachable_type'); - } - ); - } - } - - /** - * @inheritDoc - */ public function withAttachmentInformation(): GroupCollectorInterface { $this->fields[] = 'attachments.id as attachment_id'; @@ -120,20 +92,17 @@ trait AttachmentCollection return $this; } - /** - * @param string $name - * - * @return GroupCollectorInterface - */ public function attachmentNameDoesNotContain(string $name): GroupCollectorInterface { $this->hasAttachments(); $this->withAttachmentInformation(); + /** * @param int $index * @param array $object * * @return bool + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ $filter = static function (array $object) use ($name): bool { @@ -150,6 +119,7 @@ trait AttachmentCollection } } } + return false; }; $this->postFilters[] = $filter; @@ -157,20 +127,17 @@ trait AttachmentCollection return $this; } - /** - * @param string $name - * - * @return GroupCollectorInterface - */ public function attachmentNameDoesNotEnd(string $name): GroupCollectorInterface { $this->hasAttachments(); $this->withAttachmentInformation(); + /** * @param int $index * @param array $object * * @return bool + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ $filter = static function (array $object) use ($name): bool { @@ -187,6 +154,7 @@ trait AttachmentCollection } } } + return false; }; $this->postFilters[] = $filter; @@ -194,20 +162,17 @@ trait AttachmentCollection return $this; } - /** - * @param string $name - * - * @return GroupCollectorInterface - */ public function attachmentNameDoesNotStart(string $name): GroupCollectorInterface { $this->hasAttachments(); $this->withAttachmentInformation(); + /** * @param int $index * @param array $object * * @return bool + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ $filter = static function (array $object) use ($name): bool { @@ -224,6 +189,7 @@ trait AttachmentCollection } } } + return false; }; $this->postFilters[] = $filter; @@ -231,11 +197,6 @@ trait AttachmentCollection return $this; } - /** - * @param string $name - * - * @return GroupCollectorInterface - */ public function attachmentNameEnds(string $name): GroupCollectorInterface { $this->hasAttachments(); @@ -254,6 +215,7 @@ trait AttachmentCollection } } } + return false; }; $this->postFilters[] = $filter; @@ -261,11 +223,6 @@ trait AttachmentCollection return $this; } - /** - * @param string $name - * - * @return GroupCollectorInterface - */ public function attachmentNameIs(string $name): GroupCollectorInterface { $this->hasAttachments(); @@ -281,6 +238,7 @@ trait AttachmentCollection } } } + return false; }; $this->postFilters[] = $filter; @@ -288,11 +246,6 @@ trait AttachmentCollection return $this; } - /** - * @param string $name - * - * @return GroupCollectorInterface - */ public function attachmentNameIsNot(string $name): GroupCollectorInterface { $this->hasAttachments(); @@ -308,6 +261,7 @@ trait AttachmentCollection } } } + return false; }; $this->postFilters[] = $filter; @@ -315,11 +269,6 @@ trait AttachmentCollection return $this; } - /** - * @param string $name - * - * @return GroupCollectorInterface - */ public function attachmentNameStarts(string $name): GroupCollectorInterface { $this->hasAttachments(); @@ -338,6 +287,7 @@ trait AttachmentCollection } } } + return false; }; $this->postFilters[] = $filter; @@ -345,11 +295,6 @@ trait AttachmentCollection return $this; } - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function attachmentNotesAre(string $value): GroupCollectorInterface { $this->hasAttachments(); @@ -359,12 +304,14 @@ trait AttachmentCollection foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ foreach ($transaction['attachments'] as $attachment) { - /** @var Attachment|null $object */ + /** @var null|Attachment $object */ $object = auth()->user()->attachments()->find($attachment['id']); $notes = (string)$object?->notes()->first()?->text; - return $notes !== '' && $notes === $value; + + return '' !== $notes && $notes === $value; } } + return false; }; $this->postFilters[] = $filter; @@ -372,11 +319,6 @@ trait AttachmentCollection return $this; } - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function attachmentNotesAreNot(string $value): GroupCollectorInterface { $this->hasAttachments(); @@ -386,12 +328,14 @@ trait AttachmentCollection foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ foreach ($transaction['attachments'] as $attachment) { - /** @var Attachment|null $object */ + /** @var null|Attachment $object */ $object = auth()->user()->attachments()->find($attachment['id']); $notes = (string)$object?->notes()->first()?->text; - return $notes !== '' && $notes !== $value; + + return '' !== $notes && $notes !== $value; } } + return false; }; $this->postFilters[] = $filter; @@ -399,11 +343,6 @@ trait AttachmentCollection return $this; } - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function attachmentNotesContains(string $value): GroupCollectorInterface { $this->hasAttachments(); @@ -413,12 +352,14 @@ trait AttachmentCollection foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ foreach ($transaction['attachments'] as $attachment) { - /** @var Attachment|null $object */ + /** @var null|Attachment $object */ $object = auth()->user()->attachments()->find($attachment['id']); $notes = (string)$object?->notes()->first()?->text; - return $notes !== '' && str_contains(strtolower($notes), strtolower($value)); + + return '' !== $notes && str_contains(strtolower($notes), strtolower($value)); } } + return false; }; $this->postFilters[] = $filter; @@ -426,11 +367,6 @@ trait AttachmentCollection return $this; } - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function attachmentNotesDoNotContain(string $value): GroupCollectorInterface { $this->hasAttachments(); @@ -440,12 +376,14 @@ trait AttachmentCollection foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ foreach ($transaction['attachments'] as $attachment) { - /** @var Attachment|null $object */ + /** @var null|Attachment $object */ $object = auth()->user()->attachments()->find($attachment['id']); $notes = (string)$object?->notes()->first()?->text; - return $notes !== '' && !str_contains(strtolower($notes), strtolower($value)); + + return '' !== $notes && !str_contains(strtolower($notes), strtolower($value)); } } + return false; }; $this->postFilters[] = $filter; @@ -453,11 +391,6 @@ trait AttachmentCollection return $this; } - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function attachmentNotesDoNotEnd(string $value): GroupCollectorInterface { $this->hasAttachments(); @@ -467,12 +400,14 @@ trait AttachmentCollection foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ foreach ($transaction['attachments'] as $attachment) { - /** @var Attachment|null $object */ + /** @var null|Attachment $object */ $object = auth()->user()->attachments()->find($attachment['id']); $notes = (string)$object?->notes()->first()?->text; - return $notes !== '' && !str_ends_with(strtolower($notes), strtolower($value)); + + return '' !== $notes && !str_ends_with(strtolower($notes), strtolower($value)); } } + return false; }; $this->postFilters[] = $filter; @@ -480,11 +415,6 @@ trait AttachmentCollection return $this; } - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function attachmentNotesDoNotStart(string $value): GroupCollectorInterface { $this->hasAttachments(); @@ -494,12 +424,14 @@ trait AttachmentCollection foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ foreach ($transaction['attachments'] as $attachment) { - /** @var Attachment|null $object */ + /** @var null|Attachment $object */ $object = auth()->user()->attachments()->find($attachment['id']); $notes = (string)$object?->notes()->first()?->text; - return $notes !== '' && !str_starts_with(strtolower($notes), strtolower($value)); + + return '' !== $notes && !str_starts_with(strtolower($notes), strtolower($value)); } } + return false; }; $this->postFilters[] = $filter; @@ -507,11 +439,6 @@ trait AttachmentCollection return $this; } - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function attachmentNotesEnds(string $value): GroupCollectorInterface { $this->hasAttachments(); @@ -521,12 +448,14 @@ trait AttachmentCollection foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ foreach ($transaction['attachments'] as $attachment) { - /** @var Attachment|null $object */ + /** @var null|Attachment $object */ $object = auth()->user()->attachments()->find($attachment['id']); $notes = (string)$object?->notes()->first()?->text; - return $notes !== '' && str_ends_with(strtolower($notes), strtolower($value)); + + return '' !== $notes && str_ends_with(strtolower($notes), strtolower($value)); } } + return false; }; $this->postFilters[] = $filter; @@ -534,11 +463,6 @@ trait AttachmentCollection return $this; } - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function attachmentNotesStarts(string $value): GroupCollectorInterface { $this->hasAttachments(); @@ -548,12 +472,14 @@ trait AttachmentCollection foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ foreach ($transaction['attachments'] as $attachment) { - /** @var Attachment|null $object */ + /** @var null|Attachment $object */ $object = auth()->user()->attachments()->find($attachment['id']); $notes = (string)$object?->notes()->first()?->text; - return $notes !== '' && str_starts_with(strtolower($notes), strtolower($value)); + + return '' !== $notes && str_starts_with(strtolower($notes), strtolower($value)); } } + return false; }; $this->postFilters[] = $filter; @@ -563,27 +489,47 @@ trait AttachmentCollection /** * Has attachments - * - * @return GroupCollectorInterface */ public function hasNoAttachments(): GroupCollectorInterface { app('log')->debug('Add filter on no attachments.'); $this->joinAttachmentTables(); - $this->query->where(static function (Builder $q1) { // @phpstan-ignore-line + $this->query->where(static function (Builder $q1): void { // @phpstan-ignore-line $q1 ->whereNull('attachments.attachable_id') - ->orWhere(static function (Builder $q2) { + ->orWhere(static function (Builder $q2): void { $q2 ->whereNotNull('attachments.attachable_id') - ->whereNotNull('attachments.deleted_at'); + ->whereNotNull('attachments.deleted_at') + ; // id is not null // deleted at is not null. - }); + }) + ; }); - return $this; } + + /** + * Join table to get attachment information. + */ + private function joinAttachmentTables(): void + { + if (false === $this->hasJoinedAttTables) { + // join some extra tables: + $this->hasJoinedAttTables = true; + $this->query->leftJoin('attachments', 'attachments.attachable_id', '=', 'transaction_journals.id') + ->where( + static function (EloquentBuilder $q1): void { // @phpstan-ignore-line + $q1->where('attachments.attachable_type', TransactionJournal::class); + $q1->where('attachments.uploaded', true); + $q1->whereNull('attachments.deleted_at'); + $q1->orWhereNull('attachments.attachable_type'); + } + ) + ; + } + } } diff --git a/app/Helpers/Collector/Extensions/MetaCollection.php b/app/Helpers/Collector/Extensions/MetaCollection.php index 9f23d89abb..e082a635cf 100644 --- a/app/Helpers/Collector/Extensions/MetaCollection.php +++ b/app/Helpers/Collector/Extensions/MetaCollection.php @@ -33,19 +33,17 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; /** * Trait MetaCollection */ trait MetaCollection { - /** - * @inheritDoc - */ public function excludeBills(Collection $bills): GroupCollectorInterface { $this->withBillInformation(); - $this->query->where(static function (EloquentBuilder $q1) use ($bills) { // @phpstan-ignore-line + $this->query->where(static function (EloquentBuilder $q1) use ($bills): void { // @phpstan-ignore-line $q1->whereNotIn('transaction_journals.bill_id', $bills->pluck('id')->toArray()); $q1->orWhereNull('transaction_journals.bill_id'); }); @@ -55,8 +53,6 @@ trait MetaCollection /** * Will include bill name + ID, if any. - * - * @return GroupCollectorInterface */ public function withBillInformation(): GroupCollectorInterface { @@ -74,16 +70,12 @@ trait MetaCollection /** * Exclude a specific budget. - * - * @param Budget $budget - * - * @return GroupCollectorInterface */ public function excludeBudget(Budget $budget): GroupCollectorInterface { $this->withBudgetInformation(); - $this->query->where(static function (EloquentBuilder $q2) use ($budget) { // @phpstan-ignore-line + $this->query->where(static function (EloquentBuilder $q2) use ($budget): void { // @phpstan-ignore-line $q2->where('budgets.id', '!=', $budget->id); $q2->orWhereNull('budgets.id'); }); @@ -93,8 +85,6 @@ trait MetaCollection /** * Will include budget ID + name, if any. - * - * @return GroupCollectorInterface */ public function withBudgetInformation(): GroupCollectorInterface { @@ -112,14 +102,11 @@ trait MetaCollection return $this; } - /** - * @inheritDoc - */ public function excludeBudgets(Collection $budgets): GroupCollectorInterface { if ($budgets->count() > 0) { $this->withBudgetInformation(); - $this->query->where(static function (EloquentBuilder $q1) use ($budgets) { // @phpstan-ignore-line + $this->query->where(static function (EloquentBuilder $q1) use ($budgets): void { // @phpstan-ignore-line $q1->whereNotIn('budgets.id', $budgets->pluck('id')->toArray()); $q1->orWhereNull('budgets.id'); }); @@ -128,14 +115,11 @@ trait MetaCollection return $this; } - /** - * @inheritDoc - */ public function excludeCategories(Collection $categories): GroupCollectorInterface { if ($categories->count() > 0) { $this->withCategoryInformation(); - $this->query->where(static function (EloquentBuilder $q1) use ($categories) { // @phpstan-ignore-line + $this->query->where(static function (EloquentBuilder $q1) use ($categories): void { // @phpstan-ignore-line $q1->whereNotIn('categories.id', $categories->pluck('id')->toArray()); $q1->orWhereNull('categories.id'); }); @@ -146,8 +130,6 @@ trait MetaCollection /** * Will include category ID + name, if any. - * - * @return GroupCollectorInterface */ public function withCategoryInformation(): GroupCollectorInterface { @@ -167,16 +149,12 @@ trait MetaCollection /** * Exclude a specific category. - * - * @param Category $category - * - * @return GroupCollectorInterface */ public function excludeCategory(Category $category): GroupCollectorInterface { $this->withCategoryInformation(); - $this->query->where(static function (EloquentBuilder $q2) use ($category) { // @phpstan-ignore-line + $this->query->where(static function (EloquentBuilder $q2) use ($category): void { // @phpstan-ignore-line $q2->where('categories.id', '!=', $category->id); $q2->orWhereNull('categories.id'); }); @@ -184,9 +162,6 @@ trait MetaCollection return $this; } - /** - * @inheritDoc - */ public function excludeExternalId(string $externalId): GroupCollectorInterface { $this->joinMetaDataTables(); @@ -196,6 +171,726 @@ trait MetaCollection return $this; } + public function excludeExternalUrl(string $url): GroupCollectorInterface + { + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'external_url'); + $this->query->where('journal_meta.data', '!=', json_encode($url)); + + return $this; + } + + public function excludeInternalReference(string $internalReference): GroupCollectorInterface + { + $internalReference = (string) json_encode($internalReference); + $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); + + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'internal_reference'); + $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s%%', $internalReference)); + + return $this; + } + + public function excludeRecurrenceId(string $recurringId): GroupCollectorInterface + { + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'recurrence_id'); + $this->query->where('journal_meta.data', '!=', sprintf('%s', json_encode($recurringId))); + + return $this; + } + + public function externalIdContains(string $externalId): GroupCollectorInterface + { + $externalId = (string) json_encode($externalId); + $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); + + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'external_id'); + $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $externalId)); + + return $this; + } + + public function externalIdDoesNotContain(string $externalId): GroupCollectorInterface + { + $externalId = (string) json_encode($externalId); + $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); + + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'external_id'); + $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s%%', $externalId)); + + return $this; + } + + public function externalIdDoesNotEnd(string $externalId): GroupCollectorInterface + { + $externalId = (string) json_encode($externalId); + $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); + + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'external_id'); + $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s"', $externalId)); + + return $this; + } + + public function externalIdDoesNotStart(string $externalId): GroupCollectorInterface + { + $externalId = (string) json_encode($externalId); + $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); + + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'external_id'); + $this->query->where('journal_meta.data', 'LIKE', sprintf('"%s%%', $externalId)); + + return $this; + } + + public function externalIdEnds(string $externalId): GroupCollectorInterface + { + $externalId = (string) json_encode($externalId); + $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); + + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'external_id'); + $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s"', $externalId)); + + return $this; + } + + public function externalIdStarts(string $externalId): GroupCollectorInterface + { + $externalId = (string) json_encode($externalId); + $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); + + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'external_id'); + $this->query->where('journal_meta.data', 'LIKE', sprintf('"%s%%', $externalId)); + + return $this; + } + + public function externalUrlContains(string $url): GroupCollectorInterface + { + $this->joinMetaDataTables(); + $url = (string) json_encode($url); + $url = str_replace('\\', '\\\\', trim($url, '"')); + $this->query->where('journal_meta.name', '=', 'external_url'); + $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $url)); + + return $this; + } + + public function externalUrlDoesNotContain(string $url): GroupCollectorInterface + { + $this->joinMetaDataTables(); + $url = (string) json_encode($url); + $url = str_replace('\\', '\\\\', trim($url, '"')); + $this->query->where('journal_meta.name', '=', 'external_url'); + $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s%%', $url)); + + return $this; + } + + public function externalUrlDoesNotEnd(string $url): GroupCollectorInterface + { + $this->joinMetaDataTables(); + $url = (string) json_encode($url); + $url = str_replace('\\', '\\\\', ltrim($url, '"')); + $this->query->where('journal_meta.name', '=', 'external_url'); + $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s', $url)); + + return $this; + } + + public function externalUrlDoesNotStart(string $url): GroupCollectorInterface + { + $this->joinMetaDataTables(); + $url = (string) json_encode($url); + $url = str_replace('\\', '\\\\', rtrim($url, '"')); + // var_dump($url); + + $this->query->where('journal_meta.name', '=', 'external_url'); + $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%s%%', $url)); + + return $this; + } + + public function externalUrlEnds(string $url): GroupCollectorInterface + { + $this->joinMetaDataTables(); + $url = (string) json_encode($url); + $url = str_replace('\\', '\\\\', ltrim($url, '"')); + $this->query->where('journal_meta.name', '=', 'external_url'); + $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s', $url)); + + return $this; + } + + public function externalUrlStarts(string $url): GroupCollectorInterface + { + $this->joinMetaDataTables(); + $url = (string) json_encode($url); + $url = str_replace('\\', '\\\\', rtrim($url, '"')); + // var_dump($url); + + $this->query->where('journal_meta.name', '=', 'external_url'); + $this->query->where('journal_meta.data', 'LIKE', sprintf('%s%%', $url)); + + return $this; + } + + /** + * Where has no tags. + */ + public function hasAnyTag(): GroupCollectorInterface + { + $this->withTagInformation(); + $this->query->whereNotNull('tag_transaction_journal.tag_id'); + + return $this; + } + + public function withTagInformation(): GroupCollectorInterface + { + $this->fields[] = 'tags.id as tag_id'; + $this->fields[] = 'tags.tag as tag_name'; + $this->fields[] = 'tags.date as tag_date'; + $this->fields[] = 'tags.description as tag_description'; + $this->fields[] = 'tags.latitude as tag_latitude'; + $this->fields[] = 'tags.longitude as tag_longitude'; + $this->fields[] = 'tags.zoomLevel as tag_zoom_level'; + + $this->joinTagTables(); + + return $this; + } + + public function internalReferenceContains(string $internalReference): GroupCollectorInterface + { + $internalReference = (string) json_encode($internalReference); + $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); + // var_dump($internalReference); + // exit; + + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'internal_reference'); + $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $internalReference)); + + return $this; + } + + public function internalReferenceDoesNotContain(string $internalReference): GroupCollectorInterface + { + $internalReference = (string) json_encode($internalReference); + $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); + + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'internal_reference'); + $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s%%', $internalReference)); + + return $this; + } + + public function internalReferenceDoesNotEnd(string $internalReference): GroupCollectorInterface + { + $internalReference = (string) json_encode($internalReference); + $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); + + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'internal_reference'); + $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s"', $internalReference)); + + return $this; + } + + public function internalReferenceDoesNotStart(string $internalReference): GroupCollectorInterface + { + $internalReference = (string) json_encode($internalReference); + $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); + + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'internal_reference'); + $this->query->where('journal_meta.data', 'LIKE', sprintf('"%s%%', $internalReference)); + + return $this; + } + + public function internalReferenceEnds(string $internalReference): GroupCollectorInterface + { + $internalReference = (string) json_encode($internalReference); + $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); + + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'internal_reference'); + $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s"', $internalReference)); + + return $this; + } + + public function internalReferenceStarts(string $internalReference): GroupCollectorInterface + { + $internalReference = (string) json_encode($internalReference); + $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); + + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'internal_reference'); + $this->query->where('journal_meta.data', 'LIKE', sprintf('"%s%%', $internalReference)); + + return $this; + } + + public function notesContain(string $value): GroupCollectorInterface + { + $this->withNotes(); + $this->query->where('notes.text', 'LIKE', sprintf('%%%s%%', $value)); + + return $this; + } + + public function withNotes(): GroupCollectorInterface + { + if (false === $this->hasNotesInformation) { + // join bill table + $this->query->leftJoin( + 'notes', + static function (JoinClause $join): void { + $join->on('notes.noteable_id', '=', 'transaction_journals.id'); + $join->where('notes.noteable_type', '=', 'FireflyIII\Models\TransactionJournal'); + $join->whereNull('notes.deleted_at'); + } + ); + // add fields + $this->fields[] = 'notes.text as notes'; + $this->hasNotesInformation = true; + } + + return $this; + } + + public function notesDoNotContain(string $value): GroupCollectorInterface + { + $this->withNotes(); + $this->query->where(static function (Builder $q) use ($value): void { // @phpstan-ignore-line + $q->whereNull('notes.text'); + $q->orWhere('notes.text', 'NOT LIKE', sprintf('%%%s%%', $value)); + }); + + return $this; + } + + public function notesDontEndWith(string $value): GroupCollectorInterface + { + $this->withNotes(); + $this->query->where(static function (Builder $q) use ($value): void { // @phpstan-ignore-line + $q->whereNull('notes.text'); + $q->orWhere('notes.text', 'NOT LIKE', sprintf('%%%s', $value)); + }); + + return $this; + } + + public function notesDontStartWith(string $value): GroupCollectorInterface + { + $this->withNotes(); + $this->query->where(static function (Builder $q) use ($value): void { // @phpstan-ignore-line + $q->whereNull('notes.text'); + $q->orWhere('notes.text', 'NOT LIKE', sprintf('%s%%', $value)); + }); + + return $this; + } + + public function notesEndWith(string $value): GroupCollectorInterface + { + $this->withNotes(); + $this->query->where('notes.text', 'LIKE', sprintf('%%%s', $value)); + + return $this; + } + + public function notesExactly(string $value): GroupCollectorInterface + { + $this->withNotes(); + $this->query->where('notes.text', '=', sprintf('%s', $value)); + + return $this; + } + + public function notesExactlyNot(string $value): GroupCollectorInterface + { + $this->withNotes(); + $this->query->where(static function (Builder $q) use ($value): void { // @phpstan-ignore-line + $q->whereNull('notes.text'); + $q->orWhere('notes.text', '!=', sprintf('%s', $value)); + }); + + return $this; + } + + public function notesStartWith(string $value): GroupCollectorInterface + { + $this->withNotes(); + $this->query->where('notes.text', 'LIKE', sprintf('%s%%', $value)); + + return $this; + } + + /** + * Limit the search to a specific bill. + */ + public function setBill(Bill $bill): GroupCollectorInterface + { + $this->withBillInformation(); + $this->query->where('transaction_journals.bill_id', '=', $bill->id); + + return $this; + } + + /** + * Limit the search to a specific set of bills. + */ + public function setBills(Collection $bills): GroupCollectorInterface + { + $this->withBillInformation(); + $this->query->whereIn('transaction_journals.bill_id', $bills->pluck('id')->toArray()); + + return $this; + } + + /** + * Limit the search to a specific budget. + */ + public function setBudget(Budget $budget): GroupCollectorInterface + { + $this->withBudgetInformation(); + $this->query->where('budgets.id', $budget->id); + + return $this; + } + + /** + * Limit the search to a specific set of budgets. + */ + public function setBudgets(Collection $budgets): GroupCollectorInterface + { + if ($budgets->count() > 0) { + $this->withBudgetInformation(); + $this->query->whereIn('budgets.id', $budgets->pluck('id')->toArray()); + } + + return $this; + } + + /** + * Limit the search to a specific bunch of categories. + */ + public function setCategories(Collection $categories): GroupCollectorInterface + { + if ($categories->count() > 0) { + $this->withCategoryInformation(); + $this->query->whereIn('categories.id', $categories->pluck('id')->toArray()); + } + + return $this; + } + + /** + * Limit the search to a specific category. + */ + public function setCategory(Category $category): GroupCollectorInterface + { + $this->withCategoryInformation(); + $this->query->where('categories.id', $category->id); + + return $this; + } + + public function setExternalId(string $externalId): GroupCollectorInterface + { + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'external_id'); + $this->query->where('journal_meta.data', '=', sprintf('%s', json_encode($externalId))); + + return $this; + } + + public function setExternalUrl(string $url): GroupCollectorInterface + { + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'external_url'); + $this->query->where('journal_meta.data', '=', json_encode($url)); + + return $this; + } + + public function setInternalReference(string $internalReference): GroupCollectorInterface + { + $internalReference = (string) json_encode($internalReference); + $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); + + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'internal_reference'); + $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $internalReference)); + + return $this; + } + + public function setRecurrenceId(string $recurringId): GroupCollectorInterface + { + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'recurrence_id'); + $this->query->where('journal_meta.data', '=', sprintf('%s', json_encode($recurringId))); + + return $this; + } + + public function setSepaCT(string $sepaCT): GroupCollectorInterface + { + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'sepa_ct_id'); + $this->query->where('journal_meta.data', '=', sprintf('%s', json_encode($sepaCT))); + $this->query->whereNull('journal_meta.deleted_at'); + + return $this; + } + + /** + * Limit results to a specific tag. + */ + public function setTag(Tag $tag): GroupCollectorInterface + { + $this->withTagInformation(); + $this->setTags(new Collection([$tag])); + + return $this; + } + + /** + * Limit results to a specific set of tags. + */ + public function setTags(Collection $tags): GroupCollectorInterface + { + Log::debug(sprintf('Now in setTags(%d tag(s))', $tags->count())); + $this->withTagInformation(); + $this->query->whereNotNull('tag_transaction_journal.tag_id'); + + // this method adds a "postFilter" to the collector. + $list = $tags->pluck('tag')->toArray(); + $list = array_map('strtolower', $list); + $filter = static function (array $object) use ($list): bool { + Log::debug(sprintf('Now in setTags(%s) filter', implode(', ', $list))); + $expectedTagCount = count($list); + $foundTagCount = 0; + foreach ($object['transactions'] as $transaction) { + $transactionTagCount = count($transaction['tags']); + app('log')->debug(sprintf('Transaction has %d tag(s)', $transactionTagCount)); + if ($transactionTagCount < $expectedTagCount) { + app('log')->debug(sprintf('Transaction has %d tag(s), we expect %d tag(s), return false.', $transactionTagCount, $expectedTagCount)); + + return false; + } + foreach ($transaction['tags'] as $tag) { + Log::debug(sprintf('"%s" versus', strtolower($tag['name'])), $list); + if (in_array(strtolower($tag['name']), $list, true)) { + app('log')->debug(sprintf('Transaction has tag "%s" so count++.', $tag['name'])); + ++$foundTagCount; + } + } + } + Log::debug(sprintf('Found %d tags, need at least %d.', $foundTagCount, $expectedTagCount)); + + // found at least the expected tags. + return $foundTagCount >= $expectedTagCount; + }; + $this->postFilters[] = $filter; + + return $this; + } + + /** + * Without tags + */ + public function setWithoutSpecificTags(Collection $tags): GroupCollectorInterface + { + $this->withTagInformation(); + + // this method adds a "postFilter" to the collector. + $list = $tags->pluck('tag')->toArray(); + $list = array_map('strtolower', $list); + $filter = static function (array $object) use ($list): bool { + Log::debug(sprintf('Now in setWithoutSpecificTags(%s) filter', implode(', ', $list))); + foreach ($object['transactions'] as $transaction) { + app('log')->debug(sprintf('Transaction has %d tag(s)', count($transaction['tags']))); + foreach ($transaction['tags'] as $tag) { + Log::debug(sprintf('"%s" versus', strtolower($tag['name'])), $list); + if (in_array(strtolower($tag['name']), $list, true)) { + app('log')->debug(sprintf('Transaction has tag "%s", but should not have it, return false.', $tag['name'])); + + return false; + } + } + } + + return true; + }; + $this->postFilters[] = $filter; + + return $this; + } + + public function withAnyNotes(): GroupCollectorInterface + { + $this->withNotes(); + $this->query->whereNotNull('notes.text'); + + return $this; + } + + /** + * Limit results to transactions without a bill. + */ + public function withBill(): GroupCollectorInterface + { + $this->withBillInformation(); + $this->query->whereNotNull('transaction_journals.bill_id'); + + return $this; + } + + /** + * Limit results to a transactions without a budget.. + */ + public function withBudget(): GroupCollectorInterface + { + $this->withBudgetInformation(); + $this->query->whereNotNull('budget_transaction_journal.budget_id'); + + return $this; + } + + /** + * Limit results to a transactions without a category. + */ + public function withCategory(): GroupCollectorInterface + { + $this->withCategoryInformation(); + $this->query->whereNotNull('category_transaction_journal.category_id'); + + return $this; + } + + public function withExternalId(): GroupCollectorInterface + { + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'external_id'); + $this->query->whereNotNull('journal_meta.data'); + + return $this; + } + + public function withExternalUrl(): GroupCollectorInterface + { + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', 'external_url'); + $this->query->whereNotNull('journal_meta.data'); + + return $this; + } + + /** + * Limit results to a transactions without a bill. + */ + public function withoutBill(): GroupCollectorInterface + { + $this->query->whereNull('transaction_journals.bill_id'); + + return $this; + } + + /** + * Limit results to a transactions without a budget.. + */ + public function withoutBudget(): GroupCollectorInterface + { + $this->withBudgetInformation(); + $this->query->whereNull('budget_transaction_journal.budget_id'); + + return $this; + } + + /** + * Limit results to a transactions without a category. + */ + public function withoutCategory(): GroupCollectorInterface + { + $this->withCategoryInformation(); + $this->query->whereNull('category_transaction_journal.category_id'); + + return $this; + } + + public function withoutExternalId(): GroupCollectorInterface + { + $this->joinMetaDataTables(); + // TODO not sure if this will work properly. + $this->query->where(static function (Builder $q1): void { // @phpstan-ignore-line + $q1->where(static function (Builder $q2): void { + $q2->where('journal_meta.name', '=', 'external_id'); + $q2->whereNull('journal_meta.data'); + })->orWhere(static function (Builder $q3): void { + $q3->where('journal_meta.name', '!=', 'external_id'); + })->orWhere(static function (Builder $q4): void { + $q4->whereNull('journal_meta.name'); + }); + }); + + return $this; + } + + public function withoutExternalUrl(): GroupCollectorInterface + { + $this->joinMetaDataTables(); + // TODO not sure if this will work properly. + $this->query->where(static function (Builder $q1): void { // @phpstan-ignore-line + $q1->where(static function (Builder $q2): void { + $q2->where('journal_meta.name', '=', 'external_url'); + $q2->whereNull('journal_meta.data'); + })->orWhere(static function (Builder $q3): void { + $q3->where('journal_meta.name', '!=', 'external_url'); + })->orWhere(static function (Builder $q4): void { + $q4->whereNull('journal_meta.name'); + }); + }); + + return $this; + } + + public function withoutNotes(): GroupCollectorInterface + { + $this->withNotes(); + $this->query->where(static function (Builder $q): void { // @phpstan-ignore-line + $q->whereNull('notes.text'); + $q->orWhere('notes.text', ''); + }); + + return $this; + } + + /** + * Where has no tags. + */ + public function withoutTags(): GroupCollectorInterface + { + $this->withTagInformation(); + $this->query->whereNull('tag_transaction_journal.tag_id'); + + return $this; + } + /** * Join table to get tag information. */ @@ -209,266 +904,6 @@ trait MetaCollection } } - /** - * @inheritDoc - */ - public function excludeExternalUrl(string $url): GroupCollectorInterface - { - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'external_url'); - $this->query->where('journal_meta.data', '!=', json_encode($url)); - - return $this; - } - - /** - * @inheritDoc - */ - public function excludeInternalReference(string $internalReference): GroupCollectorInterface - { - $internalReference = (string)json_encode($internalReference); - $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); - - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'internal_reference'); - $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s%%', $internalReference)); - - return $this; - } - - /** - * @inheritDoc - */ - public function excludeRecurrenceId(string $recurringId): GroupCollectorInterface - { - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'recurrence_id'); - $this->query->where('journal_meta.data', '!=', sprintf('%s', json_encode($recurringId))); - - return $this; - } - - /** - * @inheritDoc - */ - public function externalIdContains(string $externalId): GroupCollectorInterface - { - $externalId = (string)json_encode($externalId); - $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); - - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'external_id'); - $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $externalId)); - - return $this; - } - - /** - * @inheritDoc - */ - public function externalIdDoesNotContain(string $externalId): GroupCollectorInterface - { - $externalId = (string)json_encode($externalId); - $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); - - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'external_id'); - $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s%%', $externalId)); - - return $this; - } - - /** - * @inheritDoc - */ - public function externalIdDoesNotEnd(string $externalId): GroupCollectorInterface - { - $externalId = (string)json_encode($externalId); - $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); - - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'external_id'); - $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s"', $externalId)); - - return $this; - } - - /** - * @inheritDoc - */ - public function externalIdDoesNotStart(string $externalId): GroupCollectorInterface - { - $externalId = (string)json_encode($externalId); - $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); - - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'external_id'); - $this->query->where('journal_meta.data', 'LIKE', sprintf('"%s%%', $externalId)); - - return $this; - } - - /** - * @inheritDoc - */ - public function externalIdEnds(string $externalId): GroupCollectorInterface - { - $externalId = (string)json_encode($externalId); - $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); - - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'external_id'); - $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s"', $externalId)); - - return $this; - } - - /** - * @inheritDoc - */ - public function externalIdStarts(string $externalId): GroupCollectorInterface - { - $externalId = (string)json_encode($externalId); - $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); - - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'external_id'); - $this->query->where('journal_meta.data', 'LIKE', sprintf('"%s%%', $externalId)); - - return $this; - } - - /** - * @param string $url - * - * @return GroupCollectorInterface - */ - public function externalUrlContains(string $url): GroupCollectorInterface - { - $this->joinMetaDataTables(); - $url = (string)json_encode($url); - $url = str_replace('\\', '\\\\', trim($url, '"')); - $this->query->where('journal_meta.name', '=', 'external_url'); - $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $url)); - - return $this; - } - - /** - * @param string $url - * - * @return GroupCollectorInterface - */ - public function externalUrlDoesNotContain(string $url): GroupCollectorInterface - { - $this->joinMetaDataTables(); - $url = (string)json_encode($url); - $url = str_replace('\\', '\\\\', trim($url, '"')); - $this->query->where('journal_meta.name', '=', 'external_url'); - $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s%%', $url)); - - return $this; - } - - /** - * @param string $url - * - * @return GroupCollectorInterface - */ - public function externalUrlDoesNotEnd(string $url): GroupCollectorInterface - { - $this->joinMetaDataTables(); - $url = (string)json_encode($url); - $url = str_replace('\\', '\\\\', ltrim($url, '"')); - $this->query->where('journal_meta.name', '=', 'external_url'); - $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s', $url)); - - return $this; - } - - /** - * @param string $url - * - * @return GroupCollectorInterface - */ - public function externalUrlDoesNotStart(string $url): GroupCollectorInterface - { - $this->joinMetaDataTables(); - $url = (string)json_encode($url); - $url = str_replace('\\', '\\\\', rtrim($url, '"')); - //var_dump($url); - - $this->query->where('journal_meta.name', '=', 'external_url'); - $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%s%%', $url)); - - return $this; - } - - /** - * @param string $url - * - * @return GroupCollectorInterface - */ - public function externalUrlEnds(string $url): GroupCollectorInterface - { - $this->joinMetaDataTables(); - $url = (string)json_encode($url); - $url = str_replace('\\', '\\\\', ltrim($url, '"')); - $this->query->where('journal_meta.name', '=', 'external_url'); - $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s', $url)); - - return $this; - } - - /** - * @param string $url - * - * @return GroupCollectorInterface - */ - public function externalUrlStarts(string $url): GroupCollectorInterface - { - $this->joinMetaDataTables(); - $url = (string)json_encode($url); - $url = str_replace('\\', '\\\\', rtrim($url, '"')); - //var_dump($url); - - $this->query->where('journal_meta.name', '=', 'external_url'); - $this->query->where('journal_meta.data', 'LIKE', sprintf('%s%%', $url)); - - return $this; - } - - /** - * Where has no tags. - * - * @return GroupCollectorInterface - */ - public function hasAnyTag(): GroupCollectorInterface - { - $this->withTagInformation(); - $this->query->whereNotNull('tag_transaction_journal.tag_id'); - - return $this; - } - - /** - * @return GroupCollectorInterface - */ - public function withTagInformation(): GroupCollectorInterface - { - $this->fields[] = 'tags.id as tag_id'; - $this->fields[] = 'tags.tag as tag_name'; - $this->fields[] = 'tags.date as tag_date'; - $this->fields[] = 'tags.description as tag_description'; - $this->fields[] = 'tags.latitude as tag_latitude'; - $this->fields[] = 'tags.longitude as tag_longitude'; - $this->fields[] = 'tags.zoomLevel as tag_zoom_level'; - - $this->joinTagTables(); - - return $this; - } - /** * Join table to get tag information. */ @@ -481,635 +916,4 @@ trait MetaCollection $this->query->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id'); } } - - /** - * @inheritDoc - */ - public function internalReferenceContains(string $internalReference): GroupCollectorInterface - { - $internalReference = (string)json_encode($internalReference); - $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); - //var_dump($internalReference); - //exit; - - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'internal_reference'); - $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $internalReference)); - - return $this; - } - - /** - * @inheritDoc - */ - public function internalReferenceDoesNotContain(string $internalReference): GroupCollectorInterface - { - $internalReference = (string)json_encode($internalReference); - $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); - - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'internal_reference'); - $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s%%', $internalReference)); - - return $this; - } - - /** - * @inheritDoc - */ - public function internalReferenceDoesNotEnd(string $internalReference): GroupCollectorInterface - { - $internalReference = (string)json_encode($internalReference); - $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); - - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'internal_reference'); - $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s"', $internalReference)); - - return $this; - } - - /** - * @inheritDoc - */ - public function internalReferenceDoesNotStart(string $internalReference): GroupCollectorInterface - { - $internalReference = (string)json_encode($internalReference); - $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); - - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'internal_reference'); - $this->query->where('journal_meta.data', 'LIKE', sprintf('"%s%%', $internalReference)); - - return $this; - } - - /** - * @inheritDoc - */ - public function internalReferenceEnds(string $internalReference): GroupCollectorInterface - { - $internalReference = (string)json_encode($internalReference); - $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); - - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'internal_reference'); - $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s"', $internalReference)); - - return $this; - } - - /** - * @inheritDoc - */ - public function internalReferenceStarts(string $internalReference): GroupCollectorInterface - { - $internalReference = (string)json_encode($internalReference); - $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); - - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'internal_reference'); - $this->query->where('journal_meta.data', 'LIKE', sprintf('"%s%%', $internalReference)); - - return $this; - } - - /** - * @param string $value - * - * @return GroupCollectorInterface - */ - public function notesContain(string $value): GroupCollectorInterface - { - $this->withNotes(); - $this->query->where('notes.text', 'LIKE', sprintf('%%%s%%', $value)); - - return $this; - } - - /** - * @inheritDoc - */ - public function withNotes(): GroupCollectorInterface - { - if (false === $this->hasNotesInformation) { - // join bill table - $this->query->leftJoin( - 'notes', - static function (JoinClause $join) { - $join->on('notes.noteable_id', '=', 'transaction_journals.id'); - $join->where('notes.noteable_type', '=', 'FireflyIII\Models\TransactionJournal'); - $join->whereNull('notes.deleted_at'); - } - ); - // add fields - $this->fields[] = 'notes.text as notes'; - $this->hasNotesInformation = true; - } - - return $this; - } - - /** - * @param string $value - * - * @return GroupCollectorInterface - */ - public function notesDoNotContain(string $value): GroupCollectorInterface - { - $this->withNotes(); - $this->query->where(static function (Builder $q) use ($value) { // @phpstan-ignore-line - $q->whereNull('notes.text'); - $q->orWhere('notes.text', 'NOT LIKE', sprintf('%%%s%%', $value)); - }); - - return $this; - } - - /** - * @param string $value - * - * @return GroupCollectorInterface - */ - public function notesDontEndWith(string $value): GroupCollectorInterface - { - $this->withNotes(); - $this->query->where(static function (Builder $q) use ($value) { // @phpstan-ignore-line - $q->whereNull('notes.text'); - $q->orWhere('notes.text', 'NOT LIKE', sprintf('%%%s', $value)); - }); - - return $this; - } - - /** - * @param string $value - * - * @return GroupCollectorInterface - */ - public function notesDontStartWith(string $value): GroupCollectorInterface - { - $this->withNotes(); - $this->query->where(static function (Builder $q) use ($value) { // @phpstan-ignore-line - $q->whereNull('notes.text'); - $q->orWhere('notes.text', 'NOT LIKE', sprintf('%s%%', $value)); - }); - - return $this; - } - - /** - * @param string $value - * - * @return GroupCollectorInterface - */ - public function notesEndWith(string $value): GroupCollectorInterface - { - $this->withNotes(); - $this->query->where('notes.text', 'LIKE', sprintf('%%%s', $value)); - - return $this; - } - - /** - * @param string $value - * - * @return GroupCollectorInterface - */ - public function notesExactly(string $value): GroupCollectorInterface - { - $this->withNotes(); - $this->query->where('notes.text', '=', sprintf('%s', $value)); - - return $this; - } - - /** - * @param string $value - * - * @return GroupCollectorInterface - */ - public function notesExactlyNot(string $value): GroupCollectorInterface - { - $this->withNotes(); - $this->query->where(static function (Builder $q) use ($value) { // @phpstan-ignore-line - $q->whereNull('notes.text'); - $q->orWhere('notes.text', '!=', sprintf('%s', $value)); - }); - - return $this; - } - - /** - * @param string $value - * - * @return GroupCollectorInterface - */ - public function notesStartWith(string $value): GroupCollectorInterface - { - $this->withNotes(); - $this->query->where('notes.text', 'LIKE', sprintf('%s%%', $value)); - - return $this; - } - - /** - * Limit the search to a specific bill. - * - * @param Bill $bill - * - * @return GroupCollectorInterface - */ - public function setBill(Bill $bill): GroupCollectorInterface - { - $this->withBillInformation(); - $this->query->where('transaction_journals.bill_id', '=', $bill->id); - - return $this; - } - - /** - * Limit the search to a specific set of bills. - * - * @param Collection $bills - * - * @return GroupCollectorInterface - */ - public function setBills(Collection $bills): GroupCollectorInterface - { - $this->withBillInformation(); - $this->query->whereIn('transaction_journals.bill_id', $bills->pluck('id')->toArray()); - - return $this; - } - - /** - * Limit the search to a specific budget. - * - * @param Budget $budget - * - * @return GroupCollectorInterface - */ - public function setBudget(Budget $budget): GroupCollectorInterface - { - $this->withBudgetInformation(); - $this->query->where('budgets.id', $budget->id); - - return $this; - } - - /** - * Limit the search to a specific set of budgets. - * - * @param Collection $budgets - * - * @return GroupCollectorInterface - */ - public function setBudgets(Collection $budgets): GroupCollectorInterface - { - if ($budgets->count() > 0) { - $this->withBudgetInformation(); - $this->query->whereIn('budgets.id', $budgets->pluck('id')->toArray()); - } - - return $this; - } - - /** - * Limit the search to a specific bunch of categories. - * - * @param Collection $categories - * - * @return GroupCollectorInterface - */ - public function setCategories(Collection $categories): GroupCollectorInterface - { - if ($categories->count() > 0) { - $this->withCategoryInformation(); - $this->query->whereIn('categories.id', $categories->pluck('id')->toArray()); - } - - return $this; - } - - /** - * Limit the search to a specific category. - * - * @param Category $category - * - * @return GroupCollectorInterface - */ - public function setCategory(Category $category): GroupCollectorInterface - { - $this->withCategoryInformation(); - $this->query->where('categories.id', $category->id); - - return $this; - } - - /** - * @inheritDoc - */ - public function setExternalId(string $externalId): GroupCollectorInterface - { - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'external_id'); - $this->query->where('journal_meta.data', '=', sprintf('%s', json_encode($externalId))); - - return $this; - } - - /** - * @inheritDoc - */ - public function setExternalUrl(string $url): GroupCollectorInterface - { - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'external_url'); - $this->query->where('journal_meta.data', '=', json_encode($url)); - - return $this; - } - - /** - * @inheritDoc - */ - public function setInternalReference(string $internalReference): GroupCollectorInterface - { - $internalReference = (string)json_encode($internalReference); - $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); - - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'internal_reference'); - $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $internalReference)); - - return $this; - } - - /** - * @inheritDoc - */ - public function setRecurrenceId(string $recurringId): GroupCollectorInterface - { - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'recurrence_id'); - $this->query->where('journal_meta.data', '=', sprintf('%s', json_encode($recurringId))); - - return $this; - } - - /** - * @inheritDoc - */ - public function setSepaCT(string $sepaCT): GroupCollectorInterface - { - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'sepa_ct_id'); - $this->query->where('journal_meta.data', '=', sprintf('%s', json_encode($sepaCT))); - $this->query->whereNull('journal_meta.deleted_at'); - - return $this; - } - - /** - * Limit results to a specific tag. - * - * @param Tag $tag - * - * @return GroupCollectorInterface - */ - public function setTag(Tag $tag): GroupCollectorInterface - { - $this->withTagInformation(); - $this->query->where('tag_transaction_journal.tag_id', $tag->id); - - return $this; - } - - /** - * Limit results to a specific set of tags. - * - * @param Collection $tags - * - * @return GroupCollectorInterface - */ - public function setTags(Collection $tags): GroupCollectorInterface - { - $this->withTagInformation(); - $this->tags = array_merge($this->tags, $tags->pluck('id')->toArray()); - $this->query->whereIn('tag_transaction_journal.tag_id', $this->tags); - - return $this; - } - - /** - * Without tags - * - * @param Collection $tags - * - * @return GroupCollectorInterface - */ - public function setWithoutSpecificTags(Collection $tags): GroupCollectorInterface - { - $this->withTagInformation(); - - // this method adds a "postFilter" to the collector. - $list = $tags->pluck('tag')->toArray(); - $filter = static function (array $object) use ($list): bool { - foreach ($object['transactions'] as $transaction) { - app('log')->debug(sprintf('Transaction has %d tag(s)', count($transaction['tags']))); - foreach ($transaction['tags'] as $tag) { - if (in_array($tag['name'], $list, true)) { - return false; - } - } - } - return true; - }; - $this->postFilters[] = $filter; - - - return $this; - } - - /** - * @return GroupCollectorInterface - */ - public function withAnyNotes(): GroupCollectorInterface - { - $this->withNotes(); - $this->query->whereNotNull('notes.text'); - - return $this; - } - - /** - * Limit results to transactions without a bill. - * - * @return GroupCollectorInterface - */ - public function withBill(): GroupCollectorInterface - { - $this->withBillInformation(); - $this->query->whereNotNull('transaction_journals.bill_id'); - - return $this; - } - - /** - * Limit results to a transactions without a budget.. - * - * @return GroupCollectorInterface - */ - public function withBudget(): GroupCollectorInterface - { - $this->withBudgetInformation(); - $this->query->whereNotNull('budget_transaction_journal.budget_id'); - - return $this; - } - - /** - * Limit results to a transactions without a category. - * - * @return GroupCollectorInterface - */ - public function withCategory(): GroupCollectorInterface - { - $this->withCategoryInformation(); - $this->query->whereNotNull('category_transaction_journal.category_id'); - - return $this; - } - - /** - * @inheritDoc - */ - public function withExternalId(): GroupCollectorInterface - { - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'external_id'); - $this->query->whereNotNull('journal_meta.data'); - - return $this; - } - - /** - * @inheritDoc - */ - public function withExternalUrl(): GroupCollectorInterface - { - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', 'external_url'); - $this->query->whereNotNull('journal_meta.data'); - - 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.. - * - * @return GroupCollectorInterface - */ - public function withoutBudget(): GroupCollectorInterface - { - $this->withBudgetInformation(); - $this->query->whereNull('budget_transaction_journal.budget_id'); - - return $this; - } - - /** - * Limit results to a transactions without a category. - * - * @return GroupCollectorInterface - */ - public function withoutCategory(): GroupCollectorInterface - { - $this->withCategoryInformation(); - $this->query->whereNull('category_transaction_journal.category_id'); - - return $this; - } - - /** - * @inheritDoc - */ - public function withoutExternalId(): GroupCollectorInterface - { - $this->joinMetaDataTables(); - // TODO not sure if this will work properly. - $this->query->where(static function (Builder $q1) { // @phpstan-ignore-line - $q1->where(static function (Builder $q2) { - $q2->where('journal_meta.name', '=', 'external_id'); - $q2->whereNull('journal_meta.data'); - })->orWhere(static function (Builder $q3) { - $q3->where('journal_meta.name', '!=', 'external_id'); - })->orWhere(static function (Builder $q4) { - $q4->whereNull('journal_meta.name'); - }); - }); - - return $this; - } - - /** - * @inheritDoc - */ - public function withoutExternalUrl(): GroupCollectorInterface - { - $this->joinMetaDataTables(); - // TODO not sure if this will work properly. - $this->query->where(static function (Builder $q1) { // @phpstan-ignore-line - $q1->where(static function (Builder $q2) { - $q2->where('journal_meta.name', '=', 'external_url'); - $q2->whereNull('journal_meta.data'); - })->orWhere(static function (Builder $q3) { - $q3->where('journal_meta.name', '!=', 'external_url'); - })->orWhere(static function (Builder $q4) { - $q4->whereNull('journal_meta.name'); - }); - }); - - return $this; - } - - /** - * @return GroupCollectorInterface - */ - public function withoutNotes(): GroupCollectorInterface - { - $this->withNotes(); - $this->query->where(static function (Builder $q) { // @phpstan-ignore-line - $q->whereNull('notes.text'); - $q->orWhere('notes.text', ''); - }); - - return $this; - } - - /** - * Where has no tags. - * - * @return GroupCollectorInterface - */ - public function withoutTags(): GroupCollectorInterface - { - $this->withTagInformation(); - $this->query->whereNull('tag_transaction_journal.tag_id'); - - return $this; - } } diff --git a/app/Helpers/Collector/Extensions/TimeCollection.php b/app/Helpers/Collector/Extensions/TimeCollection.php index 90c88ada99..2f4d8a499a 100644 --- a/app/Helpers/Collector/Extensions/TimeCollection.php +++ b/app/Helpers/Collector/Extensions/TimeCollection.php @@ -32,57 +32,34 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; */ trait TimeCollection { - /** - * @param string $day - * - * @return GroupCollectorInterface - */ public function dayAfter(string $day): GroupCollectorInterface { $this->query->whereDay('transaction_journals.date', '>=', $day); + return $this; } - /** - * @param string $day - * - * @return GroupCollectorInterface - */ public function dayBefore(string $day): GroupCollectorInterface { $this->query->whereDay('transaction_journals.date', '<=', $day); + return $this; } - /** - * @param string $day - * - * @return GroupCollectorInterface - */ public function dayIs(string $day): GroupCollectorInterface { $this->query->whereDay('transaction_journals.date', '=', $day); + return $this; } - /** - * @param string $day - * - * @return GroupCollectorInterface - */ public function dayIsNot(string $day): GroupCollectorInterface { $this->query->whereDay('transaction_journals.date', '!=', $day); + return $this; } - /** - * @param Carbon $start - * @param Carbon $end - * @param string $field - * - * @return GroupCollectorInterface - */ public function excludeMetaDateRange(Carbon $start, Carbon $end, string $field): GroupCollectorInterface { if ($end < $start) { @@ -107,9 +84,6 @@ trait TimeCollection return $this; } - /** - * @inheritDoc - */ public function withMetaDate(string $field): GroupCollectorInterface { $this->joinMetaDataTables(); @@ -119,13 +93,6 @@ trait TimeCollection return $this; } - /** - * @param Carbon $start - * @param Carbon $end - * @param string $field - * - * @return GroupCollectorInterface - */ public function excludeObjectRange(Carbon $start, Carbon $end, string $field): GroupCollectorInterface { $after = $start->format('Y-m-d 00:00:00'); @@ -137,12 +104,6 @@ trait TimeCollection return $this; } - /** - * @param Carbon $start - * @param Carbon $end - * - * @return GroupCollectorInterface - */ public function excludeRange(Carbon $start, Carbon $end): GroupCollectorInterface { if ($end < $start) { @@ -157,12 +118,6 @@ trait TimeCollection return $this; } - /** - * @param string $day - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaDayAfter(string $day, string $field): GroupCollectorInterface { $this->withMetaDate($field); @@ -181,12 +136,6 @@ trait TimeCollection return $this; } - /** - * @param string $day - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaDayBefore(string $day, string $field): GroupCollectorInterface { $this->withMetaDate($field); @@ -205,12 +154,6 @@ trait TimeCollection return $this; } - /** - * @param string $day - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaDayIs(string $day, string $field): GroupCollectorInterface { $this->withMetaDate($field); @@ -225,15 +168,10 @@ trait TimeCollection return false; }; $this->postFilters[] = $filter; + return $this; } - /** - * @param string $day - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaDayIsNot(string $day, string $field): GroupCollectorInterface { $this->withMetaDate($field); @@ -248,15 +186,10 @@ trait TimeCollection return false; }; $this->postFilters[] = $filter; + return $this; } - /** - * @param string $month - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaMonthAfter(string $month, string $field): GroupCollectorInterface { $this->withMetaDate($field); @@ -275,12 +208,6 @@ trait TimeCollection return $this; } - /** - * @param string $month - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaMonthBefore(string $month, string $field): GroupCollectorInterface { $this->withMetaDate($field); @@ -299,12 +226,6 @@ trait TimeCollection return $this; } - /** - * @param string $month - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaMonthIs(string $month, string $field): GroupCollectorInterface { $this->withMetaDate($field); @@ -319,15 +240,10 @@ trait TimeCollection return false; }; $this->postFilters[] = $filter; + return $this; } - /** - * @param string $month - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaMonthIsNot(string $month, string $field): GroupCollectorInterface { $this->withMetaDate($field); @@ -342,15 +258,10 @@ trait TimeCollection return false; }; $this->postFilters[] = $filter; + return $this; } - /** - * @param string $year - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaYearAfter(string $year, string $field): GroupCollectorInterface { $this->withMetaDate($field); @@ -369,12 +280,6 @@ trait TimeCollection return $this; } - /** - * @param string $year - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaYearBefore(string $year, string $field): GroupCollectorInterface { $this->withMetaDate($field); @@ -393,12 +298,6 @@ trait TimeCollection return $this; } - /** - * @param string $year - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaYearIs(string $year, string $field): GroupCollectorInterface { $this->withMetaDate($field); @@ -417,12 +316,6 @@ trait TimeCollection return $this; } - /** - * @param string $year - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaYearIsNot(string $year, string $field): GroupCollectorInterface { $this->withMetaDate($field); @@ -433,6 +326,7 @@ trait TimeCollection return $year !== (string)$transaction[$field]->year; } } + return true; }; $this->postFilters[] = $filter; @@ -440,200 +334,120 @@ trait TimeCollection return $this; } - /** - * @param string $month - * - * @return GroupCollectorInterface - */ public function monthAfter(string $month): GroupCollectorInterface { $this->query->whereMonth('transaction_journals.date', '>=', $month); + return $this; } - /** - * @param string $month - * - * @return GroupCollectorInterface - */ public function monthBefore(string $month): GroupCollectorInterface { $this->query->whereMonth('transaction_journals.date', '<=', $month); + return $this; } - /** - * @param string $month - * - * @return GroupCollectorInterface - */ public function monthIs(string $month): GroupCollectorInterface { $this->query->whereMonth('transaction_journals.date', '=', $month); + return $this; } - /** - * @param string $month - * - * @return GroupCollectorInterface - */ public function monthIsNot(string $month): GroupCollectorInterface { $this->query->whereMonth('transaction_journals.date', '!=', $month); + return $this; } - /** - * @param string $day - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectDayAfter(string $day, string $field): GroupCollectorInterface { $this->query->whereDay(sprintf('transaction_journals.%s', $field), '>=', $day); + return $this; } - /** - * @param string $day - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectDayBefore(string $day, string $field): GroupCollectorInterface { $this->query->whereDay(sprintf('transaction_journals.%s', $field), '<=', $day); + return $this; } - /** - * @param string $day - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectDayIs(string $day, string $field): GroupCollectorInterface { $this->query->whereDay(sprintf('transaction_journals.%s', $field), '=', $day); + return $this; } - /** - * @param string $day - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectDayIsNot(string $day, string $field): GroupCollectorInterface { $this->query->whereDay(sprintf('transaction_journals.%s', $field), '!=', $day); + return $this; } - /** - * @param string $month - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectMonthAfter(string $month, string $field): GroupCollectorInterface { $this->query->whereMonth(sprintf('transaction_journals.%s', $field), '>=', $month); + return $this; } - /** - * @param string $month - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectMonthBefore(string $month, string $field): GroupCollectorInterface { $this->query->whereMonth(sprintf('transaction_journals.%s', $field), '<=', $month); + return $this; } - /** - * @param string $month - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectMonthIs(string $month, string $field): GroupCollectorInterface { $this->query->whereMonth(sprintf('transaction_journals.%s', $field), '=', $month); + return $this; } - /** - * @param string $month - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectMonthIsNot(string $month, string $field): GroupCollectorInterface { $this->query->whereMonth(sprintf('transaction_journals.%s', $field), '!=', $month); + return $this; } - /** - * @param string $year - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectYearAfter(string $year, string $field): GroupCollectorInterface { $this->query->whereYear(sprintf('transaction_journals.%s', $field), '>=', $year); + return $this; } - /** - * @param string $year - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectYearBefore(string $year, string $field): GroupCollectorInterface { $this->query->whereYear(sprintf('transaction_journals.%s', $field), '<=', $year); + return $this; } - /** - * @param string $year - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectYearIs(string $year, string $field): GroupCollectorInterface { $this->query->whereYear(sprintf('transaction_journals.%s', $field), '=', $year); + return $this; } - /** - * @param string $year - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectYearIsNot(string $year, string $field): GroupCollectorInterface { $this->query->whereYear(sprintf('transaction_journals.%s', $field), '!=', $year); + return $this; } /** * Collect transactions after a specific date. - * - * @param Carbon $date - * - * @return GroupCollectorInterface */ public function setAfter(Carbon $date): GroupCollectorInterface { @@ -645,10 +459,6 @@ trait TimeCollection /** * Collect transactions before a specific date. - * - * @param Carbon $date - * - * @return GroupCollectorInterface */ public function setBefore(Carbon $date): GroupCollectorInterface { @@ -660,10 +470,6 @@ trait TimeCollection /** * Collect transactions created on a specific date. - * - * @param Carbon $date - * - * @return GroupCollectorInterface */ public function setCreatedAt(Carbon $date): GroupCollectorInterface { @@ -677,10 +483,6 @@ trait TimeCollection /** * Set the end time of the results to return. - * - * @param Carbon $end - * - * @return GroupCollectorInterface */ public function setEnd(Carbon $end): GroupCollectorInterface { @@ -692,12 +494,6 @@ trait TimeCollection return $this; } - /** - * @param Carbon $date - * @param string $field - * - * @return GroupCollectorInterface - */ public function setMetaAfter(Carbon $date, string $field): GroupCollectorInterface { $this->withMetaDate($field); @@ -717,12 +513,6 @@ trait TimeCollection return $this; } - /** - * @param Carbon $date - * @param string $field - * - * @return GroupCollectorInterface - */ public function setMetaBefore(Carbon $date, string $field): GroupCollectorInterface { $this->withMetaDate($field); @@ -741,13 +531,6 @@ trait TimeCollection return $this; } - /** - * @param Carbon $start - * @param Carbon $end - * @param string $field - * - * @return GroupCollectorInterface - */ public function setMetaDateRange(Carbon $start, Carbon $end, string $field): GroupCollectorInterface { if ($end < $start) { @@ -769,15 +552,10 @@ trait TimeCollection return false; }; $this->postFilters[] = $filter; + return $this; } - /** - * @param Carbon $date - * @param string $field - * - * @return GroupCollectorInterface - */ public function setObjectAfter(Carbon $date, string $field): GroupCollectorInterface { $afterStr = $date->format('Y-m-d 00:00:00'); @@ -786,26 +564,14 @@ trait TimeCollection return $this; } - /** - * @param Carbon $date - * @param string $field - * - * @return GroupCollectorInterface - */ public function setObjectBefore(Carbon $date, string $field): GroupCollectorInterface { $afterStr = $date->format('Y-m-d 00:00:00'); $this->query->where(sprintf('transaction_journals.%s', $field), '<=', $afterStr); + return $this; } - /** - * @param Carbon $start - * @param Carbon $end - * @param string $field - * - * @return GroupCollectorInterface - */ public function setObjectRange(Carbon $start, Carbon $end, string $field): GroupCollectorInterface { $after = $start->format('Y-m-d 00:00:00'); @@ -820,11 +586,6 @@ trait TimeCollection * Set the start and end time of the results to return. * * Can either or both be NULL - * - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return GroupCollectorInterface */ public function setRange(?Carbon $start, ?Carbon $end): GroupCollectorInterface { @@ -847,10 +608,6 @@ trait TimeCollection /** * Set the start time of the results to return. - * - * @param Carbon $start - * - * @return GroupCollectorInterface */ public function setStart(Carbon $start): GroupCollectorInterface { @@ -863,10 +620,6 @@ trait TimeCollection /** * Collect transactions updated on a specific date. - * - * @param Carbon $date - * - * @return GroupCollectorInterface */ public function setUpdatedAt(Carbon $date): GroupCollectorInterface { @@ -878,47 +631,31 @@ trait TimeCollection return $this; } - /** - * @param string $year - * - * @return GroupCollectorInterface - */ public function yearAfter(string $year): GroupCollectorInterface { $this->query->whereYear('transaction_journals.date', '>=', $year); + return $this; } - /** - * @param string $year - * - * @return GroupCollectorInterface - */ public function yearBefore(string $year): GroupCollectorInterface { $this->query->whereYear('transaction_journals.date', '<=', $year); + return $this; } - /** - * @param string $year - * - * @return GroupCollectorInterface - */ public function yearIs(string $year): GroupCollectorInterface { $this->query->whereYear('transaction_journals.date', '=', $year); + return $this; } - /** - * @param string $year - * - * @return GroupCollectorInterface - */ public function yearIsNot(string $year): GroupCollectorInterface { $this->query->whereYear('transaction_journals.date', '!=', $year); + return $this; } } diff --git a/app/Helpers/Collector/GroupCollector.php b/app/Helpers/Collector/GroupCollector.php index 60a3fe14a1..7f8355fa81 100644 --- a/app/Helpers/Collector/GroupCollector.php +++ b/app/Helpers/Collector/GroupCollector.php @@ -25,7 +25,6 @@ namespace FireflyIII\Helpers\Collector; use Carbon\Carbon; use Carbon\Exceptions\InvalidFormatException; -use Closure; use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\Extensions\AccountCollection; @@ -47,8 +46,6 @@ use Illuminate\Support\Collection; /** * Class GroupCollector - * - */ class GroupCollector implements GroupCollectorInterface { @@ -101,7 +98,7 @@ class GroupCollector implements GroupCollectorInterface $this->stringFields = ['amount', 'foreign_amount']; $this->total = 0; $this->fields = [ - # group + // group 'transaction_groups.id as transaction_group_id', 'transaction_groups.user_id as user_id', 'transaction_groups.user_group_id as user_group_id', @@ -109,22 +106,22 @@ class GroupCollector implements GroupCollectorInterface 'transaction_groups.updated_at as updated_at', 'transaction_groups.title as transaction_group_title', - # journal + // journal 'transaction_journals.id as transaction_journal_id', 'transaction_journals.transaction_type_id', 'transaction_journals.description', 'transaction_journals.date', 'transaction_journals.order', - # types + // types 'transaction_types.type as transaction_type_type', - # source info (always present) + // source info (always present) 'source.id as source_transaction_id', 'source.account_id as source_account_id', 'source.reconciled', - # currency info: + // currency info: 'source.amount as amount', 'source.transaction_currency_id as currency_id', 'currency.code as currency_code', @@ -132,7 +129,7 @@ class GroupCollector implements GroupCollectorInterface 'currency.symbol as currency_symbol', 'currency.decimal_places as currency_decimal_places', - # foreign currency info + // foreign currency info 'source.foreign_amount as foreign_amount', 'source.foreign_currency_id as foreign_currency_id', 'foreign_currency.code as foreign_currency_code', @@ -140,20 +137,17 @@ class GroupCollector implements GroupCollectorInterface 'foreign_currency.symbol as foreign_currency_symbol', 'foreign_currency.decimal_places as foreign_currency_decimal_places', - # destination account info (always present) + // destination account info (always present) 'destination.account_id as destination_account_id', ]; } - /** - * @inheritDoc - */ public function descriptionDoesNotEnd(array $array): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($array) { // @phpstan-ignore-line + static function (EloquentBuilder $q) use ($array): void { // @phpstan-ignore-line $q->where( - static function (EloquentBuilder $q1) use ($array) { + static function (EloquentBuilder $q1) use ($array): void { foreach ($array as $word) { $keyword = sprintf('%%%s', $word); $q1->where('transaction_journals.description', 'NOT LIKE', $keyword); @@ -161,7 +155,7 @@ class GroupCollector implements GroupCollectorInterface } ); $q->where( - static function (EloquentBuilder $q2) use ($array) { + static function (EloquentBuilder $q2) use ($array): void { foreach ($array as $word) { $keyword = sprintf('%%%s', $word); $q2->where('transaction_groups.title', 'NOT LIKE', $keyword); @@ -175,15 +169,12 @@ class GroupCollector implements GroupCollectorInterface return $this; } - /** - * @inheritDoc - */ public function descriptionDoesNotStart(array $array): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($array) { // @phpstan-ignore-line + static function (EloquentBuilder $q) use ($array): void { // @phpstan-ignore-line $q->where( - static function (EloquentBuilder $q1) use ($array) { + static function (EloquentBuilder $q1) use ($array): void { foreach ($array as $word) { $keyword = sprintf('%s%%', $word); $q1->where('transaction_journals.description', 'NOT LIKE', $keyword); @@ -191,7 +182,7 @@ class GroupCollector implements GroupCollectorInterface } ); $q->where( - static function (EloquentBuilder $q2) use ($array) { + static function (EloquentBuilder $q2) use ($array): void { foreach ($array as $word) { $keyword = sprintf('%s%%', $word); $q2->where('transaction_groups.title', 'NOT LIKE', $keyword); @@ -205,15 +196,12 @@ class GroupCollector implements GroupCollectorInterface return $this; } - /** - * @inheritDoc - */ public function descriptionEnds(array $array): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($array) { // @phpstan-ignore-line + static function (EloquentBuilder $q) use ($array): void { // @phpstan-ignore-line $q->where( - static function (EloquentBuilder $q1) use ($array) { + static function (EloquentBuilder $q1) use ($array): void { foreach ($array as $word) { $keyword = sprintf('%%%s', $word); $q1->where('transaction_journals.description', 'LIKE', $keyword); @@ -221,7 +209,7 @@ class GroupCollector implements GroupCollectorInterface } ); $q->orWhere( - static function (EloquentBuilder $q2) use ($array) { + static function (EloquentBuilder $q2) use ($array): void { foreach ($array as $word) { $keyword = sprintf('%%%s', $word); $q2->where('transaction_groups.title', 'LIKE', $keyword); @@ -234,13 +222,10 @@ class GroupCollector implements GroupCollectorInterface return $this; } - /** - * @inheritDoc - */ public function descriptionIs(string $value): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($value) { // @phpstan-ignore-line + static function (EloquentBuilder $q) use ($value): void { // @phpstan-ignore-line $q->where('transaction_journals.description', '=', $value); $q->orWhere('transaction_groups.title', '=', $value); } @@ -249,16 +234,13 @@ class GroupCollector implements GroupCollectorInterface return $this; } - /** - * @inheritDoc - */ public function descriptionIsNot(string $value): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($value) { // @phpstan-ignore-line + static function (EloquentBuilder $q) use ($value): void { // @phpstan-ignore-line $q->where('transaction_journals.description', '!=', $value); $q->where( - static function (EloquentBuilder $q2) use ($value) { + static function (EloquentBuilder $q2) use ($value): void { $q2->where('transaction_groups.title', '!=', $value); $q2->orWhereNull('transaction_groups.title'); } @@ -269,15 +251,12 @@ class GroupCollector implements GroupCollectorInterface return $this; } - /** - * @inheritDoc - */ public function descriptionStarts(array $array): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($array) { // @phpstan-ignore-line + static function (EloquentBuilder $q) use ($array): void { // @phpstan-ignore-line $q->where( - static function (EloquentBuilder $q1) use ($array) { + static function (EloquentBuilder $q1) use ($array): void { foreach ($array as $word) { $keyword = sprintf('%s%%', $word); $q1->where('transaction_journals.description', 'LIKE', $keyword); @@ -285,7 +264,7 @@ class GroupCollector implements GroupCollectorInterface } ); $q->orWhere( - static function (EloquentBuilder $q2) use ($array) { + static function (EloquentBuilder $q2) use ($array): void { foreach ($array as $word) { $keyword = sprintf('%s%%', $word); $q2->where('transaction_groups.title', 'LIKE', $keyword); @@ -298,9 +277,6 @@ class GroupCollector implements GroupCollectorInterface return $this; } - /** - * - */ public function dumpQuery(): void { $query = $this->query->select($this->fields)->toSql(); @@ -311,7 +287,7 @@ class GroupCollector implements GroupCollectorInterface $replace = (string)$param; } $pos = strpos($query, '?'); - if ($pos !== false) { + if (false !== $pos) { $query = substr_replace($query, $replace, $pos, 1); } } @@ -322,9 +298,6 @@ class GroupCollector implements GroupCollectorInterface echo ''; } - /** - * - */ public function dumpQueryInLogs(): void { app('log')->debug($this->query->select($this->fields)->toSql()); @@ -333,18 +306,14 @@ class GroupCollector implements GroupCollectorInterface /** * Limit results to NOT a specific currency, either foreign or normal one. - * - * @param TransactionCurrency $currency - * - * @return GroupCollectorInterface */ public function excludeCurrency(TransactionCurrency $currency): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($currency) { // @phpstan-ignore-line + static function (EloquentBuilder $q) use ($currency): void { // @phpstan-ignore-line $q->where('source.transaction_currency_id', '!=', $currency->id); $q->where( - static function (EloquentBuilder $q2) use ($currency) { + static function (EloquentBuilder $q2) use ($currency): void { $q2->where('source.foreign_currency_id', '!=', $currency->id); $q2->orWhereNull('source.foreign_currency_id'); } @@ -355,12 +324,9 @@ class GroupCollector implements GroupCollectorInterface return $this; } - /** - * @inheritDoc - */ public function excludeForeignCurrency(TransactionCurrency $currency): GroupCollectorInterface { - $this->query->where(static function (EloquentBuilder $q2) use ($currency) { // @phpstan-ignore-line + $this->query->where(static function (EloquentBuilder $q2) use ($currency): void { // @phpstan-ignore-line $q2->where('source.foreign_currency_id', '!=', $currency->id); $q2->orWhereNull('source.foreign_currency_id'); }); @@ -370,10 +336,6 @@ class GroupCollector implements GroupCollectorInterface /** * Limit the result to NOT a set of specific transaction groups. - * - * @param array $groupIds - * - * @return GroupCollectorInterface */ public function excludeIds(array $groupIds): GroupCollectorInterface { @@ -384,10 +346,6 @@ class GroupCollector implements GroupCollectorInterface /** * Limit the result to NOT a set of specific journals. - * - * @param array $journalIds - * - * @return GroupCollectorInterface */ public function excludeJournalIds(array $journalIds): GroupCollectorInterface { @@ -395,7 +353,6 @@ class GroupCollector implements GroupCollectorInterface // make all integers. $integerIDs = array_map('intval', $journalIds); - $this->query->whereNotIn('transaction_journals.id', $integerIDs); } @@ -404,10 +361,6 @@ class GroupCollector implements GroupCollectorInterface /** * Search for words in descriptions. - * - * @param array $array - * - * @return GroupCollectorInterface */ public function excludeSearchWords(array $array): GroupCollectorInterface { @@ -415,9 +368,9 @@ class GroupCollector implements GroupCollectorInterface return $this; } $this->query->where( - static function (EloquentBuilder $q) use ($array) { // @phpstan-ignore-line + static function (EloquentBuilder $q) use ($array): void { // @phpstan-ignore-line $q->where( - static function (EloquentBuilder $q1) use ($array) { + static function (EloquentBuilder $q1) use ($array): void { foreach ($array as $word) { $keyword = sprintf('%%%s%%', $word); $q1->where('transaction_journals.description', 'NOT LIKE', $keyword); @@ -425,7 +378,7 @@ class GroupCollector implements GroupCollectorInterface } ); $q->where( - static function (EloquentBuilder $q2) use ($array) { + static function (EloquentBuilder $q2) use ($array): void { foreach ($array as $word) { $keyword = sprintf('%%%s%%', $word); $q2->where('transaction_groups.title', 'NOT LIKE', $keyword); @@ -439,9 +392,6 @@ class GroupCollector implements GroupCollectorInterface return $this; } - /** - * @inheritDoc - */ public function excludeTypes(array $types): GroupCollectorInterface { $this->query->whereNotIn('transaction_types.type', $types); @@ -449,9 +399,6 @@ class GroupCollector implements GroupCollectorInterface return $this; } - /** - * @inheritDoc - */ public function exists(): GroupCollectorInterface { $this->query->whereNull('transaction_groups.deleted_at'); @@ -459,12 +406,10 @@ class GroupCollector implements GroupCollectorInterface 'transaction_types.type', [TransactionType::LIABILITY_CREDIT, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION] ); + return $this; } - /** - * @inheritDoc - */ public function findNothing(): GroupCollectorInterface { $this->query->where('transaction_groups.id', -1); @@ -472,9 +417,6 @@ class GroupCollector implements GroupCollectorInterface return $this; } - /** - * @return bool - */ public function getExpandGroupSearch(): bool { return $this->expandGroupSearch; @@ -482,13 +424,12 @@ class GroupCollector implements GroupCollectorInterface /** * Return the transaction journals without group information. Is useful in some instances. - * - * @return array */ public function getExtractedJournals(): array { $selection = $this->getGroups(); $return = []; + /** @var array $group */ foreach ($selection as $group) { $count = count($group['transactions']); @@ -504,8 +445,6 @@ class GroupCollector implements GroupCollectorInterface /** * Return the groups. - * - * @return Collection */ public function getGroups(): Collection { @@ -537,22 +476,217 @@ class GroupCollector implements GroupCollectorInterface } /** - * @return array + * Same as getGroups but everything is in a paginator. */ + public function getPaginatedGroups(): LengthAwarePaginator + { + $set = $this->getGroups(); + if (0 === $this->limit) { + $this->setLimit(50); + } + + return new LengthAwarePaginator($set, $this->total, $this->limit, $this->page); + } + + /** + * Limit the number of returned entries. + */ + public function setLimit(int $limit): GroupCollectorInterface + { + $this->limit = $limit; + // app('log')->debug(sprintf('GroupCollector: The limit is now %d', $limit)); + + return $this; + } + + public function isNotReconciled(): GroupCollectorInterface + { + $this->query->where('source.reconciled', 0)->where('destination.reconciled', 0); + + return $this; + } + + public function isReconciled(): GroupCollectorInterface + { + $this->query->where('source.reconciled', 1)->where('destination.reconciled', 1); + + return $this; + } + + /** + * Limit results to a specific currency, either foreign or normal one. + */ + public function setCurrency(TransactionCurrency $currency): GroupCollectorInterface + { + $this->query->where( + static function (EloquentBuilder $q) use ($currency): void { // @phpstan-ignore-line + $q->where('source.transaction_currency_id', $currency->id); + $q->orWhere('source.foreign_currency_id', $currency->id); + } + ); + + return $this; + } + + public function setExpandGroupSearch(bool $expandGroupSearch): GroupCollectorInterface + { + $this->expandGroupSearch = $expandGroupSearch; + + return $this; + } + + public function setForeignCurrency(TransactionCurrency $currency): GroupCollectorInterface + { + $this->query->where('source.foreign_currency_id', $currency->id); + + return $this; + } + + /** + * Limit the result to a set of specific transaction groups. + */ + public function setIds(array $groupIds): GroupCollectorInterface + { + $this->query->whereIn('transaction_groups.id', $groupIds); + + return $this; + } + + /** + * Limit the result to a set of specific journals. + */ + public function setJournalIds(array $journalIds): GroupCollectorInterface + { + if (0 !== count($journalIds)) { + // make all integers. + $integerIDs = array_map('intval', $journalIds); + + $this->query->whereIn('transaction_journals.id', $integerIDs); + } + + return $this; + } + + /** + * Set the page to get. + */ + public function setPage(int $page): GroupCollectorInterface + { + $page = 0 === $page ? 1 : $page; + $this->page = $page; + // app('log')->debug(sprintf('GroupCollector: page is now %d', $page)); + + return $this; + } + + /** + * Search for words in descriptions. + */ + public function setSearchWords(array $array): GroupCollectorInterface + { + if (0 === count($array)) { + return $this; + } + $this->query->where( + static function (EloquentBuilder $q) use ($array): void { // @phpstan-ignore-line + $q->where( + static function (EloquentBuilder $q1) use ($array): void { + foreach ($array as $word) { + $keyword = sprintf('%%%s%%', $word); + $q1->where('transaction_journals.description', 'LIKE', $keyword); + } + } + ); + $q->orWhere( + static function (EloquentBuilder $q2) use ($array): void { + foreach ($array as $word) { + $keyword = sprintf('%%%s%%', $word); + $q2->where('transaction_groups.title', 'LIKE', $keyword); + } + } + ); + } + ); + + return $this; + } + + /** + * Limit the search to one specific transaction group. + */ + public function setTransactionGroup(TransactionGroup $transactionGroup): GroupCollectorInterface + { + $this->query->where('transaction_groups.id', $transactionGroup->id); + + return $this; + } + + /** + * Limit the included transaction types. + */ + public function setTypes(array $types): GroupCollectorInterface + { + $this->query->whereIn('transaction_types.type', $types); + + return $this; + } + + /** + * Set the user object and start the query. + */ + public function setUser(User $user): GroupCollectorInterface + { + if (null === $this->user) { + $this->user = $user; + $this->startQuery(); + } + + return $this; + } + + /** + * Set the user object and start the query. + */ + public function setUserGroup(UserGroup $userGroup): GroupCollectorInterface + { + if (null === $this->userGroup) { + $this->userGroup = $userGroup; + $this->startQueryForGroup(); + } + + return $this; + } + + /** + * Automatically include all stuff required to make API calls work. + */ + public function withAPIInformation(): GroupCollectorInterface + { + // include source + destination account name and type. + $this->withAccountInformation() + // include category ID + name (if any) + ->withCategoryInformation() + // include budget ID + name (if any) + ->withBudgetInformation() + // include bill ID + name (if any) + ->withBillInformation() + ; + + return $this; + } + private function getCollectedGroupIds(): array { return $this->query->get(['transaction_journals.transaction_group_id'])->pluck('transaction_group_id')->toArray(); } /** - * @param Collection $collection - * - * @return Collection * @throws FireflyException */ private function parseArray(Collection $collection): Collection { $groups = []; + /** @var TransactionJournal $augumentedJournal */ foreach ($collection as $augumentedJournal) { $groupId = (int)$augumentedJournal->transaction_group_id; @@ -575,6 +709,7 @@ class GroupCollector implements GroupCollectorInterface $journalId = (int)$augumentedJournal->transaction_journal_id; // @phpstan-ignore-line $groupArray['transactions'][$journalId] = $parsedGroup; $groups[$groupId] = $groupArray; + continue; } // or parse the rest. @@ -588,7 +723,7 @@ class GroupCollector implements GroupCollectorInterface if (!array_key_exists($journalId, $groups[$groupId]['transactions'])) { // create second, third, fourth split: - $groups[$groupId]['count']++; + ++$groups[$groupId]['count']; $groups[$groupId]['transactions'][$journalId] = $this->parseAugmentedJournal($augumentedJournal); } } @@ -599,9 +734,6 @@ class GroupCollector implements GroupCollectorInterface } /** - * @param TransactionJournal $augumentedJournal - * - * @return array * @throws FireflyException */ private function parseAugmentedJournal(TransactionJournal $augumentedJournal): array @@ -615,6 +747,7 @@ class GroupCollector implements GroupCollectorInterface $result['book_date'] = null; $result['due_date'] = null; $result['process_date'] = null; + try { $result['date'] = new Carbon($result['date'], 'UTC'); $result['created_at'] = new Carbon($result['created_at'], 'UTC'); @@ -624,8 +757,9 @@ class GroupCollector implements GroupCollectorInterface $result['date']->setTimezone(config('app.timezone')); $result['created_at']->setTimezone(config('app.timezone')); $result['updated_at']->setTimezone(config('app.timezone')); - } catch (Exception $e) { // intentional generic exception + } catch (\Exception $e) { // intentional generic exception app('log')->error($e->getMessage()); + throw new FireflyException($e->getMessage(), 0, $e); } @@ -648,6 +782,7 @@ class GroupCollector implements GroupCollectorInterface if (array_key_exists('tag_id', $result) && null !== $result['tag_id']) { // assume the other fields are present as well. $tagId = (int)$augumentedJournal['tag_id']; $tagDate = null; + try { $tagDate = Carbon::parse($augumentedJournal['tag_date']); } catch (InvalidFormatException $e) { @@ -680,7 +815,6 @@ class GroupCollector implements GroupCollectorInterface $result['tag_name'], $result['tag_date'], $result['tag_description'], $result['tag_latitude'], $result['tag_longitude'], $result['tag_zoom_level'], $result['attachment_filename'], $result['attachment_id'] - ); return $result; @@ -688,10 +822,6 @@ class GroupCollector implements GroupCollectorInterface /** * Convert a selected set of fields to arrays. - * - * @param array $array - * - * @return array */ private function convertToInteger(array $array): array { @@ -702,11 +832,6 @@ class GroupCollector implements GroupCollectorInterface return $array; } - /** - * @param array $array - * - * @return array - */ private function convertToStrings(array $array): array { foreach ($this->stringFields as $field) { @@ -716,12 +841,6 @@ class GroupCollector implements GroupCollectorInterface return $array; } - /** - * @param array $existingJournal - * @param TransactionJournal $newJournal - * - * @return array - */ private function mergeTags(array $existingJournal, TransactionJournal $newJournal): array { $newArray = $newJournal->toArray(); @@ -729,6 +848,7 @@ class GroupCollector implements GroupCollectorInterface $tagId = (int)$newJournal['tag_id']; $tagDate = null; + try { $tagDate = Carbon::parse($newArray['tag_date']); } catch (InvalidFormatException $e) { @@ -746,12 +866,6 @@ class GroupCollector implements GroupCollectorInterface return $existingJournal; } - /** - * @param array $existingJournal - * @param TransactionJournal $newJournal - * - * @return array - */ private function mergeAttachments(array $existingJournal, TransactionJournal $newJournal): array { $newArray = $newJournal->toArray(); @@ -766,11 +880,6 @@ class GroupCollector implements GroupCollectorInterface return $existingJournal; } - /** - * @param array $groups - * - * @return array - */ private function parseSums(array $groups): array { /** @@ -814,24 +923,19 @@ class GroupCollector implements GroupCollectorInterface return $groups; } - /** - * @param Collection $collection - * - * @return Collection - */ private function postFilterCollection(Collection $collection): Collection { $currentCollection = $collection; app('log')->debug(sprintf('GroupCollector: postFilterCollection has %d filter(s) and %d transaction(s).', count($this->postFilters), count($currentCollection))); - /** - * @var Closure $function + * @var \Closure $function */ foreach ($this->postFilters as $function) { app('log')->debug('Applying filter...'); $nextCollection = new Collection(); + // loop everything in the current collection // and save it (or not) in the new collection. // that new collection is the next current collection @@ -849,252 +953,38 @@ class GroupCollector implements GroupCollectorInterface $currentCollection = $nextCollection; app('log')->debug(sprintf('GroupCollector: postFilterCollection has %d transaction(s) left.', count($currentCollection))); } + return $currentCollection; } - /** - * Same as getGroups but everything is in a paginator. - * - * @return LengthAwarePaginator - */ - public function getPaginatedGroups(): LengthAwarePaginator - { - $set = $this->getGroups(); - if (0 === $this->limit) { - $this->setLimit(50); - } - - return new LengthAwarePaginator($set, $this->total, $this->limit, $this->page); - } - - /** - * Limit the number of returned entries. - * - * @param int $limit - * - * @return GroupCollectorInterface - */ - public function setLimit(int $limit): GroupCollectorInterface - { - $this->limit = $limit; - //app('log')->debug(sprintf('GroupCollector: The limit is now %d', $limit)); - - return $this; - } - - /** - * @inheritDoc - */ - public function isNotReconciled(): GroupCollectorInterface - { - $this->query->where('source.reconciled', 0)->where('destination.reconciled', 0); - return $this; - } - - /** - * @inheritDoc - */ - public function isReconciled(): GroupCollectorInterface - { - $this->query->where('source.reconciled', 1)->where('destination.reconciled', 1); - return $this; - } - - /** - * Limit results to a specific currency, either foreign or normal one. - * - * @param TransactionCurrency $currency - * - * @return GroupCollectorInterface - */ - public function setCurrency(TransactionCurrency $currency): GroupCollectorInterface - { - $this->query->where( - static function (EloquentBuilder $q) use ($currency) { // @phpstan-ignore-line - $q->where('source.transaction_currency_id', $currency->id); - $q->orWhere('source.foreign_currency_id', $currency->id); - } - ); - - return $this; - } - - /** - * @param bool $expandGroupSearch - */ - public function setExpandGroupSearch(bool $expandGroupSearch): GroupCollectorInterface - { - $this->expandGroupSearch = $expandGroupSearch; - return $this; - } - - /** - * @inheritDoc - */ - public function setForeignCurrency(TransactionCurrency $currency): GroupCollectorInterface - { - $this->query->where('source.foreign_currency_id', $currency->id); - - return $this; - } - - /** - * Limit the result to a set of specific transaction groups. - * - * @param array $groupIds - * - * @return GroupCollectorInterface - */ - public function setIds(array $groupIds): GroupCollectorInterface - { - $this->query->whereIn('transaction_groups.id', $groupIds); - - return $this; - } - - /** - * Limit the result to a set of specific journals. - * - * @param array $journalIds - * - * @return GroupCollectorInterface - */ - public function setJournalIds(array $journalIds): GroupCollectorInterface - { - if (0 !== count($journalIds)) { - // make all integers. - $integerIDs = array_map('intval', $journalIds); - - - $this->query->whereIn('transaction_journals.id', $integerIDs); - } - - return $this; - } - - /** - * Set the page to get. - * - * @param int $page - * - * @return GroupCollectorInterface - */ - public function setPage(int $page): GroupCollectorInterface - { - $page = 0 === $page ? 1 : $page; - $this->page = $page; - //app('log')->debug(sprintf('GroupCollector: page is now %d', $page)); - - return $this; - } - - /** - * Search for words in descriptions. - * - * @param array $array - * - * @return GroupCollectorInterface - */ - public function setSearchWords(array $array): GroupCollectorInterface - { - if (0 === count($array)) { - return $this; - } - $this->query->where( - static function (EloquentBuilder $q) use ($array) { // @phpstan-ignore-line - $q->where( - static function (EloquentBuilder $q1) use ($array) { - foreach ($array as $word) { - $keyword = sprintf('%%%s%%', $word); - $q1->where('transaction_journals.description', 'LIKE', $keyword); - } - } - ); - $q->orWhere( - static function (EloquentBuilder $q2) use ($array) { - foreach ($array as $word) { - $keyword = sprintf('%%%s%%', $word); - $q2->where('transaction_groups.title', 'LIKE', $keyword); - } - } - ); - } - ); - - return $this; - } - - /** - * Limit the search to one specific transaction group. - * - * @param TransactionGroup $transactionGroup - * - * @return GroupCollectorInterface - */ - public function setTransactionGroup(TransactionGroup $transactionGroup): GroupCollectorInterface - { - $this->query->where('transaction_groups.id', $transactionGroup->id); - - return $this; - } - - /** - * Limit the included transaction types. - * - * @param array $types - * - * @return GroupCollectorInterface - */ - public function setTypes(array $types): GroupCollectorInterface - { - $this->query->whereIn('transaction_types.type', $types); - - return $this; - } - - /** - * Set the user object and start the query. - * - * @param User $user - * - * @return GroupCollectorInterface - */ - public function setUser(User $user): GroupCollectorInterface - { - if (null === $this->user) { - $this->user = $user; - $this->startQuery(); - } - - return $this; - } - /** * Build the query. */ 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') + // ->transactionGroups() + // ->leftJoin('transaction_journals', 'transaction_journals.transaction_group_id', 'transaction_groups.id') ->transactionJournals() ->leftJoin('transaction_groups', 'transaction_journals.transaction_group_id', 'transaction_groups.id') // join source transaction. ->leftJoin( 'transactions as source', - static function (JoinClause $join) { + static function (JoinClause $join): void { $join->on('source.transaction_journal_id', '=', 'transaction_journals.id') - ->where('source.amount', '<', 0); + ->where('source.amount', '<', 0) + ; } ) // join destination transaction ->leftJoin( 'transactions as destination', - static function (JoinClause $join) { + static function (JoinClause $join): void { $join->on('destination.transaction_journal_id', '=', 'transaction_journals.id') - ->where('destination.amount', '>', 0); + ->where('destination.amount', '>', 0) + ; } ) // left join transaction type. @@ -1109,24 +999,8 @@ class GroupCollector implements GroupCollectorInterface ->orderBy('transaction_journals.order', 'ASC') ->orderBy('transaction_journals.id', 'DESC') ->orderBy('transaction_journals.description', 'DESC') - ->orderBy('source.amount', 'DESC'); - } - - /** - * Set the user object and start the query. - * - * @param UserGroup $userGroup - * - * @return GroupCollectorInterface - */ - public function setUserGroup(UserGroup $userGroup): GroupCollectorInterface - { - if (null === $this->userGroup) { - $this->userGroup = $userGroup; - $this->startQueryForGroup(); - } - - return $this; + ->orderBy('source.amount', 'DESC') + ; } /** @@ -1134,7 +1008,7 @@ class GroupCollector implements GroupCollectorInterface */ private function startQueryForGroup(): void { - //app('log')->debug('GroupCollector::startQuery'); + // app('log')->debug('GroupCollector::startQuery'); $this->query = $this->userGroup ->transactionJournals() ->leftJoin('transaction_groups', 'transaction_journals.transaction_group_id', 'transaction_groups.id') @@ -1142,17 +1016,19 @@ class GroupCollector implements GroupCollectorInterface // join source transaction. ->leftJoin( 'transactions as source', - static function (JoinClause $join) { + static function (JoinClause $join): void { $join->on('source.transaction_journal_id', '=', 'transaction_journals.id') - ->where('source.amount', '<', 0); + ->where('source.amount', '<', 0) + ; } ) // join destination transaction ->leftJoin( 'transactions as destination', - static function (JoinClause $join) { + static function (JoinClause $join): void { $join->on('destination.transaction_journal_id', '=', 'transaction_journals.id') - ->where('destination.amount', '>', 0); + ->where('destination.amount', '>', 0) + ; } ) // left join transaction type. @@ -1167,25 +1043,7 @@ class GroupCollector implements GroupCollectorInterface ->orderBy('transaction_journals.order', 'ASC') ->orderBy('transaction_journals.id', 'DESC') ->orderBy('transaction_journals.description', 'DESC') - ->orderBy('source.amount', 'DESC'); - } - - /** - * Automatically include all stuff required to make API calls work. - * - * @return GroupCollectorInterface - */ - public function withAPIInformation(): GroupCollectorInterface - { - // include source + destination account name and type. - $this->withAccountInformation() - // include category ID + name (if any) - ->withCategoryInformation() - // include budget ID + name (if any) - ->withBudgetInformation() - // include bill ID + name (if any) - ->withBillInformation(); - - return $this; + ->orderBy('source.amount', 'DESC') + ; } } diff --git a/app/Helpers/Collector/GroupCollectorInterface.php b/app/Helpers/Collector/GroupCollectorInterface.php index 5ab713e689..2f6db27897 100644 --- a/app/Helpers/Collector/GroupCollectorInterface.php +++ b/app/Helpers/Collector/GroupCollectorInterface.php @@ -42,1504 +42,671 @@ interface GroupCollectorInterface { /** * Get transactions with a specific amount. - * - * @param string $amount - * - * @return GroupCollectorInterface */ public function amountIs(string $amount): self; - /** - * @param string $amount - * - * @return GroupCollectorInterface - */ public function amountIsNot(string $amount): self; /** * Get transactions where the amount is less than. - * - * @param string $amount - * - * @return GroupCollectorInterface */ public function amountLess(string $amount): self; /** * Get transactions where the foreign amount is more than. - * - * @param string $amount - * - * @return GroupCollectorInterface */ public function amountMore(string $amount): self; - /** - * @param string $name - * - * @return GroupCollectorInterface - */ public function attachmentNameContains(string $name): self; - /** - * @param string $name - * - * @return GroupCollectorInterface - */ public function attachmentNameDoesNotContain(string $name): self; - /** - * @param string $name - * - * @return GroupCollectorInterface - */ public function attachmentNameDoesNotEnd(string $name): self; - /** - * @param string $name - * - * @return GroupCollectorInterface - */ public function attachmentNameDoesNotStart(string $name): self; - /** - * @param string $name - * - * @return GroupCollectorInterface - */ public function attachmentNameEnds(string $name): self; - /** - * @param string $name - * - * @return GroupCollectorInterface - */ public function attachmentNameIs(string $name): self; - /** - * @param string $name - * - * @return GroupCollectorInterface - */ public function attachmentNameIsNot(string $name): self; - /** - * @param string $name - * - * @return GroupCollectorInterface - */ public function attachmentNameStarts(string $name): self; - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function attachmentNotesAre(string $value): self; - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function attachmentNotesAreNot(string $value): self; - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function attachmentNotesContains(string $value): self; - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function attachmentNotesDoNotContain(string $value): self; - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function attachmentNotesDoNotEnd(string $value): self; - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function attachmentNotesDoNotStart(string $value): self; - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function attachmentNotesEnds(string $value): self; - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function attachmentNotesStarts(string $value): self; - /** - * @param string $day - * - * @return GroupCollectorInterface - */ public function dayAfter(string $day): self; - /** - * @param string $day - * - * @return GroupCollectorInterface - */ public function dayBefore(string $day): self; - /** - * @param string $day - * - * @return GroupCollectorInterface - */ public function dayIs(string $day): self; - /** - * @param string $day - * - * @return GroupCollectorInterface - */ public function dayIsNot(string $day): self; /** * End of the description must not match: - * - * @param array $array - * - * @return GroupCollectorInterface */ public function descriptionDoesNotEnd(array $array): self; /** * Beginning of the description must not start with: - * - * @param array $array - * - * @return GroupCollectorInterface */ public function descriptionDoesNotStart(array $array): self; /** * End of the description must match: - * - * @param array $array - * - * @return GroupCollectorInterface */ public function descriptionEnds(array $array): self; /** * Description must be: - * - * @param string $value - * - * @return GroupCollectorInterface */ public function descriptionIs(string $value): self; /** * Description must not be: - * - * @param string $value - * - * @return GroupCollectorInterface */ public function descriptionIsNot(string $value): self; /** * Beginning of the description must match: - * - * @param array $array - * - * @return GroupCollectorInterface */ public function descriptionStarts(array $array): self; /** * These accounts must not be accounts. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function excludeAccounts(Collection $accounts): self; /** * Exclude a specific set of bills - * - * @param Collection $bills - * - * @return GroupCollectorInterface */ public function excludeBills(Collection $bills): self; /** * Exclude a budget - * - * @param Budget $budget - * - * @return GroupCollectorInterface */ public function excludeBudget(Budget $budget): self; /** * Exclude a budget. - * - * @param Collection $budgets - * - * @return GroupCollectorInterface */ public function excludeBudgets(Collection $budgets): self; /** * Exclude a set of categories. - * - * @param Collection $categories - * - * @return GroupCollectorInterface */ public function excludeCategories(Collection $categories): self; /** * Exclude a specific category - * - * @param Category $category - * - * @return GroupCollectorInterface */ public function excludeCategory(Category $category): self; /** * Limit results to NOT a specific currency, either foreign or normal one. - * - * @param TransactionCurrency $currency - * - * @return GroupCollectorInterface */ public function excludeCurrency(TransactionCurrency $currency): self; /** * Exclude destination accounts. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function excludeDestinationAccounts(Collection $accounts): self; /** * Look for specific external ID's. - * - * @param string $externalId - * - * @return GroupCollectorInterface */ public function excludeExternalId(string $externalId): self; - /** - * @param string $url - * - * @return GroupCollectorInterface - */ public function excludeExternalUrl(string $url): self; /** * Limit results to exclude a specific foreign currency. - * - * @param TransactionCurrency $currency - * - * @return GroupCollectorInterface */ public function excludeForeignCurrency(TransactionCurrency $currency): self; /** * Limit the result to NOT a set of specific transaction groups. - * - * @param array $groupIds - * - * @return GroupCollectorInterface */ public function excludeIds(array $groupIds): self; /** * Look for specific external ID's. - * - * @param string $internalReference - * - * @return GroupCollectorInterface */ public function excludeInternalReference(string $internalReference): self; /** * Limit the result to NOT a set of specific transaction journals. - * - * @param array $journalIds - * - * @return GroupCollectorInterface */ public function excludeJournalIds(array $journalIds): self; - /** - * @param Carbon $start - * @param Carbon $end - * @param string $field - * - * @return GroupCollectorInterface - */ public function excludeMetaDateRange(Carbon $start, Carbon $end, string $field): self; - /** - * @param Carbon $start - * @param Carbon $end - * @param string $field - * - * @return GroupCollectorInterface - */ public function excludeObjectRange(Carbon $start, Carbon $end, string $field): self; - /** - * @param Carbon $start - * @param Carbon $end - * - * @return GroupCollectorInterface - */ public function excludeRange(Carbon $start, Carbon $end): self; - /** - * @param string $recurringId - * - * @return GroupCollectorInterface - */ public function excludeRecurrenceId(string $recurringId): self; /** * Exclude words in descriptions. - * - * @param array $array - * - * @return GroupCollectorInterface */ public function excludeSearchWords(array $array): self; /** * These accounts must not be source accounts. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function excludeSourceAccounts(Collection $accounts): self; /** * Limit the included transaction types. - * - * @param array $types - * - * @return GroupCollectorInterface */ public function excludeTypes(array $types): self; - /** - * @return GroupCollectorInterface - */ public function exists(): self; - /** - * @param string $externalId - * - * @return GroupCollectorInterface - */ public function externalIdContains(string $externalId): self; - /** - * @param string $externalId - * - * @return GroupCollectorInterface - */ public function externalIdDoesNotContain(string $externalId): self; - /** - * @param string $externalId - * - * @return GroupCollectorInterface - */ public function externalIdDoesNotEnd(string $externalId): self; - /** - * @param string $externalId - * - * @return GroupCollectorInterface - */ public function externalIdDoesNotStart(string $externalId): self; - /** - * @param string $externalId - * - * @return GroupCollectorInterface - */ public function externalIdEnds(string $externalId): self; - /** - * @param string $externalId - * - * @return GroupCollectorInterface - */ public function externalIdStarts(string $externalId): self; - /** - * @param string $url - * - * @return GroupCollectorInterface - */ public function externalUrlContains(string $url): self; - /** - * @param string $url - * - * @return GroupCollectorInterface - */ public function externalUrlDoesNotContain(string $url): self; - /** - * @param string $url - * - * @return GroupCollectorInterface - */ public function externalUrlDoesNotEnd(string $url): self; - /** - * @param string $url - * - * @return GroupCollectorInterface - */ public function externalUrlDoesNotStart(string $url): self; - /** - * @param string $url - * - * @return GroupCollectorInterface - */ public function externalUrlEnds(string $url): self; - /** - * @param string $url - * - * @return GroupCollectorInterface - */ public function externalUrlStarts(string $url): self; /** * Ensure the search will find nothing at all, zero results. - * - * @return GroupCollectorInterface */ public function findNothing(): self; /** * Get transactions with a specific foreign amount. - * - * @param string $amount - * - * @return GroupCollectorInterface */ public function foreignAmountIs(string $amount): self; /** * Get transactions with a specific foreign amount. - * - * @param string $amount - * - * @return GroupCollectorInterface */ public function foreignAmountIsNot(string $amount): self; /** * Get transactions where the amount is less than. - * - * @param string $amount - * - * @return GroupCollectorInterface */ public function foreignAmountLess(string $amount): self; /** * Get transactions where the foreign amount is more than. - * - * @param string $amount - * - * @return GroupCollectorInterface */ public function foreignAmountMore(string $amount): self; - /** - * @return bool - */ public function getExpandGroupSearch(): bool; /** * Return the transaction journals without group information. Is useful in some instances. - * - * @return array */ public function getExtractedJournals(): array; /** * Return the groups. - * - * @return Collection */ public function getGroups(): Collection; /** * Same as getGroups but everything is in a paginator. - * - * @return LengthAwarePaginator */ public function getPaginatedGroups(): LengthAwarePaginator; - /** - * @return GroupCollectorInterface - */ public function hasAnyTag(): self; /** * Has attachments - * - * @return GroupCollectorInterface */ public function hasAttachments(): self; /** * Has no attachments - * - * @return GroupCollectorInterface */ public function hasNoAttachments(): self; - /** - * @param string $internalReference - * - * @return GroupCollectorInterface - */ public function internalReferenceContains(string $internalReference): self; - /** - * @param string $internalReference - * - * @return GroupCollectorInterface - */ public function internalReferenceDoesNotContain(string $internalReference): self; - /** - * @param string $internalReference - * - * @return GroupCollectorInterface - */ public function internalReferenceDoesNotEnd(string $internalReference): self; - /** - * @param string $internalReference - * - * @return GroupCollectorInterface - */ public function internalReferenceDoesNotStart(string $internalReference): self; - /** - * @param string $internalReference - * - * @return GroupCollectorInterface - */ public function internalReferenceEnds(string $internalReference): self; - /** - * @param string $internalReference - * - * @return GroupCollectorInterface - */ public function internalReferenceStarts(string $internalReference): self; /** * Only journals that are reconciled. - * - * @return GroupCollectorInterface */ public function isNotReconciled(): self; /** * Only journals that are reconciled. - * - * @return GroupCollectorInterface */ public function isReconciled(): self; - /** - * @param string $day - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaDayAfter(string $day, string $field): self; - /** - * @param string $day - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaDayBefore(string $day, string $field): self; - /** - * @param string $day - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaDayIs(string $day, string $field): self; - /** - * @param string $day - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaDayIsNot(string $day, string $field): self; - /** - * @param string $month - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaMonthAfter(string $month, string $field): self; - /** - * @param string $month - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaMonthBefore(string $month, string $field): self; - /** - * @param string $month - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaMonthIs(string $month, string $field): self; - /** - * @param string $month - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaMonthIsNot(string $month, string $field): self; - /** - * @param string $year - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaYearAfter(string $year, string $field): self; - /** - * @param string $year - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaYearBefore(string $year, string $field): self; - /** - * @param string $year - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaYearIs(string $year, string $field): self; - /** - * @param string $year - * @param string $field - * - * @return GroupCollectorInterface - */ public function metaYearIsNot(string $year, string $field): self; - /** - * @param string $month - * - * @return GroupCollectorInterface - */ public function monthAfter(string $month): self; - /** - * @param string $month - * - * @return GroupCollectorInterface - */ public function monthBefore(string $month): self; - /** - * @param string $month - * - * @return GroupCollectorInterface - */ public function monthIs(string $month): self; - /** - * @param string $month - * - * @return GroupCollectorInterface - */ public function monthIsNot(string $month): self; - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function notesContain(string $value): self; - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function notesDoNotContain(string $value): self; - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function notesDontEndWith(string $value): self; - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function notesDontStartWith(string $value): self; - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function notesEndWith(string $value): self; - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function notesExactly(string $value): self; - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function notesExactlyNot(string $value): self; - /** - * @param string $value - * - * @return GroupCollectorInterface - */ public function notesStartWith(string $value): self; - /** - * @param string $day - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectDayAfter(string $day, string $field): self; - /** - * @param string $day - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectDayBefore(string $day, string $field): self; - /** - * @param string $day - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectDayIs(string $day, string $field): self; - /** - * @param string $day - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectDayIsNot(string $day, string $field): self; - /** - * @param string $month - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectMonthAfter(string $month, string $field): self; - /** - * @param string $month - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectMonthBefore(string $month, string $field): self; - /** - * @param string $month - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectMonthIs(string $month, string $field): self; - /** - * @param string $month - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectMonthIsNot(string $month, string $field): self; - /** - * @param string $year - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectYearAfter(string $year, string $field): self; - /** - * @param string $year - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectYearBefore(string $year, string $field): self; - /** - * @param string $year - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectYearIs(string $year, string $field): self; - /** - * @param string $year - * @param string $field - * - * @return GroupCollectorInterface - */ public function objectYearIsNot(string $year, string $field): self; /** * Define which accounts can be part of the source and destination transactions. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function setAccounts(Collection $accounts): self; /** * Collect transactions after a specific date. - * - * @param Carbon $date - * - * @return GroupCollectorInterface */ public function setAfter(Carbon $date): self; /** * Collect transactions before a specific date. - * - * @param Carbon $date - * - * @return GroupCollectorInterface */ public function setBefore(Carbon $date): self; /** * Limit the search to a specific bill. - * - * @param Bill $bill - * - * @return GroupCollectorInterface */ public function setBill(Bill $bill): self; /** * Limit the search to a specific set of bills. - * - * @param Collection $bills - * - * @return GroupCollectorInterface */ public function setBills(Collection $bills): self; /** * Both source AND destination must be in this list of accounts. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function setBothAccounts(Collection $accounts): self; /** * Limit the search to a specific budget. - * - * @param Budget $budget - * - * @return GroupCollectorInterface */ public function setBudget(Budget $budget): self; /** * Limit the search to a specific set of budgets. - * - * @param Collection $budgets - * - * @return GroupCollectorInterface */ public function setBudgets(Collection $budgets): self; /** * Limit the search to a specific bunch of categories. - * - * @param Collection $categories - * - * @return GroupCollectorInterface */ public function setCategories(Collection $categories): self; /** * Limit the search to a specific category. - * - * @param Category $category - * - * @return GroupCollectorInterface */ public function setCategory(Category $category): self; /** * Collect transactions created on a specific date. - * - * @param Carbon $date - * - * @return GroupCollectorInterface */ public function setCreatedAt(Carbon $date): self; /** * Limit results to a specific currency, either foreign or normal one. - * - * @param TransactionCurrency $currency - * - * @return GroupCollectorInterface */ public function setCurrency(TransactionCurrency $currency): self; /** * Set destination accounts. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function setDestinationAccounts(Collection $accounts): self; /** * Set the end time of the results to return. - * - * @param Carbon $end - * - * @return GroupCollectorInterface */ public function setEnd(Carbon $end): self; - /** - * @param bool $expandGroupSearch - */ public function setExpandGroupSearch(bool $expandGroupSearch): self; /** * Look for specific external ID's. - * - * @param string $externalId - * - * @return GroupCollectorInterface */ public function setExternalId(string $externalId): self; - /** - * @param string $url - * - * @return GroupCollectorInterface - */ public function setExternalUrl(string $url): self; /** * Limit results to a specific foreign currency. - * - * @param TransactionCurrency $currency - * - * @return GroupCollectorInterface */ public function setForeignCurrency(TransactionCurrency $currency): self; /** * Limit the result to a set of specific transaction groups. - * - * @param array $groupIds - * - * @return GroupCollectorInterface */ public function setIds(array $groupIds): self; /** * Look for specific external ID's. - * - * @param string $internalReference - * - * @return GroupCollectorInterface */ public function setInternalReference(string $internalReference): self; /** * Limit the result to a set of specific transaction journals. - * - * @param array $journalIds - * - * @return GroupCollectorInterface */ public function setJournalIds(array $journalIds): self; /** * Limit the number of returned entries. - * - * @param int $limit - * - * @return GroupCollectorInterface */ public function setLimit(int $limit): self; /** * Collect transactions after a specific date. - * - * @param Carbon $date - * @param string $field - * - * @return GroupCollectorInterface */ public function setMetaAfter(Carbon $date, string $field): self; /** * Collect transactions before a specific date. - * - * @param Carbon $date - * @param string $field - * - * @return GroupCollectorInterface */ public function setMetaBefore(Carbon $date, string $field): self; /** * Set the start and end time of the results to return, based on meta data. - * - * @param Carbon $start - * @param Carbon $end - * @param string $field - * - * @return GroupCollectorInterface */ public function setMetaDateRange(Carbon $start, Carbon $end, string $field): self; /** * Define which accounts can NOT be part of the source and destination transactions. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function setNotAccounts(Collection $accounts): self; - /** - * @param Carbon $date - * @param string $field - * - * @return GroupCollectorInterface - */ public function setObjectAfter(Carbon $date, string $field): self; - /** - * @param Carbon $date - * @param string $field - * - * @return GroupCollectorInterface - */ public function setObjectBefore(Carbon $date, string $field): self; - /** - * @param Carbon $start - * @param Carbon $end - * @param string $field - * - * @return GroupCollectorInterface - */ public function setObjectRange(Carbon $start, Carbon $end, string $field): self; /** * Set the page to get. - * - * @param int $page - * - * @return GroupCollectorInterface */ public function setPage(int $page): self; /** * Set the start and end time of the results to return. - * - * @param Carbon $start - * @param Carbon $end - * - * @return GroupCollectorInterface */ public function setRange(Carbon $start, Carbon $end): self; /** * Look for specific recurring ID's. - * - * @param string $recurringId - * - * @return GroupCollectorInterface */ public function setRecurrenceId(string $recurringId): self; /** * Search for words in descriptions. - * - * @param array $array - * - * @return GroupCollectorInterface */ public function setSearchWords(array $array): self; - /** - * @param string $sepaCT - * - * @return GroupCollectorInterface - */ public function setSepaCT(string $sepaCT): self; /** * Set source accounts. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function setSourceAccounts(Collection $accounts): self; /** * Set the start time of the results to return. - * - * @param Carbon $start - * - * @return GroupCollectorInterface */ public function setStart(Carbon $start): self; /** * Limit results to a specific tag. - * - * @param Tag $tag - * - * @return GroupCollectorInterface */ public function setTag(Tag $tag): self; /** * Limit results to a specific set of tags. - * - * @param Collection $tags - * - * @return GroupCollectorInterface */ public function setTags(Collection $tags): self; /** * Limit the search to one specific transaction group. - * - * @param TransactionGroup $transactionGroup - * - * @return GroupCollectorInterface */ public function setTransactionGroup(TransactionGroup $transactionGroup): self; /** * Limit the included transaction types. - * - * @param array $types - * - * @return GroupCollectorInterface */ public function setTypes(array $types): self; /** * Collect transactions updated on a specific date. - * - * @param Carbon $date - * - * @return GroupCollectorInterface */ public function setUpdatedAt(Carbon $date): self; /** * Set the user object and start the query. - * - * @param User $user - * - * @return GroupCollectorInterface */ public function setUser(User $user): self; /** * Set the user group object and start the query. - * - * @param UserGroup $userGroup - * - * @return GroupCollectorInterface */ public function setUserGroup(UserGroup $userGroup): self; /** * Only when does not have these tags - * - * @param Collection $tags - * - * @return GroupCollectorInterface */ public function setWithoutSpecificTags(Collection $tags): self; /** * Either account can be set, but NOT both. This effectively excludes internal transfers. - * - * @param Collection $accounts - * - * @return GroupCollectorInterface */ public function setXorAccounts(Collection $accounts): self; /** * Automatically include all stuff required to make API calls work. - * - * @return GroupCollectorInterface */ public function withAPIInformation(): self; /** * Will include the source and destination account names and types. - * - * @return GroupCollectorInterface */ public function withAccountInformation(): self; /** * Any notes, no matter what. - * - * @return GroupCollectorInterface */ public function withAnyNotes(): self; /** * Add basic info on attachments of transactions. - * - * @return GroupCollectorInterface */ public function withAttachmentInformation(): self; /** * Limit results to transactions without a bill.. - * - * @return GroupCollectorInterface */ public function withBill(): self; /** * Include bill name + ID. - * - * @return GroupCollectorInterface */ public function withBillInformation(): self; /** * Limit results to a transactions with a budget. - * - * @return GroupCollectorInterface */ public function withBudget(): self; /** * Will include budget ID + name, if any. - * - * @return GroupCollectorInterface */ public function withBudgetInformation(): self; /** * Limit results to a transactions with a category. - * - * @return GroupCollectorInterface */ public function withCategory(): self; /** * Will include category ID + name, if any. - * - * @return GroupCollectorInterface */ public function withCategoryInformation(): self; /** * Transactions with any external ID - * - * @return GroupCollectorInterface */ public function withExternalId(): self; /** * Transactions with any external URL - * - * @return GroupCollectorInterface */ public function withExternalUrl(): self; /** * Transaction must have meta date field X. - * - * @param string $field - * - * @return GroupCollectorInterface */ public function withMetaDate(string $field): self; /** * Will include notes. - * - * @return GroupCollectorInterface */ public function withNotes(): self; /** * Add tag info. - * - * @return GroupCollectorInterface */ public function withTagInformation(): self; /** * Limit results to a transactions without a bill. - * - * @return GroupCollectorInterface */ public function withoutBill(): self; /** * Limit results to a transactions without a budget. - * - * @return GroupCollectorInterface */ public function withoutBudget(): self; /** * Limit results to a transactions without a category. - * - * @return GroupCollectorInterface */ public function withoutCategory(): self; /** * Transactions without an external ID - * - * @return GroupCollectorInterface */ public function withoutExternalId(): self; /** * Transactions without an external URL - * - * @return GroupCollectorInterface */ public function withoutExternalUrl(): self; - /** - * @return GroupCollectorInterface - */ public function withoutNotes(): self; - /** - * @return GroupCollectorInterface - */ public function withoutTags(): self; - /** - * @param string $year - * - * @return GroupCollectorInterface - */ public function yearAfter(string $year): self; - /** - * @param string $year - * - * @return GroupCollectorInterface - */ public function yearBefore(string $year): self; - /** - * @param string $year - * - * @return GroupCollectorInterface - */ public function yearIs(string $year): self; - /** - * @param string $year - * - * @return GroupCollectorInterface - */ public function yearIsNot(string $year): self; - - } diff --git a/app/Helpers/Fiscal/FiscalHelper.php b/app/Helpers/Fiscal/FiscalHelper.php index 1b6b23dd06..8516319741 100644 --- a/app/Helpers/Fiscal/FiscalHelper.php +++ b/app/Helpers/Fiscal/FiscalHelper.php @@ -24,8 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Helpers\Fiscal; use Carbon\Carbon; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class FiscalHelper. @@ -44,11 +42,7 @@ class FiscalHelper implements FiscalHelperInterface } /** - * @param Carbon $date - * * @return Carbon date object - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function endOfFiscalYear(Carbon $date): Carbon { @@ -68,11 +62,7 @@ class FiscalHelper implements FiscalHelperInterface } /** - * @param Carbon $date - * * @return Carbon date object - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function startOfFiscalYear(Carbon $date): Carbon { diff --git a/app/Helpers/Fiscal/FiscalHelperInterface.php b/app/Helpers/Fiscal/FiscalHelperInterface.php index 957c6c36dd..5285048623 100644 --- a/app/Helpers/Fiscal/FiscalHelperInterface.php +++ b/app/Helpers/Fiscal/FiscalHelperInterface.php @@ -34,8 +34,6 @@ interface FiscalHelperInterface * This method produces a clone of the Carbon date object passed, checks preferences * and calculates the last day of the fiscal year. * - * @param Carbon $date - * * @return Carbon date object */ public function endOfFiscalYear(Carbon $date): Carbon; @@ -44,8 +42,6 @@ interface FiscalHelperInterface * This method produces a clone of the Carbon date object passed, checks preferences * and calculates the first day of the fiscal year. * - * @param Carbon $date - * * @return Carbon date object */ public function startOfFiscalYear(Carbon $date): Carbon; diff --git a/app/Helpers/Report/NetWorth.php b/app/Helpers/Report/NetWorth.php index 67e5238f03..069043d961 100644 --- a/app/Helpers/Report/NetWorth.php +++ b/app/Helpers/Report/NetWorth.php @@ -50,7 +50,7 @@ class NetWorth implements NetWorthInterface private CurrencyRepositoryInterface $currencyRepos; private User $user; - private null | UserGroup $userGroup; + private null|UserGroup $userGroup; /** * This method collects the user's net worth in ALL the user's currencies @@ -58,10 +58,6 @@ class NetWorth implements NetWorthInterface * * The set of accounts has to be fed to it. * - * @param Collection $accounts - * @param Carbon $date - * - * @return array * @throws FireflyException */ public function byAccounts(Collection $accounts, Carbon $date): array @@ -142,27 +138,14 @@ class NetWorth implements NetWorthInterface $netWorth['native']['native_balance'] = bcadd($nativeBalance, $netWorth['native']['native_balance']); } $cache->store($netWorth); + $converter->summarize(); return $netWorth; } - /** - * @return AdminAccountRepositoryInterface|AccountRepositoryInterface - */ - private function getRepository(): AdminAccountRepositoryInterface | AccountRepositoryInterface + public function setUser(null|Authenticatable|User $user): void { - if (null === $this->userGroup) { - return $this->accountRepository; - } - return $this->adminAccountRepository; - } - - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void - { - if (!($user instanceof User)) { + if (!$user instanceof User) { return; } $this->user = $user; @@ -176,9 +159,6 @@ class NetWorth implements NetWorthInterface $this->currencyRepos->setUser($this->user); } - /** - * @inheritDoc - */ public function setUserGroup(UserGroup $userGroup): void { $this->userGroup = $userGroup; @@ -187,7 +167,6 @@ class NetWorth implements NetWorthInterface } /** - * @inheritDoc * @deprecated */ public function sumNetWorthByCurrency(Carbon $date): array @@ -222,21 +201,29 @@ class NetWorth implements NetWorthInterface return $return; } - /** - * @return Collection - */ + private function getRepository(): AccountRepositoryInterface|AdminAccountRepositoryInterface + { + if (null === $this->userGroup) { + return $this->accountRepository; + } + + return $this->adminAccountRepository; + } + private function getAccounts(): Collection { $accounts = $this->getRepository()->getAccountsByType( [AccountType::ASSET, AccountType::DEFAULT, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE] ); $filtered = new Collection(); + /** @var Account $account */ foreach ($accounts as $account) { if (1 === (int)$this->getRepository()->getMetaValue($account, 'include_net_worth')) { $filtered->push($account); } } + return $filtered; } } diff --git a/app/Helpers/Report/NetWorthInterface.php b/app/Helpers/Report/NetWorthInterface.php index 548401a735..0713f52c6a 100644 --- a/app/Helpers/Report/NetWorthInterface.php +++ b/app/Helpers/Report/NetWorthInterface.php @@ -31,7 +31,6 @@ use Illuminate\Support\Collection; /** * Interface NetWorthInterface - * */ interface NetWorthInterface { @@ -42,22 +41,11 @@ interface NetWorthInterface * of that amount in the native currency. * * Includes extra array with the total(!) net worth in the native currency. - * - * @param Collection $accounts - * @param Carbon $date - * - * @return array */ public function byAccounts(Collection $accounts, Carbon $date): array; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; - /** - * @param UserGroup $userGroup - */ public function setUserGroup(UserGroup $userGroup): void; /** @@ -65,9 +53,6 @@ interface NetWorthInterface * * Same as above but cleaner function with less dependencies. * - * @param Carbon $date - * - * @return array * @deprecated */ public function sumNetWorthByCurrency(Carbon $date): array; diff --git a/app/Helpers/Report/PopupReport.php b/app/Helpers/Report/PopupReport.php index 980b7b1050..c9a2c3aa9e 100644 --- a/app/Helpers/Report/PopupReport.php +++ b/app/Helpers/Report/PopupReport.php @@ -35,40 +35,28 @@ use Illuminate\Support\Collection; /** * Class PopupReport. - * - */ class PopupReport implements PopupReportInterface { /** * Collect the transactions for one account and one budget. - * - * @param Budget $budget - * @param Account $account - * @param array $attributes - * - * @return array */ public function balanceForBudget(Budget $budget, Account $account, array $attributes): array { /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts(new Collection([$account])) - ->withAccountInformation() - ->withBudgetInformation() - ->withCategoryInformation() - ->setRange($attributes['startDate'], $attributes['endDate'])->setBudget($budget); + ->withAccountInformation() + ->withBudgetInformation() + ->withCategoryInformation() + ->setRange($attributes['startDate'], $attributes['endDate'])->setBudget($budget) + ; return $collector->getExtractedJournals(); } /** * Collect the transactions for one account and no budget. - * - * @param Account $account - * @param array $attributes - * - * @return array */ public function balanceForNoBudget(Account $account, array $attributes): array { @@ -80,6 +68,7 @@ class PopupReport implements PopupReportInterface $repos = app(CurrencyRepositoryInterface::class); $currency = $repos->find((int)$currencyId); } + /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector @@ -88,7 +77,8 @@ class PopupReport implements PopupReportInterface ->withAccountInformation() ->withCategoryInformation() ->setRange($attributes['startDate'], $attributes['endDate']) - ->withoutBudget(); + ->withoutBudget() + ; if (null !== $currency) { $collector->setCurrency($currency); @@ -99,11 +89,6 @@ class PopupReport implements PopupReportInterface /** * Collect the transactions for a budget. - * - * @param Budget $budget - * @param array $attributes - * - * @return array */ public function byBudget(Budget $budget, array $attributes): array { @@ -115,13 +100,15 @@ class PopupReport implements PopupReportInterface $repos = app(CurrencyRepositoryInterface::class); $currency = $repos->find((int)$currencyId); } + /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts($attributes['accounts']) - ->withAccountInformation() - ->withBudgetInformation() - ->withCategoryInformation() - ->setRange($attributes['startDate'], $attributes['endDate']); + ->withAccountInformation() + ->withBudgetInformation() + ->withCategoryInformation() + ->setRange($attributes['startDate'], $attributes['endDate']) + ; if (null !== $currency) { $collector->setCurrency($currency); @@ -139,11 +126,6 @@ class PopupReport implements PopupReportInterface /** * Collect journals by a category. - * - * @param Category|null $category - * @param array $attributes - * - * @return array */ public function byCategory(?Category $category, array $attributes): array { @@ -160,11 +142,12 @@ class PopupReport implements PopupReportInterface $collector = app(GroupCollectorInterface::class); $collector->setAccounts($attributes['accounts']) - ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::DEPOSIT]) - ->withAccountInformation() - ->withBudgetInformation() - ->withCategoryInformation() - ->setRange($attributes['startDate'], $attributes['endDate'])->withAccountInformation(); + ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::DEPOSIT]) + ->withAccountInformation() + ->withBudgetInformation() + ->withCategoryInformation() + ->setRange($attributes['startDate'], $attributes['endDate'])->withAccountInformation() + ; if (null !== $category) { $collector->setCategory($category); @@ -182,11 +165,6 @@ class PopupReport implements PopupReportInterface /** * Group transactions by expense. - * - * @param Account $account - * @param array $attributes - * - * @return array */ public function byExpenses(Account $account, array $attributes): array { @@ -210,35 +188,33 @@ class PopupReport implements PopupReportInterface $collector = app(GroupCollectorInterface::class); // set report accounts + the request accounts: - //$set = $attributes['accounts'] ?? new Collection; - //$set->push($account); + // $set = $attributes['accounts'] ?? new Collection; + // $set->push($account); $collector->setDestinationAccounts(new Collection([$account])) - ->setRange($attributes['startDate'], $attributes['endDate']) - ->withAccountInformation() - ->withBudgetInformation() - ->withCategoryInformation() - ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]); + ->setRange($attributes['startDate'], $attributes['endDate']) + ->withAccountInformation() + ->withBudgetInformation() + ->withCategoryInformation() + ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) + ; if (null !== $currency) { $collector->setCurrency($currency); } + return $collector->getExtractedJournals(); } /** * Collect transactions by income. - * - * @param Account $account - * @param array $attributes - * - * @return array */ public function byIncome(Account $account, array $attributes): array { /** @var JournalRepositoryInterface $repository */ $repository = app(JournalRepositoryInterface::class); $repository->setUser($account->user); + /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector @@ -249,7 +225,8 @@ class PopupReport implements PopupReportInterface ->withAccountInformation() ->withBudgetInformation() ->withCategoryInformation() - ->withAccountInformation(); + ->withAccountInformation() + ; return $collector->getExtractedJournals(); } diff --git a/app/Helpers/Report/PopupReportInterface.php b/app/Helpers/Report/PopupReportInterface.php index 311dfa8501..3a03cdb049 100644 --- a/app/Helpers/Report/PopupReportInterface.php +++ b/app/Helpers/Report/PopupReportInterface.php @@ -34,62 +34,31 @@ interface PopupReportInterface { /** * Get balances for budget. - * - * @param Budget $budget - * @param Account $account - * @param array $attributes - * - * @return array */ public function balanceForBudget(Budget $budget, Account $account, array $attributes): array; /** * Get balances for transactions without a budget. - * - * @param Account $account - * @param array $attributes - * - * @return array */ public function balanceForNoBudget(Account $account, array $attributes): array; /** * Group by budget. - * - * @param Budget $budget - * @param array $attributes - * - * @return array */ public function byBudget(Budget $budget, array $attributes): array; /** * Group by category. - * - * @param Category|null $category - * @param array $attributes - * - * @return array */ public function byCategory(?Category $category, array $attributes): array; /** * Do something with expense. Sorry, I am not very inspirational here. - * - * @param Account $account - * @param array $attributes - * - * @return array */ public function byExpenses(Account $account, array $attributes): array; /** * Do something with income. Sorry, I am not very inspirational here. - * - * @param Account $account - * @param array $attributes - * - * @return array */ public function byIncome(Account $account, array $attributes): array; } diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index 92a9841d74..ce736b7d33 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -33,8 +33,6 @@ use Illuminate\Support\Collection; /** * Class ReportHelper. - * - */ class ReportHelper implements ReportHelperInterface { @@ -43,8 +41,6 @@ class ReportHelper implements ReportHelperInterface /** * ReportHelper constructor. - * - * @param BudgetRepositoryInterface $budgetRepository */ public function __construct(BudgetRepositoryInterface $budgetRepository) { @@ -56,12 +52,6 @@ class ReportHelper implements ReportHelperInterface * the users bills and their payments. * * Excludes bills which have not had a payment on the mentioned accounts. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return array */ public function getBillReport(Collection $accounts, Carbon $start, Carbon $end): array { @@ -112,10 +102,6 @@ class ReportHelper implements ReportHelperInterface /** * Generate a list of months for the report. - * - * @param Carbon $date - * - * @return array */ public function listOfMonths(Carbon $date): array { diff --git a/app/Helpers/Report/ReportHelperInterface.php b/app/Helpers/Report/ReportHelperInterface.php index 6dfceaac77..1320127896 100644 --- a/app/Helpers/Report/ReportHelperInterface.php +++ b/app/Helpers/Report/ReportHelperInterface.php @@ -36,21 +36,11 @@ interface ReportHelperInterface * the users bills and their payments. * * Excludes bills which have not had a payment on the mentioned accounts. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return array */ public function getBillReport(Collection $accounts, Carbon $start, Carbon $end): array; /** * Generate a list of months. - * - * @param Carbon $date - * - * @return array */ public function listOfMonths(Carbon $date): array; } diff --git a/app/Helpers/Update/UpdateTrait.php b/app/Helpers/Update/UpdateTrait.php index 832623daf9..acdc836917 100644 --- a/app/Helpers/Update/UpdateTrait.php +++ b/app/Helpers/Update/UpdateTrait.php @@ -24,12 +24,9 @@ declare(strict_types=1); namespace FireflyIII\Helpers\Update; use FireflyIII\Services\FireflyIIIOrg\Update\UpdateRequestInterface; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Trait UpdateTrait - * */ trait UpdateTrait { @@ -37,14 +34,11 @@ trait UpdateTrait * Returns an array with info on the next release, if any. * 'message' => 'A new version is available. * 'level' => 'info' / 'success' / 'error' - * - * @return array - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function getLatestRelease(): array { app('log')->debug('Now in getLatestRelease()'); + /** @var UpdateRequestInterface $checker */ $checker = app(UpdateRequestInterface::class); $channelConfig = app('fireflyconfig')->get('update_channel', 'stable'); diff --git a/app/Helpers/Webhook/Sha3SignatureGenerator.php b/app/Helpers/Webhook/Sha3SignatureGenerator.php index 2bc458d8b0..708088851f 100644 --- a/app/Helpers/Webhook/Sha3SignatureGenerator.php +++ b/app/Helpers/Webhook/Sha3SignatureGenerator.php @@ -25,7 +25,6 @@ namespace FireflyIII\Helpers\Webhook; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\WebhookMessage; -use JsonException; /** * Class Sha3SignatureGenerator @@ -35,7 +34,6 @@ class Sha3SignatureGenerator implements SignatureGeneratorInterface private int $version = 1; /** - * @inheritDoc * @throws FireflyException */ public function generate(WebhookMessage $message): string @@ -48,11 +46,12 @@ class Sha3SignatureGenerator implements SignatureGeneratorInterface try { $json = json_encode($message->message, JSON_THROW_ON_ERROR); - } catch (JsonException $e) { + } catch (\JsonException $e) { app('log')->error('Could not generate hash.'); app('log')->error(sprintf('JSON value: %s', $json)); app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); + throw new FireflyException('Could not generate JSON for SHA3 hash.', 0, $e); } @@ -73,9 +72,6 @@ class Sha3SignatureGenerator implements SignatureGeneratorInterface return sprintf('t=%s,v%d=%s', $timestamp, $this->getVersion(), $signature); } - /** - * @inheritDoc - */ public function getVersion(): int { return $this->version; diff --git a/app/Helpers/Webhook/SignatureGeneratorInterface.php b/app/Helpers/Webhook/SignatureGeneratorInterface.php index ebbc8a5278..f3f46b2ad7 100644 --- a/app/Helpers/Webhook/SignatureGeneratorInterface.php +++ b/app/Helpers/Webhook/SignatureGeneratorInterface.php @@ -30,17 +30,10 @@ use FireflyIII\Models\WebhookMessage; */ interface SignatureGeneratorInterface { - /** - * @param WebhookMessage $message - * - * @return string - */ public function generate(WebhookMessage $message): string; /** * Return the version of this signature generator. - * - * @return int */ public function getVersion(): int; } diff --git a/app/Http/Controllers/Account/CreateController.php b/app/Http/Controllers/Account/CreateController.php index d73a1ee05c..4279fb5d5f 100644 --- a/app/Http/Controllers/Account/CreateController.php +++ b/app/Http/Controllers/Account/CreateController.php @@ -36,11 +36,8 @@ use Illuminate\Http\Request; use Illuminate\Routing\Redirector; use Illuminate\Support\Facades\Log; use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** - * * Class CreateController */ class CreateController extends Controller @@ -52,8 +49,6 @@ class CreateController extends Controller /** * CreateController constructor. - * - */ public function __construct() { @@ -76,9 +71,6 @@ class CreateController extends Controller /** * Create a new account. * - * @param Request $request - * @param string $objectType - * * @return Factory|View */ public function create(Request $request, string $objectType) @@ -134,12 +126,9 @@ class CreateController extends Controller /** * Store the new account. * - * @param AccountFormRequest $request + * @return Redirector|RedirectResponse * - * @return RedirectResponse|Redirector * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function store(AccountFormRequest $request) { @@ -161,7 +150,7 @@ class CreateController extends Controller } // store attachment(s): - /** @var array|null $files */ + /** @var null|array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($account, $files); diff --git a/app/Http/Controllers/Account/DeleteController.php b/app/Http/Controllers/Account/DeleteController.php index a830ec9eb5..c9b9dae224 100644 --- a/app/Http/Controllers/Account/DeleteController.php +++ b/app/Http/Controllers/Account/DeleteController.php @@ -42,8 +42,6 @@ class DeleteController extends Controller /** * DeleteController constructor. - * - */ public function __construct() { @@ -65,9 +63,7 @@ class DeleteController extends Controller /** * Delete account screen. * - * @param Account $account - * - * @return Factory|RedirectResponse|Redirector|View + * @return Factory|Redirector|RedirectResponse|View */ public function delete(Account $account) { @@ -90,10 +86,7 @@ class DeleteController extends Controller /** * Delete the account. * - * @param Request $request - * @param Account $account - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function destroy(Request $request, Account $account) { diff --git a/app/Http/Controllers/Account/EditController.php b/app/Http/Controllers/Account/EditController.php index f227deda97..e30204082a 100644 --- a/app/Http/Controllers/Account/EditController.php +++ b/app/Http/Controllers/Account/EditController.php @@ -36,7 +36,6 @@ use Illuminate\Routing\Redirector; use Illuminate\View\View; /** - * * Class EditController */ class EditController extends Controller @@ -57,7 +56,7 @@ class EditController extends Controller $this->middleware( function ($request, $next) { app('view')->share('mainTitleIcon', 'fa-credit-card'); - app('view')->share('title', (string)trans('firefly.accounts')); + app('view')->share('title', (string) trans('firefly.accounts')); $this->repository = app(AccountRepositoryInterface::class); $this->attachments = app(AttachmentHelperInterface::class); @@ -68,13 +67,11 @@ class EditController extends Controller } /** - * Edit account overview. + * Edit account overview. It's complex, but it just has a lot of if/then/else. * - * @param Request $request - * @param Account $account - * @param AccountRepositoryInterface $repository + * @SuppressWarnings(PHPMD.NPathComplexity) * - * @return Factory|RedirectResponse|Redirector|View + * @return Factory|Redirector|RedirectResponse|View */ public function edit(Request $request, Account $account, AccountRepositoryInterface $repository) { @@ -83,7 +80,7 @@ class EditController extends Controller } $objectType = config('firefly.shortNamesByFullName')[$account->accountType->type]; - $subTitle = (string)trans(sprintf('firefly.edit_%s_account', $objectType), ['name' => $account->name]); + $subTitle = (string) trans(sprintf('firefly.edit_%s_account', $objectType), ['name' => $account->name]); $subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType)); $roles = $this->getRoles(); $liabilityTypes = $this->getLiabilityTypes(); @@ -108,9 +105,9 @@ class EditController extends Controller // interest calculation periods: $interestPeriods = [ - 'daily' => (string)trans('firefly.interest_calc_daily'), - 'monthly' => (string)trans('firefly.interest_calc_monthly'), - 'yearly' => (string)trans('firefly.interest_calc_yearly'), + 'daily' => (string) trans('firefly.interest_calc_daily'), + 'monthly' => (string) trans('firefly.interest_calc_monthly'), + 'yearly' => (string) trans('firefly.interest_calc_yearly'), ]; // put previous url in session if not redirect from store (not "return_to_edit"). @@ -119,7 +116,7 @@ class EditController extends Controller } $request->session()->forget('accounts.edit.fromUpdate'); - $openingBalanceAmount = (string)$repository->getOpeningBalanceAmount($account); + $openingBalanceAmount = (string) $repository->getOpeningBalanceAmount($account); if ('0' === $openingBalanceAmount) { $openingBalanceAmount = ''; } @@ -139,17 +136,17 @@ class EditController extends Controller 'cc_type' => $repository->getMetaValue($account, 'cc_type'), 'cc_monthly_payment_date' => $repository->getMetaValue($account, 'cc_monthly_payment_date'), 'BIC' => $repository->getMetaValue($account, 'BIC'), - 'opening_balance_date' => substr((string)$openingBalanceDate, 0, 10), + 'opening_balance_date' => substr((string) $openingBalanceDate, 0, 10), 'liability_type_id' => $account->account_type_id, 'opening_balance' => app('steam')->bcround($openingBalanceAmount, $currency->decimal_places), 'liability_direction' => $this->repository->getMetaValue($account, 'liability_direction'), 'virtual_balance' => app('steam')->bcround($virtualBalance, $currency->decimal_places), 'currency_id' => $currency->id, - 'include_net_worth' => $hasOldInput ? (bool)$request->old('include_net_worth') : $includeNetWorth, + 'include_net_worth' => $hasOldInput ? (bool) $request->old('include_net_worth') : $includeNetWorth, 'interest' => $repository->getMetaValue($account, 'interest'), 'interest_period' => $repository->getMetaValue($account, 'interest_period'), 'notes' => $this->repository->getNoteText($account), - 'active' => $hasOldInput ? (bool)$request->old('active') : $account->active, + 'active' => $hasOldInput ? (bool) $request->old('active') : $account->active, ]; if ('' === $openingBalanceAmount) { $preFilled['opening_balance'] = ''; @@ -157,31 +154,13 @@ class EditController extends Controller $request->session()->flash('preFilled', $preFilled); - return view( - 'accounts.edit', - compact( - 'account', - 'currency', - 'subTitle', - 'subTitleIcon', - 'locations', - 'liabilityDirections', - 'objectType', - 'roles', - 'preFilled', - 'liabilityTypes', - 'interestPeriods' - ) - ); + return view('accounts.edit', compact('account', 'currency', 'subTitle', 'subTitleIcon', 'locations', 'liabilityDirections', 'objectType', 'roles', 'preFilled', 'liabilityTypes', 'interestPeriods')); } /** * Update the account. * - * @param AccountFormRequest $request - * @param Account $account - * - * @return $this|RedirectResponse|Redirector + * @return $this|Redirector|RedirectResponse */ public function update(AccountFormRequest $request, Account $account) { @@ -192,16 +171,16 @@ class EditController extends Controller $data = $request->getAccountData(); $this->repository->update($account, $data); - $request->session()->flash('success', (string)trans('firefly.updated_account', ['name' => $account->name])); + $request->session()->flash('success', (string) trans('firefly.updated_account', ['name' => $account->name])); // store new attachment(s): - /** @var array|null $files */ + /** @var null|array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($account, $files); } if (null !== $files && auth()->user()->hasRole('demo')) { - session()->flash('info', (string)trans('firefly.no_att_demo_user')); + session()->flash('info', (string) trans('firefly.no_att_demo_user')); } if (count($this->attachments->getMessages()->get('attachments')) > 0) { @@ -210,7 +189,7 @@ class EditController extends Controller // redirect $redirect = redirect($this->getPreviousUrl('accounts.edit.url')); - if (1 === (int)$request->get('return_to_edit')) { + if (1 === (int) $request->get('return_to_edit')) { // set value so edit routine will not overwrite URL: $request->session()->put('accounts.edit.fromUpdate', true); diff --git a/app/Http/Controllers/Account/IndexController.php b/app/Http/Controllers/Account/IndexController.php index df602d506a..12ebab549b 100644 --- a/app/Http/Controllers/Account/IndexController.php +++ b/app/Http/Controllers/Account/IndexController.php @@ -33,12 +33,8 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\View\View; -use JsonException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** - * * Class IndexController */ class IndexController extends Controller @@ -49,8 +45,6 @@ class IndexController extends Controller /** * IndexController constructor. - * - */ public function __construct() { @@ -70,15 +64,10 @@ class IndexController extends Controller } /** - * @param Request $request - * @param string $objectType - * * @return Factory|View + * * @throws FireflyException - * @throws JsonException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ + * */ public function inactive(Request $request, string $objectType) { $inactivePage = true; @@ -91,8 +80,10 @@ class IndexController extends Controller $pageSize = (int)app('preferences')->get('listPageSize', 50)->data; $accounts = $collection->slice(($page - 1) * $pageSize, $pageSize); unset($collection); + /** @var Carbon $start */ $start = clone session('start', today(config('app.timezone'))->startOfMonth()); + /** @var Carbon $end */ $end = clone session('end', today(config('app.timezone'))->endOfMonth()); $start->subDay(); @@ -103,7 +94,7 @@ class IndexController extends Controller $activities = app('steam')->getLastActivities($ids); $accounts->each( - function (Account $account) use ($activities, $startBalances, $endBalances) { + function (Account $account) use ($activities, $startBalances, $endBalances): void { $account->lastActivityDate = $this->isInArrayDate($activities, $account->id); $account->startBalance = $this->isInArray($startBalances, $account->id); $account->endBalance = $this->isInArray($endBalances, $account->id); @@ -126,15 +117,10 @@ class IndexController extends Controller /** * Show list of accounts. * - * @param Request $request - * @param string $objectType - * * @return Factory|View + * * @throws FireflyException - * @throws JsonException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ + * */ public function index(Request $request, string $objectType) { app('log')->debug(sprintf('Now at %s', __METHOD__)); @@ -154,8 +140,10 @@ class IndexController extends Controller app('log')->debug(sprintf('Count of collection: %d, count of accounts: %d', $total, $accounts->count())); unset($collection); + /** @var Carbon $start */ $start = clone session('start', today(config('app.timezone'))->startOfMonth()); + /** @var Carbon $end */ $end = clone session('end', today(config('app.timezone'))->endOfMonth()); $start->subDay(); @@ -165,9 +153,8 @@ class IndexController extends Controller $endBalances = app('steam')->balancesByAccounts($accounts, $end); $activities = app('steam')->getLastActivities($ids); - $accounts->each( - function (Account $account) use ($activities, $startBalances, $endBalances) { + function (Account $account) use ($activities, $startBalances, $endBalances): void { $interest = (string)$this->repository->getMetaValue($account, 'interest'); $interest = '' === $interest ? '0' : $interest; @@ -189,6 +176,7 @@ class IndexController extends Controller ); // make paginator: app('log')->debug(sprintf('Count of accounts before LAP: %d', $accounts->count())); + /** @var LengthAwarePaginator $accounts */ $accounts = new LengthAwarePaginator($accounts, $total, $pageSize, $page); $accounts->setPath(route('accounts.index', [$objectType])); diff --git a/app/Http/Controllers/Account/ReconcileController.php b/app/Http/Controllers/Account/ReconcileController.php index 9ebe1c0d09..efaf41c6db 100644 --- a/app/Http/Controllers/Account/ReconcileController.php +++ b/app/Http/Controllers/Account/ReconcileController.php @@ -39,9 +39,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Routing\Redirector; use Illuminate\View\View; -use JsonException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class ReconcileController. @@ -53,8 +50,6 @@ class ReconcileController extends Controller /** * ReconcileController constructor. - * - */ public function __construct() { @@ -76,16 +71,10 @@ class ReconcileController extends Controller /** * Reconciliation overview. * - * @param Account $account - * @param Carbon|null $start - * @param Carbon|null $end + * @return Factory|Redirector|RedirectResponse|View * - * @return Factory|RedirectResponse|Redirector|View * @throws FireflyException - * @throws JsonException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ + * */ public function reconcile(Account $account, Carbon $start = null, Carbon $end = null) { if (!$this->isEditableAccount($account)) { @@ -106,6 +95,7 @@ class ReconcileController extends Controller if (null === $start && null === $end) { /** @var Carbon $start */ $start = clone session('start', app('navigation')->startOfPeriod(new Carbon(), $range)); + /** @var Carbon $end */ $end = clone session('end', app('navigation')->endOfPeriod(new Carbon(), $range)); } @@ -153,14 +143,9 @@ class ReconcileController extends Controller /** * Submit a new reconciliation. * - * @param ReconciliationStoreRequest $request - * @param Account $account - * @param Carbon $start - * @param Carbon $end + * @return Redirector|RedirectResponse * - * @return RedirectResponse|Redirector * @throws DuplicateTransactionException - * @throws JsonException */ public function submit(ReconciliationStoreRequest $request, Account $account, Carbon $start, Carbon $end) { @@ -202,14 +187,7 @@ class ReconcileController extends Controller /** * Creates a reconciliation group. * - * @param Account $account - * @param Carbon $start - * @param Carbon $end - * @param string $difference - * - * @return string * @throws DuplicateTransactionException - * @throws JsonException */ private function createReconciliation(Account $account, Carbon $start, Carbon $end, string $difference): string { @@ -258,11 +236,14 @@ class ReconcileController extends Controller ], ], ]; + /** @var TransactionGroupFactory $factory */ $factory = app(TransactionGroupFactory::class); + /** @var User $user */ $user = auth()->user(); $factory->setUser($user); + try { $factory->create($submission); } catch (FireflyException $e) { diff --git a/app/Http/Controllers/Account/ShowController.php b/app/Http/Controllers/Account/ShowController.php index e2e7792f11..2b9c398578 100644 --- a/app/Http/Controllers/Account/ShowController.php +++ b/app/Http/Controllers/Account/ShowController.php @@ -36,13 +36,9 @@ use Illuminate\Http\Request; use Illuminate\Routing\Redirector; use Illuminate\Support\Collection; use Illuminate\View\View; -use JsonException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class ShowController - * */ class ShowController extends Controller { @@ -52,8 +48,6 @@ class ShowController extends Controller /** * ShowController constructor. - * - */ public function __construct() { @@ -77,17 +71,10 @@ class ShowController extends Controller /** * Show an account. * - * @param Request $request - * @param Account $account - * @param Carbon|null $start - * @param Carbon|null $end + * @return Factory|Redirector|RedirectResponse|View * - * @return RedirectResponse|Redirector|Factory|View * @throws FireflyException - * @throws JsonException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ + * */ public function show(Request $request, Account $account, Carbon $start = null, Carbon $end = null) { $objectType = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type)); @@ -96,9 +83,9 @@ class ShowController extends Controller return $this->redirectAccountToAccount($account); } - /** @var Carbon $start */ + // @var Carbon $start $start ??= session('start'); - /** @var Carbon $end */ + // @var Carbon $end $end ??= session('end'); if ($end < $start) { @@ -123,20 +110,19 @@ class ShowController extends Controller $subTitle = (string)trans('firefly.all_journals_for_account', ['name' => $account->name]); } - /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector ->setAccounts(new Collection([$account])) ->setLimit($pageSize) ->setPage($page)->withAccountInformation()->withCategoryInformation() - ->setRange($start, $end); + ->setRange($start, $end) + ; // this search will not include transaction groups where this asset account (or liability) // is just part of ONE of the journals. To force this: $collector->setExpandGroupSearch(true); - $groups = $collector->getPaginatedGroups(); $groups->setPath(route('accounts.show', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')])); @@ -168,15 +154,10 @@ class ShowController extends Controller /** * Show an account. * - * @param Request $request - * @param Account $account + * @return Factory|Redirector|RedirectResponse|View * - * @return RedirectResponse|Redirector|Factory|View * @throws FireflyException - * @throws JsonException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ + * */ public function showAll(Request $request, Account $account) { if (!$this->isEditableAccount($account)) { @@ -189,12 +170,13 @@ class ShowController extends Controller $end = today(config('app.timezone')); $today = today(config('app.timezone')); $start = $this->repository->oldestJournalDate($account) ?? today(config('app.timezone'))->startOfMonth(); - $subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type); + $subTitleIcon = config('firefly.subIconsByIdentifier.'.$account->accountType->type); $page = (int)$request->get('page'); $pageSize = (int)app('preferences')->get('listPageSize', 50)->data; $currency = $this->repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency(); $subTitle = (string)trans('firefly.all_journals_for_account', ['name' => $account->name]); $periods = new Collection(); + /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page)->withAccountInformation()->withCategoryInformation(); diff --git a/app/Http/Controllers/Admin/ConfigurationController.php b/app/Http/Controllers/Admin/ConfigurationController.php index 24b1bd3f47..553f4dea69 100644 --- a/app/Http/Controllers/Admin/ConfigurationController.php +++ b/app/Http/Controllers/Admin/ConfigurationController.php @@ -30,8 +30,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Facades\Log; use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class ConfigurationController. @@ -40,8 +38,6 @@ class ConfigurationController extends Controller { /** * ConfigurationController constructor. - * - */ public function __construct() { @@ -62,8 +58,6 @@ class ConfigurationController extends Controller * Show configuration index. * * @return Factory|View - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function index() { @@ -86,10 +80,6 @@ class ConfigurationController extends Controller /** * Store new configuration values. - * - * @param ConfigurationRequest $request - * - * @return RedirectResponse */ public function postIndex(ConfigurationRequest $request): RedirectResponse { diff --git a/app/Http/Controllers/Admin/HomeController.php b/app/Http/Controllers/Admin/HomeController.php index d84d9a7eda..95c943cc2c 100644 --- a/app/Http/Controllers/Admin/HomeController.php +++ b/app/Http/Controllers/Admin/HomeController.php @@ -34,8 +34,6 @@ use Illuminate\Http\Request; use Illuminate\Routing\Redirector; use Illuminate\Support\Facades\Log; use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class HomeController. @@ -44,8 +42,6 @@ class HomeController extends Controller { /** * ConfigurationController constructor. - * - */ public function __construct() { @@ -57,8 +53,6 @@ class HomeController extends Controller * Index of the admin. * * @return Factory|View - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function index() { @@ -81,11 +75,6 @@ class HomeController extends Controller return view('admin.index', compact('title', 'mainTitleIcon', 'email', 'notifications', 'slackUrl')); } - /** - * @param Request $request - * - * @return RedirectResponse - */ public function notifications(Request $request): RedirectResponse { foreach (config('firefly.admin_notifications') as $item) { @@ -104,17 +93,19 @@ class HomeController extends Controller } session()->flash('success', (string)trans('firefly.notification_settings_saved')); + return redirect(route('admin.index')); } /** * Send a test message to the admin. * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function testMessage() { Log::channel('audit')->info('User sends test message.'); + /** @var User $user */ $user = auth()->user(); app('log')->debug('Now in testMessage() controller.'); diff --git a/app/Http/Controllers/Admin/LinkController.php b/app/Http/Controllers/Admin/LinkController.php index 38b60a53d8..ebec22fe2c 100644 --- a/app/Http/Controllers/Admin/LinkController.php +++ b/app/Http/Controllers/Admin/LinkController.php @@ -44,8 +44,6 @@ class LinkController extends Controller /** * LinkController constructor. - * - */ public function __construct() { @@ -86,10 +84,7 @@ class LinkController extends Controller /** * Delete a link form. * - * @param Request $request - * @param LinkType $linkType - * - * @return Factory|RedirectResponse|Redirector|View + * @return Factory|Redirector|RedirectResponse|View */ public function delete(Request $request, LinkType $linkType) { @@ -122,10 +117,7 @@ class LinkController extends Controller /** * Actually destroy the link. * - * @param Request $request - * @param LinkType $linkType - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function destroy(Request $request, LinkType $linkType) { @@ -143,10 +135,7 @@ class LinkController extends Controller /** * Edit a link form. * - * @param Request $request - * @param LinkType $linkType - * - * @return Factory|RedirectResponse|Redirector|View + * @return Factory|Redirector|RedirectResponse|View */ public function edit(Request $request, LinkType $linkType) { @@ -182,7 +171,7 @@ class LinkController extends Controller Log::channel('audit')->info('User on index of link types in admin.'); $linkTypes->each( - function (LinkType $linkType) { + function (LinkType $linkType): void { $linkType->journalCount = $this->repository->countJournals($linkType); } ); @@ -193,8 +182,6 @@ class LinkController extends Controller /** * Show a single link. * - * @param LinkType $linkType - * * @return Factory|View */ public function show(LinkType $linkType) @@ -211,9 +198,7 @@ class LinkController extends Controller /** * Store the new link. * - * @param LinkTypeFormRequest $request - * - * @return $this|RedirectResponse|Redirector + * @return $this|Redirector|RedirectResponse */ public function store(LinkTypeFormRequest $request) { @@ -242,10 +227,7 @@ class LinkController extends Controller /** * Update an existing link. * - * @param LinkTypeFormRequest $request - * @param LinkType $linkType - * - * @return $this|RedirectResponse|Redirector + * @return $this|Redirector|RedirectResponse */ public function update(LinkTypeFormRequest $request, LinkType $linkType) { diff --git a/app/Http/Controllers/Admin/UpdateController.php b/app/Http/Controllers/Admin/UpdateController.php index 6222135aad..e4c3b2a1cb 100644 --- a/app/Http/Controllers/Admin/UpdateController.php +++ b/app/Http/Controllers/Admin/UpdateController.php @@ -31,8 +31,6 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Routing\Redirector; use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class HomeController. @@ -62,8 +60,6 @@ class UpdateController extends Controller * Show page with update options. * * @return Factory|View - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function index() { @@ -91,9 +87,7 @@ class UpdateController extends Controller /** * Post new settings. * - * @param Request $request - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function post(Request $request) { diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 3c863ab747..1074075515 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -37,8 +37,6 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Routing\Redirector; use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class UserController. @@ -69,9 +67,7 @@ class UserController extends Controller } /** - * @param User $user - * - * @return Application|Factory|RedirectResponse|Redirector|View + * @return Application|Factory|Redirector|RedirectResponse|View */ public function delete(User $user) { @@ -86,31 +82,26 @@ class UserController extends Controller return view('admin.users.delete', compact('user', 'subTitle')); } - /** - * @param InvitedUser $invitedUser - * - * @return JsonResponse - */ public function deleteInvite(InvitedUser $invitedUser): JsonResponse { app('log')->debug('Will now delete invitation'); if ($invitedUser->redeemed) { app('log')->debug('Is already redeemed.'); session()->flash('error', trans('firefly.invite_is_already_redeemed', ['address' => $invitedUser->email])); + return response()->json(['success' => false]); } app('log')->debug('Delete!'); session()->flash('success', trans('firefly.invite_is_deleted', ['address' => $invitedUser->email])); $this->repository->deleteInvite($invitedUser); + return response()->json(['success' => true]); } /** * Destroy a user. * - * @param User $user - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function destroy(User $user) { @@ -128,8 +119,6 @@ class UserController extends Controller /** * Edit user form. * - * @param User $user - * * @return Factory|View */ public function edit(User $user) @@ -162,8 +151,6 @@ class UserController extends Controller * Show index of user manager. * * @return Factory|View - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function index() { @@ -181,7 +168,7 @@ class UserController extends Controller // add meta stuff. $users->each( - function (User $user) { + function (User $user): void { $user->isAdmin = $this->repository->hasRole($user, 'owner'); $user->has2FA = null !== $user->mfa_secret; } @@ -190,11 +177,6 @@ class UserController extends Controller return view('admin.users.index', compact('subTitle', 'subTitleIcon', 'users', 'allowInvites', 'invitedUsers')); } - /** - * @param InviteUserFormRequest $request - * - * @return RedirectResponse - */ public function invite(InviteUserFormRequest $request): RedirectResponse { $address = (string)$request->get('invited_user'); @@ -210,8 +192,6 @@ class UserController extends Controller /** * Show single user. * - * @param User $user - * * @return Factory|View */ public function show(User $user) @@ -238,17 +218,14 @@ class UserController extends Controller /** * Update single user. * - * @param UserFormRequest $request - * @param User $user - * - * @return $this|RedirectResponse|Redirector + * @return $this|Redirector|RedirectResponse */ public function update(UserFormRequest $request, User $user) { app('log')->debug('Actually here'); $data = $request->getUserData(); - //var_dump($data); + // var_dump($data); // update password if (array_key_exists('password', $data) && '' !== $data['password']) { diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php index e7a6f71a31..16997f11ab 100644 --- a/app/Http/Controllers/AttachmentController.php +++ b/app/Http/Controllers/AttachmentController.php @@ -37,7 +37,6 @@ use Illuminate\View\View; /** * Class AttachmentController. - * */ class AttachmentController extends Controller { @@ -45,8 +44,6 @@ class AttachmentController extends Controller /** * AttachmentController constructor. - * - */ public function __construct() { @@ -67,8 +64,6 @@ class AttachmentController extends Controller /** * Form to delete an attachment. * - * @param Attachment $attachment - * * @return Factory|View */ public function delete(Attachment $attachment) @@ -84,10 +79,7 @@ class AttachmentController extends Controller /** * Destroy attachment. * - * @param Request $request - * @param Attachment $attachment - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function destroy(Request $request, Attachment $attachment) { @@ -104,8 +96,6 @@ class AttachmentController extends Controller /** * Download attachment to PC. * - * @param Attachment $attachment - * * @return LaravelResponse * * @throws FireflyException @@ -121,25 +111,24 @@ class AttachmentController extends Controller $response ->header('Content-Description', 'File Transfer') ->header('Content-Type', 'application/octet-stream') - ->header('Content-Disposition', 'attachment; filename=' . $quoted) + ->header('Content-Disposition', 'attachment; filename='.$quoted) ->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', (string)strlen($content)); + ->header('Content-Length', (string)strlen($content)) + ; return $response; } + throw new FireflyException('Could not find the indicated attachment. The file is no longer there.'); } /** * Edit an attachment. * - * @param Request $request - * @param Attachment $attachment - * * @return Factory|View */ public function edit(Request $request, Attachment $attachment) @@ -181,11 +170,6 @@ class AttachmentController extends Controller /** * Update attachment. - * - * @param AttachmentFormRequest $request - * @param Attachment $attachment - * - * @return RedirectResponse */ public function update(AttachmentFormRequest $request, Attachment $attachment): RedirectResponse { @@ -209,9 +193,6 @@ class AttachmentController extends Controller /** * View attachment in browser. * - * @param Attachment $attachment - * - * @return LaravelResponse * @throws FireflyException * @throws BindingResolutionException */ @@ -239,10 +220,11 @@ class AttachmentController extends Controller [ 'Content-Security-Policy' => implode('; ', $csp), 'Content-Type' => $attachment->mime, - 'Content-Disposition' => 'inline; filename="' . $attachment->filename . '"', + 'Content-Disposition' => 'inline; filename="'.$attachment->filename.'"', ] ); } + throw new FireflyException('Could not find the indicated attachment. The file is no longer there.'); } } diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php index 6149fd77ad..2148c655ac 100644 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -32,13 +32,9 @@ use Illuminate\Foundation\Auth\SendsPasswordResetEmails; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class ForgotPasswordController - * - */ class ForgotPasswordController extends Controller { @@ -60,9 +56,6 @@ class ForgotPasswordController extends Controller /** * Send a reset link to the given user. * - * @param Request $request - * @param UserRepositoryInterface $repository - * * @return Factory|RedirectResponse|View */ public function sendResetLinkEmail(Request $request, UserRepositoryInterface $repository) @@ -75,11 +68,10 @@ class ForgotPasswordController extends Controller return view('error', compact('message')); } - $this->validateEmail($request); // verify if the user is not a demo user. If so, we give him back an error. - /** @var User|null $user */ + /** @var null|User $user */ $user = User::where('email', $request->get('email'))->first(); if (null !== $user && $repository->hasRole($user, 'demo')) { @@ -103,11 +95,9 @@ class ForgotPasswordController extends Controller /** * Show form for email recovery. * - * * @return Factory|View + * * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function showLinkRequestForm() { diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index bd8a490e9c..9bd553e74f 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Auth; use Cookie; -use DB; use FireflyIII\Events\ActuallyLoggedIn; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; @@ -41,8 +40,6 @@ use Illuminate\Http\Response; use Illuminate\Routing\Redirector; use Illuminate\Support\Facades\Log; use Illuminate\Validation\ValidationException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class LoginController @@ -50,8 +47,6 @@ use Psr\Container\NotFoundExceptionInterface; * This controller handles authenticating users for the application and * redirecting them to your home screen. The controller uses a trait * to conveniently provide its functionality to your applications. - * - */ class LoginController extends Controller { @@ -60,8 +55,6 @@ class LoginController extends Controller /** * Where to redirect users after login. - * - * @var string */ protected string $redirectTo = RouteServiceProvider::HOME; @@ -69,8 +62,6 @@ class LoginController extends Controller /** * Create a new controller instance. - * - * @return void */ public function __construct() { @@ -82,10 +73,9 @@ class LoginController extends Controller /** * Handle a login request to the application. * - * @return JsonResponse|RedirectResponse * @throws ValidationException */ - public function login(Request $request): JsonResponse | RedirectResponse + public function login(Request $request): JsonResponse|RedirectResponse { Log::channel('audit')->info(sprintf('User is trying to login using "%s"', $request->get($this->username()))); app('log')->info('User is trying to login.'); @@ -93,7 +83,7 @@ class LoginController extends Controller $this->validateLogin($request); app('log')->debug('Login data is present.'); - /** Copied directly from AuthenticatesUsers, but with logging added: */ + // Copied directly from AuthenticatesUsers, but with logging added: // If the class is using the ThrottlesLogins trait, we can automatically throttle // the login attempts for this application. We'll key this by the username and // the IP address of the client making these requests into this application. @@ -104,7 +94,7 @@ class LoginController extends Controller $this->sendLockoutResponse($request); } - /** Copied directly from AuthenticatesUsers, but with logging added: */ + // Copied directly from AuthenticatesUsers, but with logging added: if ($this->attemptLogin($request)) { Log::channel('audit')->info(sprintf('User "%s" has been logged in.', $request->get($this->username()))); app('log')->debug(sprintf('Redirect after login is %s.', $this->redirectPath())); @@ -119,7 +109,7 @@ class LoginController extends Controller } app('log')->warning('Login attempt failed.'); - /** Copied directly from AuthenticatesUsers, but with logging added: */ + // Copied directly from AuthenticatesUsers, but with logging added: // If the login attempt was unsuccessful we will increment the number of attempts // to login and redirect the user back to the login form. Of course, when this // user surpasses their maximum number of attempts they will get locked out. @@ -128,7 +118,7 @@ class LoginController extends Controller $this->sendFailedLoginResponse($request); - /** @noinspection PhpUnreachableStatementInspection */ + // @noinspection PhpUnreachableStatementInspection return response()->json([]); } @@ -142,33 +132,10 @@ class LoginController extends Controller return $this->username; } - /** - * Get the failed login response instance. - * - * @param Request $request - * - * @return void - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @throws ValidationException - */ - protected function sendFailedLoginResponse(Request $request) - { - $exception = ValidationException::withMessages( - [ - $this->username() => [trans('auth.failed')], - ] - ); - $exception->redirectTo = route('login'); - - throw $exception; - } - /** * Log the user out of the application. * - * @param Request $request - * - * @return RedirectResponse|Redirector|Response + * @return Redirector|RedirectResponse|Response */ public function logout(Request $request) { @@ -183,7 +150,7 @@ class LoginController extends Controller // also logout current 2FA tokens. $cookieName = config('google2fa.cookie_name', 'google2fa_token'); - Cookie::forget($cookieName); + \Cookie::forget($cookieName); $this->guard()->logout(); @@ -201,18 +168,15 @@ class LoginController extends Controller /** * Show the application's login form. * - * @param Request $request + * @return Application|Factory|Redirector|RedirectResponse|View * - * @return Factory|Application|View|Redirector|RedirectResponse * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function showLoginForm(Request $request) { Log::channel('audit')->info('Show login form (1.1).'); - $count = DB::table('users')->count(); + $count = \DB::table('users')->count(); $guard = config('auth.defaults.guard'); $title = (string)trans('firefly.login_page_title'); @@ -246,4 +210,23 @@ class LoginController extends Controller return view('auth.login', compact('allowRegistration', 'email', 'remember', 'allowReset', 'title', 'usernameField')); } + + /** + * Get the failed login response instance. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * + * @throws ValidationException + */ + protected function sendFailedLoginResponse(Request $request): void + { + $exception = ValidationException::withMessages( + [ + $this->username() => [trans('auth.failed')], + ] + ); + $exception->redirectTo = route('login'); + + throw $exception; + } } diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index 31a0b2cf3a..dd3b54085f 100644 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -46,8 +46,6 @@ use Psr\Container\NotFoundExceptionInterface; * This controller handles the registration of new users as well as their * validation and creation. By default this controller uses a trait to * provide this functionality without requiring any additional code. - * - */ class RegisterController extends Controller { @@ -77,9 +75,8 @@ class RegisterController extends Controller /** * Handle a registration request for the application. * - * @param Request $request - * * @return Application|Redirector|RedirectResponse + * * @throws FireflyException * @throws ValidationException */ @@ -94,7 +91,6 @@ class RegisterController extends Controller throw new FireflyException('Registration is currently not available :('); } - $this->validator($request->all())->validate(); $user = $this->createUser($request->all()); app('log')->info(sprintf('Registered new user %s', $user->email)); @@ -113,38 +109,12 @@ class RegisterController extends Controller return redirect($this->redirectPath()); } - /** - * @return bool - * @throws FireflyException - */ - protected function allowedToRegister(): bool - { - // is allowed to register? - $allowRegistration = true; - try { - $singleUserMode = app('fireflyconfig')->get('single_user_mode', config('firefly.configuration.single_user_mode'))->data; - } catch (ContainerExceptionInterface | NotFoundExceptionInterface $e) { - $singleUserMode = true; - } - $userCount = User::count(); - $guard = config('auth.defaults.guard'); - if (true === $singleUserMode && $userCount > 0 && 'web' === $guard) { - $allowRegistration = false; - } - if ('web' !== $guard) { - $allowRegistration = false; - } - return $allowRegistration; - } - /** * Show the application registration form if the invitation code is valid. * - * * @return Factory|View - * @throws ContainerExceptionInterface + * * @throws FireflyException - * @throws NotFoundExceptionInterface */ public function showInviteForm(Request $request, string $code) { @@ -174,12 +144,9 @@ class RegisterController extends Controller /** * Show the application registration form. * - * @param Request $request - * * @return Factory|View - * @throws ContainerExceptionInterface + * * @throws FireflyException - * @throws NotFoundExceptionInterface */ public function showRegistrationForm(Request $request) { @@ -197,4 +164,29 @@ class RegisterController extends Controller return view('auth.register', compact('isDemoSite', 'email', 'pageTitle')); } + + /** + * @throws FireflyException + */ + protected function allowedToRegister(): bool + { + // is allowed to register? + $allowRegistration = true; + + try { + $singleUserMode = app('fireflyconfig')->get('single_user_mode', config('firefly.configuration.single_user_mode'))->data; + } catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) { + $singleUserMode = true; + } + $userCount = User::count(); + $guard = config('auth.defaults.guard'); + if (true === $singleUserMode && $userCount > 0 && 'web' === $guard) { + $allowRegistration = false; + } + if ('web' !== $guard) { + $allowRegistration = false; + } + + return $allowRegistration; + } } diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php index 5d477d1f7e..3b1fe3148b 100644 --- a/app/Http/Controllers/Auth/ResetPasswordController.php +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -34,8 +34,6 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Password; use Illuminate\Validation\ValidationException; use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class ResetPasswordController @@ -43,8 +41,6 @@ use Psr\Container\NotFoundExceptionInterface; * This controller is responsible for handling password reset requests * and uses a simple trait to include this behavior. You're free to * explore this trait and override any methods you wish to tweak. - * - */ class ResetPasswordController extends Controller { @@ -73,11 +69,9 @@ class ResetPasswordController extends Controller /** * Reset the given user's password. * - * @param Request $request - * * @return Factory|JsonResponse|RedirectResponse|View - * @throws ValidationException * + * @throws ValidationException */ public function reset(Request $request) { @@ -99,7 +93,7 @@ class ResetPasswordController extends Controller // database. Otherwise we will parse the error and return the response. $response = $this->broker()->reset( $this->credentials($request), - function ($user, $password) { + function ($user, $password): void { $this->resetPassword($user, $password); } ); @@ -107,7 +101,7 @@ class ResetPasswordController extends Controller // If the password was successfully reset, we will redirect the user back to // the application's home authenticated view. If there is an error we can // redirect them back to where they came from with their error message. - return $response === Password::PASSWORD_RESET + return Password::PASSWORD_RESET === $response ? $this->sendResetResponse($request, $response) : $this->sendResetFailedResponse($request, $response); } @@ -117,13 +111,11 @@ class ResetPasswordController extends Controller * * If no token is present, display the link request form. * - * @param Request $request - * @param null $token + * @param null $token * * @return Factory|View + * * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function showResetForm(Request $request, $token = null) { diff --git a/app/Http/Controllers/Auth/TwoFactorController.php b/app/Http/Controllers/Auth/TwoFactorController.php index 7098c7bf7d..9d09234da5 100644 --- a/app/Http/Controllers/Auth/TwoFactorController.php +++ b/app/Http/Controllers/Auth/TwoFactorController.php @@ -54,9 +54,7 @@ class TwoFactorController extends Controller } /** - * @param Request $request - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function submitMFA(Request $request) { @@ -101,11 +99,6 @@ class TwoFactorController extends Controller /** * Each MFA history has a timestamp and a code, saving the MFA entries for 5 minutes. So if the * submitted MFA code has been submitted in the last 5 minutes, it won't work despite being valid. - * - * @param string $mfaCode - * @param array $mfaHistory - * - * @return bool */ private function inMFAHistory(string $mfaCode, array $mfaHistory): bool { @@ -143,9 +136,6 @@ class TwoFactorController extends Controller app('preferences')->set('mfa_history', $newHistory); } - /** - * @param string $mfaCode - */ private function addToMFAHistory(string $mfaCode): void { /** @var array $mfaHistory */ @@ -162,10 +152,6 @@ class TwoFactorController extends Controller /** * Checks if code is in users backup codes. - * - * @param string $mfaCode - * - * @return bool */ private function isBackupCode(string $mfaCode): bool { @@ -182,8 +168,6 @@ class TwoFactorController extends Controller /** * Remove the used code from the list of backup codes. - * - * @param string $mfaCode */ private function removeFromBackupCodes(string $mfaCode): void { diff --git a/app/Http/Controllers/Bill/CreateController.php b/app/Http/Controllers/Bill/CreateController.php index 4976785400..ccba7ba6b1 100644 --- a/app/Http/Controllers/Bill/CreateController.php +++ b/app/Http/Controllers/Bill/CreateController.php @@ -44,8 +44,6 @@ class CreateController extends Controller /** * BillController constructor. - * - */ public function __construct() { @@ -66,17 +64,16 @@ class CreateController extends Controller /** * Create a new bill. * - * @param Request $request - * * @return Factory|View */ public function create(Request $request) { $periods = []; + /** @var array $billPeriods */ $billPeriods = config('firefly.bill_periods'); foreach ($billPeriods as $current) { - $periods[$current] = (string)trans('firefly.repeat_freq_' . $current); + $periods[$current] = (string)trans('firefly.repeat_freq_'.$current); } $subTitle = (string)trans('firefly.create_new_bill'); $defaultCurrency = app('amount')->getDefaultCurrency(); @@ -92,17 +89,13 @@ class CreateController extends Controller /** * Store a new bill. - * - * @param BillStoreRequest $request - * - * @return RedirectResponse - * */ public function store(BillStoreRequest $request): RedirectResponse { $billData = $request->getBillData(); $billData['active'] = true; + try { $bill = $this->repository->store($billData); } catch (FireflyException $e) { @@ -114,7 +107,7 @@ class CreateController extends Controller $request->session()->flash('success', (string)trans('firefly.stored_new_bill', ['name' => $bill->name])); app('preferences')->mark(); - /** @var array|null $files */ + /** @var null|array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($bill, $files); diff --git a/app/Http/Controllers/Bill/DeleteController.php b/app/Http/Controllers/Bill/DeleteController.php index 3499e40176..c69aa3d22f 100644 --- a/app/Http/Controllers/Bill/DeleteController.php +++ b/app/Http/Controllers/Bill/DeleteController.php @@ -42,8 +42,6 @@ class DeleteController extends Controller /** * BillController constructor. - * - */ public function __construct() { @@ -65,8 +63,6 @@ class DeleteController extends Controller /** * Delete a bill. * - * @param Bill $bill - * * @return Factory|View */ public function delete(Bill $bill) @@ -81,10 +77,7 @@ class DeleteController extends Controller /** * Destroy a bill. * - * @param Request $request - * @param Bill $bill - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function destroy(Request $request, Bill $bill) { diff --git a/app/Http/Controllers/Bill/EditController.php b/app/Http/Controllers/Bill/EditController.php index b6a8e89735..86d07a048d 100644 --- a/app/Http/Controllers/Bill/EditController.php +++ b/app/Http/Controllers/Bill/EditController.php @@ -44,8 +44,6 @@ class EditController extends Controller /** * BillController constructor. - * - */ public function __construct() { @@ -66,19 +64,17 @@ class EditController extends Controller /** * Edit a bill. * - * @param Request $request - * @param Bill $bill - * * @return Factory|View */ public function edit(Request $request, Bill $bill) { $periods = []; + /** @var array $billPeriods */ $billPeriods = config('firefly.bill_periods'); foreach ($billPeriods as $current) { - $periods[$current] = (string)trans('firefly.' . $current); + $periods[$current] = (string)trans('firefly.'.$current); } $subTitle = (string)trans('firefly.edit_bill', ['name' => $bill->name]); @@ -114,11 +110,6 @@ class EditController extends Controller /** * Update a bill. - * - * @param BillUpdateRequest $request - * @param Bill $bill - * - * @return RedirectResponse */ public function update(BillUpdateRequest $request, Bill $bill): RedirectResponse { @@ -128,7 +119,7 @@ class EditController extends Controller $request->session()->flash('success', (string)trans('firefly.updated_bill', ['name' => $bill->name])); app('preferences')->mark(); - /** @var array|null $files */ + /** @var null|array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($bill, $files); diff --git a/app/Http/Controllers/Bill/IndexController.php b/app/Http/Controllers/Bill/IndexController.php index 60079d3eeb..1052b58895 100644 --- a/app/Http/Controllers/Bill/IndexController.php +++ b/app/Http/Controllers/Bill/IndexController.php @@ -35,8 +35,6 @@ use Illuminate\Contracts\View\View; use Illuminate\Foundation\Application; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; use Symfony\Component\HttpFoundation\ParameterBag; /** @@ -50,8 +48,6 @@ class IndexController extends Controller /** * BillController constructor. - * - */ public function __construct() { @@ -71,7 +67,7 @@ class IndexController extends Controller /** * Show all bills. */ - public function index(): View | Application | Factory | \Illuminate\Contracts\Foundation\Application + public function index(): Application|Factory|\Illuminate\Contracts\Foundation\Application|View { $this->cleanupObjectGroups(); $this->repository->correctOrder(); @@ -85,7 +81,7 @@ class IndexController extends Controller // sub one day from temp start so the last paid date is one day before it should be. $tempStart = clone $start; // 2023-06-23 do not sub one day from temp start, fix is in BillTransformer::payDates instead - //$tempStart->subDay(); + // $tempStart->subDay(); $parameters->set('start', $tempStart); $parameters->set('end', $end); @@ -99,11 +95,12 @@ class IndexController extends Controller // make bill groups: $bills = [ 0 => [ // the index is the order, not the ID. - 'object_group_id' => 0, - 'object_group_title' => (string)trans('firefly.default_group_title_name'), - 'bills' => [], + 'object_group_id' => 0, + 'object_group_title' => (string)trans('firefly.default_group_title_name'), + 'bills' => [], ], ]; + /** @var Bill $bill */ foreach ($collection as $bill) { $array = $transformer->transform($bill); @@ -138,12 +135,25 @@ class IndexController extends Controller } /** - * @param array $bills - * - * @return array + * Set the order of a bill. + */ + public function setOrder(Request $request, Bill $bill): JsonResponse + { + $objectGroupTitle = (string)$request->get('objectGroupTitle'); + $newOrder = (int)$request->get('order'); + $this->repository->setOrder($bill, $newOrder); + if ('' !== $objectGroupTitle) { + $this->repository->setObjectGroup($bill, $objectGroupTitle); + } + if ('' === $objectGroupTitle) { + $this->repository->removeObjectGroup($bill); + } + + return response()->json(['data' => 'OK']); + } + + /** * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ private function getSums(array $bills): array { @@ -169,8 +179,9 @@ class IndexController extends Controller 'period' => $range, 'per_period' => '0', ]; + // only fill in avg when bill is active. - if (count($bill['pay_dates']) > 0) { + if (null !== $bill['next_expected_match']) { $avg = bcdiv(bcadd((string)$bill['amount_min'], (string)$bill['amount_max']), '2'); $avg = bcmul($avg, (string)count($bill['pay_dates'])); $sums[$groupOrder][$currencyId]['avg'] = bcadd($sums[$groupOrder][$currencyId]['avg'], $avg); @@ -179,15 +190,10 @@ class IndexController extends Controller $sums[$groupOrder][$currencyId]['per_period'] = bcadd($sums[$groupOrder][$currencyId]['per_period'], $this->amountPerPeriod($bill, $range)); } } + return $sums; } - /** - * @param array $bill - * @param string $range - * - * @return string - */ private function amountPerPeriod(array $bill, string $range): string { $avg = bcdiv(bcadd((string)$bill['amount_min'], (string)$bill['amount_max']), '2'); @@ -229,17 +235,13 @@ class IndexController extends Controller return $perPeriod; } - /** - * @param array $sums - * - * @return array - */ private function getTotals(array $sums): array { $totals = []; if (count($sums) < 2) { return []; } + /** * @var array $array */ @@ -266,27 +268,4 @@ class IndexController extends Controller return $totals; } - - /** - * Set the order of a bill. - * - * @param Request $request - * @param Bill $bill - * - * @return JsonResponse - */ - public function setOrder(Request $request, Bill $bill): JsonResponse - { - $objectGroupTitle = (string)$request->get('objectGroupTitle'); - $newOrder = (int)$request->get('order'); - $this->repository->setOrder($bill, $newOrder); - if ('' !== $objectGroupTitle) { - $this->repository->setObjectGroup($bill, $objectGroupTitle); - } - if ('' === $objectGroupTitle) { - $this->repository->removeObjectGroup($bill); - } - - return response()->json(['data' => 'OK']); - } } diff --git a/app/Http/Controllers/Bill/ShowController.php b/app/Http/Controllers/Bill/ShowController.php index c59850189c..78ac8b4fec 100644 --- a/app/Http/Controllers/Bill/ShowController.php +++ b/app/Http/Controllers/Bill/ShowController.php @@ -41,8 +41,6 @@ use Illuminate\View\View; use League\Fractal\Manager; use League\Fractal\Resource\Item; use League\Fractal\Serializer\DataArraySerializer; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; use Symfony\Component\HttpFoundation\ParameterBag; /** @@ -54,8 +52,6 @@ class ShowController extends Controller /** * BillController constructor. - * - */ public function __construct() { @@ -77,10 +73,7 @@ class ShowController extends Controller /** * Rescan bills for transactions. * - * @param Request $request - * @param Bill $bill - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function rescan(Request $request, Bill $bill) { @@ -117,20 +110,17 @@ class ShowController extends Controller /** * Show a bill. * - * @param Request $request - * @param Bill $bill - * * @return Factory|View - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function show(Request $request, Bill $bill) { // add info about rules: $rules = $this->repository->getRulesForBill($bill); $subTitle = $bill->name; + /** @var Carbon $start */ $start = session('start'); + /** @var Carbon $end */ $end = session('end'); $year = $start->year; @@ -159,11 +149,11 @@ class ShowController extends Controller $object = $manager->createData($resource)->toArray(); $object['data']['currency'] = $bill->transactionCurrency; - /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setBill($bill)->setLimit($pageSize)->setPage($page)->withBudgetInformation() - ->withCategoryInformation()->withAccountInformation(); + ->withCategoryInformation()->withAccountInformation() + ; $groups = $collector->getPaginatedGroups(); $groups->setPath(route('bills.show', [$bill->id])); @@ -171,7 +161,6 @@ class ShowController extends Controller $collection = $this->repository->getAttachments($bill); $attachments = new Collection(); - if ($collection->count() > 0) { /** @var AttachmentTransformer $transformer */ $transformer = app(AttachmentTransformer::class); @@ -182,7 +171,6 @@ class ShowController extends Controller ); } - return view('bills.show', compact('attachments', 'groups', 'rules', 'yearAverage', 'overallAverage', 'year', 'object', 'bill', 'subTitle')); } } diff --git a/app/Http/Controllers/Budget/BudgetLimitController.php b/app/Http/Controllers/Budget/BudgetLimitController.php index cc9d868ca4..dc7c9dac1d 100644 --- a/app/Http/Controllers/Budget/BudgetLimitController.php +++ b/app/Http/Controllers/Budget/BudgetLimitController.php @@ -43,7 +43,6 @@ use Illuminate\Support\Collection; use Illuminate\View\View; /** - * * Class BudgetLimitController */ class BudgetLimitController extends Controller @@ -76,10 +75,6 @@ class BudgetLimitController extends Controller } /** - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * * @return Factory|View */ public function create(Budget $budget, Carbon $start, Carbon $end) @@ -106,9 +101,7 @@ class BudgetLimitController extends Controller } /** - * @param BudgetLimit $budgetLimit - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function delete(BudgetLimit $budgetLimit) { @@ -121,12 +114,9 @@ class BudgetLimitController extends Controller /** * TODO why redirect AND json response? * - * @param Request $request - * - * @return RedirectResponse|JsonResponse * @throws FireflyException */ - public function store(Request $request): RedirectResponse | JsonResponse + public function store(Request $request): JsonResponse|RedirectResponse { app('log')->debug('Going to store new budget-limit.', $request->all()); // first search for existing one and update it if necessary. @@ -159,6 +149,7 @@ class BudgetLimitController extends Controller if (null !== $limit) { $this->blRepository->destroyBudgetLimit($limit); } + // return empty=ish array: return response()->json([]); } @@ -205,12 +196,6 @@ class BudgetLimitController extends Controller return redirect(route('budgets.index')); } - /** - * @param Request $request - * @param BudgetLimit $budgetLimit - * - * @return JsonResponse - */ public function update(Request $request, BudgetLimit $budgetLimit): JsonResponse { $amount = (string)$request->get('amount'); @@ -229,6 +214,7 @@ class BudgetLimitController extends Controller 'left_per_day_formatted' => app('amount')->formatAnything($currency, '0'), 'transaction_currency_id' => $currency->id, ]; + return response()->json($array); } if ((int)$amount > 268435456) { // 268 million, intentional integer diff --git a/app/Http/Controllers/Budget/CreateController.php b/app/Http/Controllers/Budget/CreateController.php index 451da6fe52..4ca9cd5592 100644 --- a/app/Http/Controllers/Budget/CreateController.php +++ b/app/Http/Controllers/Budget/CreateController.php @@ -44,8 +44,6 @@ class CreateController extends Controller /** * CreateController constructor. - * - */ public function __construct() { @@ -65,8 +63,6 @@ class CreateController extends Controller /** * Form to create a budget. * - * @param Request $request - * * @return Factory|View */ public function create(Request $request) @@ -110,9 +106,6 @@ class CreateController extends Controller /** * Stores a budget. * - * @param BudgetFormStoreRequest $request - * - * @return RedirectResponse * @throws FireflyException */ public function store(BudgetFormStoreRequest $request): RedirectResponse @@ -125,7 +118,7 @@ class CreateController extends Controller app('preferences')->mark(); // store attachment(s): - /** @var array|null $files */ + /** @var null|array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($budget, $files); diff --git a/app/Http/Controllers/Budget/DeleteController.php b/app/Http/Controllers/Budget/DeleteController.php index bb8158395c..a817d0f74a 100644 --- a/app/Http/Controllers/Budget/DeleteController.php +++ b/app/Http/Controllers/Budget/DeleteController.php @@ -33,7 +33,6 @@ use Illuminate\Routing\Redirector; use Illuminate\View\View; /** - * * Class DeleteController */ class DeleteController extends Controller @@ -43,8 +42,6 @@ class DeleteController extends Controller /** * DeleteController constructor. - * - */ public function __construct() { @@ -64,8 +61,6 @@ class DeleteController extends Controller /** * Deletes a budget. * - * @param Budget $budget - * * @return Factory|View */ public function delete(Budget $budget) @@ -81,10 +76,7 @@ class DeleteController extends Controller /** * Destroys a budget. * - * @param Request $request - * @param Budget $budget - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function destroy(Request $request, Budget $budget) { diff --git a/app/Http/Controllers/Budget/EditController.php b/app/Http/Controllers/Budget/EditController.php index 1d0c009ddb..281e372985 100644 --- a/app/Http/Controllers/Budget/EditController.php +++ b/app/Http/Controllers/Budget/EditController.php @@ -35,7 +35,6 @@ use Illuminate\Http\Request; use Illuminate\View\View; /** - * * Class EditController */ class EditController extends Controller @@ -45,8 +44,6 @@ class EditController extends Controller /** * EditController constructor. - * - */ public function __construct() { @@ -67,9 +64,6 @@ class EditController extends Controller /** * Budget edit form. * - * @param Request $request - * @param Budget $budget - * * @return Factory|View */ public function edit(Request $request, Budget $budget) @@ -121,11 +115,6 @@ class EditController extends Controller /** * Budget update routine. - * - * @param BudgetFormUpdateRequest $request - * @param Budget $budget - * - * @return RedirectResponse */ public function update(BudgetFormUpdateRequest $request, Budget $budget): RedirectResponse { @@ -139,7 +128,7 @@ class EditController extends Controller $redirect = redirect($this->getPreviousUrl('budgets.edit.url')); // store new attachment(s): - /** @var array|null $files */ + /** @var null|array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($budget, $files); diff --git a/app/Http/Controllers/Budget/IndexController.php b/app/Http/Controllers/Budget/IndexController.php index 4a8ce7c181..5bf99dfb42 100644 --- a/app/Http/Controllers/Budget/IndexController.php +++ b/app/Http/Controllers/Budget/IndexController.php @@ -41,12 +41,8 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Collection; use Illuminate\View\View; -use JsonException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** - * * Class IndexController */ class IndexController extends Controller @@ -61,8 +57,6 @@ class IndexController extends Controller /** * IndexController constructor. - * - */ public function __construct() { @@ -87,15 +81,10 @@ class IndexController extends Controller /** * Show all budgets. * - * @param Carbon|null $start - * @param Carbon|null $end - * * @return Factory|View + * * @throws FireflyException - * @throws JsonException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ + * */ public function index(Carbon $start = null, Carbon $end = null) { $this->abRepository->cleanup(); @@ -115,7 +104,6 @@ class IndexController extends Controller $end ??= session('end', today(config('app.timezone'))->endOfMonth()); } - $defaultCurrency = app('amount')->getDefaultCurrency(); $currencies = $this->currencyRepository->get(); $budgeted = '0'; @@ -135,7 +123,7 @@ class IndexController extends Controller // get budgeted for default currency: if (0 === count($availableBudgets)) { - $budgeted = $this->blRepository->budgeted($start, $end, $defaultCurrency, ); + $budgeted = $this->blRepository->budgeted($start, $end, $defaultCurrency); $spentArr = $this->opsRepository->sumExpenses($start, $end, null, null, $defaultCurrency); $spent = $spentArr[$defaultCurrency->id]['sum'] ?? '0'; unset($spentArr); @@ -171,17 +159,30 @@ class IndexController extends Controller ); } - /** - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ + public function reorder(Request $request, BudgetRepositoryInterface $repository): JsonResponse + { + $this->abRepository->cleanup(); + $budgetIds = $request->get('budgetIds'); + + foreach ($budgetIds as $index => $budgetId) { + $budgetId = (int)$budgetId; + $budget = $repository->find($budgetId); + if (null !== $budget) { + app('log')->debug(sprintf('Set budget #%d ("%s") to position %d', $budget->id, $budget->name, $index + 1)); + $repository->setBudgetOrder($budget, $index + 1); + } + } + app('preferences')->mark(); + + return response()->json(['OK']); + } + private function getAllAvailableBudgets(Carbon $start, Carbon $end): array { // get all available budgets. $ab = $this->abRepository->get($start, $end); $availableBudgets = []; + // for each, complement with spent amount: /** @var AvailableBudget $entry */ foreach ($ab as $entry) { @@ -194,7 +195,7 @@ class IndexController extends Controller $array['spent'] = $spentArr[$entry->transaction_currency_id]['sum'] ?? '0'; // budgeted in period: - $budgeted = $this->blRepository->budgeted($entry->start_date, $entry->end_date, $entry->transactionCurrency, ); + $budgeted = $this->blRepository->budgeted($entry->start_date, $entry->end_date, $entry->transactionCurrency); $array['budgeted'] = $budgeted; $availableBudgets[] = $array; unset($spentArr); @@ -203,14 +204,6 @@ class IndexController extends Controller return $availableBudgets; } - /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $currencies - * @param TransactionCurrency $defaultCurrency - * - * @return array - */ private function getAllBudgets(Carbon $start, Carbon $end, Collection $currencies, TransactionCurrency $defaultCurrency): array { // get all budgets, and paginate them into $budgets. @@ -229,6 +222,7 @@ class IndexController extends Controller $array['attachments'] = $this->repository->getAttachments($current); $array['auto_budget'] = $this->repository->getAutoBudget($current); $budgetLimits = $this->blRepository->getBudgetLimits($current, $start, $end); + /** @var BudgetLimit $limit */ foreach ($budgetLimits as $limit) { app('log')->debug(sprintf('Working on budget limit #%d', $limit->id)); @@ -264,11 +258,6 @@ class IndexController extends Controller return $budgets; } - /** - * @param array $budgets - * - * @return array - */ private function getSums(array $budgets): array { $sums = [ @@ -284,11 +273,11 @@ class IndexController extends Controller $currencyId = $spent['currency_id']; $sums['spent'][$currencyId] ??= [ - 'amount' => '0', - 'currency_id' => $spent['currency_id'], - 'currency_symbol' => $spent['currency_symbol'], - 'currency_decimal_places' => $spent['currency_decimal_places'], - ]; + 'amount' => '0', + 'currency_id' => $spent['currency_id'], + 'currency_symbol' => $spent['currency_symbol'], + 'currency_decimal_places' => $spent['currency_decimal_places'], + ]; $sums['spent'][$currencyId]['amount'] = bcadd($sums['spent'][$currencyId]['amount'], $spent['spent']); } @@ -297,23 +286,24 @@ class IndexController extends Controller $currencyId = $budgeted['currency_id']; $sums['budgeted'][$currencyId] ??= [ - 'amount' => '0', - 'currency_id' => $budgeted['currency_id'], - 'currency_symbol' => $budgeted['currency_symbol'], - 'currency_decimal_places' => $budgeted['currency_decimal_places'], - ]; + 'amount' => '0', + 'currency_id' => $budgeted['currency_id'], + 'currency_symbol' => $budgeted['currency_symbol'], + 'currency_decimal_places' => $budgeted['currency_decimal_places'], + ]; $sums['budgeted'][$currencyId]['amount'] = bcadd($sums['budgeted'][$currencyId]['amount'], $budgeted['amount']); // also calculate how much left from budgeted: $sums['left'][$currencyId] ??= [ - 'amount' => '0', - 'currency_id' => $budgeted['currency_id'], - 'currency_symbol' => $budgeted['currency_symbol'], - 'currency_decimal_places' => $budgeted['currency_decimal_places'], - ]; + 'amount' => '0', + 'currency_id' => $budgeted['currency_id'], + 'currency_symbol' => $budgeted['currency_symbol'], + 'currency_decimal_places' => $budgeted['currency_decimal_places'], + ]; } } + // final calculation for 'left': /** * @var int $currencyId @@ -326,28 +316,4 @@ class IndexController extends Controller return $sums; } - - /** - * @param Request $request - * @param BudgetRepositoryInterface $repository - * - * @return JsonResponse - */ - public function reorder(Request $request, BudgetRepositoryInterface $repository): JsonResponse - { - $this->abRepository->cleanup(); - $budgetIds = $request->get('budgetIds'); - - foreach ($budgetIds as $index => $budgetId) { - $budgetId = (int)$budgetId; - $budget = $repository->find($budgetId); - if (null !== $budget) { - app('log')->debug(sprintf('Set budget #%d ("%s") to position %d', $budget->id, $budget->name, $index + 1)); - $repository->setBudgetOrder($budget, $index + 1); - } - } - app('preferences')->mark(); - - return response()->json(['OK']); - } } diff --git a/app/Http/Controllers/Budget/ShowController.php b/app/Http/Controllers/Budget/ShowController.php index 9188ddf0bf..4c53d7a4ee 100644 --- a/app/Http/Controllers/Budget/ShowController.php +++ b/app/Http/Controllers/Budget/ShowController.php @@ -37,11 +37,8 @@ use FireflyIII\Support\Http\Controllers\PeriodOverview; use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** - * * Class ShowController */ class ShowController extends Controller @@ -54,8 +51,6 @@ class ShowController extends Controller /** * ShowController constructor. - * - */ public function __construct() { @@ -76,20 +71,15 @@ class ShowController extends Controller /** * Show transactions without a budget. * - * @param Request $request - * @param Carbon|null $start - * @param Carbon|null $end - * * @return Factory|View + * * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function noBudget(Request $request, Carbon $start = null, Carbon $end = null) { - /** @var Carbon $start */ + // @var Carbon $start $start ??= session('start'); - /** @var Carbon $end */ + // @var Carbon $end $end ??= session('end'); $subTitle = trans( 'firefly.without_budget_between', @@ -106,7 +96,8 @@ class ShowController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setLimit($pageSize)->setPage($page) - ->withoutBudget()->withAccountInformation()->withCategoryInformation(); + ->withoutBudget()->withAccountInformation()->withCategoryInformation() + ; $groups = $collector->getPaginatedGroups(); $groups->setPath(route('budgets.no-budget')); @@ -116,11 +107,7 @@ class ShowController extends Controller /** * Shows ALL transactions without a budget. * - * @param Request $request - * * @return Factory|View - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function noBudgetAll(Request $request) { @@ -134,7 +121,8 @@ class ShowController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setLimit($pageSize)->setPage($page) - ->withoutBudget()->withAccountInformation()->withCategoryInformation(); + ->withoutBudget()->withAccountInformation()->withCategoryInformation() + ; $groups = $collector->getPaginatedGroups(); $groups->setPath(route('budgets.no-budget-all')); @@ -144,12 +132,7 @@ class ShowController extends Controller /** * Show a single budget. * - * @param Request $request - * @param Budget $budget - * * @return Factory|View - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function show(Request $request, Budget $budget) { @@ -166,8 +149,9 @@ class ShowController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($allStart, $allEnd)->setBudget($budget) - ->withAccountInformation() - ->setLimit($pageSize)->setPage($page)->withBudgetInformation()->withCategoryInformation(); + ->withAccountInformation() + ->setLimit($pageSize)->setPage($page)->withBudgetInformation()->withCategoryInformation() + ; $groups = $collector->getPaginatedGroups(); $groups->setPath(route('budgets.show', [$budget->id])); @@ -179,14 +163,9 @@ class ShowController extends Controller /** * Show a single budget by a budget limit. * - * @param Request $request - * @param Budget $budget - * @param BudgetLimit $budgetLimit - * * @return Factory|View + * * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function showByBudgetLimit(Request $request, Budget $budget, BudgetLimit $budgetLimit) { @@ -211,9 +190,11 @@ class ShowController extends Controller $collector = app(GroupCollectorInterface::class); $collector->setRange($budgetLimit->start_date, $budgetLimit->end_date)->withAccountInformation() - ->setBudget($budget)->setLimit($pageSize)->setPage($page)->withBudgetInformation()->withCategoryInformation(); + ->setBudget($budget)->setLimit($pageSize)->setPage($page)->withBudgetInformation()->withCategoryInformation() + ; $groups = $collector->getPaginatedGroups(); $groups->setPath(route('budgets.show.limit', [$budget->id, $budgetLimit->id])); + /** @var Carbon $start */ $start = session('first', today(config('app.timezone'))->startOfYear()); $end = today(config('app.timezone')); diff --git a/app/Http/Controllers/Category/CreateController.php b/app/Http/Controllers/Category/CreateController.php index dbd2ed4322..3874304e16 100644 --- a/app/Http/Controllers/Category/CreateController.php +++ b/app/Http/Controllers/Category/CreateController.php @@ -44,8 +44,6 @@ class CreateController extends Controller /** * CategoryController constructor. - * - */ public function __construct() { @@ -66,8 +64,6 @@ class CreateController extends Controller /** * Create category. * - * @param Request $request - * * @return Factory|View */ public function create(Request $request) @@ -84,9 +80,8 @@ class CreateController extends Controller /** * Store new category. * - * @param CategoryFormRequest $request + * @return $this|Redirector|RedirectResponse * - * @return $this|RedirectResponse|Redirector * @throws FireflyException */ public function store(CategoryFormRequest $request) @@ -98,7 +93,7 @@ class CreateController extends Controller app('preferences')->mark(); // store attachment(s): - /** @var array|null $files */ + /** @var null|array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($category, $files); diff --git a/app/Http/Controllers/Category/DeleteController.php b/app/Http/Controllers/Category/DeleteController.php index 78b87dc95e..0c2db15c03 100644 --- a/app/Http/Controllers/Category/DeleteController.php +++ b/app/Http/Controllers/Category/DeleteController.php @@ -42,8 +42,6 @@ class DeleteController extends Controller /** * CategoryController constructor. - * - */ public function __construct() { @@ -63,8 +61,6 @@ class DeleteController extends Controller /** * Delete a category. * - * @param Category $category - * * @return Factory|View */ public function delete(Category $category) @@ -80,10 +76,7 @@ class DeleteController extends Controller /** * Destroy a category. * - * @param Request $request - * @param Category $category - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function destroy(Request $request, Category $category) { diff --git a/app/Http/Controllers/Category/EditController.php b/app/Http/Controllers/Category/EditController.php index c5abab1bcc..0b00ca1918 100644 --- a/app/Http/Controllers/Category/EditController.php +++ b/app/Http/Controllers/Category/EditController.php @@ -44,8 +44,6 @@ class EditController extends Controller /** * CategoryController constructor. - * - */ public function __construct() { @@ -66,9 +64,6 @@ class EditController extends Controller /** * Edit a category. * - * @param Request $request - * @param Category $category - * * @return Factory|View */ public function edit(Request $request, Category $category) @@ -91,10 +86,7 @@ class EditController extends Controller /** * Update category. * - * @param CategoryFormRequest $request - * @param Category $category - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function update(CategoryFormRequest $request, Category $category) { @@ -105,7 +97,7 @@ class EditController extends Controller app('preferences')->mark(); // store new attachment(s): - /** @var array|null $files */ + /** @var null|array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($category, $files); diff --git a/app/Http/Controllers/Category/IndexController.php b/app/Http/Controllers/Category/IndexController.php index 5dc6e902ac..e6b7f8c1f1 100644 --- a/app/Http/Controllers/Category/IndexController.php +++ b/app/Http/Controllers/Category/IndexController.php @@ -31,8 +31,6 @@ use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class IndexController @@ -44,8 +42,6 @@ class IndexController extends Controller /** * CategoryController constructor. - * - */ public function __construct() { @@ -65,11 +61,7 @@ class IndexController extends Controller /** * Show all categories. * - * @param Request $request - * * @return Factory|View - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function index(Request $request) { @@ -80,7 +72,7 @@ class IndexController extends Controller $collection = $collection->slice(($page - 1) * $pageSize, $pageSize); $collection->each( - function (Category $category) { + function (Category $category): void { $category->lastActivity = $this->repository->lastUseDate($category, new Collection()); } ); diff --git a/app/Http/Controllers/Category/NoCategoryController.php b/app/Http/Controllers/Category/NoCategoryController.php index 00eaa19cc6..f0e123b480 100644 --- a/app/Http/Controllers/Category/NoCategoryController.php +++ b/app/Http/Controllers/Category/NoCategoryController.php @@ -34,11 +34,8 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\Support\Collection; use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** - * * Class NoCategoryController */ class NoCategoryController extends Controller @@ -49,8 +46,6 @@ class NoCategoryController extends Controller /** * CategoryController constructor. - * - */ public function __construct() { @@ -71,21 +66,16 @@ class NoCategoryController extends Controller /** * Show transactions without a category. * - * @param Request $request - * @param Carbon|null $start - * @param Carbon|null $end - * * @return Factory|View + * * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function show(Request $request, Carbon $start = null, Carbon $end = null) { app('log')->debug('Start of noCategory()'); - /** @var Carbon $start */ + // @var Carbon $start $start ??= session('start'); - /** @var Carbon $end */ + // @var Carbon $end $end ??= session('end'); $page = (int)$request->get('page'); $pageSize = (int)app('preferences')->get('listPageSize', 50)->data; @@ -101,9 +91,10 @@ class NoCategoryController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end) - ->setLimit($pageSize)->setPage($page)->withoutCategory() - ->withAccountInformation()->withBudgetInformation() - ->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]); + ->setLimit($pageSize)->setPage($page)->withoutCategory() + ->withAccountInformation()->withBudgetInformation() + ->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]) + ; $groups = $collector->getPaginatedGroups(); $groups->setPath(route('categories.no-category', [$start->format('Y-m-d'), $end->format('Y-m-d')])); @@ -113,11 +104,7 @@ class NoCategoryController extends Controller /** * Show all transactions without a category. * - * @param Request $request - * * @return Factory|View - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function showAll(Request $request) { @@ -138,8 +125,9 @@ class NoCategoryController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutCategory() - ->withAccountInformation()->withBudgetInformation() - ->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]); + ->withAccountInformation()->withBudgetInformation() + ->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]) + ; $groups = $collector->getPaginatedGroups(); $groups->setPath(route('categories.no-category.all')); diff --git a/app/Http/Controllers/Category/ShowController.php b/app/Http/Controllers/Category/ShowController.php index 2e78674a41..44d56a0266 100644 --- a/app/Http/Controllers/Category/ShowController.php +++ b/app/Http/Controllers/Category/ShowController.php @@ -34,13 +34,9 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\Support\Collection; use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** - * * Class ShowController - * */ class ShowController extends Controller { @@ -51,8 +47,6 @@ class ShowController extends Controller /** * CategoryController constructor. - * - */ public function __construct() { @@ -73,21 +67,15 @@ class ShowController extends Controller /** * Show a single category. * - * @param Request $request - * @param Category $category - * @param Carbon|null $start - * @param Carbon|null $end - * * @return Factory|View + * * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function show(Request $request, Category $category, Carbon $start = null, Carbon $end = null) { - /** @var Carbon $start */ + // @var Carbon $start $start ??= session('start', today(config('app.timezone'))->startOfMonth()); - /** @var Carbon $end */ + // @var Carbon $end $end ??= session('end', today(config('app.timezone'))->endOfMonth()); $subTitleIcon = 'fa-bookmark'; $page = (int)$request->get('page'); @@ -108,8 +96,9 @@ class ShowController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->setLimit($pageSize)->setPage($page) - ->withAccountInformation() - ->setCategory($category)->withBudgetInformation()->withCategoryInformation(); + ->withAccountInformation() + ->setCategory($category)->withBudgetInformation()->withCategoryInformation() + ; $groups = $collector->getPaginatedGroups(); $groups->setPath($path); @@ -120,12 +109,7 @@ class ShowController extends Controller /** * Show all transactions within a category. * - * @param Request $request - * @param Category $category - * * @return Factory|View - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function showAll(Request $request, Category $category) { @@ -139,6 +123,7 @@ class ShowController extends Controller $subTitle = (string)trans('firefly.all_journals_for_category', ['name' => $category->name]); $first = $this->repository->firstUseDate($category); + /** @var Carbon $start */ $start = $first ?? today(config('app.timezone')); $end = today(config('app.timezone')); @@ -148,8 +133,9 @@ class ShowController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->setLimit($pageSize)->setPage($page) - ->withAccountInformation() - ->setCategory($category)->withBudgetInformation()->withCategoryInformation(); + ->withAccountInformation() + ->setCategory($category)->withBudgetInformation()->withCategoryInformation() + ; $groups = $collector->getPaginatedGroups(); $groups->setPath($path); diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php index 4225290114..e77745adeb 100644 --- a/app/Http/Controllers/Chart/AccountController.php +++ b/app/Http/Controllers/Chart/AccountController.php @@ -40,13 +40,9 @@ use FireflyIII\Support\Http\Controllers\ChartGeneration; use FireflyIII\Support\Http\Controllers\DateCalculation; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; -use JsonException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class AccountController. - * */ class AccountController extends Controller { @@ -60,8 +56,6 @@ class AccountController extends Controller /** * AccountController constructor. - * - */ public function __construct() { @@ -82,14 +76,12 @@ class AccountController extends Controller * Shows the balances for all the user's expense accounts (on the front page). * * This chart is (multi) currency aware. - * - * @return JsonResponse - * @throws JsonException */ public function expenseAccounts(): JsonResponse { /** @var Carbon $start */ $start = clone session('start', today(config('app.timezone'))->startOfMonth()); + /** @var Carbon $end */ $end = clone session('end', today(config('app.timezone'))->endOfMonth()); $cache = new CacheProperties(); @@ -150,12 +142,12 @@ class AccountController extends Controller foreach ($currencies as $currencyId => $currency) { $dataSet = [ - 'label' => (string)trans('firefly.spent'), - 'type' => 'bar', - 'currency_symbol' => $currency->symbol, - 'currency_code' => $currency->code, - 'entries' => $this->expandNames($tempData), - ]; + 'label' => (string)trans('firefly.spent'), + 'type' => 'bar', + 'currency_symbol' => $currency->symbol, + 'currency_code' => $currency->code, + 'entries' => $this->expandNames($tempData), + ]; $chartData[$currencyId] = $dataSet; } @@ -174,11 +166,6 @@ class AccountController extends Controller /** * Expenses per budget for all time, as shown on account overview. - * - * @param AccountRepositoryInterface $repository - * @param Account $account - * - * @return JsonResponse */ public function expenseBudgetAll(AccountRepositoryInterface $repository, Account $account): JsonResponse { @@ -190,12 +177,6 @@ class AccountController extends Controller /** * Expenses per budget, as shown on account overview. - * - * @param Account $account - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse */ public function expenseBudget(Account $account, Carbon $start, Carbon $end): JsonResponse { @@ -207,6 +188,7 @@ class AccountController extends Controller if ($cache->has()) { return response()->json($cache->get()); } + /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withBudgetInformation()->setTypes([TransactionType::WITHDRAWAL]); @@ -214,6 +196,7 @@ class AccountController extends Controller $chartData = []; $result = []; $budgetIds = []; + /** @var array $journal */ foreach ($journals as $journal) { $budgetId = (int)$journal['budget_id']; @@ -248,11 +231,6 @@ class AccountController extends Controller /** * Expenses grouped by category for account. - * - * @param AccountRepositoryInterface $repository - * @param Account $account - * - * @return JsonResponse */ public function expenseCategoryAll(AccountRepositoryInterface $repository, Account $account): JsonResponse { @@ -264,12 +242,6 @@ class AccountController extends Controller /** * Expenses per category for one single account. - * - * @param Account $account - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse */ public function expenseCategory(Account $account, Carbon $start, Carbon $end): JsonResponse { @@ -321,14 +293,8 @@ class AccountController extends Controller /** * Shows the balances for all the user's frontpage accounts. * - * @param AccountRepositoryInterface $repository - * - * @return JsonResponse * @throws FireflyException - * @throws JsonException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ + * */ public function frontpage(AccountRepositoryInterface $repository): JsonResponse { $start = clone session('start', today(config('app.timezone'))->startOfMonth()); @@ -349,11 +315,6 @@ class AccountController extends Controller /** * Shows the income grouped by category for an account, in all time. - * - * @param AccountRepositoryInterface $repository - * @param Account $account - * - * @return JsonResponse */ public function incomeCategoryAll(AccountRepositoryInterface $repository, Account $account): JsonResponse { @@ -365,12 +326,6 @@ class AccountController extends Controller /** * Shows all income per account for each category. - * - * @param Account $account - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse */ public function incomeCategory(Account $account, Carbon $start, Carbon $end): JsonResponse { @@ -391,6 +346,7 @@ class AccountController extends Controller $journals = $collector->getExtractedJournals(); $result = []; $chartData = []; + /** @var array $journal */ foreach ($journals as $journal) { $key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']); @@ -422,14 +378,7 @@ class AccountController extends Controller /** * Shows overview of account during a single period. * - * @param Account $account - * @param Carbon $start - * - * @param Carbon $end - * - * @return JsonResponse * @throws FireflyException - * @throws JsonException */ public function period(Account $account, Carbon $start, Carbon $end): JsonResponse { @@ -460,78 +409,12 @@ class AccountController extends Controller return response()->json($data); } - /** - * @param Carbon $start - * @param Carbon $end - * @param Account $account - * @param TransactionCurrency $currency - * - * @return array - * @throws FireflyException - * @throws JsonException - */ - private function periodByCurrency(Carbon $start, Carbon $end, Account $account, TransactionCurrency $currency): array - { - app('log')->debug(sprintf('Now in periodByCurrency("%s", "%s", %s, "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'), $account->id, $currency->code)); - $locale = app('steam')->getLocale(); - $step = $this->calculateStep($start, $end); - $result = [ - 'label' => sprintf('%s (%s)', $account->name, $currency->symbol), - 'currency_symbol' => $currency->symbol, - 'currency_code' => $currency->code, - ]; - $entries = []; - $current = clone $start; - app('log')->debug(sprintf('Step is %s', $step)); - - // fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041 - // have to make sure this chart is always based on the balance at the END of the period. - // This period depends on the size of the chart - $current = app('navigation')->endOfX($current, $step, null); - app('log')->debug(sprintf('$current date is %s', $current->format('Y-m-d'))); - if ('1D' === $step) { - // per day the entire period, balance for every day. - $format = (string)trans('config.month_and_day_js', [], $locale); - $range = app('steam')->balanceInRange($account, $start, $end, $currency); - $previous = array_values($range)[0]; - while ($end >= $current) { - $theDate = $current->format('Y-m-d'); - $balance = $range[$theDate] ?? $previous; - $label = $current->isoFormat($format); - $entries[$label] = (float)$balance; - $previous = $balance; - $current->addDay(); - } - } - if ('1W' === $step || '1M' === $step || '1Y' === $step) { - while ($end >= $current) { - app('log')->debug(sprintf('Current is: %s', $current->format('Y-m-d'))); - $balance = (float)app('steam')->balance($account, $current, $currency); - $label = app('navigation')->periodShow($current, $step); - $entries[$label] = $balance; - $current = app('navigation')->addPeriod($current, $step, 0); - // here too, to fix #8041, the data is corrected to the end of the period. - $current = app('navigation')->endOfX($current, $step, null); - } - } - $result['entries'] = $entries; - - return $result; - } - /** * Shows the balances for a given set of dates and accounts. * * TODO this chart is not multi currency aware. * - * @param Collection $accounts - * - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse * @throws FireflyException - * @throws JsonException */ public function report(Collection $accounts, Carbon $start, Carbon $end): JsonResponse { @@ -542,14 +425,12 @@ class AccountController extends Controller * Shows the balances for all the user's revenue accounts. * * This chart is multi-currency aware. - * - * @return JsonResponse - * @throws JsonException */ public function revenueAccounts(): JsonResponse { /** @var Carbon $start */ $start = clone session('start', today(config('app.timezone'))->startOfMonth()); + /** @var Carbon $end */ $end = clone session('end', today(config('app.timezone'))->endOfMonth()); $cache = new CacheProperties(); @@ -610,12 +491,12 @@ class AccountController extends Controller foreach ($currencies as $currencyId => $currency) { $dataSet = [ - 'label' => (string)trans('firefly.earned'), - 'type' => 'bar', - 'currency_symbol' => $currency->symbol, - 'currency_code' => $currency->code, - 'entries' => $this->expandNames($tempData), - ]; + 'label' => (string)trans('firefly.earned'), + 'type' => 'bar', + 'currency_symbol' => $currency->symbol, + 'currency_code' => $currency->code, + 'entries' => $this->expandNames($tempData), + ]; $chartData[$currencyId] = $dataSet; } @@ -631,4 +512,56 @@ class AccountController extends Controller return response()->json($data); } + + /** + * @throws FireflyException + */ + private function periodByCurrency(Carbon $start, Carbon $end, Account $account, TransactionCurrency $currency): array + { + app('log')->debug(sprintf('Now in periodByCurrency("%s", "%s", %s, "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'), $account->id, $currency->code)); + $locale = app('steam')->getLocale(); + $step = $this->calculateStep($start, $end); + $result = [ + 'label' => sprintf('%s (%s)', $account->name, $currency->symbol), + 'currency_symbol' => $currency->symbol, + 'currency_code' => $currency->code, + ]; + $entries = []; + $current = clone $start; + app('log')->debug(sprintf('Step is %s', $step)); + + // fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041 + // have to make sure this chart is always based on the balance at the END of the period. + // This period depends on the size of the chart + $current = app('navigation')->endOfX($current, $step, null); + app('log')->debug(sprintf('$current date is %s', $current->format('Y-m-d'))); + if ('1D' === $step) { + // per day the entire period, balance for every day. + $format = (string)trans('config.month_and_day_js', [], $locale); + $range = app('steam')->balanceInRange($account, $start, $end, $currency); + $previous = array_values($range)[0]; + while ($end >= $current) { + $theDate = $current->format('Y-m-d'); + $balance = $range[$theDate] ?? $previous; + $label = $current->isoFormat($format); + $entries[$label] = (float)$balance; + $previous = $balance; + $current->addDay(); + } + } + if ('1W' === $step || '1M' === $step || '1Y' === $step) { + while ($end >= $current) { + app('log')->debug(sprintf('Current is: %s', $current->format('Y-m-d'))); + $balance = (float)app('steam')->balance($account, $current, $currency); + $label = app('navigation')->periodShow($current, $step); + $entries[$label] = $balance; + $current = app('navigation')->addPeriod($current, $step, 0); + // here too, to fix #8041, the data is corrected to the end of the period. + $current = app('navigation')->endOfX($current, $step, null); + } + } + $result['entries'] = $entries; + + return $result; + } } diff --git a/app/Http/Controllers/Chart/BillController.php b/app/Http/Controllers/Chart/BillController.php index 7ee1e0221b..e2319d6572 100644 --- a/app/Http/Controllers/Chart/BillController.php +++ b/app/Http/Controllers/Chart/BillController.php @@ -41,8 +41,6 @@ class BillController extends Controller /** * BillController constructor. - * - */ public function __construct() { @@ -52,10 +50,6 @@ class BillController extends Controller /** * Shows all bills and whether or not they've been paid this month (pie chart). - * - * @param BillRepositoryInterface $repository - * - * @return JsonResponse */ public function frontpage(BillRepositoryInterface $repository): JsonResponse { @@ -85,6 +79,7 @@ class BillController extends Controller 'currency_code' => $info['code'], ]; } + /** * @var array $info */ @@ -107,9 +102,6 @@ class BillController extends Controller /** * Shows overview for a single bill. * - * @param Bill $bill - * - * @return JsonResponse * @throws FireflyException */ public function single(Bill $bill): JsonResponse @@ -179,7 +171,6 @@ class BillController extends Controller $amount = bcmul($journal['foreign_amount'], '-1'); } - $chartData[2]['entries'][$date] = bcadd($chartData[2]['entries'][$date], $amount); // amount of journal } diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index e58755eb82..031ce9f6a5 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -45,7 +45,6 @@ use Illuminate\Support\Collection; /** * Class BudgetController. - * */ class BudgetController extends Controller { @@ -60,8 +59,6 @@ class BudgetController extends Controller /** * BudgetController constructor. - * - */ public function __construct() { @@ -82,15 +79,12 @@ class BudgetController extends Controller /** * Shows overview of a single budget. - * - * @param Budget $budget - * - * @return JsonResponse */ public function budget(Budget $budget): JsonResponse { /** @var Carbon $start */ $start = $this->repository->firstUseDate($budget) ?? session('start', today(config('app.timezone'))); + /** @var Carbon $end */ $end = session('end', today(config('app.timezone'))); $cache = new CacheProperties(); @@ -148,11 +142,6 @@ class BudgetController extends Controller /** * Shows the amount left in a specific budget limit. * - * @param Budget $budget - * @param BudgetLimit $budgetLimit - * - * @return JsonResponse - * * @throws FireflyException */ public function budgetLimit(Budget $budget, BudgetLimit $budgetLimit): JsonResponse @@ -199,11 +188,6 @@ class BudgetController extends Controller /** * Shows how much is spent per asset account. - * - * @param Budget $budget - * @param BudgetLimit|null $budgetLimit - * - * @return JsonResponse */ public function expenseAsset(Budget $budget, ?BudgetLimit $budgetLimit = null): JsonResponse { @@ -253,10 +237,10 @@ class BudgetController extends Controller $title = sprintf('%s (%s)', $names[$assetId] ?? '(empty)', $info['currency_name']); $chartData[$title] = [ - 'amount' => $info['amount'], - 'currency_symbol' => $info['currency_symbol'], - 'currency_code' => $info['currency_code'], - ]; + 'amount' => $info['amount'], + 'currency_symbol' => $info['currency_symbol'], + 'currency_code' => $info['currency_code'], + ]; } $data = $this->generator->multiCurrencyPieChart($chartData); @@ -267,11 +251,6 @@ class BudgetController extends Controller /** * Shows how much is spent per category. - * - * @param Budget $budget - * @param BudgetLimit|null $budgetLimit - * - * @return JsonResponse */ public function expenseCategory(Budget $budget, ?BudgetLimit $budgetLimit = null): JsonResponse { @@ -330,12 +309,6 @@ class BudgetController extends Controller /** * Shows how much is spent per expense account. - * - * - * @param Budget $budget - * @param BudgetLimit|null $budgetLimit - * - * @return JsonResponse */ public function expenseExpense(Budget $budget, ?BudgetLimit $budgetLimit = null): JsonResponse { @@ -364,6 +337,7 @@ class BudgetController extends Controller $journals = $collector->getExtractedJournals(); $result = []; $chartData = []; + /** @var array $journal */ foreach ($journals as $journal) { $key = sprintf('%d-%d', $journal['destination_account_id'], $journal['currency_id']); @@ -397,8 +371,6 @@ class BudgetController extends Controller /** * Shows a budget list with spent/left/overspent. - * - * @return JsonResponse */ public function frontpage(): JsonResponse { @@ -430,15 +402,8 @@ class BudgetController extends Controller * Shows a budget overview chart (spent and budgeted). * * Suppress warning because this method will be replaced by API calls. + * * @SuppressWarnings(PHPMD.ExcessiveParameterList) - * - * @param Budget $budget - * @param TransactionCurrency $currency - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse */ public function period(Budget $budget, TransactionCurrency $currency, Collection $accounts, Carbon $start, Carbon $end): JsonResponse { @@ -507,13 +472,6 @@ class BudgetController extends Controller /** * Shows a chart for transactions without a budget. - * - * @param TransactionCurrency $currency - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse */ public function periodNoBudget(TransactionCurrency $currency, Collection $accounts, Carbon $start, Carbon $end): JsonResponse { diff --git a/app/Http/Controllers/Chart/BudgetReportController.php b/app/Http/Controllers/Chart/BudgetReportController.php index 9be5621994..2d5a3b16dd 100644 --- a/app/Http/Controllers/Chart/BudgetReportController.php +++ b/app/Http/Controllers/Chart/BudgetReportController.php @@ -37,7 +37,6 @@ use Illuminate\Support\Collection; * Separate controller because many helper functions are shared. * * Class BudgetReportController - * */ class BudgetReportController extends Controller { @@ -52,8 +51,6 @@ class BudgetReportController extends Controller /** * BudgetReportController constructor. - * - */ public function __construct() { @@ -70,13 +67,6 @@ class BudgetReportController extends Controller /** * Chart that groups the expenses by budget. - * - * @param Collection $accounts - * @param Collection $budgets - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse */ public function budgetExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end): JsonResponse { @@ -106,13 +96,6 @@ class BudgetReportController extends Controller /** * Chart that groups the expenses by budget. - * - * @param Collection $accounts - * @param Collection $budgets - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse */ public function categoryExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end): JsonResponse { @@ -143,13 +126,6 @@ class BudgetReportController extends Controller /** * Chart that groups expenses by the account. - * - * @param Collection $accounts - * @param Collection $budgets - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse */ public function destinationAccountExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end): JsonResponse { @@ -180,13 +156,6 @@ class BudgetReportController extends Controller /** * Main overview of a budget in the budget report. - * - * @param Collection $accounts - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse */ public function mainChart(Collection $accounts, Budget $budget, Carbon $start, Carbon $end): JsonResponse { @@ -226,38 +195,8 @@ class BudgetReportController extends Controller return response()->json($data); } - /** - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - private function makeEntries(Carbon $start, Carbon $end): array - { - $return = []; - $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end); - $preferredRange = app('navigation')->preferredRangeFormat($start, $end); - $currentStart = clone $start; - while ($currentStart <= $end) { - $currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange); - $key = $currentStart->isoFormat($format); - $return[$key] = '0'; - $currentStart = clone $currentEnd; - $currentStart->addDay()->startOfDay(); - } - - return $return; - } - /** * Chart that groups expenses by the account. - * - * @param Collection $accounts - * @param Collection $budgets - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse */ public function sourceAccountExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end): JsonResponse { @@ -285,4 +224,21 @@ class BudgetReportController extends Controller return response()->json($data); } + + private function makeEntries(Carbon $start, Carbon $end): array + { + $return = []; + $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end); + $preferredRange = app('navigation')->preferredRangeFormat($start, $end); + $currentStart = clone $start; + while ($currentStart <= $end) { + $currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange); + $key = $currentStart->isoFormat($format); + $return[$key] = '0'; + $currentStart = clone $currentEnd; + $currentStart->addDay()->startOfDay(); + } + + return $return; + } } diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 087edaeb40..1462f2046b 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -39,8 +39,6 @@ use FireflyIII\Support\Http\Controllers\ChartGeneration; use FireflyIII\Support\Http\Controllers\DateCalculation; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class CategoryController. @@ -56,8 +54,6 @@ class CategoryController extends Controller /** * CategoryController constructor. - * - */ public function __construct() { @@ -70,12 +66,7 @@ class CategoryController extends Controller * Show an overview for a category for all time, per month/week/year. * TODO test method, for category refactor. * - * @param Category $category - * - * @return JsonResponse * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function all(Category $category): JsonResponse { @@ -86,6 +77,7 @@ class CategoryController extends Controller if ($cache->has()) { return response()->json($cache->get()); } + /** @var CategoryRepositoryInterface $repository */ $repository = app(CategoryRepositoryInterface::class); $start = $repository->firstUseDate($category) ?? $this->getDate(); @@ -102,19 +94,9 @@ class CategoryController extends Controller return response()->json($data); } - /** - * @return Carbon - */ - private function getDate(): Carbon - { - return today(config('app.timezone')); - } - /** * Shows the category chart on the front page. * TODO test method for category refactor. - * - * @return JsonResponse */ public function frontPage(): JsonResponse { @@ -140,13 +122,6 @@ class CategoryController extends Controller /** * Chart report. * TODO test method for category refactor. - * - * @param Category $category - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse */ public function reportPeriod(Category $category, Collection $accounts, Carbon $start, Carbon $end): JsonResponse { @@ -166,15 +141,68 @@ class CategoryController extends Controller return response()->json($data); } + /** + * Chart for period for transactions without a category. + * TODO test me. + */ + public function reportPeriodNoCategory(Collection $accounts, Carbon $start, Carbon $end): JsonResponse + { + $cache = new CacheProperties(); + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('chart.category.period.no-cat'); + $cache->addProperty($accounts->pluck('id')->toArray()); + if ($cache->has()) { + return response()->json($cache->get()); + } + $data = $this->reportPeriodChart($accounts, $start, $end, null); + + $cache->store($data); + + return response()->json($data); + } + + /** + * Chart for a specific period. + * TODO test me, for category refactor. + * + * @throws FireflyException + */ + public function specificPeriod(Category $category, Carbon $date): JsonResponse + { + $range = app('navigation')->getViewRange(false); + $start = app('navigation')->startOfPeriod($date, $range); + $end = session()->get('end'); + if ($end < $start) { + [$end, $start] = [$start, $end]; + } + + $cache = new CacheProperties(); + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty($category->id); + $cache->addProperty('chart.category.period-chart'); + if ($cache->has()) { + return response()->json($cache->get()); + } + + /** @var WholePeriodChartGenerator $chartGenerator */ + $chartGenerator = app(WholePeriodChartGenerator::class); + $chartData = $chartGenerator->generate($category, $start, $end); + $data = $this->generator->multiSet($chartData); + + $cache->store($data); + + return response()->json($data); + } + + private function getDate(): Carbon + { + return today(config('app.timezone')); + } + /** * Generate report chart for either with or without category. - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * @param Category|null $category - * - * @return array */ private function reportPeriodChart(Collection $accounts, Carbon $start, Carbon $end, ?Category $category): array { @@ -211,19 +239,19 @@ class CategoryController extends Controller $inKey = sprintf('%d-in', $currencyId); $chartData[$outKey] = [ - 'label' => sprintf('%s (%s)', (string)trans('firefly.spent'), $currencyInfo['currency_name']), - 'entries' => [], - 'type' => 'bar', - 'backgroundColor' => 'rgba(219, 68, 55, 0.5)', // red - ]; + 'label' => sprintf('%s (%s)', (string)trans('firefly.spent'), $currencyInfo['currency_name']), + 'entries' => [], + 'type' => 'bar', + 'backgroundColor' => 'rgba(219, 68, 55, 0.5)', // red + ]; $chartData[$inKey] = [ - 'label' => sprintf('%s (%s)', (string)trans('firefly.earned'), $currencyInfo['currency_name']), - 'entries' => [], - 'type' => 'bar', - 'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green - ]; + 'label' => sprintf('%s (%s)', (string)trans('firefly.earned'), $currencyInfo['currency_name']), + 'entries' => [], + 'type' => 'bar', + 'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green + ]; // loop empty periods: foreach (array_keys($periods) as $period) { $label = $periods[$period]; @@ -251,71 +279,4 @@ class CategoryController extends Controller return $this->generator->multiSet($chartData); } - - /** - * Chart for period for transactions without a category. - * TODO test me. - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ - public function reportPeriodNoCategory(Collection $accounts, Carbon $start, Carbon $end): JsonResponse - { - $cache = new CacheProperties(); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty('chart.category.period.no-cat'); - $cache->addProperty($accounts->pluck('id')->toArray()); - if ($cache->has()) { - return response()->json($cache->get()); - } - $data = $this->reportPeriodChart($accounts, $start, $end, null); - - $cache->store($data); - - return response()->json($data); - } - - /** - * Chart for a specific period. - * TODO test me, for category refactor. - * - * @param Category $category - * @param Carbon $date - * - * @return JsonResponse - * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ - public function specificPeriod(Category $category, Carbon $date): JsonResponse - { - $range = app('navigation')->getViewRange(false); - $start = app('navigation')->startOfPeriod($date, $range); - $end = session()->get('end'); - if ($end < $start) { - [$end, $start] = [$start, $end]; - } - - $cache = new CacheProperties(); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($category->id); - $cache->addProperty('chart.category.period-chart'); - if ($cache->has()) { - return response()->json($cache->get()); - } - - /** @var WholePeriodChartGenerator $chartGenerator */ - $chartGenerator = app(WholePeriodChartGenerator::class); - $chartData = $chartGenerator->generate($category, $start, $end); - $data = $this->generator->multiSet($chartData); - - $cache->store($data); - - return response()->json($data); - } } diff --git a/app/Http/Controllers/Chart/CategoryReportController.php b/app/Http/Controllers/Chart/CategoryReportController.php index 25f87a46c3..dc44a64947 100644 --- a/app/Http/Controllers/Chart/CategoryReportController.php +++ b/app/Http/Controllers/Chart/CategoryReportController.php @@ -45,13 +45,12 @@ class CategoryReportController extends Controller /** @var GeneratorInterface Chart generation methods. */ private $generator; + /** @var OperationsRepositoryInterface */ private $opsRepository; /** * CategoryReportController constructor. - * - */ public function __construct() { @@ -66,14 +65,6 @@ class CategoryReportController extends Controller ); } - /** - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function budgetExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -102,14 +93,6 @@ class CategoryReportController extends Controller return response()->json($data); } - /** - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function categoryExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -137,14 +120,6 @@ class CategoryReportController extends Controller return response()->json($data); } - /** - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function categoryIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -172,14 +147,6 @@ class CategoryReportController extends Controller return response()->json($data); } - /** - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function destinationExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -208,14 +175,6 @@ class CategoryReportController extends Controller return response()->json($data); } - /** - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function destinationIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -244,15 +203,6 @@ class CategoryReportController extends Controller return response()->json($data); } - /** - * @param Collection $accounts - * @param Category $category - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - * - */ public function mainChart(Collection $accounts, Category $category, Carbon $start, Carbon $end): JsonResponse { $chartData = []; @@ -319,39 +269,6 @@ class CategoryReportController extends Controller return response()->json($data); } - /** - * TODO duplicate function - * - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - private function makeEntries(Carbon $start, Carbon $end): array - { - $return = []; - $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end); - $preferredRange = app('navigation')->preferredRangeFormat($start, $end); - $currentStart = clone $start; - while ($currentStart <= $end) { - $currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange); - $key = $currentStart->isoFormat($format); - $return[$key] = '0'; - $currentStart = clone $currentEnd; - $currentStart->addDay()->startOfDay(); - } - - return $return; - } - - /** - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function sourceExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -380,14 +297,6 @@ class CategoryReportController extends Controller return response()->json($data); } - /** - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function sourceIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -415,4 +324,24 @@ class CategoryReportController extends Controller return response()->json($data); } + + /** + * TODO duplicate function + */ + private function makeEntries(Carbon $start, Carbon $end): array + { + $return = []; + $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end); + $preferredRange = app('navigation')->preferredRangeFormat($start, $end); + $currentStart = clone $start; + while ($currentStart <= $end) { + $currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange); + $key = $currentStart->isoFormat($format); + $return[$key] = '0'; + $currentStart = clone $currentEnd; + $currentStart->addDay()->startOfDay(); + } + + return $return; + } } diff --git a/app/Http/Controllers/Chart/DoubleReportController.php b/app/Http/Controllers/Chart/DoubleReportController.php index 7e006af206..12e588e416 100644 --- a/app/Http/Controllers/Chart/DoubleReportController.php +++ b/app/Http/Controllers/Chart/DoubleReportController.php @@ -33,13 +33,13 @@ use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; /** - * * Class DoubleReportController */ class DoubleReportController extends Controller { /** @var GeneratorInterface Chart generation methods. */ private $generator; + /** @var OperationsRepositoryInterface */ private $opsRepository; @@ -48,8 +48,6 @@ class DoubleReportController extends Controller /** * CategoryReportController constructor. - * - */ public function __construct() { @@ -65,14 +63,6 @@ class DoubleReportController extends Controller ); } - /** - * @param Collection $accounts - * @param Collection $others - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function budgetExpense(Collection $accounts, Collection $others, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -100,14 +90,6 @@ class DoubleReportController extends Controller return response()->json($data); } - /** - * @param Collection $accounts - * @param Collection $others - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function categoryExpense(Collection $accounts, Collection $others, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -135,14 +117,6 @@ class DoubleReportController extends Controller return response()->json($data); } - /** - * @param Collection $accounts - * @param Collection $others - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function categoryIncome(Collection $accounts, Collection $others, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -170,15 +144,6 @@ class DoubleReportController extends Controller return response()->json($data); } - /** - * @param Collection $accounts - * @param Account $account - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - * - */ public function mainChart(Collection $accounts, Account $account, Carbon $start, Carbon $end): JsonResponse { $chartData = []; @@ -247,64 +212,6 @@ class DoubleReportController extends Controller return response()->json($data); } - /** - * TODO duplicate function - * - * @param Collection $accounts - * @param int $id - * @param string $name - * @param null|string $iban - * - * @return string - */ - private function getCounterpartName(Collection $accounts, int $id, string $name, ?string $iban): string - { - /** @var Account $account */ - foreach ($accounts as $account) { - if ($account->name === $name && $account->id !== $id) { - return $account->name; - } - if (null !== $account->iban && $account->iban === $iban && $account->id !== $id) { - return $account->iban; - } - } - - return $name; - } - - /** - * TODO duplicate function - * - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - private function makeEntries(Carbon $start, Carbon $end): array - { - $return = []; - $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end); - $preferredRange = app('navigation')->preferredRangeFormat($start, $end); - $currentStart = clone $start; - while ($currentStart <= $end) { - $currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange); - $key = $currentStart->isoFormat($format); - $return[$key] = '0'; - $currentStart = clone $currentEnd; - $currentStart->addDay()->startOfDay(); - } - - return $return; - } - - /** - * @param Collection $accounts - * @param Collection $others - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function tagExpense(Collection $accounts, Collection $others, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -331,6 +238,60 @@ class DoubleReportController extends Controller $amount = app('steam')->positive($journal['amount']); $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } + + // loop each tag: + /** @var array $tag */ + foreach ($journal['tags'] as $tag) { + if (in_array($journalId, $includedJournals, true)) { + continue; + } + $includedJournals[] = $journalId; + // do something + $tagName = $tag['name']; + $title = sprintf('%s (%s)', $tagName, $currency['currency_name']); + $result[$title] ??= [ + 'amount' => '0', + 'currency_symbol' => $currency['currency_symbol'], + 'currency_code' => $currency['currency_code'], + ]; + $amount = app('steam')->positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); + } + } + } + + $data = $this->generator->multiCurrencyPieChart($result); + + return response()->json($data); + } + + public function tagIncome(Collection $accounts, Collection $others, Carbon $start, Carbon $end): JsonResponse + { + $result = []; + $joined = $this->repository->expandWithDoubles($others); + $accounts = $accounts->merge($joined); + $income = $this->opsRepository->listIncome($start, $end, $accounts); + $includedJournals = []; + // loop income. + foreach ($income as $currency) { + foreach ($currency['transaction_journals'] as $journal) { + $journalId = $journal['transaction_journal_id']; + + // no tags? also deserves a sport + if (0 === count($journal['tags'])) { + $includedJournals[] = $journalId; + // do something + $tagName = trans('firefly.no_tags'); + $title = sprintf('%s (%s)', $tagName, $currency['currency_name']); + $result[$title] ??= [ + 'amount' => '0', + 'currency_symbol' => $currency['currency_symbol'], + 'currency_code' => $currency['currency_code'], + ]; + $amount = app('steam')->positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); + } + // loop each tag: /** @var array $tag */ foreach ($journal['tags'] as $tag) { @@ -358,62 +319,40 @@ class DoubleReportController extends Controller } /** - * @param Collection $accounts - * @param Collection $others - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse + * TODO duplicate function */ - public function tagIncome(Collection $accounts, Collection $others, Carbon $start, Carbon $end): JsonResponse + private function getCounterpartName(Collection $accounts, int $id, string $name, ?string $iban): string { - $result = []; - $joined = $this->repository->expandWithDoubles($others); - $accounts = $accounts->merge($joined); - $income = $this->opsRepository->listIncome($start, $end, $accounts); - $includedJournals = []; - // loop income. - foreach ($income as $currency) { - foreach ($currency['transaction_journals'] as $journal) { - $journalId = $journal['transaction_journal_id']; - - // no tags? also deserves a sport - if (0 === count($journal['tags'])) { - $includedJournals[] = $journalId; - // do something - $tagName = trans('firefly.no_tags'); - $title = sprintf('%s (%s)', $tagName, $currency['currency_name']); - $result[$title] ??= [ - 'amount' => '0', - 'currency_symbol' => $currency['currency_symbol'], - 'currency_code' => $currency['currency_code'], - ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); - } - // loop each tag: - /** @var array $tag */ - foreach ($journal['tags'] as $tag) { - if (in_array($journalId, $includedJournals, true)) { - continue; - } - $includedJournals[] = $journalId; - // do something - $tagName = $tag['name']; - $title = sprintf('%s (%s)', $tagName, $currency['currency_name']); - $result[$title] ??= [ - 'amount' => '0', - 'currency_symbol' => $currency['currency_symbol'], - 'currency_code' => $currency['currency_code'], - ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); - } + /** @var Account $account */ + foreach ($accounts as $account) { + if ($account->name === $name && $account->id !== $id) { + return $account->name; + } + if (null !== $account->iban && $account->iban === $iban && $account->id !== $id) { + return $account->iban; } } - $data = $this->generator->multiCurrencyPieChart($result); + return $name; + } - return response()->json($data); + /** + * TODO duplicate function + */ + private function makeEntries(Carbon $start, Carbon $end): array + { + $return = []; + $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end); + $preferredRange = app('navigation')->preferredRangeFormat($start, $end); + $currentStart = clone $start; + while ($currentStart <= $end) { + $currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange); + $key = $currentStart->isoFormat($format); + $return[$key] = '0'; + $currentStart = clone $currentEnd; + $currentStart->addDay()->startOfDay(); + } + + return $return; } } diff --git a/app/Http/Controllers/Chart/ExpenseReportController.php b/app/Http/Controllers/Chart/ExpenseReportController.php index 56d2f4c2a0..7af88f454e 100644 --- a/app/Http/Controllers/Chart/ExpenseReportController.php +++ b/app/Http/Controllers/Chart/ExpenseReportController.php @@ -33,7 +33,6 @@ use FireflyIII\Support\Http\Controllers\AugumentData; use FireflyIII\Support\Http\Controllers\TransactionCalculation; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; -use JsonException; /** * Separate controller because many helper functions are shared. @@ -45,15 +44,11 @@ class ExpenseReportController extends Controller use AugumentData; use TransactionCalculation; - /** @var AccountRepositoryInterface The account repository */ - protected $accountRepository; - /** @var GeneratorInterface Chart generation methods. */ - protected $generator; + protected AccountRepositoryInterface $accountRepository; + protected GeneratorInterface $generator; /** * ExpenseReportController constructor. - * - */ public function __construct() { @@ -73,13 +68,7 @@ class ExpenseReportController extends Controller * * TODO this chart is not multi currency aware. * - * @param Collection $accounts - * @param Collection $expense - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - * @throws JsonException + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function mainChart(Collection $accounts, Collection $expense, Carbon $start, Carbon $end): JsonResponse { @@ -98,9 +87,8 @@ class ExpenseReportController extends Controller $chartData = []; $currentStart = clone $start; $combined = $this->combineAccounts($expense); - // make "all" set: - $all = new Collection(); - foreach ($combined as $name => $combination) { + $all = new Collection(); + foreach ($combined as $combination) { $all = $all->merge($combination); } @@ -113,28 +101,28 @@ class ExpenseReportController extends Controller // first is always expense account: /** @var Account $exp */ $exp = $combination->first(); - $chartData[$exp->id . '-in'] = [ - 'label' => sprintf('%s (%s)', $name, (string)trans('firefly.income')), + $chartData[$exp->id.'-in'] = [ + 'label' => sprintf('%s (%s)', $name, (string) trans('firefly.income')), 'type' => 'bar', 'yAxisID' => 'y-axis-0', 'entries' => [], ]; - $chartData[$exp->id . '-out'] = [ - 'label' => sprintf('%s (%s)', $name, (string)trans('firefly.expenses')), + $chartData[$exp->id.'-out'] = [ + 'label' => sprintf('%s (%s)', $name, (string) trans('firefly.expenses')), 'type' => 'bar', 'yAxisID' => 'y-axis-0', 'entries' => [], ]; // total in, total out: - $chartData[$exp->id . '-total-in'] = [ - 'label' => sprintf('%s (%s)', $name, (string)trans('firefly.sum_of_income')), + $chartData[$exp->id.'-total-in'] = [ + 'label' => sprintf('%s (%s)', $name, (string) trans('firefly.sum_of_income')), 'type' => 'line', 'fill' => false, 'yAxisID' => 'y-axis-1', 'entries' => [], ]; - $chartData[$exp->id . '-total-out'] = [ - 'label' => sprintf('%s (%s)', $name, (string)trans('firefly.sum_of_expenses')), + $chartData[$exp->id.'-total-out'] = [ + 'label' => sprintf('%s (%s)', $name, (string) trans('firefly.sum_of_expenses')), 'type' => 'line', 'fill' => false, 'yAxisID' => 'y-axis-1', @@ -147,7 +135,7 @@ class ExpenseReportController extends Controller while ($currentStart < $end) { $currentEnd = clone $currentStart; - $currentEnd = $currentEnd->$function(); // @phpstan-ignore-line + $currentEnd = $currentEnd->{$function}(); // @phpstan-ignore-line // get expenses grouped by opposing name: $expenses = $this->groupByName($this->getExpensesForOpposing($accounts, $all, $currentStart, $currentEnd)); @@ -158,10 +146,10 @@ class ExpenseReportController extends Controller // first is always expense account: /** @var Account $exp */ $exp = $combination->first(); - $labelIn = $exp->id . '-in'; - $labelOut = $exp->id . '-out'; - $labelSumIn = $exp->id . '-total-in'; - $labelSumOut = $exp->id . '-total-out'; + $labelIn = $exp->id.'-in'; + $labelOut = $exp->id.'-out'; + $labelSumIn = $exp->id.'-total-in'; + $labelSumOut = $exp->id.'-total-out'; $currentIncome = bcmul($income[$name] ?? '0', '-1'); $currentExpense = $expenses[$name] ?? '0'; @@ -177,6 +165,7 @@ class ExpenseReportController extends Controller $chartData[$labelSumIn]['entries'][$label] = $sumOfIncome[$exp->id]; $chartData[$labelSumOut]['entries'][$label] = $sumOfExpense[$exp->id]; } + /** @var Carbon $currentStart */ $currentStart = clone $currentEnd; $currentStart->addDay(); diff --git a/app/Http/Controllers/Chart/PiggyBankController.php b/app/Http/Controllers/Chart/PiggyBankController.php index 125b7e26d6..c1095d1e09 100644 --- a/app/Http/Controllers/Chart/PiggyBankController.php +++ b/app/Http/Controllers/Chart/PiggyBankController.php @@ -46,8 +46,6 @@ class PiggyBankController extends Controller /** * PiggyBankController constructor. - * - */ public function __construct() { @@ -61,10 +59,6 @@ class PiggyBankController extends Controller * * TODO this chart is not multi currency aware. * - * @param PiggyBankRepositoryInterface $repository - * @param PiggyBank $piggyBank - * - * @return JsonResponse * @throws FireflyException */ public function history(PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank): JsonResponse @@ -83,7 +77,7 @@ class PiggyBankController extends Controller // get first event or start date of piggy bank or today $startDate = $piggyBank->startdate ?? today(config('app.timezone')); - /** @var PiggyBankEvent|null $firstEvent */ + /** @var null|PiggyBankEvent $firstEvent */ $firstEvent = $set->first(); $firstDate = null === $firstEvent ? new Carbon() : $firstEvent->date; diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php index 737519c4f5..0aaff2b61e 100644 --- a/app/Http/Controllers/Chart/ReportController.php +++ b/app/Http/Controllers/Chart/ReportController.php @@ -36,7 +36,6 @@ use FireflyIII\Support\Http\Controllers\BasicDataSupport; use FireflyIII\Support\Http\Controllers\ChartGeneration; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; -use JsonException; /** * Class ReportController. @@ -46,13 +45,10 @@ class ReportController extends Controller use BasicDataSupport; use ChartGeneration; - /** @var GeneratorInterface Chart generation methods. */ - protected $generator; + protected GeneratorInterface $generator; /** * ReportController constructor. - * - */ public function __construct() { @@ -64,12 +60,6 @@ class ReportController extends Controller /** * This chart, by default, is shown on the multi-year and year report pages, * which means that giving it a 2 week "period" should be enough granularity. - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse */ public function netWorth(Collection $accounts, Carbon $start, Carbon $end): JsonResponse { @@ -85,6 +75,7 @@ class ReportController extends Controller $locale = app('steam')->getLocale(); $current = clone $start; $chartData = []; + /** @var NetWorthInterface $helper */ $helper = app(NetWorthInterface::class); $helper->setUser(auth()->user()); @@ -117,10 +108,10 @@ class ReportController extends Controller continue; } $currencyId = $netWorthItem['currency_id']; - $label = $current->isoFormat((string)trans('config.month_and_day_js', [], $locale)); + $label = $current->isoFormat((string) trans('config.month_and_day_js', [], $locale)); if (!array_key_exists($currencyId, $chartData)) { $chartData[$currencyId] = [ - 'label' => 'Net worth in ' . $netWorthItem['currency_name'], + 'label' => 'Net worth in '.$netWorthItem['currency_name'], 'type' => 'line', 'currency_symbol' => $netWorthItem['currency_symbol'], 'currency_code' => $netWorthItem['currency_code'], @@ -141,12 +132,7 @@ class ReportController extends Controller /** * Shows income and expense, debit/credit: operations. * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - * @throws JsonException + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function operations(Collection $accounts, Carbon $start, Carbon $end): JsonResponse { @@ -164,12 +150,9 @@ class ReportController extends Controller $titleFormat = app('navigation')->preferredCarbonLocalizedFormat($start, $end); $preferredRange = app('navigation')->preferredRangeFormat($start, $end); $ids = $accounts->pluck('id')->toArray(); + $data = []; + $chartData = []; - // get journals for entire period: - $data = []; - $chartData = [ - - ]; /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->withAccountInformation(); @@ -181,13 +164,13 @@ class ReportController extends Controller /** @var array $journal */ foreach ($journals as $journal) { $period = $journal['date']->format($format); - $currencyId = (int)$journal['currency_id']; + $currencyId = (int) $journal['currency_id']; $data[$currencyId] ??= [ 'currency_id' => $currencyId, 'currency_symbol' => $journal['currency_symbol'], 'currency_code' => $journal['currency_code'], 'currency_name' => $journal['currency_name'], - 'currency_decimal_places' => (int)$journal['currency_decimal_places'], + 'currency_decimal_places' => (int) $journal['currency_decimal_places'], ]; $data[$currencyId][$period] ??= [ 'period' => $period, @@ -200,19 +183,7 @@ class ReportController extends Controller // deposit = incoming // transfer or reconcile or opening balance, and these accounts are the destination. - if ( - TransactionType::DEPOSIT === $journal['transaction_type_type'] - || - - ( - ( - TransactionType::TRANSFER === $journal['transaction_type_type'] - || TransactionType::RECONCILIATION === $journal['transaction_type_type'] - || TransactionType::OPENING_BALANCE === $journal['transaction_type_type'] - ) - && in_array($journal['destination_account_id'], $ids, true) - ) - ) { + if (TransactionType::DEPOSIT === $journal['transaction_type_type'] || ((TransactionType::TRANSFER === $journal['transaction_type_type'] || TransactionType::RECONCILIATION === $journal['transaction_type_type'] || TransactionType::OPENING_BALANCE === $journal['transaction_type_type']) && in_array($journal['destination_account_id'], $ids, true))) { $key = 'earned'; } $data[$currencyId][$period][$key] = bcadd($data[$currencyId][$period][$key], $amount); @@ -222,7 +193,7 @@ class ReportController extends Controller /** @var array $currency */ foreach ($data as $currency) { $income = [ - 'label' => (string)trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]), + 'label' => (string) trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]), 'type' => 'bar', 'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green 'currency_id' => $currency['currency_id'], @@ -231,22 +202,21 @@ class ReportController extends Controller 'entries' => [], ]; $expense = [ - 'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]), + 'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]), 'type' => 'bar', 'backgroundColor' => 'rgba(219, 68, 55, 0.5)', // red 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], 'entries' => [], - ]; // loop all possible periods between $start and $end $currentStart = clone $start; while ($currentStart <= $end) { $key = $currentStart->format($format); $title = $currentStart->isoFormat($titleFormat); - $income['entries'][$title] = app('steam')->bcround(($currency[$key]['earned'] ?? '0'), $currency['currency_decimal_places']); - $expense['entries'][$title] = app('steam')->bcround(($currency[$key]['spent'] ?? '0'), $currency['currency_decimal_places']); + $income['entries'][$title] = app('steam')->bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); + $expense['entries'][$title] = app('steam')->bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']); $currentStart = app('navigation')->addPeriod($currentStart, $preferredRange, 0); } diff --git a/app/Http/Controllers/Chart/TagReportController.php b/app/Http/Controllers/Chart/TagReportController.php index 6eb24c40fd..0750cf8635 100644 --- a/app/Http/Controllers/Chart/TagReportController.php +++ b/app/Http/Controllers/Chart/TagReportController.php @@ -49,8 +49,6 @@ class TagReportController extends Controller /** * TagReportController constructor. - * - */ public function __construct() { @@ -67,14 +65,6 @@ class TagReportController extends Controller ); } - /** - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function budgetExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -103,14 +93,6 @@ class TagReportController extends Controller return response()->json($data); } - /** - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function categoryExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -139,14 +121,6 @@ class TagReportController extends Controller return response()->json($data); } - /** - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function categoryIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -175,14 +149,6 @@ class TagReportController extends Controller return response()->json($data); } - /** - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function destinationExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -211,14 +177,6 @@ class TagReportController extends Controller return response()->json($data); } - /** - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function destinationIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -249,14 +207,6 @@ class TagReportController extends Controller /** * Generate main tag overview chart. - * - * @param Collection $accounts - * @param Tag $tag - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - * */ public function mainChart(Collection $accounts, Tag $tag, Carbon $start, Carbon $end): JsonResponse { @@ -324,39 +274,6 @@ class TagReportController extends Controller return response()->json($data); } - /** - * TODO duplicate function - * - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - private function makeEntries(Carbon $start, Carbon $end): array - { - $return = []; - $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end); - $preferredRange = app('navigation')->preferredRangeFormat($start, $end); - $currentStart = clone $start; - while ($currentStart <= $end) { - $currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange); - $key = $currentStart->isoFormat($format); - $return[$key] = '0'; - $currentStart = clone $currentEnd; - $currentStart->addDay()->startOfDay(); - } - - return $return; - } - - /** - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function sourceExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -385,14 +302,6 @@ class TagReportController extends Controller return response()->json($data); } - /** - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function sourceIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -421,14 +330,6 @@ class TagReportController extends Controller return response()->json($data); } - /** - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function tagExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -455,14 +356,6 @@ class TagReportController extends Controller return response()->json($data); } - /** - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function tagIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse { $result = []; @@ -488,4 +381,24 @@ class TagReportController extends Controller return response()->json($data); } + + /** + * TODO duplicate function + */ + private function makeEntries(Carbon $start, Carbon $end): array + { + $return = []; + $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end); + $preferredRange = app('navigation')->preferredRangeFormat($start, $end); + $currentStart = clone $start; + while ($currentStart <= $end) { + $currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange); + $key = $currentStart->isoFormat($format); + $return[$key] = '0'; + $currentStart = clone $currentEnd; + $currentStart->addDay()->startOfDay(); + } + + return $return; + } } diff --git a/app/Http/Controllers/Chart/TransactionController.php b/app/Http/Controllers/Chart/TransactionController.php index 5d73fcc2a0..65c73207d4 100644 --- a/app/Http/Controllers/Chart/TransactionController.php +++ b/app/Http/Controllers/Chart/TransactionController.php @@ -50,9 +50,6 @@ class TransactionController extends Controller } /** - * @param Carbon $start - * @param Carbon $end - * * @return JsonResponse */ public function budgets(Carbon $start, Carbon $end) @@ -64,6 +61,7 @@ class TransactionController extends Controller if ($cache->has()) { return response()->json($cache->get()); } + /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end); @@ -92,10 +90,6 @@ class TransactionController extends Controller } /** - * @param string $objectType - * @param Carbon $start - * @param Carbon $end - * * @return JsonResponse */ public function categories(string $objectType, Carbon $start, Carbon $end) @@ -108,6 +102,7 @@ class TransactionController extends Controller if ($cache->has()) { return response()->json($cache->get()); } + /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end); @@ -145,10 +140,6 @@ class TransactionController extends Controller } /** - * @param string $objectType - * @param Carbon $start - * @param Carbon $end - * * @return JsonResponse */ public function destinationAccounts(string $objectType, Carbon $start, Carbon $end) @@ -161,6 +152,7 @@ class TransactionController extends Controller if ($cache->has()) { return response()->json($cache->get()); } + /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end); @@ -198,10 +190,6 @@ class TransactionController extends Controller } /** - * @param string $objectType - * @param Carbon $start - * @param Carbon $end - * * @return JsonResponse */ public function sourceAccounts(string $objectType, Carbon $start, Carbon $end) @@ -214,6 +202,7 @@ class TransactionController extends Controller if ($cache->has()) { return response()->json($cache->get()); } + /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end); diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 876b4e1962..108467e35d 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -29,11 +29,12 @@ use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Routing\Controller as BaseController; -use Route; /** * Class Controller. * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.NumberOfChildren) */ abstract class Controller extends BaseController { @@ -50,8 +51,6 @@ abstract class Controller extends BaseController /** * Controller constructor. - * - */ public function __construct() { @@ -112,7 +111,7 @@ abstract class Controller extends BaseController app('view')->share('locale', $locale); app('view')->share('shownDemo', $shownDemo); app('view')->share('current_route_name', $page); - app('view')->share('original_route_name', Route::currentRouteName()); + app('view')->share('original_route_name', \Route::currentRouteName()); } app('view')->share('darkMode', $darkMode); diff --git a/app/Http/Controllers/DebugController.php b/app/Http/Controllers/DebugController.php index c45380af83..cf30b5eb2e 100644 --- a/app/Http/Controllers/DebugController.php +++ b/app/Http/Controllers/DebugController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers; use Carbon\Carbon; -use DB; use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Middleware\IsDemoUser; @@ -40,13 +39,9 @@ use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Monolog\Handler\RotatingFileHandler; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; -use const PHP_SAPI; /** * Class DebugController - * */ class DebugController extends Controller { @@ -54,8 +49,6 @@ class DebugController extends Controller /** * DebugController constructor. - * - */ public function __construct() { @@ -78,15 +71,15 @@ class DebugController extends Controller Log::critical('This is a test message at the CRITICAL level.'); Log::alert('This is a test message at the ALERT level.'); Log::emergency('This is a test message at the EMERGENCY level.'); + throw new FireflyException('A very simple test error.'); } /** * Clear log and session. * - * @param Request $request + * @return Redirector|RedirectResponse * - * @return RedirectResponse|Redirector * @throws FireflyException */ public function flush(Request $request) @@ -101,9 +94,10 @@ class DebugController extends Controller app('log')->debug('Call route:clear...'); Artisan::call('route:clear'); app('log')->debug('Call twig:clean...'); + try { Artisan::call('twig:clean'); - } catch (Exception $e) { // intentional generic exception + } catch (\Exception $e) { // intentional generic exception throw new FireflyException($e->getMessage(), 0, $e); } @@ -117,6 +111,7 @@ class DebugController extends Controller * Show debug info. * * @return Factory|View + * * @throws FireflyException */ public function index() @@ -140,15 +135,27 @@ class DebugController extends Controller } if ('' !== $logContent) { // last few lines - $logContent = 'Truncated from this point <----|' . substr((string)$logContent, -16384); + $logContent = 'Truncated from this point <----|'.substr((string)$logContent, -16384); } return view('debug', compact('table', 'now', 'logContent')); } /** - * @return string + * Flash all types of messages. + * + * @return Redirector|RedirectResponse */ + public function testFlash(Request $request) + { + $request->session()->flash('success', 'This is a success message.'); + $request->session()->flash('info', 'This is an info message.'); + $request->session()->flash('warning', 'This is a warning.'); + $request->session()->flash('error', 'This is an error!'); + + return redirect(route('home')); + } + private function generateTable(): string { // system information: @@ -160,20 +167,18 @@ class DebugController extends Controller return (string)view('partials.debug-table', compact('system', 'docker', 'app', 'user')); } - /** - * @return array - */ private function getSystemInformation(): array { $maxFileSize = app('steam')->phpBytes((string)ini_get('upload_max_filesize')); $maxPostSize = app('steam')->phpBytes((string)ini_get('post_max_size')); - $drivers = DB::availableDrivers(); - $currentDriver = DB::getDriverName(); + $drivers = \DB::availableDrivers(); + $currentDriver = \DB::getDriverName(); + return [ 'db_version' => app('fireflyconfig')->get('db_version', 1)->data, 'php_version' => PHP_VERSION, 'php_os' => PHP_OS, - 'interface' => PHP_SAPI, + 'interface' => \PHP_SAPI, 'bcscale' => bcscale(), 'display_errors' => ini_get('display_errors'), 'error_reporting' => $this->errorReporting((int)ini_get('error_reporting')), @@ -183,9 +188,6 @@ class DebugController extends Controller ]; } - /** - * @return array - */ private function getBuildInfo(): array { $return = [ @@ -194,21 +196,22 @@ class DebugController extends Controller 'build_date' => '(unknown)', 'base_build' => '(unknown)', 'base_build_date' => '(unknown)', - ]; + try { if (file_exists('/var/www/counter-main.txt')) { $return['build'] = trim((string)file_get_contents('/var/www/counter-main.txt')); } - } catch (Exception $e) { // @phpstan-ignore-line + } catch (\Exception $e) { // @phpstan-ignore-line app('log')->debug('Could not check build counter, but thats ok.'); app('log')->warning($e->getMessage()); } + try { if (file_exists('/var/www/build-date-main.txt')) { $return['build_date'] = trim((string)file_get_contents('/var/www/build-date-main.txt')); } - } catch (Exception $e) { // @phpstan-ignore-line + } catch (\Exception $e) { // @phpstan-ignore-line app('log')->debug('Could not check build date, but thats ok.'); app('log')->warning($e->getMessage()); } @@ -218,12 +221,10 @@ class DebugController extends Controller if ('' !== (string)env('BASE_IMAGE_DATE')) { $return['base_build_date'] = env('BASE_IMAGE_DATE'); } + return $return; } - /** - * @return array - */ private function getAppInfo(): array { $userGuard = config('auth.defaults.guard'); @@ -243,8 +244,8 @@ class DebugController extends Controller 'audit_log_channel' => envNonEmpty('AUDIT_LOG_CHANNEL', '(empty)'), 'default_language' => (string)config('firefly.default_language'), 'default_locale' => (string)config('firefly.default_locale'), - 'remote_header' => $userGuard === 'remote_user_guard' ? config('auth.guard_header') : 'N/A', - 'remote_mail_header' => $userGuard === 'remote_user_guard' ? config('auth.guard_email') : 'N/A', + 'remote_header' => 'remote_user_guard' === $userGuard ? config('auth.guard_header') : 'N/A', + 'remote_mail_header' => 'remote_user_guard' === $userGuard ? config('auth.guard_email') : 'N/A', 'stateful_domains' => implode(', ', config('sanctum.stateful')), // the dates for the cron job are based on the recurring cron job's times. @@ -256,11 +257,6 @@ class DebugController extends Controller ]; } - /** - * @return array - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function getuserInfo(): array { $userFlags = $this->getUserFlags(); @@ -292,12 +288,10 @@ class DebugController extends Controller ]; } - /** - * @return string - */ private function getUserFlags(): string { $flags = []; + /** @var User $user */ $user = auth()->user(); @@ -338,24 +332,7 @@ class DebugController extends Controller if ($user->bills()->count() > 0) { $flags[] = ':email:'; } + return implode(' ', $flags); } - - /** - * Flash all types of messages. - * - * @param Request $request - * - * @return RedirectResponse|Redirector - */ - public function testFlash(Request $request) - { - $request->session()->flash('success', 'This is a success message.'); - $request->session()->flash('info', 'This is an info message.'); - $request->session()->flash('warning', 'This is a warning.'); - $request->session()->flash('error', 'This is an error!'); - - return redirect(route('home')); - } - } diff --git a/app/Http/Controllers/Export/IndexController.php b/app/Http/Controllers/Export/IndexController.php index bbcc5249dc..e99532fe21 100644 --- a/app/Http/Controllers/Export/IndexController.php +++ b/app/Http/Controllers/Export/IndexController.php @@ -32,8 +32,6 @@ use FireflyIII\Support\Export\ExportDataGenerator; use Illuminate\Contracts\View\Factory; use Illuminate\Http\Response as LaravelResponse; use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class IndexController @@ -44,8 +42,6 @@ class IndexController extends Controller /** * IndexController constructor. - * - */ public function __construct() { @@ -65,10 +61,7 @@ class IndexController extends Controller } /** - * @return LaravelResponse * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function export(): LaravelResponse { @@ -90,19 +83,21 @@ class IndexController extends Controller $name = sprintf('%s_transaction_export.csv', date('Y_m_d')); $quoted = sprintf('"%s"', addcslashes($name, '"\\')); + // headers for CSV file. /** @var LaravelResponse $response */ $response = response($result['transactions']); $response ->header('Content-Description', 'File Transfer') ->header('Content-Type', 'text/x-csv') - ->header('Content-Disposition', 'attachment; filename=' . $quoted) - //->header('Content-Transfer-Encoding', 'binary') + ->header('Content-Disposition', 'attachment; filename='.$quoted) + // ->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', (string)strlen($result['transactions'])); + ->header('Content-Length', (string)strlen($result['transactions'])) + ; // return CSV file made from 'transactions' array. return $response; diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 38db72eb10..403add26aa 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -25,7 +25,6 @@ namespace FireflyIII\Http\Controllers; use Carbon\Carbon; use Carbon\Exceptions\InvalidFormatException; -use Exception; use FireflyIII\Events\RequestedVersionCheckStatus; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; @@ -46,8 +45,6 @@ class HomeController extends Controller { /** * HomeController constructor. - * - */ public function __construct() { @@ -60,15 +57,13 @@ class HomeController extends Controller /** * Change index date range. * - * @param Request $request - * - * @return JsonResponse - * @throws Exception + * @throws \Exception */ public function dateRange(Request $request): JsonResponse { $stringStart = ''; $stringEnd = ''; + try { $stringStart = e((string)$request->get('start')); $start = Carbon::createFromFormat('Y-m-d', $stringStart); @@ -76,6 +71,7 @@ class HomeController extends Controller app('log')->error(sprintf('Start: could not parse date string "%s" so ignore it.', $stringStart)); $start = Carbon::now()->startOfMonth(); } + try { $stringEnd = e((string)$request->get('end')); $end = Carbon::createFromFormat('Y-m-d', $stringEnd); @@ -120,9 +116,6 @@ class HomeController extends Controller /** * Show index. * - * @param AccountRepositoryInterface $repository - * - * @return mixed * @throws FireflyException */ public function index(AccountRepositoryInterface $repository): mixed @@ -142,9 +135,9 @@ class HomeController extends Controller $frontPageArray = []; } - /** @var Carbon $start */ $start = session('start', today(config('app.timezone'))->startOfMonth()); + /** @var Carbon $end */ $end = session('end', today(config('app.timezone'))->endOfMonth()); $accounts = $repository->getAccountsById($frontPageArray); diff --git a/app/Http/Controllers/JavascriptController.php b/app/Http/Controllers/JavascriptController.php index aaf1d6f230..1de5892e55 100644 --- a/app/Http/Controllers/JavascriptController.php +++ b/app/Http/Controllers/JavascriptController.php @@ -33,9 +33,6 @@ use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Controllers\GetConfigurationData; use Illuminate\Http\Request; use Illuminate\Http\Response; -use JsonException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class JavascriptController. @@ -46,12 +43,6 @@ class JavascriptController extends Controller /** * Show info about accounts. - * - * @param AccountRepositoryInterface $repository - * - * @return Response - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function accounts(AccountRepositoryInterface $repository): Response { @@ -72,20 +63,18 @@ class JavascriptController extends Controller return response() ->view('javascript.accounts', $data) - ->header('Content-Type', 'text/javascript'); + ->header('Content-Type', 'text/javascript') + ; } /** * Get info about currencies. - * - * @param CurrencyRepositoryInterface $repository - * - * @return Response */ public function currencies(CurrencyRepositoryInterface $repository): Response { $currencies = $repository->get(); $data = ['currencies' => []]; + /** @var TransactionCurrency $currency */ foreach ($currencies as $currency) { $currencyId = $currency->id; @@ -95,21 +84,15 @@ class JavascriptController extends Controller return response() ->view('javascript.currencies', $data) - ->header('Content-Type', 'text/javascript'); + ->header('Content-Type', 'text/javascript') + ; } /** * Show some common variables to be used in scripts. * - * @param Request $request - * @param AccountRepositoryInterface $repository - * - * @return Response * @throws FireflyException - * @throws JsonException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ + * */ public function variables(Request $request, AccountRepositoryInterface $repository): Response { $account = $repository->find((int)$request->get('account')); @@ -139,18 +122,18 @@ class JavascriptController extends Controller return response() ->view('javascript.variables', $data) - ->header('Content-Type', 'text/javascript'); + ->header('Content-Type', 'text/javascript') + ; } /** * Bit of a hack but OK. - * - * @return Response */ public function variablesV2(): Response { /** @var Carbon $start */ $start = clone session('start', today(config('app.timezone'))->startOfMonth()); + /** @var Carbon $end */ $end = clone session('end', today(config('app.timezone'))->endOfMonth()); @@ -161,6 +144,7 @@ class JavascriptController extends Controller return response() ->view('v2.javascript.variables', $data) - ->header('Content-Type', 'text/javascript'); + ->header('Content-Type', 'text/javascript') + ; } } diff --git a/app/Http/Controllers/Json/AutoCompleteController.php b/app/Http/Controllers/Json/AutoCompleteController.php index f77c76c8e2..b749d626ad 100644 --- a/app/Http/Controllers/Json/AutoCompleteController.php +++ b/app/Http/Controllers/Json/AutoCompleteController.php @@ -28,6 +28,4 @@ use FireflyIII\Http\Controllers\Controller; /** * Class AutoCompleteController. */ -class AutoCompleteController extends Controller -{ -} +class AutoCompleteController extends Controller {} diff --git a/app/Http/Controllers/Json/BoxController.php b/app/Http/Controllers/Json/BoxController.php index aa29ecd438..556d844657 100644 --- a/app/Http/Controllers/Json/BoxController.php +++ b/app/Http/Controllers/Json/BoxController.php @@ -49,18 +49,22 @@ class BoxController extends Controller * 1) If the user has available amount this period and has NOT overspent: left to spend box. * 2) if the user has no available amount set this period: spent per day * - * @return JsonResponse + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function available(): JsonResponse { app('log')->debug('Now in available()'); + /** @var OperationsRepositoryInterface $opsRepository */ $opsRepository = app(OperationsRepositoryInterface::class); + /** @var AvailableBudgetRepositoryInterface $abRepository */ $abRepository = app(AvailableBudgetRepositoryInterface::class); $abRepository->cleanup(); + /** @var Carbon $start */ $start = session('start', today(config('app.timezone'))->startOfMonth()); + /** @var Carbon $end */ $end = session('end', today(config('app.timezone'))->endOfMonth()); $today = today(config('app.timezone')); @@ -92,6 +96,7 @@ class BoxController extends Controller $availableBudget->end_date->format('Y-m-d'), $availableBudget->amount )); + return $availableBudget; } @@ -139,21 +144,19 @@ class BoxController extends Controller $cache->store($return); app('log')->debug('Now done with available()'); + return response()->json($return); } /** * Current total balance. - * - * @param CurrencyRepositoryInterface $repository - * - * @return JsonResponse */ public function balance(CurrencyRepositoryInterface $repository): JsonResponse { // Cache result, return cache if present. /** @var Carbon $start */ $start = session('start', today(config('app.timezone'))->startOfMonth()); + /** @var Carbon $end */ $end = session('end', today(config('app.timezone'))->endOfMonth()); $cache = new CacheProperties(); @@ -173,8 +176,10 @@ class BoxController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end) - ->setTypes([TransactionType::DEPOSIT]); + ->setTypes([TransactionType::DEPOSIT]) + ; $set = $collector->getExtractedJournals(); + /** @var array $journal */ foreach ($set as $journal) { $currencyId = (int)$journal['currency_id']; @@ -189,8 +194,10 @@ class BoxController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end) - ->setTypes([TransactionType::WITHDRAWAL]); + ->setTypes([TransactionType::WITHDRAWAL]) + ; $set = $collector->getExtractedJournals(); + /** @var array $journal */ foreach ($set as $journal) { $currencyId = (int)$journal['currency_id']; @@ -229,8 +236,6 @@ class BoxController extends Controller /** * Total user net worth. - * - * @return JsonResponse */ public function netWorth(): JsonResponse { diff --git a/app/Http/Controllers/Json/BudgetController.php b/app/Http/Controllers/Json/BudgetController.php index bbad8baef0..10479402f3 100644 --- a/app/Http/Controllers/Json/BudgetController.php +++ b/app/Http/Controllers/Json/BudgetController.php @@ -46,8 +46,6 @@ class BudgetController extends Controller /** * IndexController constructor. - * - */ public function __construct() { @@ -67,13 +65,6 @@ class BudgetController extends Controller ); } - /** - * @param TransactionCurrency $currency - * @param Carbon $start - * @param Carbon $end - * - * @return JsonResponse - */ public function getBudgetInformation(TransactionCurrency $currency, Carbon $start, Carbon $end): JsonResponse { $budgeted = $this->blRepository->budgeted($start, $end, $currency); diff --git a/app/Http/Controllers/Json/FrontpageController.php b/app/Http/Controllers/Json/FrontpageController.php index 44d5611346..bd7d756bb5 100644 --- a/app/Http/Controllers/Json/FrontpageController.php +++ b/app/Http/Controllers/Json/FrontpageController.php @@ -28,7 +28,6 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\PiggyBank; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use Illuminate\Http\JsonResponse; -use Throwable; /** * Class FrontpageController. @@ -38,15 +37,13 @@ class FrontpageController extends Controller /** * Piggy bank pie chart. * - * @param PiggyBankRepositoryInterface $repository - * - * @return JsonResponse * @throws FireflyException */ public function piggyBanks(PiggyBankRepositoryInterface $repository): JsonResponse { $set = $repository->getPiggyBanks(); $info = []; + /** @var PiggyBank $piggyBank */ foreach ($set as $piggyBank) { $amount = $repository->getCurrentAmount($piggyBank); @@ -72,10 +69,11 @@ class FrontpageController extends Controller if (0 !== count($info)) { try { $html = view('json.piggy-banks', compact('info'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render json.piggy-banks: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $html = 'Could not render view.'; + throw new FireflyException($html, 0, $e); } } diff --git a/app/Http/Controllers/Json/IntroController.php b/app/Http/Controllers/Json/IntroController.php index 1c02784a40..6ecfcb818c 100644 --- a/app/Http/Controllers/Json/IntroController.php +++ b/app/Http/Controllers/Json/IntroController.php @@ -37,11 +37,6 @@ class IntroController extends Controller /** * Returns the introduction wizard for a page. - * - * @param string $route - * @param string|null $specificPage - * - * @return JsonResponse */ public function getIntroSteps(string $route, string $specificPage = null): JsonResponse { @@ -72,10 +67,6 @@ class IntroController extends Controller /** * Returns true if there is a general outro step. - * - * @param string $route - * - * @return bool */ public function hasOutroStep(string $route): bool { @@ -98,19 +89,15 @@ class IntroController extends Controller /** * Enable the boxes for a specific page again. * - * @param string $route - * @param string|null $specialPage - * - * @return JsonResponse * @throws FireflyException */ public function postEnable(string $route, string $specialPage = null): JsonResponse { $specialPage ??= ''; $route = str_replace('.', '_', $route); - $key = 'shown_demo_' . $route; + $key = 'shown_demo_'.$route; if ('' !== $specialPage) { - $key .= '_' . $specialPage; + $key .= '_'.$specialPage; } app('log')->debug(sprintf('Going to mark the following route as NOT done: %s with special "%s" (%s)', $route, $specialPage, $key)); app('preferences')->set($key, false); @@ -122,18 +109,14 @@ class IntroController extends Controller /** * Set that you saw them. * - * @param string $route - * @param string|null $specialPage - * - * @return JsonResponse * @throws FireflyException */ public function postFinished(string $route, string $specialPage = null): JsonResponse { $specialPage ??= ''; - $key = 'shown_demo_' . $route; + $key = 'shown_demo_'.$route; if ('' !== $specialPage) { - $key .= '_' . $specialPage; + $key .= '_'.$specialPage; } app('log')->debug(sprintf('Going to mark the following route as done: %s with special "%s" (%s)', $route, $specialPage, $key)); app('preferences')->set($key, true); diff --git a/app/Http/Controllers/Json/ReconcileController.php b/app/Http/Controllers/Json/ReconcileController.php index 47eac09ab3..8b080391fc 100644 --- a/app/Http/Controllers/Json/ReconcileController.php +++ b/app/Http/Controllers/Json/ReconcileController.php @@ -34,11 +34,8 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Collection; -use JsonException; -use Throwable; /** - * * Class ReconcileController */ class ReconcileController extends Controller @@ -47,8 +44,6 @@ class ReconcileController extends Controller /** * ReconcileController constructor. - * - */ public function __construct() { @@ -69,14 +64,7 @@ class ReconcileController extends Controller /** * Overview of reconciliation. * - * @param Request $request - * @param Account|null $account - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return JsonResponse * @throws FireflyException - * @throws JsonException */ public function overview(Request $request, Account $account = null, Carbon $start = null, Carbon $end = null): JsonResponse { @@ -99,7 +87,7 @@ class ReconcileController extends Controller $clearedJournals = []; $clearedIds = $request->get('cleared') ?? []; $journals = []; - /* Collect all submitted journals */ + // Collect all submitted journals if (count($selectedIds) > 0) { /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); @@ -107,7 +95,7 @@ class ReconcileController extends Controller $journals = $collector->getExtractedJournals(); } - /* Collect all journals already reconciled */ + // Collect all journals already reconciled if (count($clearedIds) > 0) { /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); @@ -116,6 +104,7 @@ class ReconcileController extends Controller } app('log')->debug('Start transaction loop'); + /** @var array $journal */ foreach ($journals as $journal) { $amount = $this->processJournal($account, $accountCurrency, $journal, $amount); @@ -154,10 +143,11 @@ class ReconcileController extends Controller 'selectedIds' ) )->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('View error: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $view = sprintf('Could not render accounts.reconcile.overview: %s', $e->getMessage()); + throw new FireflyException($view, 0, $e); } @@ -169,56 +159,12 @@ class ReconcileController extends Controller return response()->json($return); } - /** - * @param Account $account - * @param TransactionCurrency $currency - * @param array $journal - * @param string $amount - * - * @return string - */ - private function processJournal(Account $account, TransactionCurrency $currency, array $journal, string $amount): string - { - $toAdd = '0'; - app('log')->debug(sprintf('User submitted %s #%d: "%s"', $journal['transaction_type_type'], $journal['transaction_journal_id'], $journal['description'])); - - // not much magic below we need to cover using tests. - - if ($account->id === $journal['source_account_id']) { - if ($currency->id === $journal['currency_id']) { - $toAdd = $journal['amount']; - } - if (null !== $journal['foreign_currency_id'] && $journal['foreign_currency_id'] === $currency->id) { - $toAdd = $journal['foreign_amount']; - } - } - if ($account->id === $journal['destination_account_id']) { - if ($currency->id === $journal['currency_id']) { - $toAdd = bcmul($journal['amount'], '-1'); - } - if (null !== $journal['foreign_currency_id'] && $journal['foreign_currency_id'] === $currency->id) { - $toAdd = bcmul($journal['foreign_amount'], '-1'); - } - } - - - app('log')->debug(sprintf('Going to add %s to %s', $toAdd, $amount)); - $amount = bcadd($amount, $toAdd); - app('log')->debug(sprintf('Result is %s', $amount)); - - return $amount; - } - /** * Returns a list of transactions in a modal. * - * @param Account $account - * @param Carbon|null $start - * @param Carbon|null $end - * * @return JsonResponse + * * @throws FireflyException - * @throws JsonException */ public function transactions(Account $account, Carbon $start = null, Carbon $end = null) { @@ -246,8 +192,9 @@ class ReconcileController extends Controller $collector = app(GroupCollectorInterface::class); $collector->setAccounts(new Collection([$account])) - ->setRange($selectionStart, $selectionEnd) - ->withBudgetInformation()->withCategoryInformation()->withAccountInformation(); + ->setRange($selectionStart, $selectionEnd) + ->withBudgetInformation()->withCategoryInformation()->withAccountInformation() + ; $array = $collector->getExtractedJournals(); $journals = $this->processTransactions($account, $array); @@ -256,27 +203,55 @@ class ReconcileController extends Controller 'accounts.reconcile.transactions', compact('account', 'journals', 'currency', 'start', 'end', 'selectionStart', 'selectionEnd') )->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $html = sprintf('Could not render accounts.reconcile.transactions: %s', $e->getMessage()); + throw new FireflyException($html, 0, $e); } return response()->json(['html' => $html, 'startBalance' => $startBalance, 'endBalance' => $endBalance]); } + private function processJournal(Account $account, TransactionCurrency $currency, array $journal, string $amount): string + { + $toAdd = '0'; + app('log')->debug(sprintf('User submitted %s #%d: "%s"', $journal['transaction_type_type'], $journal['transaction_journal_id'], $journal['description'])); + + // not much magic below we need to cover using tests. + + if ($account->id === $journal['source_account_id']) { + if ($currency->id === $journal['currency_id']) { + $toAdd = $journal['amount']; + } + if (null !== $journal['foreign_currency_id'] && $journal['foreign_currency_id'] === $currency->id) { + $toAdd = $journal['foreign_amount']; + } + } + if ($account->id === $journal['destination_account_id']) { + if ($currency->id === $journal['currency_id']) { + $toAdd = bcmul($journal['amount'], '-1'); + } + if (null !== $journal['foreign_currency_id'] && $journal['foreign_currency_id'] === $currency->id) { + $toAdd = bcmul($journal['foreign_amount'], '-1'); + } + } + + app('log')->debug(sprintf('Going to add %s to %s', $toAdd, $amount)); + $amount = bcadd($amount, $toAdd); + app('log')->debug(sprintf('Result is %s', $amount)); + + return $amount; + } + /** * "fix" amounts to make it easier on the reconciliation overview: - * - * @param Account $account - * @param array $array - * - * @return array */ private function processTransactions(Account $account, array $array): array { $journals = []; + /** @var array $journal */ foreach ($array as $journal) { $inverse = false; @@ -302,7 +277,6 @@ class ReconcileController extends Controller } } - $journals[] = $journal; } diff --git a/app/Http/Controllers/Json/RecurrenceController.php b/app/Http/Controllers/Json/RecurrenceController.php index 0ac57139f8..10a5999584 100644 --- a/app/Http/Controllers/Json/RecurrenceController.php +++ b/app/Http/Controllers/Json/RecurrenceController.php @@ -41,8 +41,6 @@ class RecurrenceController extends Controller /** * RecurrenceController constructor. - * - */ public function __construct() { @@ -61,9 +59,7 @@ class RecurrenceController extends Controller /** * Shows all events for a repetition. Used in calendar. * - * @param Request $request - * - * @return JsonResponse + * @SuppressWarnings(PHPMD.NPathComplexity) * * @throws FireflyException */ @@ -126,7 +122,7 @@ class RecurrenceController extends Controller foreach ($occurrences as $current) { if ($current->gte($start)) { $event = [ - 'id' => $repetitionType . $firstDate->format('Ymd'), + 'id' => $repetitionType.$firstDate->format('Ymd'), 'title' => 'X', 'allDay' => true, 'start' => $current->format('Y-m-d'), @@ -143,15 +139,12 @@ class RecurrenceController extends Controller /** * Suggests repetition moments. - * - * @param Request $request - * - * @return JsonResponse */ public function suggest(Request $request): JsonResponse { $string = '' === (string)$request->get('date') ? date('Y-m-d') : (string)$request->get('date'); $today = today(config('app.timezone'))->startOfDay(); + try { $date = Carbon::createFromFormat('Y-m-d', $string, config('app.timezone')); } catch (InvalidFormatException $e) { diff --git a/app/Http/Controllers/Json/RuleController.php b/app/Http/Controllers/Json/RuleController.php index fcec63cedc..f0abfea161 100644 --- a/app/Http/Controllers/Json/RuleController.php +++ b/app/Http/Controllers/Json/RuleController.php @@ -27,7 +27,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Throwable; /** * Class RuleController @@ -37,9 +36,6 @@ class RuleController extends Controller /** * Render HTML form for rule action. * - * @param Request $request - * - * @return JsonResponse * @throws FireflyException */ public function action(Request $request): JsonResponse @@ -48,14 +44,16 @@ class RuleController extends Controller $keys = array_keys(config('firefly.rule-actions')); $actions = []; foreach ($keys as $key) { - $actions[$key] = (string)trans('firefly.rule_action_' . $key . '_choice'); + $actions[$key] = (string)trans('firefly.rule_action_'.$key.'_choice'); } + try { $view = view('rules.partials.action', compact('actions', 'count'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render rules.partials.action: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $view = 'Could not render view.'; + throw new FireflyException($view, 0, $e); } @@ -65,9 +63,6 @@ class RuleController extends Controller /** * Render HTML for rule trigger. * - * @param Request $request - * - * @return JsonResponse * @throws FireflyException */ public function trigger(Request $request): JsonResponse @@ -84,10 +79,11 @@ class RuleController extends Controller try { $view = view('rules.partials.trigger', compact('triggers', 'count'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render rules.partials.trigger: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $view = 'Could not render view.'; + throw new FireflyException($view, 0, $e); } diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php index de2beda01a..e3d87ba255 100644 --- a/app/Http/Controllers/NewUserController.php +++ b/app/Http/Controllers/NewUserController.php @@ -62,7 +62,7 @@ class NewUserController extends Controller /** * Form the user gets when he has no data in the system. * - * @return RedirectResponse|Redirector|Factory|View + * @return Factory|Redirector|RedirectResponse|View */ public function index() { @@ -84,10 +84,8 @@ class NewUserController extends Controller /** * Store his new settings. * - * @param NewUserFormRequest $request - * @param CurrencyRepositoryInterface $currencyRepository + * @return Redirector|RedirectResponse * - * @return RedirectResponse|Redirector * @throws FireflyException */ public function submit(NewUserFormRequest $request, CurrencyRepositoryInterface $currencyRepository) diff --git a/app/Http/Controllers/ObjectGroup/DeleteController.php b/app/Http/Controllers/ObjectGroup/DeleteController.php index c0a3b41ecd..f9115ec7f4 100644 --- a/app/Http/Controllers/ObjectGroup/DeleteController.php +++ b/app/Http/Controllers/ObjectGroup/DeleteController.php @@ -40,8 +40,6 @@ class DeleteController extends Controller /** * PiggyBankController constructor. - * - */ public function __construct() { @@ -62,8 +60,6 @@ class DeleteController extends Controller /** * Delete a piggy bank. * - * @param ObjectGroup $objectGroup - * * @return Factory|View */ public function delete(ObjectGroup $objectGroup) @@ -79,10 +75,6 @@ class DeleteController extends Controller /** * Destroy the piggy bank. - * - * @param ObjectGroup $objectGroup - * - * @return RedirectResponse */ public function destroy(ObjectGroup $objectGroup): RedirectResponse { diff --git a/app/Http/Controllers/ObjectGroup/EditController.php b/app/Http/Controllers/ObjectGroup/EditController.php index 507804d773..17322740a5 100644 --- a/app/Http/Controllers/ObjectGroup/EditController.php +++ b/app/Http/Controllers/ObjectGroup/EditController.php @@ -43,8 +43,6 @@ class EditController extends Controller /** * PiggyBankController constructor. - * - */ public function __construct() { @@ -65,8 +63,6 @@ class EditController extends Controller /** * Edit an object group. * - * @param ObjectGroup $objectGroup - * * @return Factory|View */ public function edit(ObjectGroup $objectGroup) @@ -85,10 +81,7 @@ class EditController extends Controller /** * Update a piggy bank. * - * @param ObjectGroupFormRequest $request - * @param ObjectGroup $objectGroup - * - * @return Application|RedirectResponse|Redirector + * @return Application|Redirector|RedirectResponse */ public function update(ObjectGroupFormRequest $request, ObjectGroup $objectGroup) { diff --git a/app/Http/Controllers/ObjectGroup/IndexController.php b/app/Http/Controllers/ObjectGroup/IndexController.php index 990c569b91..168b3ea601 100644 --- a/app/Http/Controllers/ObjectGroup/IndexController.php +++ b/app/Http/Controllers/ObjectGroup/IndexController.php @@ -41,8 +41,6 @@ class IndexController extends Controller /** * IndexController constructor. - * - */ public function __construct() { @@ -74,9 +72,6 @@ class IndexController extends Controller } /** - * @param Request $request - * @param ObjectGroup $objectGroup - * * @return JsonResponse */ public function setOrder(Request $request, ObjectGroup $objectGroup) diff --git a/app/Http/Controllers/PiggyBank/AmountController.php b/app/Http/Controllers/PiggyBank/AmountController.php index 38a876cbea..5fba6d7dbf 100644 --- a/app/Http/Controllers/PiggyBank/AmountController.php +++ b/app/Http/Controllers/PiggyBank/AmountController.php @@ -44,8 +44,6 @@ class AmountController extends Controller /** * PiggyBankController constructor. - * - */ public function __construct() { @@ -67,8 +65,6 @@ class AmountController extends Controller /** * Add money to piggy bank. * - * @param PiggyBank $piggyBank - * * @return Factory|View */ public function add(PiggyBank $piggyBank) @@ -88,8 +84,6 @@ class AmountController extends Controller /** * Add money to piggy bank (for mobile devices). * - * @param PiggyBank $piggyBank - * * @return Factory|View */ public function addMobile(PiggyBank $piggyBank) @@ -111,11 +105,6 @@ class AmountController extends Controller /** * Add money to piggy bank. - * - * @param Request $request - * @param PiggyBank $piggyBank - * - * @return RedirectResponse */ public function postAdd(Request $request, PiggyBank $piggyBank): RedirectResponse { @@ -139,7 +128,7 @@ class AmountController extends Controller return redirect(route('piggy-banks.index')); } - app('log')->error('Cannot add ' . $amount . ' because canAddAmount returned false.'); + app('log')->error('Cannot add '.$amount.' because canAddAmount returned false.'); session()->flash( 'error', (string)trans( @@ -153,11 +142,6 @@ class AmountController extends Controller /** * Remove money from piggy bank. - * - * @param Request $request - * @param PiggyBank $piggyBank - * - * @return RedirectResponse */ public function postRemove(Request $request, PiggyBank $piggyBank): RedirectResponse { @@ -196,8 +180,6 @@ class AmountController extends Controller /** * Remove money from piggy bank form. * - * @param PiggyBank $piggyBank - * * @return Factory|View */ public function remove(PiggyBank $piggyBank) @@ -211,8 +193,6 @@ class AmountController extends Controller /** * Remove money from piggy bank (for mobile devices). * - * @param PiggyBank $piggyBank - * * @return Factory|View */ public function removeMobile(PiggyBank $piggyBank) diff --git a/app/Http/Controllers/PiggyBank/CreateController.php b/app/Http/Controllers/PiggyBank/CreateController.php index 4a3ec07f11..efb04aa816 100644 --- a/app/Http/Controllers/PiggyBank/CreateController.php +++ b/app/Http/Controllers/PiggyBank/CreateController.php @@ -44,8 +44,6 @@ class CreateController extends Controller /** * PiggyBankController constructor. - * - */ public function __construct() { @@ -86,9 +84,8 @@ class CreateController extends Controller /** * Store a new piggy bank. * - * @param PiggyBankStoreRequest $request + * @return Redirector|RedirectResponse * - * @return RedirectResponse|Redirector * @throws FireflyException */ public function store(PiggyBankStoreRequest $request) @@ -103,7 +100,7 @@ class CreateController extends Controller app('preferences')->mark(); // store attachment(s): - /** @var array|null $files */ + /** @var null|array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($piggyBank, $files); diff --git a/app/Http/Controllers/PiggyBank/DeleteController.php b/app/Http/Controllers/PiggyBank/DeleteController.php index 45d17de6a1..754b62f01d 100644 --- a/app/Http/Controllers/PiggyBank/DeleteController.php +++ b/app/Http/Controllers/PiggyBank/DeleteController.php @@ -40,8 +40,6 @@ class DeleteController extends Controller /** * PiggyBankController constructor. - * - */ public function __construct() { @@ -62,8 +60,6 @@ class DeleteController extends Controller /** * Delete a piggy bank. * - * @param PiggyBank $piggyBank - * * @return Factory|View */ public function delete(PiggyBank $piggyBank) @@ -78,10 +74,6 @@ class DeleteController extends Controller /** * Destroy the piggy bank. - * - * @param PiggyBank $piggyBank - * - * @return RedirectResponse */ public function destroy(PiggyBank $piggyBank): RedirectResponse { diff --git a/app/Http/Controllers/PiggyBank/EditController.php b/app/Http/Controllers/PiggyBank/EditController.php index 6824bb4ab2..25f2d22f94 100644 --- a/app/Http/Controllers/PiggyBank/EditController.php +++ b/app/Http/Controllers/PiggyBank/EditController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\PiggyBank; -use Amount; use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\PiggyBankUpdateRequest; @@ -47,8 +46,6 @@ class EditController extends Controller /** * PiggyBankController constructor. - * - */ public function __construct() { @@ -62,6 +59,7 @@ class EditController extends Controller $this->attachments = app(AttachmentHelperInterface::class); $this->piggyRepos = app(PiggyBankRepositoryInterface::class); $this->accountRepository = app(AccountRepositoryInterface::class); + return $next($request); } ); @@ -70,8 +68,6 @@ class EditController extends Controller /** * Edit a piggy bank. * - * @param PiggyBank $piggyBank - * * @return Factory|View */ public function edit(PiggyBank $piggyBank) @@ -113,10 +109,7 @@ class EditController extends Controller /** * Update a piggy bank. * - * @param PiggyBankUpdateRequest $request - * @param PiggyBank $piggyBank - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function update(PiggyBankUpdateRequest $request, PiggyBank $piggyBank) { @@ -127,7 +120,7 @@ class EditController extends Controller app('preferences')->mark(); // store new attachment(s): - /** @var array|null $files */ + /** @var null|array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($piggyBank, $files); diff --git a/app/Http/Controllers/PiggyBank/IndexController.php b/app/Http/Controllers/PiggyBank/IndexController.php index 65db2fd08a..a41660f1cf 100644 --- a/app/Http/Controllers/PiggyBank/IndexController.php +++ b/app/Http/Controllers/PiggyBank/IndexController.php @@ -36,7 +36,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\View\View; -use JsonException; use Symfony\Component\HttpFoundation\ParameterBag; /** @@ -50,8 +49,6 @@ class IndexController extends Controller /** * PiggyBankController constructor. - * - */ public function __construct() { @@ -75,8 +72,8 @@ class IndexController extends Controller * TODO very complicated function. * * @return Factory|View + * * @throws FireflyException - * @throws JsonException */ public function index() { @@ -84,6 +81,7 @@ class IndexController extends Controller $this->piggyRepos->resetOrder(); $collection = $this->piggyRepos->getPiggyBanks(); $accounts = []; + /** @var Carbon $end */ $end = session('end', today(config('app.timezone'))->endOfMonth()); @@ -101,6 +99,7 @@ class IndexController extends Controller /** @var AccountTransformer $accountTransformer */ $accountTransformer = app(AccountTransformer::class); $accountTransformer->setParameters($parameters); + /** @var PiggyBank $piggy */ foreach ($collection as $piggy) { $array = $transformer->transform($piggy); @@ -143,10 +142,23 @@ class IndexController extends Controller } /** - * @param array $piggyBanks - * - * @return array + * Set the order of a piggy bank. */ + public function setOrder(Request $request, PiggyBank $piggyBank): JsonResponse + { + $objectGroupTitle = (string)$request->get('objectGroupTitle'); + $newOrder = (int)$request->get('order'); + $this->piggyRepos->setOrder($piggyBank, $newOrder); + if ('' !== $objectGroupTitle) { + $this->piggyRepos->setObjectGroup($piggyBank, $objectGroupTitle); + } + if ('' === $objectGroupTitle) { + $this->piggyRepos->removeObjectGroup($piggyBank); + } + + return response()->json(['data' => 'OK']); + } + private function makeSums(array $piggyBanks): array { $sums = []; @@ -181,27 +193,4 @@ class IndexController extends Controller return $piggyBanks; } - - /** - * Set the order of a piggy bank. - * - * @param Request $request - * @param PiggyBank $piggyBank - * - * @return JsonResponse - */ - public function setOrder(Request $request, PiggyBank $piggyBank): JsonResponse - { - $objectGroupTitle = (string)$request->get('objectGroupTitle'); - $newOrder = (int)$request->get('order'); - $this->piggyRepos->setOrder($piggyBank, $newOrder); - if ('' !== $objectGroupTitle) { - $this->piggyRepos->setObjectGroup($piggyBank, $objectGroupTitle); - } - if ('' === $objectGroupTitle) { - $this->piggyRepos->removeObjectGroup($piggyBank); - } - - return response()->json(['data' => 'OK']); - } } diff --git a/app/Http/Controllers/PiggyBank/ShowController.php b/app/Http/Controllers/PiggyBank/ShowController.php index d9ff1f9e36..10da91919f 100644 --- a/app/Http/Controllers/PiggyBank/ShowController.php +++ b/app/Http/Controllers/PiggyBank/ShowController.php @@ -32,7 +32,6 @@ use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Transformers\PiggyBankTransformer; use Illuminate\Contracts\View\Factory; use Illuminate\View\View; -use JsonException; use Symfony\Component\HttpFoundation\ParameterBag; /** @@ -44,8 +43,6 @@ class ShowController extends Controller /** * PiggyBankController constructor. - * - */ public function __construct() { @@ -66,11 +63,9 @@ class ShowController extends Controller /** * Show a single piggy bank. * - * @param PiggyBank $piggyBank - * * @return Factory|View + * * @throws FireflyException - * @throws JsonException */ public function show(PiggyBank $piggyBank) { diff --git a/app/Http/Controllers/Popup/ReportController.php b/app/Http/Controllers/Popup/ReportController.php index e8e65940d0..c321bcb806 100644 --- a/app/Http/Controllers/Popup/ReportController.php +++ b/app/Http/Controllers/Popup/ReportController.php @@ -31,7 +31,6 @@ use Illuminate\Http\Request; /** * Class ReportController. - * */ class ReportController extends Controller { @@ -40,9 +39,6 @@ class ReportController extends Controller /** * Generate popup view. * - * @param Request $request - * - * @return JsonResponse * @throws FireflyException */ public function general(Request $request): JsonResponse @@ -61,6 +57,7 @@ class ReportController extends Controller 'category-entry' => $this->categoryEntry($attributes), 'budget-entry' => $this->budgetEntry($attributes), }; + return response()->json(['html' => $html]); } } diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index a526b3061b..6bff371140 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -34,9 +34,6 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Routing\Redirector; use Illuminate\View\View; -use JsonException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class PreferencesController. @@ -45,8 +42,6 @@ class PreferencesController extends Controller { /** * PreferencesController constructor. - * - */ public function __construct() { @@ -54,7 +49,7 @@ class PreferencesController extends Controller $this->middleware( static 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); @@ -65,20 +60,16 @@ class PreferencesController extends Controller /** * Show overview of preferences. * - * @param AccountRepositoryInterface $repository - * * @return Factory|View + * * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function index(AccountRepositoryInterface $repository) { - $accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]); - $isDocker = env('IS_DOCKER', false); - - // group accounts + $accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]); + $isDocker = env('IS_DOCKER', false); $groupedAccounts = []; + /** @var Account $account */ foreach ($accounts as $account) { $type = $account->accountType->type; @@ -91,7 +82,7 @@ class PreferencesController extends Controller if ('opt_group_' === $role) { $role = 'opt_group_defaultAsset'; } - $groupedAccounts[(string)trans(sprintf('firefly.%s', $role))][$account->id] = $account->name; + $groupedAccounts[(string) trans(sprintf('firefly.%s', $role))][$account->id] = $account->name; } ksort($groupedAccounts); @@ -114,7 +105,7 @@ class PreferencesController extends Controller if (is_array($fiscalYearStartStr)) { $fiscalYearStartStr = '01-01'; } - $fiscalYearStart = sprintf('%s-%s', date('Y'), (string)$fiscalYearStartStr); + $fiscalYearStart = sprintf('%s-%s', date('Y'), (string) $fiscalYearStartStr); $tjOptionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data; $availableDarkModes = config('firefly.available_dark_modes'); @@ -129,12 +120,12 @@ class PreferencesController extends Controller // list of locales also has "equal" which makes it equal to whatever the language is. try { - $locales = json_decode((string)file_get_contents(resource_path(sprintf('lang/%s/locales.json', $language))), true, 512, JSON_THROW_ON_ERROR); - } catch (JsonException $e) { + $locales = json_decode((string) file_get_contents(resource_path(sprintf('lang/%s/locales.json', $language))), true, 512, JSON_THROW_ON_ERROR); + } catch (\JsonException $e) { app('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)) { @@ -148,38 +139,18 @@ class PreferencesController extends Controller $slackUrl = ''; } - return view( - 'preferences.index', - compact( - 'language', - 'groupedAccounts', - 'isDocker', - 'frontPageAccounts', - 'languages', - 'darkMode', - 'availableDarkModes', - 'notifications', - 'slackUrl', - 'locales', - 'locale', - 'tjOptionalFields', - 'viewRange', - 'customFiscalYear', - 'listPageSize', - 'fiscalYearStart' - ) - ); + return view('preferences.index', compact('language', 'groupedAccounts', 'isDocker', 'frontPageAccounts', 'languages', 'darkMode', 'availableDarkModes', 'notifications', 'slackUrl', 'locales', 'locale', 'tjOptionalFields', 'viewRange', 'customFiscalYear', 'listPageSize', 'fiscalYearStart')); } /** * Store new preferences. * - * @param Request $request + * @return Redirector|RedirectResponse * - * @return RedirectResponse|Redirector * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.NPathComplexity) */ public function postIndex(Request $request) { @@ -187,7 +158,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); } @@ -211,10 +182,9 @@ class PreferencesController extends Controller session()->forget('end'); session()->forget('range'); - // slack URL: if (!auth()->user()->hasRole('demo')) { - $url = (string)$request->get('slackUrl'); + $url = (string) $request->get('slackUrl'); if (UrlValidator::isValidWebhookURL($url)) { app('preferences')->set('slack_webhook_url', $url); } @@ -224,18 +194,17 @@ class PreferencesController extends Controller } // custom fiscal year - $customFiscalYear = 1 === (int)$request->get('customFiscalYear'); - $string = strtotime((string)$request->get('fiscalYearStart')); + $customFiscalYear = 1 === (int) $request->get('customFiscalYear'); + $string = strtotime((string) $request->get('fiscalYearStart')); if (false !== $string) { $fiscalYearStart = date('m-d', $string); 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); } @@ -283,7 +252,7 @@ class PreferencesController extends Controller app('preferences')->set('darkMode', $darkMode); } - session()->flash('success', (string)trans('firefly.saved_preferences')); + session()->flash('success', (string) trans('firefly.saved_preferences')); app('preferences')->mark(); return redirect(route('preferences.index')); diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 5f724b5714..6d1ff813c2 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -24,8 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers; use Auth; -use DB; -use Exception; use FireflyIII\Events\UserChangedEmail; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\ValidationException; @@ -38,8 +36,6 @@ use FireflyIII\Models\Preference; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Support\Http\Controllers\CreateStuff; use FireflyIII\User; -use Google2FA; -use Hash; use Illuminate\Auth\AuthenticationException; use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Foundation\Application; @@ -54,14 +50,11 @@ use PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException; use PragmaRX\Google2FA\Exceptions\InvalidCharactersException; use PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException; use PragmaRX\Recovery\Recovery; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class ProfileController. * * @method Guard guard() - * */ class ProfileController extends Controller { @@ -71,8 +64,6 @@ class ProfileController extends Controller /** * ProfileController constructor. - * - */ public function __construct() { @@ -96,16 +87,11 @@ class ProfileController extends Controller /** * View that generates a 2FA code for the user. * - * @param Request $request - * - * @return Factory|View|RedirectResponse * @throws IncompatibleWithGoogleAuthenticatorException * @throws InvalidCharactersException * @throws SecretKeyTooShortException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ - public function code(Request $request): Factory | View | RedirectResponse + public function code(Request $request): Factory|RedirectResponse|View { if (!$this->internalAuth) { $request->session()->flash('error', trans('firefly.external_user_mgt_disabled')); @@ -119,7 +105,7 @@ class ProfileController extends Controller // generate secret if not in session if (null === $secretPreference) { // generate secret + store + flash - $secret = Google2FA::generateSecretKey(); + $secret = \Google2FA::generateSecretKey(); app('preferences')->set('temp-mfa-secret', $secret); } @@ -152,7 +138,7 @@ class ProfileController extends Controller $codes = implode("\r\n", $recoveryCodes); - $image = Google2FA::getQRCodeInline($domain, auth()->user()->email, (string)$secret); + $image = \Google2FA::getQRCodeInline($domain, auth()->user()->email, (string)$secret); return view('profile.code', compact('image', 'secret', 'codes')); } @@ -160,22 +146,19 @@ class ProfileController extends Controller /** * Screen to confirm email change. * - * @param UserRepositoryInterface $repository - * @param string $token - * - * @return RedirectResponse|Redirector - * * @throws FireflyException */ - public function confirmEmailChange(UserRepositoryInterface $repository, string $token): RedirectResponse | Redirector + public function confirmEmailChange(UserRepositoryInterface $repository, string $token): Redirector|RedirectResponse { if (!$this->internalAuth) { throw new FireflyException(trans('firefly.external_user_mgt_disabled')); } + // find preference with this token value. /** @var Collection $set */ $set = app('preferences')->findByName('email_change_confirm_token'); $user = null; + /** @var Preference $preference */ foreach ($set as $preference) { if ($preference->data === $token) { @@ -196,12 +179,8 @@ class ProfileController extends Controller /** * Delete your account view. - * - * @param Request $request - * - * @return View|RedirectResponse */ - public function deleteAccount(Request $request): View | RedirectResponse + public function deleteAccount(Request $request): RedirectResponse|View { if (!$this->internalAuth) { $request->session()->flash('error', trans('firefly.external_user_mgt_disabled')); @@ -217,15 +196,15 @@ class ProfileController extends Controller /** * Delete 2FA routine. - * */ - public function deleteCode(Request $request): RedirectResponse | Redirector + public function deleteCode(Request $request): Redirector|RedirectResponse { if (!$this->internalAuth) { $request->session()->flash('error', trans('firefly.external_user_mgt_disabled')); return redirect(route('profile.index')); } + /** @var UserRepositoryInterface $repository */ $repository = app(UserRepositoryInterface::class); @@ -245,9 +224,8 @@ class ProfileController extends Controller /** * Enable 2FA screen. - * */ - public function enable2FA(Request $request): RedirectResponse | Redirector + public function enable2FA(Request $request): Redirector|RedirectResponse { if (!$this->internalAuth) { $request->session()->flash('error', trans('firefly.external_user_mgt_disabled')); @@ -274,17 +252,14 @@ class ProfileController extends Controller /** * Index for profile. * - * @return Factory|View * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ - public function index(): Factory | View + public function index(): Factory|View { /** @var User $user */ $user = auth()->user(); $isInternalAuth = $this->internalAuth; - $count = DB::table('oauth_clients')->where('personal_access_client', true)->whereNull('user_id')->count(); + $count = \DB::table('oauth_clients')->where('personal_access_client', true)->whereNull('user_id')->count(); $subTitle = $user->email; $userId = $user->id; $enabled2FA = null !== $user->mfa_secret; @@ -298,7 +273,7 @@ class ProfileController extends Controller if (0 === $count) { /** @var ClientRepository $repository */ $repository = app(ClientRepository::class); - $repository->createPersonalAccessClient(null, config('app.name') . ' Personal Access Client', 'http://localhost'); + $repository->createPersonalAccessClient(null, config('app.name').' Personal Access Client', 'http://localhost'); } $accessToken = app('preferences')->get('access_token'); @@ -313,10 +288,7 @@ class ProfileController extends Controller ); } - /** - * @return Factory|View|RedirectResponse - */ - public function logoutOtherSessions(): Factory | View | RedirectResponse + public function logoutOtherSessions(): Factory|RedirectResponse|View { if (!$this->internalAuth) { session()->flash('info', (string)trans('firefly.external_auth_disabled')); @@ -328,12 +300,9 @@ class ProfileController extends Controller } /** - * @param Request $request - * - * @return Factory|View|RedirectResponse * @throws FireflyException */ - public function newBackupCodes(Request $request): Factory | View | RedirectResponse + public function newBackupCodes(Request $request): Factory|RedirectResponse|View { if (!$this->internalAuth) { $request->session()->flash('error', trans('firefly.external_user_mgt_disabled')); @@ -344,10 +313,11 @@ class ProfileController extends Controller // generate recovery codes: $recovery = app(Recovery::class); $recoveryCodes = $recovery->lowercase() - ->setCount(8) // Generate 8 codes - ->setBlocks(2) // Every code must have 7 blocks - ->setChars(6) // Each block must have 16 chars - ->toArray(); + ->setCount(8) // Generate 8 codes + ->setBlocks(2) // Every code must have 7 blocks + ->setChars(6) // Each block must have 16 chars + ->toArray() + ; $codes = implode("\r\n", $recoveryCodes); app('preferences')->set('mfa_recovery', $recoveryCodes); @@ -358,13 +328,8 @@ class ProfileController extends Controller /** * Submit the change email form. - * - * @param EmailFormRequest $request - * @param UserRepositoryInterface $repository - * - * @return Factory|RedirectResponse|Redirector */ - public function postChangeEmail(EmailFormRequest $request, UserRepositoryInterface $repository): Factory | RedirectResponse | Redirector + public function postChangeEmail(EmailFormRequest $request, UserRepositoryInterface $repository): Factory|Redirector|RedirectResponse { if (!$this->internalAuth) { $request->session()->flash('error', trans('firefly.external_user_mgt_disabled')); @@ -384,7 +349,7 @@ class ProfileController extends Controller $existing = $repository->findByEmail($newEmail); if (null !== $existing) { // force user logout. - Auth::guard()->logout(); // @phpstan-ignore-line (does not recognize function) + \Auth::guard()->logout(); // @phpstan-ignore-line (does not recognize function) $request->session()->invalidate(); session()->flash('success', (string)trans('firefly.email_changed')); @@ -398,7 +363,7 @@ class ProfileController extends Controller event(new UserChangedEmail($user, $newEmail, $oldEmail)); // force user logout. - Auth::guard()->logout(); // @phpstan-ignore-line (does not recognize function) + \Auth::guard()->logout(); // @phpstan-ignore-line (does not recognize function) $request->session()->invalidate(); session()->flash('success', (string)trans('firefly.email_changed')); @@ -407,12 +372,8 @@ class ProfileController extends Controller /** * Change your email address. - * - * @param Request $request - * - * @return Factory|RedirectResponse|View */ - public function changeEmail(Request $request): Factory | RedirectResponse | View + public function changeEmail(Request $request): Factory|RedirectResponse|View { if (!$this->internalAuth) { $request->session()->flash('error', trans('firefly.external_user_mgt_disabled')); @@ -431,10 +392,7 @@ class ProfileController extends Controller /** * Submit change password form. * - * @param ProfileFormRequest $request - * @param UserRepositoryInterface $repository - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function postChangePassword(ProfileFormRequest $request, UserRepositoryInterface $repository) { @@ -447,8 +405,10 @@ class ProfileController extends Controller // the request has already validated both new passwords must be equal. $current = $request->get('current_password'); $new = $request->get('new_password'); + /** @var User $user */ $user = auth()->user(); + try { $this->validatePassword($user, $current, $new); } catch (ValidationException $e) { @@ -466,9 +426,7 @@ class ProfileController extends Controller /** * Change your password. * - * @param Request $request - * - * @return Factory|RedirectResponse|Redirector|View + * @return Factory|Redirector|RedirectResponse|View */ public function changePassword(Request $request) { @@ -488,12 +446,9 @@ class ProfileController extends Controller /** * Submit 2FA for the first time. * - * @param TokenFormRequest $request + * @return Redirector|RedirectResponse * - * @return RedirectResponse|Redirector * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function postCode(TokenFormRequest $request) { @@ -505,6 +460,7 @@ class ProfileController extends Controller /** @var User $user */ $user = auth()->user(); + /** @var UserRepositoryInterface $repository */ $repository = app(UserRepositoryInterface::class); $secret = app('preferences')->get('temp-mfa-secret')?->data; @@ -532,7 +488,7 @@ class ProfileController extends Controller // make sure MFA is logged out. if ('testing' !== config('app.env')) { - Google2FA::logout(); + \Google2FA::logout(); } // drop all info from session: @@ -542,13 +498,144 @@ class ProfileController extends Controller } /** - * TODO duplicate code. + * Submit delete account. * - * @param string $mfaCode + * @return Redirector|RedirectResponse + */ + public function postDeleteAccount(UserRepositoryInterface $repository, DeleteAccountFormRequest $request) + { + if (!$this->internalAuth) { + $request->session()->flash('error', trans('firefly.external_user_mgt_disabled')); + + return redirect(route('profile.index')); + } + + if (!\Hash::check($request->get('password'), auth()->user()->password)) { + session()->flash('error', (string)trans('firefly.invalid_password')); + + return redirect(route('profile.delete-account')); + } + + /** @var User $user */ + $user = auth()->user(); + app('log')->info(sprintf('User #%d has opted to delete their account', auth()->user()->id)); + // make repository delete user: + auth()->logout(); + session()->flush(); + $repository->destroy($user); + + return redirect(route('index')); + } + + /** + * @return Application|Redirector|RedirectResponse + * + * @throws AuthenticationException + */ + public function postLogoutOtherSessions(Request $request) + { + if (!$this->internalAuth) { + session()->flash('info', (string)trans('firefly.external_auth_disabled')); + + return redirect(route('profile.index')); + } + $creds = [ + 'email' => auth()->user()->email, + 'password' => $request->get('password'), + ]; + if (\Auth::once($creds)) { + \Auth::logoutOtherDevices($request->get('password')); + session()->flash('info', (string)trans('firefly.other_sessions_logged_out')); + + return redirect(route('profile.index')); + } + session()->flash('error', (string)trans('auth.failed')); + + return redirect(route('profile.index')); + } + + /** + * Regenerate access token. + * + * @return Redirector|RedirectResponse + * + * @throws \Exception + */ + public function regenerate(Request $request) + { + if (!$this->internalAuth) { + $request->session()->flash('error', trans('firefly.external_user_mgt_disabled')); + + return redirect(route('profile.index')); + } + + /** @var User $user */ + $user = auth()->user(); + $token = $user->generateAccessToken(); + app('preferences')->set('access_token', $token); + session()->flash('success', (string)trans('firefly.token_regenerated')); + + return redirect(route('profile.index')); + } + + /** + * Undo change of user email address. + * + * @return Redirector|RedirectResponse + * + * @throws FireflyException + */ + public function undoEmailChange(UserRepositoryInterface $repository, string $token, string $hash) + { + if (!$this->internalAuth) { + throw new FireflyException(trans('firefly.external_user_mgt_disabled')); + } + + // find preference with this token value. + $set = app('preferences')->findByName('email_change_undo_token'); + $user = null; + + /** @var Preference $preference */ + foreach ($set as $preference) { + if ($preference->data === $token) { + $user = $preference->user; + } + } + if (null === $user) { + throw new FireflyException('Invalid token.'); + } + + // found user.which email address to return to? + $set = app('preferences')->beginsWith($user, 'previous_email_'); + + /** @var string $match */ + $match = null; + foreach ($set as $entry) { + $hashed = hash('sha256', sprintf('%s%s', (string)config('app.key'), $entry->data)); + if ($hashed === $hash) { + $match = $entry->data; + + break; + } + } + if (null === $match) { + throw new FireflyException('Invalid token.'); + } + // change user back + // now actually update user: + $repository->changeEmail($user, $match); + $repository->unblockUser($user); + + // return to login. + session()->flash('success', (string)trans('firefly.login_with_old_email')); + + return redirect(route('login')); + } + + /** + * TODO duplicate code. * * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ private function addToMFAHistory(string $mfaCode): void { @@ -585,144 +672,4 @@ class ProfileController extends Controller } app('preferences')->set('mfa_history', $newHistory); } - - /** - * Submit delete account. - * - * @param UserRepositoryInterface $repository - * @param DeleteAccountFormRequest $request - * - * @return RedirectResponse|Redirector - */ - public function postDeleteAccount(UserRepositoryInterface $repository, DeleteAccountFormRequest $request) - { - if (!$this->internalAuth) { - $request->session()->flash('error', trans('firefly.external_user_mgt_disabled')); - - return redirect(route('profile.index')); - } - - if (!Hash::check($request->get('password'), auth()->user()->password)) { - session()->flash('error', (string)trans('firefly.invalid_password')); - - return redirect(route('profile.delete-account')); - } - /** @var User $user */ - $user = auth()->user(); - app('log')->info(sprintf('User #%d has opted to delete their account', auth()->user()->id)); - // make repository delete user: - auth()->logout(); - session()->flush(); - $repository->destroy($user); - - return redirect(route('index')); - } - - /** - * @param Request $request - * - * @return Application|RedirectResponse|Redirector - * @throws AuthenticationException - */ - public function postLogoutOtherSessions(Request $request) - { - if (!$this->internalAuth) { - session()->flash('info', (string)trans('firefly.external_auth_disabled')); - - return redirect(route('profile.index')); - } - $creds = [ - 'email' => auth()->user()->email, - 'password' => $request->get('password'), - ]; - if (Auth::once($creds)) { - Auth::logoutOtherDevices($request->get('password')); - session()->flash('info', (string)trans('firefly.other_sessions_logged_out')); - - return redirect(route('profile.index')); - } - session()->flash('error', (string)trans('auth.failed')); - - return redirect(route('profile.index')); - } - - /** - * Regenerate access token. - * - * @param Request $request - * - * @return RedirectResponse|Redirector - * @throws Exception - */ - public function regenerate(Request $request) - { - if (!$this->internalAuth) { - $request->session()->flash('error', trans('firefly.external_user_mgt_disabled')); - - return redirect(route('profile.index')); - } - - /** @var User $user */ - $user = auth()->user(); - $token = $user->generateAccessToken(); - app('preferences')->set('access_token', $token); - session()->flash('success', (string)trans('firefly.token_regenerated')); - - return redirect(route('profile.index')); - } - - /** - * Undo change of user email address. - * - * @param UserRepositoryInterface $repository - * @param string $token - * @param string $hash - * - * @return RedirectResponse|Redirector - * - * @throws FireflyException - */ - public function undoEmailChange(UserRepositoryInterface $repository, string $token, string $hash) - { - if (!$this->internalAuth) { - throw new FireflyException(trans('firefly.external_user_mgt_disabled')); - } - - // find preference with this token value. - $set = app('preferences')->findByName('email_change_undo_token'); - $user = null; - /** @var Preference $preference */ - foreach ($set as $preference) { - if ($preference->data === $token) { - $user = $preference->user; - } - } - if (null === $user) { - throw new FireflyException('Invalid token.'); - } - - // found user.which email address to return to? - $set = app('preferences')->beginsWith($user, 'previous_email_'); - /** @var string $match */ - $match = null; - foreach ($set as $entry) { - $hashed = hash('sha256', sprintf('%s%s', (string)config('app.key'), $entry->data)); - if ($hashed === $hash) { - $match = $entry->data; - break; - } - } - if (null === $match) { - throw new FireflyException('Invalid token.'); - } - // change user back - // now actually update user: - $repository->changeEmail($user, $match); - $repository->unblockUser($user); - - // return to login. - session()->flash('success', (string)trans('firefly.login_with_old_email')); - - return redirect(route('login')); - } } diff --git a/app/Http/Controllers/Recurring/CreateController.php b/app/Http/Controllers/Recurring/CreateController.php index 0fcd8bf211..31a4116298 100644 --- a/app/Http/Controllers/Recurring/CreateController.php +++ b/app/Http/Controllers/Recurring/CreateController.php @@ -40,7 +40,6 @@ use Illuminate\Routing\Redirector; use Illuminate\View\View; /** - * * Class CreateController */ class CreateController extends Controller @@ -52,8 +51,6 @@ class CreateController extends Controller /** * CreateController constructor. - * - */ public function __construct() { @@ -79,8 +76,6 @@ class CreateController extends Controller /** * Create a new recurring transaction. * - * @param Request $request - * * @return Factory|View */ public function create(Request $request) @@ -124,10 +119,9 @@ class CreateController extends Controller } /** - * @param Request $request - * @param TransactionJournal $journal - * * @return Factory|\Illuminate\Contracts\View\View + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function createFromJournal(Request $request, TransactionJournal $journal) { @@ -155,12 +149,12 @@ class CreateController extends Controller RecurrenceRepetition::WEEKEND_TO_MONDAY => (string)trans('firefly.jump_to_monday'), ]; - // fill prefilled with journal info $type = strtolower($journal->transactionType->type); /** @var Transaction $source */ $source = $journal->transactions()->where('amount', '<', 0)->first(); + /** @var Transaction $dest */ $dest = $journal->transactions()->where('amount', '>', 0)->first(); $category = null !== $journal->categories()->first() ? $journal->categories()->first()->name : ''; @@ -221,14 +215,14 @@ class CreateController extends Controller /** * Store a recurring transaction. * - * @param RecurrenceFormRequest $request + * @return Redirector|RedirectResponse * - * @return RedirectResponse|Redirector * @throws FireflyException */ public function store(RecurrenceFormRequest $request) { $data = $request->getAll(); + try { $recurrence = $this->recurring->store($data); } catch (FireflyException $e) { @@ -241,7 +235,7 @@ class CreateController extends Controller app('preferences')->mark(); // store attachment(s): - /** @var array|null $files */ + /** @var null|array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($recurrence, $files); diff --git a/app/Http/Controllers/Recurring/DeleteController.php b/app/Http/Controllers/Recurring/DeleteController.php index 829ae2b9fe..638c8eaae9 100644 --- a/app/Http/Controllers/Recurring/DeleteController.php +++ b/app/Http/Controllers/Recurring/DeleteController.php @@ -42,8 +42,6 @@ class DeleteController extends Controller /** * DeleteController constructor. - * - */ public function __construct() { @@ -65,8 +63,6 @@ class DeleteController extends Controller /** * Delete a recurring transaction form. * - * @param Recurrence $recurrence - * * @return Factory|View */ public function delete(Recurrence $recurrence) @@ -83,16 +79,12 @@ class DeleteController extends Controller /** * Destroy the recurring transaction. * - * @param RecurringRepositoryInterface $repository - * @param Request $request - * @param Recurrence $recurrence - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function destroy(RecurringRepositoryInterface $repository, Request $request, Recurrence $recurrence) { $repository->destroy($recurrence); - $request->session()->flash('success', (string)trans('firefly.' . 'recurrence_deleted', ['title' => $recurrence->title])); + $request->session()->flash('success', (string)trans('firefly.recurrence_deleted', ['title' => $recurrence->title])); app('preferences')->mark(); return redirect($this->getPreviousUrl('recurrences.delete.url')); diff --git a/app/Http/Controllers/Recurring/EditController.php b/app/Http/Controllers/Recurring/EditController.php index 8b674ec82c..d8a0e00c5a 100644 --- a/app/Http/Controllers/Recurring/EditController.php +++ b/app/Http/Controllers/Recurring/EditController.php @@ -41,7 +41,6 @@ use Illuminate\View\View; use Symfony\Component\HttpFoundation\ParameterBag; /** - * * Class EditController */ class EditController extends Controller @@ -53,8 +52,6 @@ class EditController extends Controller /** * EditController constructor. - * - */ public function __construct() { @@ -80,12 +77,9 @@ class EditController extends Controller /** * Edit a recurring transaction. * - * @param Request $request - * @param Recurrence $recurrence - * * @return Factory|View - * @throws FireflyException * + * @throws FireflyException */ public function edit(Request $request, Recurrence $recurrence) { @@ -107,7 +101,7 @@ class EditController extends Controller $repetition = $recurrence->recurrenceRepetitions()->first(); $currentRepType = $repetition->repetition_type; if ('' !== $repetition->repetition_moment) { - $currentRepType .= ',' . $repetition->repetition_moment; + $currentRepType .= ','.$repetition->repetition_moment; } // put previous url in session if not redirect from store (not "return_to_edit"). @@ -167,10 +161,8 @@ class EditController extends Controller /** * Update the recurring transaction. * - * @param RecurrenceFormRequest $request - * @param Recurrence $recurrence + * @return Redirector|RedirectResponse * - * @return RedirectResponse|Redirector * @throws FireflyException */ public function update(RecurrenceFormRequest $request, Recurrence $recurrence) @@ -181,7 +173,7 @@ class EditController extends Controller $request->session()->flash('success', (string)trans('firefly.updated_recurrence', ['title' => $recurrence->title])); // store new attachment(s): - /** @var array|null $files */ + /** @var null|array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($recurrence, $files); diff --git a/app/Http/Controllers/Recurring/IndexController.php b/app/Http/Controllers/Recurring/IndexController.php index c7af4775b4..4a5237af4c 100644 --- a/app/Http/Controllers/Recurring/IndexController.php +++ b/app/Http/Controllers/Recurring/IndexController.php @@ -34,12 +34,9 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; use Symfony\Component\HttpFoundation\ParameterBag; /** - * * Class IndexController */ class IndexController extends Controller @@ -50,8 +47,6 @@ class IndexController extends Controller /** * IndexController constructor. - * - */ public function __construct() { @@ -74,12 +69,9 @@ class IndexController extends Controller * TODO the notes of a recurrence are pretty pointless at this moment. * Show all recurring transactions. * - * @param Request $request - * * @return Factory|View + * * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function index(Request $request) { @@ -98,6 +90,7 @@ class IndexController extends Controller $transformer->setParameters(new ParameterBag()); $recurring = []; + /** @var Recurrence $recurrence */ foreach ($recurrences as $recurrence) { $year->addYear(); diff --git a/app/Http/Controllers/Recurring/ShowController.php b/app/Http/Controllers/Recurring/ShowController.php index fa9b4a2445..406e819e6d 100644 --- a/app/Http/Controllers/Recurring/ShowController.php +++ b/app/Http/Controllers/Recurring/ShowController.php @@ -38,7 +38,6 @@ use Illuminate\View\View; use Symfony\Component\HttpFoundation\ParameterBag; /** - * * Class ShowController */ class ShowController extends Controller @@ -50,8 +49,6 @@ class ShowController extends Controller /** * IndexController constructor. - * - */ public function __construct() { @@ -74,14 +71,14 @@ class ShowController extends Controller /** * Show a single recurring transaction. * - * @param Recurrence $recurrence - * * @return Factory|View + * * @throws FireflyException */ public function show(Recurrence $recurrence) { $repos = app(AttachmentRepositoryInterface::class); + /** @var RecurrenceTransformer $transformer */ $transformer = app(RecurrenceTransformer::class); $transformer->setParameters(new ParameterBag()); @@ -108,12 +105,12 @@ class ShowController extends Controller $attachments = $recurrence->attachments()->get(); $array['attachments'] = []; $attachmentTransformer = app(AttachmentTransformer::class); + /** @var Attachment $attachment */ foreach ($attachments as $attachment) { $item = $attachmentTransformer->transform($attachment); $item['file_exists'] = $repos->exists($attachment); // TODO this should be part of the transformer $array['attachments'][] = $item; - } $subTitle = (string)trans('firefly.overview_for_recurrence', ['title' => $recurrence->title]); diff --git a/app/Http/Controllers/Recurring/TriggerController.php b/app/Http/Controllers/Recurring/TriggerController.php index d7e9ac678d..714a101a11 100644 --- a/app/Http/Controllers/Recurring/TriggerController.php +++ b/app/Http/Controllers/Recurring/TriggerController.php @@ -1,6 +1,5 @@ getAll(); @@ -55,6 +48,7 @@ class TriggerController extends Controller // fire the recurring cron job on the given date, then post-date the created transaction. app('log')->info(sprintf('Trigger: will now fire recurring cron job task for date "%s".', $date->format('Y-m-d H:i:s'))); + /** @var CreateRecurringTransactions $job */ $job = app(CreateRecurringTransactions::class); $job->setRecurrences(new Collection([$recurrence])); @@ -64,6 +58,7 @@ class TriggerController extends Controller app('log')->debug('Done with recurrence.'); $groups = $job->getGroups(); + /** @var TransactionGroup $group */ foreach ($groups as $group) { /** @var TransactionJournal $journal */ @@ -86,7 +81,6 @@ class TriggerController extends Controller $request->session()->flash('success_url', route('transactions.show', [$first->id])); } - return redirect(route('recurring.show', [$recurrence->id])); } } diff --git a/app/Http/Controllers/Report/AccountController.php b/app/Http/Controllers/Report/AccountController.php index b8d1149df8..444a943c9f 100644 --- a/app/Http/Controllers/Report/AccountController.php +++ b/app/Http/Controllers/Report/AccountController.php @@ -29,7 +29,6 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; -use Throwable; /** * Class AccountController. @@ -39,11 +38,6 @@ class AccountController extends Controller /** * Show partial overview for account balances. * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string * @throws FireflyException */ public function general(Collection $accounts, Carbon $start, Carbon $end): string @@ -61,12 +55,14 @@ class AccountController extends Controller /** @var AccountTaskerInterface $accountTasker */ $accountTasker = app(AccountTaskerInterface::class); $accountReport = $accountTasker->getAccountReport($accounts, $start, $end); + try { $result = view('reports.partials.accounts', compact('accountReport'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render reports.partials.accounts: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $result = 'Could not render view.'; + throw new FireflyException($result, 0, $e); } diff --git a/app/Http/Controllers/Report/BalanceController.php b/app/Http/Controllers/Report/BalanceController.php index 617feffeab..a6943a76ad 100644 --- a/app/Http/Controllers/Report/BalanceController.php +++ b/app/Http/Controllers/Report/BalanceController.php @@ -32,7 +32,6 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use Illuminate\Support\Collection; -use Throwable; /** * Class BalanceController. @@ -61,11 +60,8 @@ class BalanceController extends Controller /** * Show overview of budget balances. * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException */ public function general(Collection $accounts, Carbon $start, Carbon $end) @@ -74,6 +70,7 @@ class BalanceController extends Controller 'budgets' => [], 'accounts' => [], ]; + /** @var Account $account */ foreach ($accounts as $account) { $report['accounts'][$account->id] = [ @@ -96,10 +93,13 @@ class BalanceController extends Controller 'sums' => [], // per currency ]; $spent = []; + /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $journals = $collector->setRange($start, $end)->setSourceAccounts($accounts)->setTypes([TransactionType::WITHDRAWAL])->setBudget($budget) - ->getExtractedJournals(); + ->getExtractedJournals() + ; + /** @var array $journal */ foreach ($journals as $journal) { $sourceAccount = $journal['source_account_id']; @@ -137,12 +137,14 @@ class BalanceController extends Controller $report['budgets'][$budgetId]['spent'] = $spent; // get transactions in budget } + try { $result = view('reports.partials.balance', compact('report'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render reports.partials.balance: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $result = 'Could not render view.'; + throw new FireflyException($result, 0, $e); } diff --git a/app/Http/Controllers/Report/BillController.php b/app/Http/Controllers/Report/BillController.php index dce8da7893..d758f0369f 100644 --- a/app/Http/Controllers/Report/BillController.php +++ b/app/Http/Controllers/Report/BillController.php @@ -29,7 +29,6 @@ use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; -use Throwable; /** * Class BillController @@ -37,11 +36,8 @@ use Throwable; class BillController extends Controller { /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * * @return mixed|string + * * @throws FireflyException */ public function overview(Collection $accounts, Carbon $start, Carbon $end) // chart properties for cache: @@ -54,15 +50,18 @@ class BillController extends Controller if ($cache->has()) { return $cache->get(); } + /** @var ReportHelperInterface $helper */ $helper = app(ReportHelperInterface::class); $report = $helper->getBillReport($accounts, $start, $end); + try { $result = view('reports.partials.bills', compact('report'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render reports.partials.budgets: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $result = 'Could not render view.'; + throw new FireflyException($result, 0, $e); } diff --git a/app/Http/Controllers/Report/BudgetController.php b/app/Http/Controllers/Report/BudgetController.php index 7e05b96eee..25dd72f4ef 100644 --- a/app/Http/Controllers/Report/BudgetController.php +++ b/app/Http/Controllers/Report/BudgetController.php @@ -35,8 +35,6 @@ use FireflyIII\Support\Report\Budget\BudgetReportGenerator; use Illuminate\Contracts\View\Factory; use Illuminate\Support\Collection; use Illuminate\View\View; -use JsonException; -use Throwable; /** * Class BudgetController. @@ -49,8 +47,6 @@ class BudgetController extends Controller /** * ExpenseReportController constructor. - * - */ public function __construct() { @@ -67,14 +63,9 @@ class BudgetController extends Controller /** * Partial used in the budget report. * - * @param Collection $accounts - * @param Collection $budgets - * @param Carbon $start - * @param Carbon $end - * * @return Factory|View + * * @throws FireflyException - * @throws JsonException */ public function accountPerBudget(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end) { @@ -94,11 +85,6 @@ class BudgetController extends Controller } /** - * @param Collection $accounts - * @param Collection $budgets - * @param Carbon $start - * @param Carbon $end - * * @return Factory|View */ public function accounts(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end) @@ -106,6 +92,7 @@ class BudgetController extends Controller $spent = $this->opsRepository->listExpenses($start, $end, $accounts, $budgets); $report = []; $sums = []; + /** @var Account $account */ foreach ($accounts as $account) { $accountId = $account->id; @@ -150,12 +137,8 @@ class BudgetController extends Controller } /** - * @param Collection $accounts - * @param Collection $budgets - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException */ public function avgExpenses(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end) @@ -179,7 +162,7 @@ class BudgetController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_decimal_places' => $currency['currency_decimal_places'], ]; - $result[$key]['transactions']++; + ++$result[$key]['transactions']; $result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']); $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']); $result[$key]['avg_float'] = (float)$result[$key]['avg']; // intentional float @@ -193,10 +176,11 @@ class BudgetController extends Controller try { $result = view('reports.budget.partials.avg-expenses', compact('result'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); app('log')->error($e->getTraceAsString()); + throw new FireflyException($result, 0, $e); } @@ -204,11 +188,6 @@ class BudgetController extends Controller } /** - * @param Collection $accounts - * @param Collection $budgets - * @param Carbon $start - * @param Carbon $end - * * @return Factory|View */ public function budgets(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end) @@ -216,6 +195,7 @@ class BudgetController extends Controller $spent = $this->opsRepository->listExpenses($start, $end, $accounts, $budgets); $sums = []; $report = []; + /** @var Budget $budget */ foreach ($budgets as $budget) { $budgetId = $budget->id; @@ -234,6 +214,7 @@ class BudgetController extends Controller 'currency_decimal_places' => $currency['currency_decimal_places'], 'sum' => '0', ]; + /** @var array $budget */ foreach ($currency['budgets'] as $budget) { $budgetId = $budget['id']; @@ -247,7 +228,6 @@ class BudgetController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], 'currency_decimal_places' => $currency['currency_decimal_places'], - ]; $report[$budgetId]['currencies'][$currencyId]['sum'] = bcadd($report[$budgetId]['currencies'][$currencyId]['sum'], $journal['amount']); $sums[$currencyId]['sum'] = bcadd($sums[$currencyId]['sum'], $journal['amount']); @@ -274,13 +254,9 @@ class BudgetController extends Controller /** * Show partial overview of budgets. * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException - * @throws JsonException */ public function general(Collection $accounts, Carbon $start, Carbon $end) { @@ -301,11 +277,8 @@ class BudgetController extends Controller /** * Show budget overview for a period. * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * * @return mixed|string + * * @throws FireflyException */ public function period(Collection $accounts, Carbon $start, Carbon $end) @@ -329,7 +302,7 @@ class BudgetController extends Controller foreach ($currency['budgets'] as $budget) { $count = 0; foreach ($budget['transaction_journals'] as $journal) { - $count++; + ++$count; $key = sprintf('%d-%d', $budget['id'], $currency['currency_id']); $dateKey = $journal['date']->format($keyFormat); $report[$key] ??= [ @@ -344,18 +317,20 @@ class BudgetController extends Controller 'entries' => [], ]; $report[$key]['entries'][$dateKey] ??= '0'; - $report[$key]['entries'][$dateKey] = bcadd($journal['amount'], $report[$key] ['entries'][$dateKey]); - $report[$key]['sum'] = bcadd($report[$key] ['sum'], $journal['amount']); + $report[$key]['entries'][$dateKey] = bcadd($journal['amount'], $report[$key]['entries'][$dateKey]); + $report[$key]['sum'] = bcadd($report[$key]['sum'], $journal['amount']); $report[$key]['avg'] = bcdiv($report[$key]['sum'], (string)count($periods)); } } } + try { $result = view('reports.partials.budget-period', compact('report', 'periods'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $result = 'Could not render view.'; + throw new FireflyException($result, 0, $e); } @@ -365,12 +340,8 @@ class BudgetController extends Controller } /** - * @param Collection $accounts - * @param Collection $budgets - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException */ public function topExpenses(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end) @@ -406,9 +377,10 @@ class BudgetController extends Controller try { $result = view('reports.budget.partials.top-expenses', compact('result'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); + throw new FireflyException($result, 0, $e); } diff --git a/app/Http/Controllers/Report/CategoryController.php b/app/Http/Controllers/Report/CategoryController.php index fd1f86a862..f1e2db294d 100644 --- a/app/Http/Controllers/Report/CategoryController.php +++ b/app/Http/Controllers/Report/CategoryController.php @@ -36,7 +36,6 @@ use FireflyIII\Support\Report\Category\CategoryReportGenerator; use Illuminate\Contracts\View\Factory; use Illuminate\Support\Collection; use Illuminate\View\View; -use Throwable; /** * Class CategoryController. @@ -50,8 +49,6 @@ class CategoryController extends Controller /** * ExpenseReportController constructor. - * - */ public function __construct() { @@ -67,11 +64,6 @@ class CategoryController extends Controller } /** - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * * @return Factory|View */ public function accountPerCategory(Collection $accounts, Collection $categories, Carbon $start, Carbon $end) @@ -79,6 +71,7 @@ class CategoryController extends Controller $spent = $this->opsRepository->listExpenses($start, $end, $accounts, $categories); $earned = $this->opsRepository->listIncome($start, $end, $accounts, $categories); $report = []; + /** @var Account $account */ foreach ($accounts as $account) { $accountId = $account->id; @@ -108,10 +101,10 @@ class CategoryController extends Controller $report[$sourceAccountId]['currencies'][$currencyId]['categories'][$category['id']] ??= [ - 'spent' => '0', - 'earned' => '0', - 'sum' => '0', - ]; + 'spent' => '0', + 'earned' => '0', + 'sum' => '0', + ]; $report[$sourceAccountId]['currencies'][$currencyId]['categories'][$category['id']]['spent'] = bcadd( $report[$sourceAccountId]['currencies'][$currencyId]['categories'][$category['id']]['spent'], $journal['amount'] @@ -133,18 +126,18 @@ class CategoryController extends Controller $destinationId = $journal['destination_account_id']; $report[$destinationId]['currencies'][$currencyId] ??= [ - 'currency_id' => $currency['currency_id'], - 'currency_symbol' => $currency['currency_symbol'], - 'currency_name' => $currency['currency_name'], - 'currency_decimal_places' => $currency['currency_decimal_places'], - 'categories' => [], - ]; + 'currency_id' => $currency['currency_id'], + 'currency_symbol' => $currency['currency_symbol'], + 'currency_name' => $currency['currency_name'], + 'currency_decimal_places' => $currency['currency_decimal_places'], + 'categories' => [], + ]; $report[$destinationId]['currencies'][$currencyId]['categories'][$category['id']] ??= [ - 'spent' => '0', - 'earned' => '0', - 'sum' => '0', - ]; + 'spent' => '0', + 'earned' => '0', + 'sum' => '0', + ]; $report[$destinationId]['currencies'][$currencyId]['categories'][$category['id']]['earned'] = bcadd( $report[$destinationId]['currencies'][$currencyId]['categories'][$category['id']]['earned'], $journal['amount'] @@ -161,12 +154,9 @@ class CategoryController extends Controller } /** - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * * @return Factory|View + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function accounts(Collection $accounts, Collection $categories, Carbon $start, Carbon $end) { @@ -174,6 +164,7 @@ class CategoryController extends Controller $earned = $this->opsRepository->listIncome($start, $end, $accounts, $categories); $report = []; $sums = []; + /** @var Account $account */ foreach ($accounts as $account) { $accountId = $account->id; @@ -265,12 +256,8 @@ class CategoryController extends Controller } /** - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException */ public function avgExpenses(Collection $accounts, Collection $categories, Carbon $start, Carbon $end) @@ -294,10 +281,10 @@ class CategoryController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_decimal_places' => $currency['currency_decimal_places'], ]; - $result[$key]['transactions']++; + ++$result[$key]['transactions']; $result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']); - $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']); - $result[$key]['avg_float'] = (float)$result[$key]['avg']; // intentional float + $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string) $result[$key]['transactions']); + $result[$key]['avg_float'] = (float) $result[$key]['avg']; // intentional float } } } @@ -308,9 +295,10 @@ class CategoryController extends Controller try { $result = view('reports.category.partials.avg-expenses', compact('result'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); + throw new FireflyException($result, 0, $e); } @@ -318,12 +306,8 @@ class CategoryController extends Controller } /** - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException */ public function avgIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end) @@ -347,10 +331,10 @@ class CategoryController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_decimal_places' => $currency['currency_decimal_places'], ]; - $result[$key]['transactions']++; + ++$result[$key]['transactions']; $result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']); - $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']); - $result[$key]['avg_float'] = (float)$result[$key]['avg']; + $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string) $result[$key]['transactions']); + $result[$key]['avg_float'] = (float) $result[$key]['avg']; } } } @@ -361,9 +345,10 @@ class CategoryController extends Controller try { $result = view('reports.category.partials.avg-income', compact('result'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); + throw new FireflyException($result, 0, $e); } @@ -371,12 +356,9 @@ class CategoryController extends Controller } /** - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * * @return Factory|View + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function categories(Collection $accounts, Collection $categories, Carbon $start, Carbon $end) { @@ -384,6 +366,7 @@ class CategoryController extends Controller $earned = $this->opsRepository->listIncome($start, $end, $accounts, $categories); $sums = []; $report = []; + /** @var Category $category */ foreach ($categories as $category) { $categoryId = $category->id; @@ -404,6 +387,7 @@ class CategoryController extends Controller 'spent_sum' => '0', 'total_sum' => '0', ]; + /** @var array $category */ foreach ($currency['categories'] as $category) { $categoryId = $category['id']; @@ -445,6 +429,7 @@ class CategoryController extends Controller 'spent_sum' => '0', 'total_sum' => '0', ]; + /** @var array $category */ foreach ($currency['categories'] as $category) { $categoryId = $category['id']; @@ -481,11 +466,8 @@ class CategoryController extends Controller /** * Show overview of expenses in category. * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * * @return mixed|string + * * @throws FireflyException */ public function expenses(Collection $accounts, Carbon $start, Carbon $end) @@ -528,7 +510,6 @@ class CategoryController extends Controller 'currency_decimal_places' => $currencyRow['currency_decimal_places'], 'sum' => '0', 'entries' => [], - ]; foreach ($categoryRow['transaction_journals'] as $journal) { $date = $journal['date']->format($format); @@ -546,13 +527,13 @@ class CategoryController extends Controller try { $result = view('reports.partials.category-period', compact('report', 'periods'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render category::expenses: %s', $e->getMessage())); $result = sprintf('An error prevented Firefly III from rendering: %s. Apologies.', $e->getMessage()); + throw new FireflyException($result, 0, $e); } - $cache->store($result); return $result; @@ -561,12 +542,6 @@ class CategoryController extends Controller /** * Show overview of income in category. * - * @param Collection $accounts - * - * @param Carbon $start - * @param Carbon $end - * - * @return string * @throws FireflyException */ public function income(Collection $accounts, Carbon $start, Carbon $end): string @@ -609,7 +584,6 @@ class CategoryController extends Controller 'currency_decimal_places' => $currencyRow['currency_decimal_places'], 'sum' => '0', 'entries' => [], - ]; foreach ($categoryRow['transaction_journals'] as $journal) { $date = $journal['date']->format($format); @@ -625,13 +599,13 @@ class CategoryController extends Controller try { $result = view('reports.partials.category-period', compact('report', 'periods'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render category::expenses: %s', $e->getMessage())); $result = sprintf('An error prevented Firefly III from rendering: %s. Apologies.', $e->getMessage()); + throw new FireflyException($result, 0, $e); } - $cache->store($result); return $result; @@ -640,11 +614,6 @@ class CategoryController extends Controller /** * Show overview of category transactions on the default report. * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string * @throws FireflyException */ public function operations(Collection $accounts, Carbon $start, Carbon $end): string @@ -667,13 +636,13 @@ class CategoryController extends Controller $generator->operations(); $report = $generator->getReport(); - try { $result = view('reports.partials.categories', compact('report'))->render(); $cache->store($result); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render category::expenses: %s', $e->getMessage())); $result = sprintf('An error prevented Firefly III from rendering: %s. Apologies.', $e->getMessage()); + throw new FireflyException($result, 0, $e); } @@ -681,12 +650,8 @@ class CategoryController extends Controller } /** - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException */ public function topExpenses(Collection $accounts, Collection $categories, Carbon $start, Carbon $end) @@ -699,7 +664,7 @@ class CategoryController extends Controller $result[] = [ 'description' => $journal['description'], 'transaction_group_id' => $journal['transaction_group_id'], - 'amount_float' => (float)$journal['amount'], + 'amount_float' => (float) $journal['amount'], 'amount' => $journal['amount'], 'date' => $journal['date']->isoFormat($this->monthAndDayFormat), 'date_sort' => $journal['date']->format('Y-m-d'), @@ -722,9 +687,10 @@ class CategoryController extends Controller try { $result = view('reports.category.partials.top-expenses', compact('result'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); + throw new FireflyException($e->getMessage(), 0, $e); } @@ -732,12 +698,8 @@ class CategoryController extends Controller } /** - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException */ public function topIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end) @@ -750,7 +712,7 @@ class CategoryController extends Controller $result[] = [ 'description' => $journal['description'], 'transaction_group_id' => $journal['transaction_group_id'], - 'amount_float' => (float)$journal['amount'], + 'amount_float' => (float) $journal['amount'], 'amount' => $journal['amount'], 'date' => $journal['date']->isoFormat($this->monthAndDayFormat), 'date_sort' => $journal['date']->format('Y-m-d'), @@ -773,9 +735,10 @@ class CategoryController extends Controller try { $result = view('reports.category.partials.top-income', compact('result'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); + throw new FireflyException($e->getMessage(), 0, $e); } diff --git a/app/Http/Controllers/Report/DoubleController.php b/app/Http/Controllers/Report/DoubleController.php index ee0835e66c..f5799e95c9 100644 --- a/app/Http/Controllers/Report/DoubleController.php +++ b/app/Http/Controllers/Report/DoubleController.php @@ -33,26 +33,19 @@ use FireflyIII\Support\Http\Controllers\AugumentData; use Illuminate\Contracts\View\Factory; use Illuminate\Support\Collection; use Illuminate\View\View; -use Throwable; /** * Class DoubleController - * */ class DoubleController extends Controller { use AugumentData; - /** @var AccountRepositoryInterface The account repository */ - protected $accountRepository; - - /** @var OperationsRepositoryInterface */ - private $opsRepository; + protected AccountRepositoryInterface $accountRepository; + private OperationsRepositoryInterface $opsRepository; /** * Constructor for ExpenseController - * - */ public function __construct() { @@ -70,12 +63,8 @@ class DoubleController extends Controller } /** - * @param Collection $accounts - * @param Collection $doubles - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException */ public function avgExpenses(Collection $accounts, Collection $doubles, Carbon $start, Carbon $end) @@ -100,7 +89,7 @@ class DoubleController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_decimal_places' => $currency['currency_decimal_places'], ]; - $result[$key]['transactions']++; + ++$result[$key]['transactions']; $result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']); $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']); $result[$key]['avg_float'] = (float)$result[$key]['avg']; @@ -113,9 +102,10 @@ class DoubleController extends Controller try { $result = view('reports.double.partials.avg-expenses', compact('result'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); + throw new FireflyException($e->getMessage(), 0, $e); } @@ -123,12 +113,8 @@ class DoubleController extends Controller } /** - * @param Collection $accounts - * @param Collection $doubles - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException */ public function avgIncome(Collection $accounts, Collection $doubles, Carbon $start, Carbon $end) @@ -153,7 +139,7 @@ class DoubleController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_decimal_places' => $currency['currency_decimal_places'], ]; - $result[$key]['transactions']++; + ++$result[$key]['transactions']; $result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']); $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']); $result[$key]['avg_float'] = (float)$result[$key]['avg']; @@ -166,9 +152,10 @@ class DoubleController extends Controller try { $result = view('reports.double.partials.avg-income', compact('result'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); + throw new FireflyException($e->getMessage(), 0, $e); } @@ -176,12 +163,9 @@ class DoubleController extends Controller } /** - * @param Collection $accounts - * @param Collection $double - * @param Carbon $start - * @param Carbon $end - * * @return Factory|View + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function operations(Collection $accounts, Collection $double, Carbon $start, Carbon $end) { @@ -295,36 +279,6 @@ class DoubleController extends Controller } /** - * TODO this method is duplicated. - * - * @param Collection $accounts - * @param int $id - * @param string $name - * @param string|null $iban - * - * @return string - */ - private function getCounterpartName(Collection $accounts, int $id, string $name, ?string $iban): string - { - /** @var Account $account */ - foreach ($accounts as $account) { - if ($account->name === $name && $account->id !== $id) { - return $account->name; - } - if (null !== $account->iban && $account->iban === $iban && $account->id !== $id) { - return $account->iban; - } - } - - return $name; - } - - /** - * @param Collection $accounts - * @param Collection $double - * @param Carbon $start - * @param Carbon $end - * * @return Factory|View */ public function operationsPerAsset(Collection $accounts, Collection $double, Carbon $start, Carbon $end) @@ -420,12 +374,8 @@ class DoubleController extends Controller } /** - * @param Collection $accounts - * @param Collection $doubles - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException */ public function topExpenses(Collection $accounts, Collection $doubles, Carbon $start, Carbon $end) @@ -461,9 +411,10 @@ class DoubleController extends Controller try { $result = view('reports.double.partials.top-expenses', compact('result'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); + throw new FireflyException($e->getMessage(), 0, $e); } @@ -471,12 +422,8 @@ class DoubleController extends Controller } /** - * @param Collection $accounts - * @param Collection $doubles - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException */ public function topIncome(Collection $accounts, Collection $doubles, Carbon $start, Carbon $end) @@ -512,12 +459,31 @@ class DoubleController extends Controller try { $result = view('reports.double.partials.top-income', compact('result'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); + throw new FireflyException($e->getMessage(), 0, $e); } return $result; } + + /** + * TODO this method is duplicated. + */ + private function getCounterpartName(Collection $accounts, int $id, string $name, ?string $iban): string + { + /** @var Account $account */ + foreach ($accounts as $account) { + if ($account->name === $name && $account->id !== $id) { + return $account->name; + } + if (null !== $account->iban && $account->iban === $iban && $account->id !== $id) { + return $account->iban; + } + } + + return $name; + } } diff --git a/app/Http/Controllers/Report/OperationsController.php b/app/Http/Controllers/Report/OperationsController.php index 556c794679..ced490bb3f 100644 --- a/app/Http/Controllers/Report/OperationsController.php +++ b/app/Http/Controllers/Report/OperationsController.php @@ -29,7 +29,6 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; -use Throwable; /** * Class OperationsController. @@ -41,8 +40,6 @@ class OperationsController extends Controller /** * OperationsController constructor. - * - */ public function __construct() { @@ -61,11 +58,8 @@ class OperationsController extends Controller /** * View of income and expense. * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * * @return mixed|string + * * @throws FireflyException */ public function expenses(Collection $accounts, Carbon $start, Carbon $end) @@ -81,12 +75,14 @@ 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(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render reports.partials.income-expense: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $result = 'Could not render view.'; + throw new FireflyException($result, 0, $e); } @@ -98,11 +94,6 @@ class OperationsController extends Controller /** * View of income. * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string * @throws FireflyException */ public function income(Collection $accounts, Carbon $start, Carbon $end): string @@ -118,12 +109,14 @@ 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(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render reports.partials.income-expenses: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $result = 'Could not render view.'; + throw new FireflyException($result, 0, $e); } @@ -135,11 +128,8 @@ class OperationsController extends Controller /** * Overview of income and expense. * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * * @return mixed|string + * * @throws FireflyException */ public function operations(Collection $accounts, Carbon $start, Carbon $end) @@ -177,10 +167,11 @@ class OperationsController extends Controller try { $result = view('reports.partials.operations', compact('sums'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render reports.partials.operations: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $result = 'Could not render view.'; + throw new FireflyException($result, 0, $e); } $cache->store($result); diff --git a/app/Http/Controllers/Report/TagController.php b/app/Http/Controllers/Report/TagController.php index 72567c3674..435864cb01 100644 --- a/app/Http/Controllers/Report/TagController.php +++ b/app/Http/Controllers/Report/TagController.php @@ -32,10 +32,8 @@ use FireflyIII\Repositories\Tag\OperationsRepositoryInterface; use Illuminate\Contracts\View\Factory; use Illuminate\Support\Collection; use Illuminate\View\View; -use Throwable; /** - * * Class TagController */ class TagController extends Controller @@ -44,8 +42,6 @@ class TagController extends Controller /** * ExpenseReportController constructor. - * - */ public function __construct() { @@ -60,11 +56,6 @@ class TagController extends Controller } /** - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * * @return Factory|View */ public function accountPerTag(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) @@ -72,6 +63,7 @@ class TagController extends Controller $spent = $this->opsRepository->listExpenses($start, $end, $accounts, $tags); $earned = $this->opsRepository->listIncome($start, $end, $accounts, $tags); $report = []; + /** @var Account $account */ foreach ($accounts as $account) { $accountId = $account->id; @@ -101,10 +93,10 @@ class TagController extends Controller $report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']] ??= [ - 'spent' => '0', - 'earned' => '0', - 'sum' => '0', - ]; + 'spent' => '0', + 'earned' => '0', + 'sum' => '0', + ]; $report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']]['spent'] = bcadd( $report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']]['spent'], $journal['amount'] @@ -126,18 +118,18 @@ class TagController extends Controller $destinationId = $journal['destination_account_id']; $report[$destinationId]['currencies'][$currencyId] ??= [ - 'currency_id' => $currency['currency_id'], - 'currency_symbol' => $currency['currency_symbol'], - 'currency_name' => $currency['currency_name'], - 'currency_decimal_places' => $currency['currency_decimal_places'], - 'tags' => [], - ]; + 'currency_id' => $currency['currency_id'], + 'currency_symbol' => $currency['currency_symbol'], + 'currency_name' => $currency['currency_name'], + 'currency_decimal_places' => $currency['currency_decimal_places'], + 'tags' => [], + ]; $report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']] ??= [ - 'spent' => '0', - 'earned' => '0', - 'sum' => '0', - ]; + 'spent' => '0', + 'earned' => '0', + 'sum' => '0', + ]; $report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']]['earned'] = bcadd( $report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']]['earned'], $journal['amount'] @@ -154,12 +146,9 @@ class TagController extends Controller } /** - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * * @return Factory|View + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function accounts(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) { @@ -167,6 +156,7 @@ class TagController extends Controller $earned = $this->opsRepository->listIncome($start, $end, $accounts, $tags); $report = []; $sums = []; + /** @var Account $account */ foreach ($accounts as $account) { $accountId = $account->id; @@ -258,12 +248,8 @@ class TagController extends Controller } /** - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException */ public function avgExpenses(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) @@ -287,10 +273,10 @@ class TagController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_decimal_places' => $currency['currency_decimal_places'], ]; - $result[$key]['transactions']++; + ++$result[$key]['transactions']; $result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']); - $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']); - $result[$key]['avg_float'] = (float)$result[$key]['avg']; + $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string) $result[$key]['transactions']); + $result[$key]['avg_float'] = (float) $result[$key]['avg']; } } } @@ -301,9 +287,10 @@ class TagController extends Controller try { $result = view('reports.tag.partials.avg-expenses', compact('result'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); + throw new FireflyException($result, 0, $e); } @@ -311,12 +298,8 @@ class TagController extends Controller } /** - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException */ public function avgIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) @@ -340,10 +323,10 @@ class TagController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_decimal_places' => $currency['currency_decimal_places'], ]; - $result[$key]['transactions']++; + ++$result[$key]['transactions']; $result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']); - $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']); - $result[$key]['avg_float'] = (float)$result[$key]['avg']; + $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string) $result[$key]['transactions']); + $result[$key]['avg_float'] = (float) $result[$key]['avg']; } } } @@ -354,9 +337,10 @@ class TagController extends Controller try { $result = view('reports.tag.partials.avg-income', compact('result'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); + throw new FireflyException($result, 0, $e); } @@ -364,12 +348,9 @@ class TagController extends Controller } /** - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * * @return Factory|View + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function tags(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) { @@ -377,6 +358,7 @@ class TagController extends Controller $earned = $this->opsRepository->listIncome($start, $end, $accounts, $tags); $sums = []; $report = []; + /** @var Tag $tag */ foreach ($tags as $tag) { $tagId = $tag->id; @@ -397,6 +379,7 @@ class TagController extends Controller 'spent_sum' => '0', 'total_sum' => '0', ]; + /** @var array $tag */ foreach ($currency['tags'] as $tag) { $tagId = $tag['id']; @@ -438,6 +421,7 @@ class TagController extends Controller 'spent_sum' => '0', 'total_sum' => '0', ]; + /** @var array $tag */ foreach ($currency['tags'] as $tag) { $tagId = $tag['id']; @@ -472,12 +456,8 @@ class TagController extends Controller } /** - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException */ public function topExpenses(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) @@ -490,7 +470,7 @@ class TagController extends Controller $result[] = [ 'description' => $journal['description'], 'transaction_group_id' => $journal['transaction_group_id'], - 'amount_float' => (float)$journal['amount'], + 'amount_float' => (float) $journal['amount'], 'amount' => $journal['amount'], 'date' => $journal['date']->isoFormat($this->monthAndDayFormat), 'date_sort' => $journal['date']->format('Y-m-d'), @@ -513,9 +493,10 @@ class TagController extends Controller try { $result = view('reports.tag.partials.top-expenses', compact('result'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); + throw new FireflyException($result, 0, $e); } @@ -523,12 +504,8 @@ class TagController extends Controller } /** - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException */ public function topIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) @@ -541,7 +518,7 @@ class TagController extends Controller $result[] = [ 'description' => $journal['description'], 'transaction_group_id' => $journal['transaction_group_id'], - 'amount_float' => (float)$journal['amount'], // intentional float. + 'amount_float' => (float) $journal['amount'], // intentional float. 'amount' => $journal['amount'], 'date' => $journal['date']->isoFormat($this->monthAndDayFormat), 'date_sort' => $journal['date']->format('Y-m-d'), @@ -564,9 +541,10 @@ class TagController extends Controller try { $result = view('reports.tag.partials.top-income', compact('result'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); + throw new FireflyException($result, 0, $e); } diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index fa129a000c..8c9e51a5f8 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -39,12 +39,9 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Routing\Redirector; use Illuminate\Support\Collection; use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class ReportController. - * */ class ReportController extends Controller { @@ -76,11 +73,7 @@ class ReportController extends Controller /** * Show audit report. * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return Factory|View|string + * @return Factory|string|View * * @throws FireflyException */ @@ -111,12 +104,7 @@ class ReportController extends Controller /** * Show budget report. * - * @param Collection $accounts - * @param Collection $budgets - * @param Carbon $start - * @param Carbon $end - * - * @return Factory|View|string + * @return Factory|string|View * * @throws FireflyException */ @@ -148,12 +136,7 @@ class ReportController extends Controller /** * Show category report. * - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * - * @return Factory|View|string + * @return Factory|string|View * * @throws FireflyException */ @@ -185,11 +168,7 @@ class ReportController extends Controller /** * Show default report. * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return Factory|View|string + * @return Factory|string|View * * @throws FireflyException */ @@ -221,12 +200,8 @@ class ReportController extends Controller /** * Show account report. * - * @param Collection $accounts - * @param Collection $expense - * @param Carbon $start - * @param Carbon $end - * * @return string + * * @throws FireflyException */ public function doubleReport(Collection $accounts, Collection $expense, Carbon $start, Carbon $end) @@ -258,11 +233,7 @@ class ReportController extends Controller /** * Show index. * - * @param AccountRepositoryInterface $repository - * * @return Factory|View - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function index(AccountRepositoryInterface $repository) { @@ -276,6 +247,7 @@ class ReportController extends Controller // group accounts by role: $groupedAccounts = []; + /** @var Account $account */ foreach ($accounts as $account) { $type = $account->accountType->type; @@ -301,9 +273,8 @@ class ReportController extends Controller /** * Show options for reports. * - * @param string $reportType - * * @return JsonResponse + * * @throws FireflyException */ public function options(string $reportType) @@ -322,14 +293,9 @@ class ReportController extends Controller /** * Process the submit of report. * - * @param ReportFormRequest $request - * - * @return RedirectResponse|Redirector|View - * * @throws FireflyException - * */ - public function postIndex(ReportFormRequest $request): RedirectResponse | Redirector | View + public function postIndex(ReportFormRequest $request): Redirector|RedirectResponse|View { // report type: $reportType = $request->get('report_type'); @@ -391,12 +357,8 @@ class ReportController extends Controller /** * Get a tag report. * - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end + * @return Factory|string|View * - * @return Factory|View|string * @throws FireflyException */ public function tagReport(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) diff --git a/app/Http/Controllers/Rule/CreateController.php b/app/Http/Controllers/Rule/CreateController.php index 18bdb10815..c7e491836e 100644 --- a/app/Http/Controllers/Rule/CreateController.php +++ b/app/Http/Controllers/Rule/CreateController.php @@ -52,8 +52,6 @@ class CreateController extends Controller /** * RuleController constructor. - * - */ public function __construct() { @@ -74,10 +72,8 @@ class CreateController extends Controller /** * Create a new rule. It will be stored under the given $ruleGroup. * - * @param Request $request - * @param RuleGroup|null $ruleGroup - * * @return Factory|View + * * @throws FireflyException */ public function create(Request $request, RuleGroup $ruleGroup = null) @@ -105,7 +101,7 @@ class CreateController extends Controller } $oldTriggers = $this->parseFromOperators($operators); } - //var_dump($oldTriggers);exit; + // var_dump($oldTriggers);exit; // restore actions and triggers from old input: if (is_array($request->old()) && count($request->old()) > 0) { @@ -141,10 +137,8 @@ class CreateController extends Controller /** * Create a new rule. It will be stored under the given $ruleGroup. * - * @param Request $request - * @param Bill $bill - * * @return Factory|View + * * @throws FireflyException */ public function createFromBill(Request $request, Bill $bill) @@ -193,10 +187,8 @@ class CreateController extends Controller } /** - * @param Request $request - * @param TransactionJournal $journal - * * @return Factory|\Illuminate\Contracts\View\View + * * @throws FireflyException */ public function createFromJournal(Request $request, TransactionJournal $journal) @@ -243,11 +235,6 @@ class CreateController extends Controller ); } - /** - * @param Request $request - * - * @return JsonResponse - */ public function duplicate(Request $request): JsonResponse { $ruleId = (int)$request->get('id'); @@ -262,10 +249,7 @@ class CreateController extends Controller /** * Store the new rule. * - * @param RuleFormRequest $request - * - * @return RedirectResponse|Redirector - * + * @return Redirector|RedirectResponse */ public function store(RuleFormRequest $request) { diff --git a/app/Http/Controllers/Rule/DeleteController.php b/app/Http/Controllers/Rule/DeleteController.php index 5f208b1735..73e5e04f01 100644 --- a/app/Http/Controllers/Rule/DeleteController.php +++ b/app/Http/Controllers/Rule/DeleteController.php @@ -40,8 +40,6 @@ class DeleteController extends Controller /** * RuleController constructor. - * - */ public function __construct() { @@ -62,8 +60,6 @@ class DeleteController extends Controller /** * Delete a given rule. * - * @param Rule $rule - * * @return Factory|View */ public function delete(Rule $rule) @@ -78,10 +74,6 @@ class DeleteController extends Controller /** * Actually destroy the given rule. - * - * @param Rule $rule - * - * @return RedirectResponse */ public function destroy(Rule $rule): RedirectResponse { diff --git a/app/Http/Controllers/Rule/EditController.php b/app/Http/Controllers/Rule/EditController.php index c159ca4685..9b7b927194 100644 --- a/app/Http/Controllers/Rule/EditController.php +++ b/app/Http/Controllers/Rule/EditController.php @@ -37,7 +37,6 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Routing\Redirector; use Illuminate\View\View; -use Throwable; /** * Class EditController @@ -51,8 +50,6 @@ class EditController extends Controller /** * RuleController constructor. - * - */ public function __construct() { @@ -73,10 +70,8 @@ class EditController extends Controller /** * Edit a rule. * - * @param Request $request - * @param Rule $rule - * * @return Factory|View + * * @throws FireflyException */ public function edit(Request $request, Rule $rule) @@ -120,7 +115,6 @@ class EditController extends Controller 'active' => $hasOldInput ? (bool)$request->old('active') : $rule->active, 'stop_processing' => $hasOldInput ? (bool)$request->old('stop_processing') : $rule->stop_processing, 'strict' => $hasOldInput ? (bool)$request->old('strict') : $rule->strict, - ]; // get rule trigger for update / store-journal: @@ -139,9 +133,29 @@ class EditController extends Controller } /** - * @param array $submittedOperators + * Update the rule. * - * @return array + * @return Redirector|RedirectResponse + */ + public function update(RuleFormRequest $request, Rule $rule) + { + $data = $request->getRuleData(); + + $this->ruleRepos->update($rule, $data); + + session()->flash('success', (string)trans('firefly.updated_rule', ['title' => $rule->title])); + app('preferences')->mark(); + $redirect = redirect($this->getPreviousUrl('rules.edit.url')); + if (1 === (int)$request->get('return_to_edit')) { + session()->put('rules.edit.fromUpdate', true); + + $redirect = redirect(route('rules.edit', [$rule->id]))->withInput(['return_to_edit' => 1]); + } + + return $redirect; + } + + /** * @throws FireflyException */ private function parseFromOperators(array $submittedOperators): array @@ -170,41 +184,16 @@ class EditController extends Controller 'triggers' => $triggers, ] )->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { $message = sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage()); app('log')->debug($message); app('log')->error($e->getTraceAsString()); + throw new FireflyException($message, 0, $e); } - $index++; + ++$index; } return $renderedEntries; } - - /** - * Update the rule. - * - * @param RuleFormRequest $request - * @param Rule $rule - * - * @return RedirectResponse|Redirector - */ - public function update(RuleFormRequest $request, Rule $rule) - { - $data = $request->getRuleData(); - - $this->ruleRepos->update($rule, $data); - - session()->flash('success', (string)trans('firefly.updated_rule', ['title' => $rule->title])); - app('preferences')->mark(); - $redirect = redirect($this->getPreviousUrl('rules.edit.url')); - if (1 === (int)$request->get('return_to_edit')) { - session()->put('rules.edit.fromUpdate', true); - - $redirect = redirect(route('rules.edit', [$rule->id]))->withInput(['return_to_edit' => 1]); - } - - return $redirect; - } } diff --git a/app/Http/Controllers/Rule/IndexController.php b/app/Http/Controllers/Rule/IndexController.php index d83e726877..d86f902c36 100644 --- a/app/Http/Controllers/Rule/IndexController.php +++ b/app/Http/Controllers/Rule/IndexController.php @@ -47,8 +47,6 @@ class IndexController extends Controller /** * RuleController constructor. - * - */ public function __construct() { @@ -79,13 +77,6 @@ class IndexController extends Controller return view('rules.index', compact('ruleGroups')); } - /** - * @param Request $request - * @param Rule $rule - * @param RuleGroup $ruleGroup - * - * @return JsonResponse - */ public function moveRule(Request $request, Rule $rule, RuleGroup $ruleGroup): JsonResponse { $order = (int)$request->get('order'); @@ -94,11 +85,6 @@ class IndexController extends Controller return response()->json([]); } - /** - * @param Rule $rule - * - * @return RedirectResponse - */ public function search(Rule $rule): RedirectResponse { $route = route('search.index'); diff --git a/app/Http/Controllers/Rule/SelectController.php b/app/Http/Controllers/Rule/SelectController.php index 2d77544620..d879355c1c 100644 --- a/app/Http/Controllers/Rule/SelectController.php +++ b/app/Http/Controllers/Rule/SelectController.php @@ -32,18 +32,15 @@ use FireflyIII\Models\Rule; use FireflyIII\Models\RuleTrigger; use FireflyIII\Support\Http\Controllers\RuleManagement; use FireflyIII\TransactionRules\Engine\RuleEngineInterface; -use FireflyIII\TransactionRules\TransactionMatcher; use FireflyIII\User; use Illuminate\Contracts\View\Factory; use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Collection; use Illuminate\View\View; -use Throwable; /** * Class SelectController. - * */ class SelectController extends Controller { @@ -68,11 +65,6 @@ class SelectController extends Controller /** * Execute the given rule on a set of existing transactions. - * - * @param SelectTransactionsRequest $request - * @param Rule $rule - * - * @return RedirectResponse */ public function execute(SelectTransactionsRequest $request, Rule $rule): RedirectResponse { @@ -104,12 +96,8 @@ class SelectController extends Controller /** * View to select transactions by a rule. - * - * @param Rule $rule - * - * @return Factory|View|RedirectResponse */ - public function selectTransactions(Rule $rule): Factory | View | RedirectResponse + public function selectTransactions(Rule $rule): Factory|RedirectResponse|View { if (false === $rule->active) { session()->flash('warning', trans('firefly.cannot_fire_inactive_rules')); @@ -128,15 +116,13 @@ class SelectController extends Controller * This method allows the user to test a certain set of rule triggers. The rule triggers are passed along * using the URL parameters (GET), and are usually put there using a Javascript thing. * - * @param TestRuleFormRequest $request - * - * @return JsonResponse * @throws FireflyException */ public function testTriggers(TestRuleFormRequest $request): JsonResponse { // build fake rule $rule = new Rule(); + /** @var \Illuminate\Database\Eloquent\Collection $triggers */ $triggers = new Collection(); $rule->strict = '1' === $request->get('strict'); @@ -180,12 +166,14 @@ class SelectController extends Controller // Return json response $view = 'ERROR, see logs.'; + try { $view = view('list.journals-array-tiny', ['groups' => $collection])->render(); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { app('log')->error(sprintf('Could not render view in testTriggers(): %s', $exception->getMessage())); app('log')->error($exception->getTraceAsString()); $view = sprintf('Could not render list.journals-tiny: %s', $exception->getMessage()); + throw new FireflyException($view, 0, $exception); } @@ -196,9 +184,6 @@ class SelectController extends Controller * This method allows the user to test a certain set of rule triggers. The rule triggers are grabbed from * the rule itself. * - * @param Rule $rule - * - * @return JsonResponse * @throws FireflyException */ public function testTriggersByRule(Rule $rule): JsonResponse @@ -223,12 +208,14 @@ class SelectController extends Controller // Return json response $view = 'ERROR, see logs.'; + try { $view = view('list.journals-array-tiny', ['groups' => $collection])->render(); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $message = sprintf('Could not render view in testTriggersByRule(): %s', $exception->getMessage()); app('log')->error($message); app('log')->error($exception->getTraceAsString()); + throw new FireflyException($message, 0, $exception); } diff --git a/app/Http/Controllers/RuleGroup/CreateController.php b/app/Http/Controllers/RuleGroup/CreateController.php index c6d667d66c..add430dd97 100644 --- a/app/Http/Controllers/RuleGroup/CreateController.php +++ b/app/Http/Controllers/RuleGroup/CreateController.php @@ -41,8 +41,6 @@ class CreateController extends Controller /** * CreateController constructor. - * - */ public function __construct() { @@ -82,9 +80,7 @@ class CreateController extends Controller /** * Store the rule group. * - * @param RuleGroupFormRequest $request - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function store(RuleGroupFormRequest $request) { diff --git a/app/Http/Controllers/RuleGroup/DeleteController.php b/app/Http/Controllers/RuleGroup/DeleteController.php index c488a609c7..e8b81c7e1d 100644 --- a/app/Http/Controllers/RuleGroup/DeleteController.php +++ b/app/Http/Controllers/RuleGroup/DeleteController.php @@ -42,8 +42,6 @@ class DeleteController extends Controller /** * DeleteController constructor. - * - */ public function __construct() { @@ -64,8 +62,6 @@ class DeleteController extends Controller /** * Delete a rule group. * - * @param RuleGroup $ruleGroup - * * @return Factory|View */ public function delete(RuleGroup $ruleGroup) @@ -81,10 +77,7 @@ class DeleteController extends Controller /** * Actually destroy the rule group. * - * @param Request $request - * @param RuleGroup $ruleGroup - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function destroy(Request $request, RuleGroup $ruleGroup) { diff --git a/app/Http/Controllers/RuleGroup/EditController.php b/app/Http/Controllers/RuleGroup/EditController.php index 6f0ae47a71..70064887c3 100644 --- a/app/Http/Controllers/RuleGroup/EditController.php +++ b/app/Http/Controllers/RuleGroup/EditController.php @@ -43,8 +43,6 @@ class EditController extends Controller /** * EditController constructor. - * - */ public function __construct() { @@ -65,9 +63,6 @@ class EditController extends Controller /** * Edit a rule group. * - * @param Request $request - * @param RuleGroup $ruleGroup - * * @return Factory|View */ public function edit(Request $request, RuleGroup $ruleGroup) @@ -90,10 +85,6 @@ class EditController extends Controller /** * Move a rule group in either direction. - * - * @param Request $request - * - * @return JsonResponse */ public function moveGroup(Request $request): JsonResponse { @@ -117,16 +108,14 @@ class EditController extends Controller } } } + return new JsonResponse(['OK']); } /** * Update the rule group. * - * @param RuleGroupFormRequest $request - * @param RuleGroup $ruleGroup - * - * @return $this|RedirectResponse|Redirector + * @return $this|Redirector|RedirectResponse */ public function update(RuleGroupFormRequest $request, RuleGroup $ruleGroup) { diff --git a/app/Http/Controllers/RuleGroup/ExecutionController.php b/app/Http/Controllers/RuleGroup/ExecutionController.php index be6285e55f..fa85478f57 100644 --- a/app/Http/Controllers/RuleGroup/ExecutionController.php +++ b/app/Http/Controllers/RuleGroup/ExecutionController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\RuleGroup; use Carbon\Carbon; -use Exception; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\SelectTransactionsRequest; use FireflyIII\Models\RuleGroup; @@ -44,8 +43,6 @@ class ExecutionController extends Controller /** * ExecutionController constructor. - * - */ public function __construct() { @@ -66,11 +63,7 @@ class ExecutionController extends Controller /** * Execute the given rulegroup on a set of existing transactions. * - * @param SelectTransactionsRequest $request - * @param RuleGroup $ruleGroup - * - * @return RedirectResponse - * @throws Exception + * @throws \Exception */ public function execute(SelectTransactionsRequest $request, RuleGroup $ruleGroup): RedirectResponse { @@ -103,8 +96,6 @@ class ExecutionController extends Controller /** * Select transactions to apply the group on. * - * @param RuleGroup $ruleGroup - * * @return Factory|View */ public function selectTransactions(RuleGroup $ruleGroup) diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index 86d4c13bad..08bec489b0 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -30,7 +30,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\View\View; -use Throwable; /** * Class SearchController. @@ -57,9 +56,6 @@ class SearchController extends Controller /** * Do the search. * - * @param Request $request - * @param SearchInterface $searcher - * * @return Factory|View */ public function index(Request $request, SearchInterface $searcher) @@ -98,10 +94,6 @@ class SearchController extends Controller /** * JSON request that does the work. * - * @param Request $request - * @param SearchInterface $searcher - * - * @return JsonResponse * @throws FireflyException */ public function search(Request $request, SearchInterface $searcher): JsonResponse @@ -120,15 +112,16 @@ class SearchController extends Controller $hasPages = $groups->hasPages(); $searchTime = round($searcher->searchTime(), 3); // in seconds $parameters = ['search' => $fullQuery]; - $url = route('search.index') . '?' . http_build_query($parameters); + $url = route('search.index').'?'.http_build_query($parameters); $groups->setPath($url); try { $html = view('search.search', compact('groups', 'hasPages', 'searchTime'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render search.search: %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $html = 'Could not render view.'; + throw new FireflyException($html, 0, $e); } diff --git a/app/Http/Controllers/System/CronController.php b/app/Http/Controllers/System/CronController.php index 75c26d4206..2e4f731831 100644 --- a/app/Http/Controllers/System/CronController.php +++ b/app/Http/Controllers/System/CronController.php @@ -33,7 +33,7 @@ use Illuminate\Http\Response; class CronController { /** - * @return Application|ResponseFactory|Response + * @return Application|Response|ResponseFactory */ public function cron() { diff --git a/app/Http/Controllers/System/HealthcheckController.php b/app/Http/Controllers/System/HealthcheckController.php index 2a17ca2f5f..8bec13fcd9 100644 --- a/app/Http/Controllers/System/HealthcheckController.php +++ b/app/Http/Controllers/System/HealthcheckController.php @@ -34,12 +34,11 @@ class HealthcheckController extends Controller { /** * Sends 'OK' info when app is alive - * - * @return Response */ public function check(): Response { User::count(); // sanity check for database health. Will crash if not OK. + return response('OK', 200); } } diff --git a/app/Http/Controllers/System/InstallController.php b/app/Http/Controllers/System/InstallController.php index e5eccf6b11..81ba395305 100644 --- a/app/Http/Controllers/System/InstallController.php +++ b/app/Http/Controllers/System/InstallController.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\System; -use Artisan; use Cache; use Exception; use FireflyIII\Exceptions\FireflyException; @@ -38,8 +37,6 @@ use phpseclib3\Crypt\RSA; /** * Class InstallController - * - */ class InstallController extends Controller { @@ -90,11 +87,6 @@ class InstallController extends Controller return view('install.index'); } - /** - * @param Request $request - * - * @return JsonResponse - */ public function runCommand(Request $request): JsonResponse { $requestIndex = (int)$request->get('index'); @@ -112,6 +104,7 @@ class InstallController extends Controller $command = $indexes[$requestIndex]; $parameters = $this->upgradeCommands[$command]; app('log')->debug(sprintf('Will now execute command "%s" with parameters', $command), $parameters); + try { $result = $this->executeCommand($command, $parameters); } catch (FireflyException $e) { @@ -126,48 +119,21 @@ class InstallController extends Controller if (false === $result) { $response['errorMessage'] = $this->lastError; $response['error'] = true; + return response()->json($response); } $response['hasNextCommand'] = array_key_exists($requestIndex + 1, $indexes); $response['previous'] = $command; } + return response()->json($response); } - /** - * @param string $command - * @param array $args - * - * @return bool - * @throws FireflyException - */ - private function executeCommand(string $command, array $args): bool - { - app('log')->debug(sprintf('Will now call command %s with args.', $command), $args); - try { - if ('generate-keys' === $command) { - $this->keys(); - } - if ('generate-keys' !== $command) { - Artisan::call($command, $args); - app('log')->debug(Artisan::output()); - } - } catch (Exception $e) { // intentional generic exception - throw new FireflyException($e->getMessage(), 0, $e); - } - // clear cache as well. - Cache::clear(); - app('preferences')->mark(); - - return true; - } - /** * Create specific RSA keys. */ public function keys(): void { - $key = RSA::createKey(4096); [$publicKey, $privateKey] = [ @@ -182,4 +148,29 @@ class InstallController extends Controller file_put_contents($publicKey, (string)$key->getPublicKey()); file_put_contents($privateKey, $key->toString('PKCS1')); } + + /** + * @throws FireflyException + */ + private function executeCommand(string $command, array $args): bool + { + app('log')->debug(sprintf('Will now call command %s with args.', $command), $args); + + try { + if ('generate-keys' === $command) { + $this->keys(); + } + if ('generate-keys' !== $command) { + \Artisan::call($command, $args); + app('log')->debug(\Artisan::output()); + } + } catch (\Exception $e) { // intentional generic exception + throw new FireflyException($e->getMessage(), 0, $e); + } + // clear cache as well. + \Cache::clear(); + app('preferences')->mark(); + + return true; + } } diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index 7bc69c140f..2a6f8f994b 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -35,8 +35,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class TagController. @@ -102,8 +100,6 @@ class TagController extends Controller /** * Delete a tag. * - * @param Tag $tag - * * @return Factory|View */ public function delete(Tag $tag) @@ -119,8 +115,6 @@ class TagController extends Controller /** * Edit a tag. * - * @param Tag $tag - * * @return Factory|View */ public function edit(Tag $tag) @@ -154,8 +148,6 @@ class TagController extends Controller /** * Edit a tag. * - * @param TagRepositoryInterface $repository - * * @return Factory|View */ public function index(TagRepositoryInterface $repository) @@ -179,11 +171,6 @@ class TagController extends Controller return view('tags.index', compact('tags', 'count')); } - /** - * @param Request $request - * - * @return RedirectResponse - */ public function massDestroy(Request $request): RedirectResponse { $tags = $request->get('tags'); @@ -198,7 +185,7 @@ class TagController extends Controller $tag = $this->repository->find($tagId); if (null !== $tag) { $this->repository->destroy($tag); - $count++; + ++$count; } } session()->flash('success', trans_choice('firefly.deleted_x_tags', $count)); @@ -208,10 +195,6 @@ class TagController extends Controller /** * Destroy a tag. - * - * @param Tag $tag - * - * @return RedirectResponse */ public function destroy(Tag $tag): RedirectResponse { @@ -227,15 +210,9 @@ class TagController extends Controller /** * Show a single tag. * - * @param Request $request - * @param Tag $tag - * @param Carbon|null $start - * @param Carbon|null $end - * * @return Factory|View + * * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function show(Request $request, Tag $tag, Carbon $start = null, Carbon $end = null) { @@ -266,7 +243,8 @@ class TagController extends Controller $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withAccountInformation() - ->setTag($tag)->withBudgetInformation()->withCategoryInformation(); + ->setTag($tag)->withBudgetInformation()->withCategoryInformation() + ; $groups = $collector->getPaginatedGroups(); $groups->setPath($path); $sums = $this->repository->sumsOfTag($tag, $start, $end); @@ -277,12 +255,7 @@ class TagController extends Controller /** * Show a single tag over all time. * - * @param Request $request - * @param Tag $tag - * * @return Factory|View - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function showAll(Request $request, Tag $tag) { @@ -297,10 +270,12 @@ class TagController extends Controller $attachments = $this->repository->getAttachments($tag); $path = route('tags.show', [$tag->id, 'all']); $location = $this->repository->getLocation($tag); + /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withAccountInformation() - ->setTag($tag)->withBudgetInformation()->withCategoryInformation(); + ->setTag($tag)->withBudgetInformation()->withCategoryInformation() + ; $groups = $collector->getPaginatedGroups(); $groups->setPath($path); $sums = $this->repository->sumsOfTag($tag, $start, $end); @@ -310,10 +285,6 @@ class TagController extends Controller /** * Store a tag. - * - * @param TagFormRequest $request - * - * @return RedirectResponse */ public function store(TagFormRequest $request): RedirectResponse { @@ -327,7 +298,7 @@ class TagController extends Controller app('preferences')->mark(); // store attachment(s): - /** @var array|null $files */ + /** @var null|array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachmentsHelper->saveAttachmentsForModel($result, $files); @@ -351,11 +322,6 @@ class TagController extends Controller /** * Update a tag. - * - * @param TagFormRequest $request - * @param Tag $tag - * - * @return RedirectResponse */ public function update(TagFormRequest $request, Tag $tag): RedirectResponse { @@ -366,7 +332,7 @@ class TagController extends Controller app('preferences')->mark(); // store new attachment(s): - /** @var array|null $files */ + /** @var null|array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachmentsHelper->saveAttachmentsForModel($tag, $files); diff --git a/app/Http/Controllers/Transaction/BulkController.php b/app/Http/Controllers/Transaction/BulkController.php index 58f889bcb2..a765ec1f5a 100644 --- a/app/Http/Controllers/Transaction/BulkController.php +++ b/app/Http/Controllers/Transaction/BulkController.php @@ -46,8 +46,6 @@ class BulkController extends Controller /** * BulkController constructor. - * - */ public function __construct() { @@ -69,8 +67,6 @@ class BulkController extends Controller * * TODO user wont be able to tell if the journal is part of a split. * - * @param array $journals - * * @return Factory|View */ public function edit(array $journals) @@ -92,9 +88,7 @@ class BulkController extends Controller /** * Update all journals. * - * @param BulkEditJournalRequest $request - * - * @return Application|RedirectResponse|Redirector + * @return Application|Redirector|RedirectResponse */ public function update(BulkEditJournalRequest $request) { @@ -114,7 +108,7 @@ class BulkController extends Controller $resultB = $this->updateJournalTags($journal, $tagsAction, explode(',', $request->convertString('tags'))); $resultC = $this->updateJournalCategory($journal, $ignoreCategory, $request->convertString('category')); if ($resultA || $resultB || $resultC) { - $count++; + ++$count; $collection->push($journal); } } @@ -133,13 +127,6 @@ class BulkController extends Controller return redirect($this->getPreviousUrl('transactions.bulk-edit.url')); } - /** - * @param TransactionJournal $journal - * @param bool $ignoreUpdate - * @param int $budgetId - * - * @return bool - */ private function updateJournalBudget(TransactionJournal $journal, bool $ignoreUpdate, int $budgetId): bool { if (true === $ignoreUpdate) { @@ -151,13 +138,6 @@ class BulkController extends Controller return true; } - /** - * @param TransactionJournal $journal - * @param string $action - * @param array $tags - * - * @return bool - */ private function updateJournalTags(TransactionJournal $journal, string $action, array $tags): bool { if ('do_replace' === $action) { @@ -173,13 +153,6 @@ class BulkController extends Controller return true; } - /** - * @param TransactionJournal $journal - * @param bool $ignoreUpdate - * @param string $category - * - * @return bool - */ private function updateJournalCategory(TransactionJournal $journal, bool $ignoreUpdate, string $category): bool { if (true === $ignoreUpdate) { diff --git a/app/Http/Controllers/Transaction/ConvertController.php b/app/Http/Controllers/Transaction/ConvertController.php index 55a062addb..be15b5c1e2 100644 --- a/app/Http/Controllers/Transaction/ConvertController.php +++ b/app/Http/Controllers/Transaction/ConvertController.php @@ -56,8 +56,6 @@ class ConvertController extends Controller /** * ConvertController constructor. - * - */ public function __construct() { @@ -78,12 +76,9 @@ class ConvertController extends Controller /** * Show overview of a to be converted transaction. * - * @param TransactionType $destinationType - * @param TransactionGroup $group - * - * @return RedirectResponse|Redirector|Factory|View - * @throws Exception + * @return Factory|Redirector|RedirectResponse|View * + * @throws \Exception */ public function index(TransactionType $destinationType, TransactionGroup $group) { @@ -100,7 +95,7 @@ class ConvertController extends Controller $groupTitle = $group->title ?? $first->description; $groupArray = $transformer->transformObject($group); - $subTitle = (string)trans('firefly.convert_to_' . $destinationType->type, ['description' => $groupTitle]); + $subTitle = (string)trans('firefly.convert_to_'.$destinationType->type, ['description' => $groupTitle]); $subTitleIcon = 'fa-exchange'; // get a list of asset accounts and liabilities and stuff, in various combinations: @@ -116,7 +111,7 @@ class ConvertController extends Controller if ($sourceType->type === $destinationType->type) { // cannot convert to its own type. app('log')->debug('This is already a transaction of the expected type..'); - session()->flash('info', (string)trans('firefly.convert_is_already_type_' . $destinationType->type)); + session()->flash('info', (string)trans('firefly.convert_is_already_type_'.$destinationType->type)); return redirect(route('transactions.show', [$group->id])); } @@ -140,142 +135,10 @@ class ConvertController extends Controller ); } - /** - * @return array - */ - private function getValidDepositSources(): array - { - // make repositories - $liabilityTypes = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN]; - $accountList = $this->accountRepository - ->getActiveAccountsByType([AccountType::REVENUE, AccountType::CASH, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]); - $grouped = []; - // group accounts: - /** @var Account $account */ - foreach ($accountList as $account) { - $role = (string)$this->accountRepository->getMetaValue($account, 'account_role'); - $name = $account->name; - if ('' === $role) { - $role = 'no_account_type'; - } - - // maybe it's a liability thing: - if (in_array($account->accountType->type, $liabilityTypes, true)) { - $role = 'l_' . $account->accountType->type; - } - if (AccountType::CASH === $account->accountType->type) { - $role = 'cash_account'; - $name = sprintf('(%s)', trans('firefly.cash')); - } - if (AccountType::REVENUE === $account->accountType->type) { - $role = 'revenue_account'; - } - - $key = (string)trans('firefly.opt_group_' . $role); - $grouped[$key][$account->id] = $name; - } - - return $grouped; - } - - /** - * @return array - */ - private function getValidWithdrawalDests(): array - { - // make repositories - $liabilityTypes = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN]; - $accountList = $this->accountRepository->getActiveAccountsByType( - [AccountType::EXPENSE, AccountType::CASH, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE] - ); - $grouped = []; - // group accounts: - /** @var Account $account */ - foreach ($accountList as $account) { - $role = (string)$this->accountRepository->getMetaValue($account, 'account_role'); - $name = $account->name; - if ('' === $role) { - $role = 'no_account_type'; - } - - // maybe it's a liability thing: - if (in_array($account->accountType->type, $liabilityTypes, true)) { - $role = 'l_' . $account->accountType->type; - } - if (AccountType::CASH === $account->accountType->type) { - $role = 'cash_account'; - $name = sprintf('(%s)', trans('firefly.cash')); - } - if (AccountType::EXPENSE === $account->accountType->type) { - $role = 'expense_account'; - } - - $key = (string)trans('firefly.opt_group_' . $role); - $grouped[$key][$account->id] = $name; - } - - return $grouped; - } - - /** - * @return array - * @throws Exception - */ - private function getLiabilities(): array - { - // make repositories - $accountList = $this->accountRepository->getActiveAccountsByType([AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]); - $defaultCurrency = app('amount')->getDefaultCurrency(); - $grouped = []; - // group accounts: - /** @var Account $account */ - foreach ($accountList as $account) { - $balance = app('steam')->balance($account, today()); - $currency = $this->accountRepository->getAccountCurrency($account) ?? $defaultCurrency; - $role = 'l_' . $account->accountType->type; - $key = (string)trans('firefly.opt_group_' . $role); - $grouped[$key][$account->id] = $account->name . ' (' . app('amount')->formatAnything($currency, $balance, false) . ')'; - } - - return $grouped; - } - - /** - * @return array - * @throws Exception - */ - private function getAssetAccounts(): array - { - // make repositories - $accountList = $this->accountRepository->getActiveAccountsByType([AccountType::ASSET]); - $defaultCurrency = app('amount')->getDefaultCurrency(); - $grouped = []; - // group accounts: - /** @var Account $account */ - foreach ($accountList as $account) { - $balance = app('steam')->balance($account, today()); - $currency = $this->accountRepository->getAccountCurrency($account) ?? $defaultCurrency; - $role = (string)$this->accountRepository->getMetaValue($account, 'account_role'); - if ('' === $role) { - $role = 'no_account_type'; - } - - $key = (string)trans('firefly.opt_group_' . $role); - $grouped[$key][$account->id] = $account->name . ' (' . app('amount')->formatAnything($currency, $balance, false) . ')'; - } - - return $grouped; - } - /** * Do the conversion. * - * @param Request $request - * @param TransactionType $destinationType - * @param TransactionGroup $group - * - * @return RedirectResponse|Redirector - * + * @return Redirector|RedirectResponse */ public function postIndex(Request $request, TransactionType $destinationType, TransactionGroup $group) { @@ -298,18 +161,137 @@ class ConvertController extends Controller // correct transfers: $group->refresh(); - session()->flash('success', (string)trans('firefly.converted_to_' . $destinationType->type)); + session()->flash('success', (string)trans('firefly.converted_to_'.$destinationType->type)); event(new UpdatedTransactionGroup($group, true, true)); return redirect(route('transactions.show', [$group->id])); } + private function getValidDepositSources(): array + { + // make repositories + $liabilityTypes = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN]; + $accountList = $this->accountRepository + ->getActiveAccountsByType([AccountType::REVENUE, AccountType::CASH, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]) + ; + $grouped = []; + + // group accounts: + /** @var Account $account */ + foreach ($accountList as $account) { + $role = (string)$this->accountRepository->getMetaValue($account, 'account_role'); + $name = $account->name; + if ('' === $role) { + $role = 'no_account_type'; + } + + // maybe it's a liability thing: + if (in_array($account->accountType->type, $liabilityTypes, true)) { + $role = 'l_'.$account->accountType->type; + } + if (AccountType::CASH === $account->accountType->type) { + $role = 'cash_account'; + $name = sprintf('(%s)', trans('firefly.cash')); + } + if (AccountType::REVENUE === $account->accountType->type) { + $role = 'revenue_account'; + } + + $key = (string)trans('firefly.opt_group_'.$role); + $grouped[$key][$account->id] = $name; + } + + return $grouped; + } + + private function getValidWithdrawalDests(): array + { + // make repositories + $liabilityTypes = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN]; + $accountList = $this->accountRepository->getActiveAccountsByType( + [AccountType::EXPENSE, AccountType::CASH, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE] + ); + $grouped = []; + + // group accounts: + /** @var Account $account */ + foreach ($accountList as $account) { + $role = (string)$this->accountRepository->getMetaValue($account, 'account_role'); + $name = $account->name; + if ('' === $role) { + $role = 'no_account_type'; + } + + // maybe it's a liability thing: + if (in_array($account->accountType->type, $liabilityTypes, true)) { + $role = 'l_'.$account->accountType->type; + } + if (AccountType::CASH === $account->accountType->type) { + $role = 'cash_account'; + $name = sprintf('(%s)', trans('firefly.cash')); + } + if (AccountType::EXPENSE === $account->accountType->type) { + $role = 'expense_account'; + } + + $key = (string)trans('firefly.opt_group_'.$role); + $grouped[$key][$account->id] = $name; + } + + return $grouped; + } + + /** + * @throws \Exception + */ + private function getLiabilities(): array + { + // make repositories + $accountList = $this->accountRepository->getActiveAccountsByType([AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]); + $defaultCurrency = app('amount')->getDefaultCurrency(); + $grouped = []; + + // group accounts: + /** @var Account $account */ + foreach ($accountList as $account) { + $balance = app('steam')->balance($account, today()); + $currency = $this->accountRepository->getAccountCurrency($account) ?? $defaultCurrency; + $role = 'l_'.$account->accountType->type; + $key = (string)trans('firefly.opt_group_'.$role); + $grouped[$key][$account->id] = $account->name.' ('.app('amount')->formatAnything($currency, $balance, false).')'; + } + + return $grouped; + } + + /** + * @throws \Exception + */ + private function getAssetAccounts(): array + { + // make repositories + $accountList = $this->accountRepository->getActiveAccountsByType([AccountType::ASSET]); + $defaultCurrency = app('amount')->getDefaultCurrency(); + $grouped = []; + + // group accounts: + /** @var Account $account */ + foreach ($accountList as $account) { + $balance = app('steam')->balance($account, today()); + $currency = $this->accountRepository->getAccountCurrency($account) ?? $defaultCurrency; + $role = (string)$this->accountRepository->getMetaValue($account, 'account_role'); + if ('' === $role) { + $role = 'no_account_type'; + } + + $key = (string)trans('firefly.opt_group_'.$role); + $grouped[$key][$account->id] = $account->name.' ('.app('amount')->formatAnything($currency, $balance, false).')'; + } + + return $grouped; + } + /** - * @param TransactionJournal $journal - * @param TransactionType $transactionType - * @param array $data - * - * @return TransactionJournal * @throws FireflyException */ private function convertJournal(TransactionJournal $journal, TransactionType $transactionType, array $data): TransactionJournal @@ -329,8 +311,8 @@ class ConvertController extends Controller $sourceName = '' === $sourceName ? null : (string)$sourceName; $destinationId = '' === $destinationId || null === $destinationId ? null : (int)$destinationId; $destinationName = '' === $destinationName ? null : (string)$destinationName; - $validSource = $validator->validateSource(['id' => $sourceId, 'name' => $sourceName,]); - $validDestination = $validator->validateDestination(['id' => $destinationId, 'name' => $destinationName,]); + $validSource = $validator->validateSource(['id' => $sourceId, 'name' => $sourceName]); + $validDestination = $validator->validateDestination(['id' => $destinationId, 'name' => $destinationName]); if (false === $validSource) { throw new FireflyException(sprintf(trans('firefly.convert_invalid_source'), $journal->id)); @@ -348,6 +330,7 @@ class ConvertController extends Controller 'destination_name' => $destinationName, 'type' => $transactionType->type, ]; + /** @var JournalUpdateService $service */ $service = app(JournalUpdateService::class); $service->setTransactionJournal($journal); diff --git a/app/Http/Controllers/Transaction/CreateController.php b/app/Http/Controllers/Transaction/CreateController.php index 6c1808a257..99a79a895c 100644 --- a/app/Http/Controllers/Transaction/CreateController.php +++ b/app/Http/Controllers/Transaction/CreateController.php @@ -33,9 +33,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\View; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use JsonException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class CreateController @@ -46,8 +43,6 @@ class CreateController extends Controller /** * CreateController constructor. - * - */ public function __construct() { @@ -64,11 +59,6 @@ class CreateController extends Controller ); } - /** - * @param Request $request - * - * @return JsonResponse - */ public function cloneGroup(Request $request): JsonResponse { $groupId = (int)$request->get('id'); @@ -103,14 +93,10 @@ class CreateController extends Controller /** * Create a new transaction group. * - * @param string|null $objectType - * * @return Factory|View + * * @throws FireflyException - * @throws JsonException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ + * */ public function create(?string $objectType) { app('preferences')->mark(); diff --git a/app/Http/Controllers/Transaction/DeleteController.php b/app/Http/Controllers/Transaction/DeleteController.php index 9c039827d7..67a50e9c61 100644 --- a/app/Http/Controllers/Transaction/DeleteController.php +++ b/app/Http/Controllers/Transaction/DeleteController.php @@ -45,8 +45,6 @@ class DeleteController extends Controller /** * IndexController constructor. - * - */ public function __construct() { @@ -68,9 +66,7 @@ class DeleteController extends Controller /** * Shows the form that allows a user to delete a transaction journal. * - * @param TransactionGroup $group - * - * @return Factory|View|Redirector|RedirectResponse + * @return Factory|Redirector|RedirectResponse|View */ public function delete(TransactionGroup $group) { @@ -85,7 +81,7 @@ class DeleteController extends Controller throw new NotFoundHttpException(); } $objectType = strtolower($journal->transaction_type_type ?? $journal->transactionType->type); - $subTitle = (string)trans('firefly.delete_' . $objectType, ['description' => $group->title ?? $journal->description]); + $subTitle = (string)trans('firefly.delete_'.$objectType, ['description' => $group->title ?? $journal->description]); $previous = app('steam')->getSafePreviousUrl(); // put previous url in session app('log')->debug('Will try to remember previous URL'); @@ -96,12 +92,8 @@ class DeleteController extends Controller /** * Actually destroys the journal. - * - * @param TransactionGroup $group - * - * @return RedirectResponse|Redirector */ - public function destroy(TransactionGroup $group): RedirectResponse | Redirector + public function destroy(TransactionGroup $group): Redirector|RedirectResponse { app('log')->debug(sprintf('Now in %s(#%d).', __METHOD__, $group->id)); if (!$this->isEditableGroup($group)) { @@ -113,10 +105,11 @@ class DeleteController extends Controller throw new NotFoundHttpException(); } $objectType = strtolower($journal->transaction_type_type ?? $journal->transactionType->type); - session()->flash('success', (string)trans('firefly.deleted_' . strtolower($objectType), ['description' => $group->title ?? $journal->description])); + session()->flash('success', (string)trans('firefly.deleted_'.strtolower($objectType), ['description' => $group->title ?? $journal->description])); // grab asset account(s) from group: $accounts = []; + /** @var TransactionJournal $currentJournal */ foreach ($group->transactionJournals as $currentJournal) { /** @var Transaction $transaction */ @@ -138,7 +131,6 @@ class DeleteController extends Controller } app('preferences')->mark(); - return redirect($this->getPreviousUrl('transactions.delete.url')); } } diff --git a/app/Http/Controllers/Transaction/EditController.php b/app/Http/Controllers/Transaction/EditController.php index 50b7b00e9f..3c7d819095 100644 --- a/app/Http/Controllers/Transaction/EditController.php +++ b/app/Http/Controllers/Transaction/EditController.php @@ -43,8 +43,6 @@ class EditController extends Controller /** * IndexController constructor. - * - */ public function __construct() { @@ -64,9 +62,7 @@ class EditController extends Controller } /** - * @param TransactionGroup $transactionGroup - * - * @return Factory|View|RedirectResponse|Redirector + * @return Factory|Redirector|RedirectResponse|View */ public function edit(TransactionGroup $transactionGroup) { @@ -82,7 +78,6 @@ class EditController extends Controller $accountToTypes = config('firefly.account_to_transaction'); $expectedSourceTypes = config('firefly.expected_source_types'); $allowedSourceDests = config('firefly.source_dests'); - // $defaultCurrency = app('amount')->getDefaultCurrency(); $cash = $repository->getCashAccount(); @@ -106,14 +101,10 @@ class EditController extends Controller ); } - /** - * @param TransactionJournal $journal - * - * @return JsonResponse - */ public function unreconcile(TransactionJournal $journal): JsonResponse { $this->repository->unreconcileById($journal->id); + return response()->json([], 204); } } diff --git a/app/Http/Controllers/Transaction/IndexController.php b/app/Http/Controllers/Transaction/IndexController.php index 895e2f5b93..b2219ebcf6 100644 --- a/app/Http/Controllers/Transaction/IndexController.php +++ b/app/Http/Controllers/Transaction/IndexController.php @@ -32,8 +32,6 @@ use FireflyIII\Support\Http\Controllers\PeriodOverview; use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class IndexController @@ -46,8 +44,6 @@ class IndexController extends Controller /** * IndexController constructor. - * - */ public function __construct() { @@ -69,15 +65,9 @@ class IndexController extends Controller /** * Index for a range of transactions. * - * @param Request $request - * @param string $objectType - * @param Carbon|null $start - * @param Carbon|null $end - * * @return Factory|View + * * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function index(Request $request, string $objectType, Carbon $start = null, Carbon $end = null) { @@ -85,8 +75,8 @@ class IndexController extends Controller $objectType = 'transfer'; } - $subTitleIcon = config('firefly.transactionIconsByType.' . $objectType); - $types = config('firefly.transactionTypesByType.' . $objectType); + $subTitleIcon = config('firefly.transactionIconsByType.'.$objectType); + $types = config('firefly.transactionTypesByType.'.$objectType); $page = (int)$request->get('page'); $pageSize = (int)app('preferences')->get('listPageSize', 50)->data; if (null === $start) { @@ -114,13 +104,14 @@ class IndexController extends Controller $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end) - ->setTypes($types) - ->setLimit($pageSize) - ->setPage($page) - ->withBudgetInformation() - ->withCategoryInformation() - ->withAccountInformation() - ->withAttachmentInformation(); + ->setTypes($types) + ->setLimit($pageSize) + ->setPage($page) + ->withBudgetInformation() + ->withCategoryInformation() + ->withAccountInformation() + ->withAttachmentInformation() + ; $groups = $collector->getPaginatedGroups(); $groups->setPath($path); @@ -130,17 +121,12 @@ class IndexController extends Controller /** * Index for ALL transactions. * - * @param Request $request - * @param string $objectType - * * @return Factory|View - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function indexAll(Request $request, string $objectType) { - $subTitleIcon = config('firefly.transactionIconsByType.' . $objectType); - $types = config('firefly.transactionTypesByType.' . $objectType); + $subTitleIcon = config('firefly.transactionIconsByType.'.$objectType); + $types = config('firefly.transactionTypesByType.'.$objectType); $page = (int)$request->get('page'); $pageSize = (int)app('preferences')->get('listPageSize', 50)->data; $path = route('transactions.index.all', [$objectType]); @@ -148,19 +134,20 @@ class IndexController extends Controller $start = null === $first ? new Carbon() : $first->date; $last = $this->repository->getLast(); $end = null !== $last ? $last->date : today(config('app.timezone')); - $subTitle = (string)trans('firefly.all_' . $objectType); + $subTitle = (string)trans('firefly.all_'.$objectType); /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end) - ->setTypes($types) - ->setLimit($pageSize) - ->setPage($page) - ->withAccountInformation() - ->withBudgetInformation() - ->withCategoryInformation() - ->withAttachmentInformation(); + ->setTypes($types) + ->setLimit($pageSize) + ->setPage($page) + ->withAccountInformation() + ->withBudgetInformation() + ->withCategoryInformation() + ->withAttachmentInformation() + ; $groups = $collector->getPaginatedGroups(); $groups->setPath($path); diff --git a/app/Http/Controllers/Transaction/LinkController.php b/app/Http/Controllers/Transaction/LinkController.php index d3ed4e2f29..7c3935bc77 100644 --- a/app/Http/Controllers/Transaction/LinkController.php +++ b/app/Http/Controllers/Transaction/LinkController.php @@ -45,8 +45,6 @@ class LinkController extends Controller /** * LinkController constructor. - * - */ public function __construct() { @@ -68,8 +66,6 @@ class LinkController extends Controller /** * Delete a link. * - * @param TransactionJournalLink $link - * * @return Factory|View */ public function delete(TransactionJournalLink $link) @@ -84,9 +80,7 @@ class LinkController extends Controller /** * Actually destroy it. * - * @param TransactionJournalLink $link - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function destroy(TransactionJournalLink $link) { @@ -99,8 +93,6 @@ class LinkController extends Controller } /** - * @param TransactionJournal $journal - * * @return Factory|View */ public function modal(TransactionJournal $journal) @@ -113,10 +105,7 @@ class LinkController extends Controller /** * Store a new link. * - * @param JournalLinkRequest $request - * @param TransactionJournal $journal - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function store(JournalLinkRequest $request, TransactionJournal $journal) { @@ -153,9 +142,7 @@ class LinkController extends Controller /** * Switch link from A <> B to B <> A. * - * @param Request $request - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ public function switchLink(Request $request) { diff --git a/app/Http/Controllers/Transaction/MassController.php b/app/Http/Controllers/Transaction/MassController.php index 30411a6521..ce07434149 100644 --- a/app/Http/Controllers/Transaction/MassController.php +++ b/app/Http/Controllers/Transaction/MassController.php @@ -41,11 +41,9 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Routing\Redirector; use Illuminate\Support\Facades\Log; use Illuminate\View\View as IlluminateView; -use InvalidArgumentException; /** * Class MassController. - * */ class MassController extends Controller { @@ -53,8 +51,6 @@ class MassController extends Controller /** * MassController constructor. - * - */ public function __construct() { @@ -73,10 +69,6 @@ class MassController extends Controller /** * Mass delete transactions. - * - * @param array $journals - * - * @return IlluminateView */ public function delete(array $journals): IlluminateView { @@ -91,10 +83,7 @@ class MassController extends Controller /** * Do the mass delete. * - * @param MassDeleteJournalRequest $request - * * @return Application|Redirector|RedirectResponse - * */ public function destroy(MassDeleteJournalRequest $request) { @@ -103,15 +92,18 @@ class MassController extends Controller $count = 0; if (is_array($ids)) { app('log')->debug('Array of IDs', $ids); + /** @var string $journalId */ foreach ($ids as $journalId) { app('log')->debug(sprintf('Searching for ID #%d', $journalId)); - /** @var TransactionJournal|null $journal */ + + /** @var null|TransactionJournal $journal */ $journal = $this->repository->find((int)$journalId); if (null !== $journal && (int)$journalId === $journal->id) { $this->repository->destroyJournal($journal); ++$count; app('log')->debug(sprintf('Deleted transaction journal #%d', $journalId)); + continue; } app('log')->debug(sprintf('Could not find transaction journal #%d', $journalId)); @@ -126,10 +118,6 @@ class MassController extends Controller /** * Mass edit of journals. - * - * @param array $journals - * - * @return IlluminateView */ public function edit(array $journals): IlluminateView { @@ -165,9 +153,8 @@ class MassController extends Controller /** * Mass update of journals. * - * @param MassEditJournalRequest $request + * @return Redirector|RedirectResponse * - * @return RedirectResponse|Redirector * @throws FireflyException */ public function update(MassEditJournalRequest $request) @@ -178,12 +165,14 @@ class MassController extends Controller throw new FireflyException('This is not an array.'); } $count = 0; + /** @var string $journalId */ foreach ($journalIds as $journalId) { $integer = (int)$journalId; + try { $this->updateJournal($integer, $request); - $count++; + ++$count; } catch (FireflyException $e) { // @ignoreException } @@ -197,9 +186,6 @@ class MassController extends Controller } /** - * @param int $journalId - * @param MassEditJournalRequest $request - * * @throws FireflyException */ private function updateJournal(int $journalId, MassEditJournalRequest $request): void @@ -233,13 +219,6 @@ class MassController extends Controller event(new UpdatedTransactionGroup($journal->transactionGroup, true, true)); } - /** - * @param MassEditJournalRequest $request - * @param int $journalId - * @param string $key - * - * @return Carbon|null - */ private function getDateFromRequest(MassEditJournalRequest $request, int $journalId, string $key): ?Carbon { $value = $request->get($key); @@ -249,9 +228,10 @@ class MassController extends Controller if (!array_key_exists($journalId, $value)) { return null; } + try { $carbon = Carbon::parse($value[$journalId]); - } catch (InvalidArgumentException $e) { + } catch (\InvalidArgumentException $e) { Log::warning(sprintf('Could not parse "%s" but dont mind', $value[$journalId])); Log::warning($e->getMessage()); @@ -261,13 +241,6 @@ class MassController extends Controller return $carbon; } - /** - * @param MassEditJournalRequest $request - * @param int $journalId - * @param string $string - * - * @return string|null - */ private function getStringFromRequest(MassEditJournalRequest $request, int $journalId, string $string): ?string { $value = $request->get($string); @@ -281,13 +254,6 @@ class MassController extends Controller return (string)$value[$journalId]; } - /** - * @param MassEditJournalRequest $request - * @param int $journalId - * @param string $string - * - * @return int|null - */ private function getIntFromRequest(MassEditJournalRequest $request, int $journalId, string $string): ?int { $value = $request->get($string); diff --git a/app/Http/Controllers/Transaction/ShowController.php b/app/Http/Controllers/Transaction/ShowController.php index cf67431732..0569eef271 100644 --- a/app/Http/Controllers/Transaction/ShowController.php +++ b/app/Http/Controllers/Transaction/ShowController.php @@ -65,8 +65,6 @@ class ShowController extends Controller } /** - * @param TransactionGroup $transactionGroup - * * @return JsonResponse */ public function debugShow(TransactionGroup $transactionGroup) @@ -75,14 +73,13 @@ class ShowController extends Controller } /** - * @param TransactionGroup $transactionGroup - * * @return Factory|View + * * @throws FireflyException */ public function show(TransactionGroup $transactionGroup) { - /** @var TransactionJournal|null $first */ + /** @var null|TransactionJournal $first */ $first = $transactionGroup->transactionJournals()->first(['transaction_journals.*']); $splits = $transactionGroup->transactionJournals()->count(); @@ -120,7 +117,6 @@ class ShowController extends Controller $attachments = $this->repository->getAttachments($transactionGroup); $links = $this->repository->getLinks($transactionGroup); - return view( 'transactions.show', compact( @@ -141,11 +137,6 @@ class ShowController extends Controller ); } - /** - * @param array $group - * - * @return array - */ private function getAmounts(array $group): array { $amounts = []; @@ -160,10 +151,10 @@ class ShowController extends Controller } $amounts[$symbol]['amount'] = bcadd($amounts[$symbol]['amount'], $transaction['amount']); if (null !== $transaction['foreign_amount'] && '' !== $transaction['foreign_amount'] - && bccomp( + && 0 !== bccomp( '0', $transaction['foreign_amount'] - ) !== 0) { + )) { // same for foreign currency: $foreignSymbol = $transaction['foreign_currency_symbol']; if (!array_key_exists($foreignSymbol, $amounts)) { @@ -183,11 +174,6 @@ class ShowController extends Controller return $amounts; } - /** - * @param array $group - * - * @return array - */ private function getAccounts(array $group): array { $accounts = [ diff --git a/app/Http/Controllers/TransactionCurrency/CreateController.php b/app/Http/Controllers/TransactionCurrency/CreateController.php index 7f1967ec8d..8e6c199261 100644 --- a/app/Http/Controllers/TransactionCurrency/CreateController.php +++ b/app/Http/Controllers/TransactionCurrency/CreateController.php @@ -1,6 +1,5 @@ user(); $data = $request->getCurrencyData(); if (!$this->userRepository->hasRole($user, 'owner')) { - app('log')->error('User ' . auth()->user()->id . ' is not admin, but tried to store a currency.'); + app('log')->error('User '.auth()->user()->id.' is not admin, but tried to store a currency.'); Log::channel('audit')->info('Tried to create (POST) currency without admin rights.', $data); return redirect($this->getPreviousUrl('currencies.create.url'))->withInput(); } $data['enabled'] = true; + try { $currency = $this->repository->store($data); } catch (FireflyException $e) { diff --git a/app/Http/Controllers/TransactionCurrency/DeleteController.php b/app/Http/Controllers/TransactionCurrency/DeleteController.php index 28190f5f49..c1e1dd7969 100644 --- a/app/Http/Controllers/TransactionCurrency/DeleteController.php +++ b/app/Http/Controllers/TransactionCurrency/DeleteController.php @@ -1,6 +1,5 @@ getPreviousUrl('currencies.delete.url')); } - } diff --git a/app/Http/Controllers/TransactionCurrency/EditController.php b/app/Http/Controllers/TransactionCurrency/EditController.php index 46f3e56272..aa42eac591 100644 --- a/app/Http/Controllers/TransactionCurrency/EditController.php +++ b/app/Http/Controllers/TransactionCurrency/EditController.php @@ -1,6 +1,5 @@ getPreviousUrl('currencies.edit.url')); } - } diff --git a/app/Http/Controllers/TransactionCurrency/IndexController.php b/app/Http/Controllers/TransactionCurrency/IndexController.php index f73d6b7304..581f73ae18 100644 --- a/app/Http/Controllers/TransactionCurrency/IndexController.php +++ b/app/Http/Controllers/TransactionCurrency/IndexController.php @@ -1,6 +1,5 @@ userGroupDefault ? 0 : 1; $enabled = true === $currency->userGroupEnabled ? 0 : 1; + return sprintf('%s-%s-%s', $default, $enabled, $currency->code); } ); @@ -101,5 +93,4 @@ class IndexController extends Controller return view('currencies.index', compact('currencies', 'isOwner')); } - } diff --git a/app/Http/Controllers/Webhooks/CreateController.php b/app/Http/Controllers/Webhooks/CreateController.php index bae29cbffb..9b4a6499bb 100644 --- a/app/Http/Controllers/Webhooks/CreateController.php +++ b/app/Http/Controllers/Webhooks/CreateController.php @@ -33,9 +33,6 @@ use Illuminate\View\View; */ class CreateController extends Controller { - /** - * - */ public function __construct() { parent::__construct(); @@ -61,6 +58,7 @@ class CreateController extends Controller public function index() { $previousUrl = $this->rememberPreviousUrl('webhooks.create.url'); + return view('webhooks.create', compact('previousUrl')); } } diff --git a/app/Http/Controllers/Webhooks/DeleteController.php b/app/Http/Controllers/Webhooks/DeleteController.php index 4ad1173cfd..1101707ce0 100644 --- a/app/Http/Controllers/Webhooks/DeleteController.php +++ b/app/Http/Controllers/Webhooks/DeleteController.php @@ -36,8 +36,6 @@ class DeleteController extends Controller { /** * DeleteController constructor. - * - */ public function __construct() { @@ -59,9 +57,7 @@ class DeleteController extends Controller /** * Delete account screen. * - * @param Webhook $webhook - * - * @return Factory|Application|View + * @return Application|Factory|View */ public function index(Webhook $webhook) { diff --git a/app/Http/Controllers/Webhooks/EditController.php b/app/Http/Controllers/Webhooks/EditController.php index 12c0e9816d..ce48c3c6df 100644 --- a/app/Http/Controllers/Webhooks/EditController.php +++ b/app/Http/Controllers/Webhooks/EditController.php @@ -36,8 +36,6 @@ class EditController extends Controller { /** * DeleteController constructor. - * - */ public function __construct() { @@ -58,9 +56,7 @@ class EditController extends Controller /** * Delete account screen. * - * @param Webhook $webhook - * - * @return Factory|Application|View + * @return Application|Factory|View */ public function index(Webhook $webhook) { diff --git a/app/Http/Controllers/Webhooks/IndexController.php b/app/Http/Controllers/Webhooks/IndexController.php index 886828e891..36bfb80e30 100644 --- a/app/Http/Controllers/Webhooks/IndexController.php +++ b/app/Http/Controllers/Webhooks/IndexController.php @@ -33,9 +33,6 @@ use Illuminate\View\View; */ class IndexController extends Controller { - /** - * - */ public function __construct() { parent::__construct(); diff --git a/app/Http/Controllers/Webhooks/ShowController.php b/app/Http/Controllers/Webhooks/ShowController.php index f6fe12c778..b9cf1ae61d 100644 --- a/app/Http/Controllers/Webhooks/ShowController.php +++ b/app/Http/Controllers/Webhooks/ShowController.php @@ -36,8 +36,6 @@ class ShowController extends Controller { /** * DeleteController constructor. - * - */ public function __construct() { @@ -58,9 +56,7 @@ class ShowController extends Controller /** * Delete account screen. * - * @param Webhook $webhook - * - * @return Factory|Application|View + * @return Application|Factory|View */ public function index(Webhook $webhook) { diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index f1ff165468..139547582c 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -54,8 +54,6 @@ use PragmaRX\Google2FALaravel\Middleware as MFAMiddleware; /** * Class Kernel - * - */ class Kernel extends HttpKernel { @@ -124,7 +122,7 @@ class Kernel extends HttpKernel VerifyCsrfToken::class, Binder::class, Authenticate::class, - //RedirectIfTwoFactorAuthenticated::class, + // RedirectIfTwoFactorAuthenticated::class, ], // MUST be logged in @@ -169,7 +167,7 @@ class Kernel extends HttpKernel ShareErrorsFromSession::class, VerifyCsrfToken::class, Authenticate::class, - //AuthenticateTwoFactor::class, + // AuthenticateTwoFactor::class, IsAdmin::class, Range::class, Binder::class, diff --git a/app/Http/Middleware/AcceptHeaders.php b/app/Http/Middleware/AcceptHeaders.php index cd3c7f64c2..915c5432f6 100644 --- a/app/Http/Middleware/AcceptHeaders.php +++ b/app/Http/Middleware/AcceptHeaders.php @@ -29,18 +29,13 @@ use Illuminate\Http\Request; use Illuminate\Http\Response; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -/** - * - */ class AcceptHeaders { /** * Handle the incoming requests. * - * @param Request $request - * @param callable $next - * * @return Response + * * @throws BadHttpHeaderException */ public function handle(Request $request, callable $next): mixed @@ -50,7 +45,6 @@ class AcceptHeaders $contentTypes = ['application/x-www-form-urlencoded', 'application/json', 'application/vnd.api+json', 'application/octet-stream']; $submitted = (string)$request->header('Content-Type'); - // if bad Accept header, send error. if (!$request->accepts($accepts)) { throw new BadHttpHeaderException(sprintf('Accept header "%s" is not something this server can provide.', $request->header('Accept'))); @@ -59,34 +53,31 @@ class AcceptHeaders if (('POST' === $method || 'PUT' === $method) && !$request->hasHeader('Content-Type')) { $error = new BadHttpHeaderException('Content-Type header cannot be empty.'); $error->statusCode = 415; + throw $error; } if (('POST' === $method || 'PUT' === $method) && !$this->acceptsHeader($submitted, $contentTypes)) { $error = new BadHttpHeaderException(sprintf('Content-Type cannot be "%s"', $submitted)); $error->statusCode = 415; + throw $error; } // throw bad request if trace id is not a UUID $uuid = $request->header('X-Trace-Id'); - if (is_string($uuid) && '' !== trim($uuid) && (preg_match('/^[a-f\d]{8}(-[a-f\d]{4}){4}[a-f\d]{8}$/i', trim($uuid)) !== 1)) { + if (is_string($uuid) && '' !== trim($uuid) && (1 !== preg_match('/^[a-f\d]{8}(-[a-f\d]{4}){4}[a-f\d]{8}$/i', trim($uuid)))) { throw new BadRequestHttpException('Bad X-Trace-Id header.'); } return $next($request); } - /** - * @param string $content - * @param array $accepted - * - * @return bool - */ private function acceptsHeader(string $content, array $accepted): bool { if (str_contains($content, ';')) { $content = trim(explode(';', $content)[0]); } + return in_array($content, $accepted, true); } } diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index 388a566dda..400482d09d 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; -use Closure; use FireflyIII\Exceptions\FireflyException; use FireflyIII\User; use Illuminate\Auth\AuthenticationException; @@ -38,17 +37,11 @@ class Authenticate { /** * The authentication factory instance. - * - * @var Auth */ protected Auth $auth; /** * Create a new middleware instance. - * - * @param Auth $auth - * - * @return void */ public function __construct(Auth $auth) { @@ -59,7 +52,6 @@ class Authenticate * Handle an incoming request. * * @param Request $request - * @param Closure $next * @param string[] ...$guards * * @return mixed @@ -67,7 +59,7 @@ class Authenticate * @throws FireflyException * @throws AuthenticationException */ - public function handle($request, Closure $next, ...$guards) + public function handle($request, \Closure $next, ...$guards) { $this->authenticate($request, $guards); @@ -78,18 +70,19 @@ class Authenticate * Determine if the user is logged in to any of the given guards. * * @param mixed $request - * @param array $guards * * @return mixed + * * @throws FireflyException * @throws AuthenticationException + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ protected function authenticate($request, array $guards) { if (0 === count($guards)) { // go for default guard: - /** @noinspection PhpUndefinedMethodInspection */ + // @noinspection PhpUndefinedMethodInspection if ($this->auth->check()) { // do an extra check on user object. /** @noinspection PhpUndefinedMethodInspection */ @@ -98,7 +91,7 @@ class Authenticate $this->validateBlockedUser($user, $guards); } - /** @noinspection PhpUndefinedMethodInspection */ + // @noinspection PhpUndefinedMethodInspection return $this->auth->authenticate(); } @@ -110,6 +103,7 @@ class Authenticate if ($result) { $user = $this->auth->guard($guard)->user(); $this->validateBlockedUser($user, $guards); + // According to PHPstan the method returns void, but we'll see. return $this->auth->shouldUse($guard); // @phpstan-ignore-line } @@ -119,10 +113,6 @@ class Authenticate } /** - * @param User|null $user - * @param array $guards - * - * @return void * @throws AuthenticationException */ private function validateBlockedUser(?User $user, array $guards): void @@ -139,7 +129,7 @@ class Authenticate } app('log')->warning('User is blocked, cannot use authentication method.'); app('session')->flash('logoutMessage', $message); - /** @noinspection PhpUndefinedMethodInspection */ + // @noinspection PhpUndefinedMethodInspection $this->auth->logout(); // @phpstan-ignore-line (thinks function is undefined) throw new AuthenticationException('Blocked account.', $guards); diff --git a/app/Http/Middleware/Binder.php b/app/Http/Middleware/Binder.php index 8c8c6e4bd5..9f4413a06d 100644 --- a/app/Http/Middleware/Binder.php +++ b/app/Http/Middleware/Binder.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; -use Closure; use FireflyIII\Support\Domain; use Illuminate\Contracts\Auth\Factory as Auth; use Illuminate\Http\Request; @@ -40,6 +39,7 @@ class Binder * @var Auth */ protected $auth; + /** * The binders. * @@ -49,8 +49,6 @@ class Binder /** * Binder constructor. - * - * @param Auth $auth */ public function __construct(Auth $auth) { @@ -62,12 +60,10 @@ class Binder * Handle an incoming request. * * @param Request $request - * @param Closure $next * * @return mixed - * */ - public function handle($request, Closure $next) + public function handle($request, \Closure $next) { foreach ($request->route()->parameters() as $key => $value) { if (array_key_exists($key, $this->binders)) { @@ -82,10 +78,6 @@ class Binder /** * Do the binding. * - * @param string $key - * @param string $value - * @param Route $route - * * @return mixed */ private function performBinding(string $key, string $value, Route $route) diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php index def51a126c..4f7fb66aba 100644 --- a/app/Http/Middleware/EncryptCookies.php +++ b/app/Http/Middleware/EncryptCookies.php @@ -27,9 +27,5 @@ use Illuminate\Cookie\Middleware\EncryptCookies as Middleware; /** * Class EncryptCookies - * - */ -class EncryptCookies extends Middleware -{ -} +class EncryptCookies extends Middleware {} diff --git a/app/Http/Middleware/InstallationId.php b/app/Http/Middleware/InstallationId.php index 7a741e9ea6..297fe8ca6b 100644 --- a/app/Http/Middleware/InstallationId.php +++ b/app/Http/Middleware/InstallationId.php @@ -23,12 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; -use Closure; use FireflyIII\Support\System\GeneratesInstallationId; use Illuminate\Http\Request; /** - * * Class InstallationId */ class InstallationId @@ -39,13 +37,10 @@ class InstallationId * Handle an incoming request. * * @param Request $request - * @param Closure $next * * @return mixed - * - * */ - public function handle($request, Closure $next) + public function handle($request, \Closure $next) { $this->generateInstallationId(); diff --git a/app/Http/Middleware/Installer.php b/app/Http/Middleware/Installer.php index 9e1920a891..4b7c7126c5 100644 --- a/app/Http/Middleware/Installer.php +++ b/app/Http/Middleware/Installer.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; -use Closure; use DB; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Support\System\OAuthKeys; @@ -33,8 +32,6 @@ use Illuminate\Http\Request; /** * Class Installer - * - * */ class Installer { @@ -42,16 +39,14 @@ class Installer * Handle an incoming request. * * @param Request $request - * @param Closure $next * * @return mixed * * @throws FireflyException - * */ - public function handle($request, Closure $next) + public function handle($request, \Closure $next) { - //app('log')->debug(sprintf('Installer middleware for URL %s', $request->url())); + // app('log')->debug(sprintf('Installer middleware for URL %s', $request->url())); // ignore installer in test environment. if ('testing' === config('app.env')) { return $next($request); @@ -60,7 +55,7 @@ class Installer $url = $request->url(); $strpos = stripos($url, '/install'); if (false !== $strpos) { - //app('log')->debug(sprintf('URL is %s, will NOT run installer middleware', $url)); + // app('log')->debug(sprintf('URL is %s, will NOT run installer middleware', $url)); return $next($request); } @@ -72,23 +67,39 @@ class Installer return response()->redirectTo(route('installer.index')); } OAuthKeys::verifyKeysRoutine(); + // update scheme version // update firefly version return $next($request); } + /** + * Is access denied error. + */ + protected function isAccessDenied(string $message): bool + { + return false !== stripos($message, 'Access denied'); + } + + /** + * Is no tables exist error. + */ + protected function noTablesExist(string $message): bool + { + return false !== stripos($message, 'Base table or view not found'); + } + /** * Check if the tables are created and accounted for. * - * @return bool * @throws FireflyException */ private function hasNoTables(): bool { - //app('log')->debug('Now in routine hasNoTables()'); + // app('log')->debug('Now in routine hasNoTables()'); try { - DB::table('users')->count(); + \DB::table('users')->count(); } catch (QueryException $e) { $message = $e->getMessage(); app('log')->error(sprintf('Error message trying to access users-table: %s', $message)); @@ -105,42 +116,17 @@ class Installer return true; } + throw new FireflyException(sprintf('Could not access the database: %s', $message), 0, $e); } - //app('log')->debug('Everything seems OK with the tables.'); + // app('log')->debug('Everything seems OK with the tables.'); return false; } - /** - * Is access denied error. - * - * @param string $message - * - * @return bool - */ - protected function isAccessDenied(string $message): bool - { - return false !== stripos($message, 'Access denied'); - } - - /** - * Is no tables exist error. - * - * @param string $message - * - * @return bool - */ - protected function noTablesExist(string $message): bool - { - return false !== stripos($message, 'Base table or view not found'); - } - /** * Check if the "db_version" variable is correct. - * - * @return bool */ private function oldDBVersion(): bool { @@ -159,15 +145,13 @@ class Installer return true; } - //app('log')->info(sprintf('Configured DB version (%d) equals expected DB version (%d)', $dbVersion, $configVersion)); + // app('log')->info(sprintf('Configured DB version (%d) equals expected DB version (%d)', $dbVersion, $configVersion)); return false; } /** * Check if the "firefly_version" variable is correct. - * - * @return bool */ private function oldVersion(): bool { @@ -186,7 +170,7 @@ class Installer return true; } - //app('log')->info(sprintf('Installed Firefly III version (%s) equals expected Firefly III version (%s)', $dbVersion, $configVersion)); + // app('log')->info(sprintf('Installed Firefly III version (%s) equals expected Firefly III version (%s)', $dbVersion, $configVersion)); return false; } diff --git a/app/Http/Middleware/InterestingMessage.php b/app/Http/Middleware/InterestingMessage.php index 60f139e566..ff07fb2c3c 100644 --- a/app/Http/Middleware/InterestingMessage.php +++ b/app/Http/Middleware/InterestingMessage.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; -use Closure; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; use FireflyIII\Models\TransactionCurrency; @@ -40,13 +39,9 @@ class InterestingMessage /** * Flashes the user an interesting message if the URL parameters warrant it. * - * @param Request $request - * @param Closure $next - * * @return mixed - * */ - public function handle(Request $request, Closure $next) + public function handle(Request $request, \Closure $next) { if ($this->testing()) { return $next($request); @@ -76,20 +71,12 @@ class InterestingMessage return $next($request); } - /** - * @return bool - */ private function testing(): bool { // ignore middleware in test environment. return 'testing' === config('app.env') || !auth()->check(); } - /** - * @param Request $request - * - * @return bool - */ private function groupMessage(Request $request): bool { // get parameters from request. @@ -99,9 +86,6 @@ class InterestingMessage return null !== $transactionGroupId && null !== $message; } - /** - * @param Request $request - */ private function handleGroupMessage(Request $request): void { // get parameters from request. @@ -109,7 +93,7 @@ class InterestingMessage $message = $request->get('message'); // send message about newly created transaction group. - /** @var TransactionGroup|null $group */ + /** @var null|TransactionGroup $group */ $group = auth()->user()->transactionGroups()->with(['transactionJournals', 'transactionJournals.transactionType'])->find((int)$transactionGroupId); if (null === $group) { @@ -118,7 +102,7 @@ class InterestingMessage $count = $group->transactionJournals->count(); - /** @var TransactionJournal|null $journal */ + /** @var null|TransactionJournal $journal */ $journal = $group->transactionJournals->first(); if (null === $journal) { return; @@ -140,11 +124,6 @@ class InterestingMessage } } - /** - * @param Request $request - * - * @return bool - */ private function accountMessage(Request $request): bool { // get parameters from request. @@ -154,16 +133,13 @@ class InterestingMessage return null !== $accountId && null !== $message; } - /** - * @param Request $request - */ private function handleAccountMessage(Request $request): void { // get parameters from request. $accountId = $request->get('account_id'); $message = $request->get('message'); - /** @var Account|null $account */ + /** @var null|Account $account */ $account = auth()->user()->accounts()->withTrashed()->find($accountId); if (null === $account) { @@ -180,11 +156,6 @@ class InterestingMessage } } - /** - * @param Request $request - * - * @return bool - */ private function billMessage(Request $request): bool { // get parameters from request. @@ -194,16 +165,13 @@ class InterestingMessage return null !== $billId && null !== $message; } - /** - * @param Request $request - */ private function handleBillMessage(Request $request): void { // get parameters from request. $billId = $request->get('bill_id'); $message = $request->get('message'); - /** @var Bill|null $bill */ + /** @var null|Bill $bill */ $bill = auth()->user()->bills()->withTrashed()->find($billId); if (null === $bill) { @@ -217,11 +185,6 @@ class InterestingMessage } } - /** - * @param Request $request - * - * @return bool - */ private function webhookMessage(Request $request): bool { // get parameters from request. @@ -231,16 +194,13 @@ class InterestingMessage return null !== $webhookId && null !== $message; } - /** - * @param Request $request - */ private function handleWebhookMessage(Request $request): void { // get parameters from request. $webhookId = $request->get('webhook_id'); $message = $request->get('message'); - /** @var Webhook|null $webhook */ + /** @var null|Webhook $webhook */ $webhook = auth()->user()->webhooks()->withTrashed()->find($webhookId); if (null === $webhook) { @@ -257,11 +217,6 @@ class InterestingMessage } } - /** - * @param Request $request - * - * @return bool - */ private function currencyMessage(Request $request): bool { // get parameters from request. @@ -271,11 +226,6 @@ class InterestingMessage return null !== $code && null !== $message; } - /** - * @param Request $request - * - * @return void - */ private function handleCurrencyMessage(Request $request): void { // params: @@ -283,7 +233,7 @@ class InterestingMessage $code = $request->get('code'); $message = $request->get('message'); - /** @var TransactionCurrency|null $currency */ + /** @var null|TransactionCurrency $currency */ $currency = TransactionCurrency::whereCode($code)->first(); if (null === $currency) { diff --git a/app/Http/Middleware/IsAdmin.php b/app/Http/Middleware/IsAdmin.php index 0bde6c57d4..ab5aee6a5e 100644 --- a/app/Http/Middleware/IsAdmin.php +++ b/app/Http/Middleware/IsAdmin.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; -use Closure; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Http\Request; @@ -37,13 +36,11 @@ class IsAdmin /** * Handle an incoming request. Must be admin. * - * @param Request $request - * @param Closure $next - * @param string|null $guard + * @param null|string $guard * * @return mixed */ - public function handle(Request $request, Closure $next, $guard = null) + public function handle(Request $request, \Closure $next, $guard = null) { if (Auth::guard($guard)->guest()) { if ($request->ajax()) { @@ -52,8 +49,10 @@ class IsAdmin return response()->redirectTo(route('login')); } + /** @var User $user */ $user = auth()->user(); + /** @var UserRepositoryInterface $repository */ $repository = app(UserRepositoryInterface::class); if (!$repository->hasRole($user, 'owner')) { diff --git a/app/Http/Middleware/IsDemoUser.php b/app/Http/Middleware/IsDemoUser.php index e5ff35a34e..c26e9ed60f 100644 --- a/app/Http/Middleware/IsDemoUser.php +++ b/app/Http/Middleware/IsDemoUser.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; -use Closure; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Http\Request; @@ -36,14 +35,11 @@ class IsDemoUser /** * Handle an incoming request. * - * @param Request $request - * @param Closure $next - * * @return mixed */ - public function handle(Request $request, Closure $next) + public function handle(Request $request, \Closure $next) { - /** @var User|null $user */ + /** @var null|User $user */ $user = $request->user(); if (null === $user) { return $next($request); diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index 78d56c5a99..f98960fb68 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -23,9 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; -use App; use Carbon\Carbon; -use Closure; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\Http\Controllers\RequestInformation; use Illuminate\Http\Request; @@ -40,12 +38,9 @@ class Range /** * Handle an incoming request. * - * @param Request $request - * @param Closure $next - * * @return mixed */ - public function handle(Request $request, Closure $next) + public function handle(Request $request, \Closure $next) { if (null !== $request->user()) { // set start, end and finish: @@ -101,7 +96,7 @@ class Range // get locale preference: $language = app('steam')->getLanguage(); $locale = app('steam')->getLocale(); - App::setLocale($language); + \App::setLocale($language); Carbon::setLocale(substr($locale, 0, 2)); $localeArray = app('steam')->getLocaleArray($locale); diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php index b5bc8281ef..5bffbb7146 100644 --- a/app/Http/Middleware/RedirectIfAuthenticated.php +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -23,14 +23,11 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; -use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; /** * Class RedirectIfAuthenticated - * - */ class RedirectIfAuthenticated { @@ -38,12 +35,11 @@ class RedirectIfAuthenticated * Handle an incoming request. * * @param Request $request - * @param Closure $next - * @param string|null $guard + * @param null|string $guard * * @return mixed */ - public function handle($request, Closure $next, $guard = null) + public function handle($request, \Closure $next, $guard = null) { if (Auth::guard($guard)->check()) { return response()->redirectTo(route('index')); diff --git a/app/Http/Middleware/SecureHeaders.php b/app/Http/Middleware/SecureHeaders.php index cf8021ba71..0db3a4066d 100644 --- a/app/Http/Middleware/SecureHeaders.php +++ b/app/Http/Middleware/SecureHeaders.php @@ -23,13 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; -use Closure; -use Exception; use Illuminate\Http\Request; use Illuminate\Support\Facades\Vite; /** - * * Class SecureHeaders */ class SecureHeaders @@ -37,13 +34,11 @@ class SecureHeaders /** * Handle an incoming request. * - * @param Request $request - * @param Closure $next - * * @return mixed - * @throws Exception + * + * @throws \Exception */ - public function handle(Request $request, Closure $next) + public function handle(Request $request, \Closure $next) { // generate and share nonce. $nonce = base64_encode(random_bytes(16)); @@ -79,15 +74,15 @@ class SecureHeaders $featurePolicies = [ "geolocation 'none'", "midi 'none'", - //"notifications 'none'", - //"push 'self'", + // "notifications 'none'", + // "push 'self'", "sync-xhr 'self'", "microphone 'none'", "camera 'none'", "magnetometer 'none'", "gyroscope 'none'", - //"speaker 'none'", - //"vibrate 'none'", + // "speaker 'none'", + // "vibrate 'none'", "fullscreen 'self'", "payment 'none'", ]; @@ -112,8 +107,6 @@ class SecureHeaders /** * Return part of a CSP header allowing scripts from Matomo. - * - * @return string */ private function getTrackingScriptSource(): string { diff --git a/app/Http/Middleware/StartFireflySession.php b/app/Http/Middleware/StartFireflySession.php index 753965a3d4..dad24baa3c 100644 --- a/app/Http/Middleware/StartFireflySession.php +++ b/app/Http/Middleware/StartFireflySession.php @@ -29,15 +29,12 @@ use Illuminate\Session\Middleware\StartSession; /** * Class StartFireflySession. - * - */ class StartFireflySession extends StartSession { /** * Store the current URL for the request if necessary. * - * @param Request $request * @param Session $session */ protected function storeCurrentUrl(Request $request, $session): void diff --git a/app/Http/Middleware/TrimStrings.php b/app/Http/Middleware/TrimStrings.php index 4903cf6d1b..d0362148b9 100644 --- a/app/Http/Middleware/TrimStrings.php +++ b/app/Http/Middleware/TrimStrings.php @@ -27,8 +27,6 @@ use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware; /** * Class TrimStrings - * - */ class TrimStrings extends Middleware { diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php index 51094a3040..1b957bcc5e 100644 --- a/app/Http/Middleware/TrustProxies.php +++ b/app/Http/Middleware/TrustProxies.php @@ -28,8 +28,6 @@ use Symfony\Component\HttpFoundation\Request; /** * Class TrustProxies - * - */ class TrustProxies extends Middleware { diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index 2e981f1aae..03d670e78b 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -27,9 +27,5 @@ use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware; /** * Class VerifyCsrfToken. - * - */ -class VerifyCsrfToken extends Middleware -{ -} +class VerifyCsrfToken extends Middleware {} diff --git a/app/Http/Requests/AccountFormRequest.php b/app/Http/Requests/AccountFormRequest.php index 4796680562..c1eea25eb2 100644 --- a/app/Http/Requests/AccountFormRequest.php +++ b/app/Http/Requests/AccountFormRequest.php @@ -45,8 +45,6 @@ class AccountFormRequest extends FormRequest /** * Get all data. - * - * @return array */ public function getAccountData(): array { @@ -95,8 +93,6 @@ class AccountFormRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { @@ -112,22 +108,22 @@ class AccountFormRequest extends FormRequest 'virtual_balance' => 'numeric|nullable|max:1000000000', 'currency_id' => 'exists:transaction_currencies,id', 'account_number' => 'between:1,255|uniqueAccountNumberForUser|nullable', - 'account_role' => 'in:' . $accountRoles, + 'account_role' => 'in:'.$accountRoles, 'active' => 'boolean', - 'cc_type' => 'in:' . $ccPaymentTypes, + 'cc_type' => 'in:'.$ccPaymentTypes, 'amount_currency_id_opening_balance' => 'exists:transaction_currencies,id', 'amount_currency_id_virtual_balance' => 'exists:transaction_currencies,id', - 'what' => 'in:' . $types, + 'what' => 'in:'.$types, 'interest_period' => 'in:daily,monthly,yearly', ]; $rules = Location::requestRules($rules); - /** @var Account|null $account */ + /** @var null|Account $account */ $account = $this->route()->parameter('account'); if (null !== $account) { // add rules: $rules['id'] = 'belongsToUser:accounts'; - $rules['name'] = 'required|max:1024|min:1|uniqueAccountForUser:' . $account->id; + $rules['name'] = 'required|max:1024|min:1|uniqueAccountForUser:'.$account->id; $rules['iban'] = ['iban', 'nullable', new UniqueIban($account, $account->accountType->type)]; } diff --git a/app/Http/Requests/AttachmentFormRequest.php b/app/Http/Requests/AttachmentFormRequest.php index 3df4d8da60..7a51343ded 100644 --- a/app/Http/Requests/AttachmentFormRequest.php +++ b/app/Http/Requests/AttachmentFormRequest.php @@ -29,8 +29,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class AttachmentFormRequest. - * - */ class AttachmentFormRequest extends FormRequest { @@ -39,8 +37,6 @@ class AttachmentFormRequest extends FormRequest /** * Returns the data required by the controller. - * - * @return array */ public function getAttachmentData(): array { @@ -52,8 +48,6 @@ class AttachmentFormRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/BillStoreRequest.php b/app/Http/Requests/BillStoreRequest.php index 1061e7d0d5..124d62aa9f 100644 --- a/app/Http/Requests/BillStoreRequest.php +++ b/app/Http/Requests/BillStoreRequest.php @@ -37,8 +37,6 @@ class BillStoreRequest extends FormRequest /** * Returns the data required by the controller. - * - * @return array */ public function getBillData(): array { @@ -61,8 +59,6 @@ class BillStoreRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/BillUpdateRequest.php b/app/Http/Requests/BillUpdateRequest.php index 7d84ef2c2c..c8189282ed 100644 --- a/app/Http/Requests/BillUpdateRequest.php +++ b/app/Http/Requests/BillUpdateRequest.php @@ -38,8 +38,6 @@ class BillUpdateRequest extends FormRequest /** * Returns the data required by the controller. - * - * @return array */ public function getBillData(): array { @@ -62,8 +60,6 @@ class BillUpdateRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/BudgetFormStoreRequest.php b/app/Http/Requests/BudgetFormStoreRequest.php index 1cd04340e5..1219b791ea 100644 --- a/app/Http/Requests/BudgetFormStoreRequest.php +++ b/app/Http/Requests/BudgetFormStoreRequest.php @@ -40,8 +40,6 @@ class BudgetFormStoreRequest extends FormRequest /** * Returns the data required by the controller. - * - * @return array */ public function getBudgetData(): array { @@ -57,8 +55,6 @@ class BudgetFormStoreRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { @@ -74,15 +70,11 @@ class BudgetFormStoreRequest extends FormRequest /** * 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) { + function (Validator $validator): void { // validate all account info $this->validateAutoBudgetAmount($validator); } diff --git a/app/Http/Requests/BudgetFormUpdateRequest.php b/app/Http/Requests/BudgetFormUpdateRequest.php index 4cf7c48ce4..7c1134a08d 100644 --- a/app/Http/Requests/BudgetFormUpdateRequest.php +++ b/app/Http/Requests/BudgetFormUpdateRequest.php @@ -41,8 +41,6 @@ class BudgetFormUpdateRequest extends FormRequest /** * Returns the data required by the controller. - * - * @return array */ public function getBudgetData(): array { @@ -58,18 +56,16 @@ class BudgetFormUpdateRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { $nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name'; - /** @var Budget|null $budget */ + /** @var null|Budget $budget */ $budget = $this->route()->parameter('budget'); if (null !== $budget) { - $nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name,' . $budget->id; + $nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name,'.$budget->id; } return [ @@ -84,15 +80,11 @@ class BudgetFormUpdateRequest extends FormRequest /** * 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) { + function (Validator $validator): void { // validate all account info $this->validateAutoBudgetAmount($validator); } diff --git a/app/Http/Requests/BudgetIncomeRequest.php b/app/Http/Requests/BudgetIncomeRequest.php index 43ed98c284..a89b818c3f 100644 --- a/app/Http/Requests/BudgetIncomeRequest.php +++ b/app/Http/Requests/BudgetIncomeRequest.php @@ -28,8 +28,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class BudgetIncomeRequest. - * - */ class BudgetIncomeRequest extends FormRequest { @@ -37,8 +35,6 @@ class BudgetIncomeRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/BulkEditJournalRequest.php b/app/Http/Requests/BulkEditJournalRequest.php index 1598e22a79..22b4ad2fc8 100644 --- a/app/Http/Requests/BulkEditJournalRequest.php +++ b/app/Http/Requests/BulkEditJournalRequest.php @@ -37,8 +37,6 @@ class BulkEditJournalRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/CategoryFormRequest.php b/app/Http/Requests/CategoryFormRequest.php index 9be9ad514c..f9b4e5b77c 100644 --- a/app/Http/Requests/CategoryFormRequest.php +++ b/app/Http/Requests/CategoryFormRequest.php @@ -38,8 +38,6 @@ class CategoryFormRequest extends FormRequest /** * Get information for the controller. - * - * @return array */ public function getCategoryData(): array { @@ -51,17 +49,16 @@ class CategoryFormRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { $nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name'; - /** @var Category|null $category */ + + /** @var null|Category $category */ $category = $this->route()->parameter('category'); if (null !== $category) { - $nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name,' . $category->id; + $nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name,'.$category->id; } // fixed diff --git a/app/Http/Requests/ConfigurationRequest.php b/app/Http/Requests/ConfigurationRequest.php index 7517fbf878..7d47107919 100644 --- a/app/Http/Requests/ConfigurationRequest.php +++ b/app/Http/Requests/ConfigurationRequest.php @@ -28,8 +28,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class ConfigurationRequest. - * - */ class ConfigurationRequest extends FormRequest { @@ -37,8 +35,6 @@ class ConfigurationRequest extends FormRequest /** * Returns the data required by the controller. - * - * @return array */ public function getConfigurationData(): array { @@ -50,8 +46,6 @@ class ConfigurationRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/CurrencyFormRequest.php b/app/Http/Requests/CurrencyFormRequest.php index 2b5c3a1eaf..e71d06804c 100644 --- a/app/Http/Requests/CurrencyFormRequest.php +++ b/app/Http/Requests/CurrencyFormRequest.php @@ -38,8 +38,6 @@ class CurrencyFormRequest extends FormRequest /** * Returns the data required by the controller. - * - * @return array */ public function getCurrencyData(): array { @@ -54,8 +52,6 @@ class CurrencyFormRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { @@ -68,7 +64,7 @@ class CurrencyFormRequest extends FormRequest 'enabled' => 'in:0,1', ]; - /** @var TransactionCurrency|null $currency */ + /** @var null|TransactionCurrency $currency */ $currency = $this->route()->parameter('currency'); if (null !== $currency) { diff --git a/app/Http/Requests/DeleteAccountFormRequest.php b/app/Http/Requests/DeleteAccountFormRequest.php index 51d65450ec..884983b50a 100644 --- a/app/Http/Requests/DeleteAccountFormRequest.php +++ b/app/Http/Requests/DeleteAccountFormRequest.php @@ -28,8 +28,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class DeleteAccountFormRequest. - * - */ class DeleteAccountFormRequest extends FormRequest { @@ -37,8 +35,6 @@ class DeleteAccountFormRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/EmailFormRequest.php b/app/Http/Requests/EmailFormRequest.php index 1c7429e84f..23d5d9d24c 100644 --- a/app/Http/Requests/EmailFormRequest.php +++ b/app/Http/Requests/EmailFormRequest.php @@ -29,8 +29,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class EmailFormRequest. - * - */ class EmailFormRequest extends FormRequest { @@ -39,8 +37,6 @@ class EmailFormRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/InviteUserFormRequest.php b/app/Http/Requests/InviteUserFormRequest.php index 23cdfe9300..9cdd8267d6 100644 --- a/app/Http/Requests/InviteUserFormRequest.php +++ b/app/Http/Requests/InviteUserFormRequest.php @@ -38,8 +38,6 @@ class InviteUserFormRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/JournalLinkRequest.php b/app/Http/Requests/JournalLinkRequest.php index 68f2f53d42..2ecc6171e1 100644 --- a/app/Http/Requests/JournalLinkRequest.php +++ b/app/Http/Requests/JournalLinkRequest.php @@ -38,8 +38,6 @@ class JournalLinkRequest extends FormRequest /** * Returns the data required by the controller. - * - * @return array */ public function getLinkInfo(): array { @@ -56,14 +54,13 @@ class JournalLinkRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { // all possible combinations of link types and inward / outward: $combinations = []; $linkTypes = LinkType::get(['id']); + /** @var LinkType $type */ foreach ($linkTypes as $type) { $combinations[] = sprintf('%d_inward', $type->id); diff --git a/app/Http/Requests/LinkTypeFormRequest.php b/app/Http/Requests/LinkTypeFormRequest.php index 2705775ba7..8d65dfc666 100644 --- a/app/Http/Requests/LinkTypeFormRequest.php +++ b/app/Http/Requests/LinkTypeFormRequest.php @@ -37,8 +37,6 @@ class LinkTypeFormRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/MassDeleteJournalRequest.php b/app/Http/Requests/MassDeleteJournalRequest.php index ca81fda9ff..ba44776059 100644 --- a/app/Http/Requests/MassDeleteJournalRequest.php +++ b/app/Http/Requests/MassDeleteJournalRequest.php @@ -28,8 +28,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class MassDeleteJournalRequest. - * - */ class MassDeleteJournalRequest extends FormRequest { @@ -37,8 +35,6 @@ class MassDeleteJournalRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/MassEditJournalRequest.php b/app/Http/Requests/MassEditJournalRequest.php index 37fe8c6037..0f2700d89c 100644 --- a/app/Http/Requests/MassEditJournalRequest.php +++ b/app/Http/Requests/MassEditJournalRequest.php @@ -28,8 +28,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class MassEditJournalRequest. - * - */ class MassEditJournalRequest extends FormRequest { @@ -37,8 +35,6 @@ class MassEditJournalRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/NewUserFormRequest.php b/app/Http/Requests/NewUserFormRequest.php index 5a3f4b4380..4f10def9ef 100644 --- a/app/Http/Requests/NewUserFormRequest.php +++ b/app/Http/Requests/NewUserFormRequest.php @@ -29,8 +29,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class NewUserFormRequest. - * - */ class NewUserFormRequest extends FormRequest { @@ -39,8 +37,6 @@ class NewUserFormRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/ObjectGroupFormRequest.php b/app/Http/Requests/ObjectGroupFormRequest.php index cf77969ebc..7cb795afee 100644 --- a/app/Http/Requests/ObjectGroupFormRequest.php +++ b/app/Http/Requests/ObjectGroupFormRequest.php @@ -38,8 +38,6 @@ class ObjectGroupFormRequest extends FormRequest /** * Returns the data required by the controller. - * - * @return array */ public function getObjectGroupData(): array { @@ -50,12 +48,10 @@ class ObjectGroupFormRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { - /** @var ObjectGroup|null $objectGroup */ + /** @var null|ObjectGroup $objectGroup */ $objectGroup = $this->route()->parameter('objectGroup'); $titleRule = 'required|between:1,255|uniqueObjectGroup'; diff --git a/app/Http/Requests/PiggyBankStoreRequest.php b/app/Http/Requests/PiggyBankStoreRequest.php index 9cb20e0467..01bf5524b8 100644 --- a/app/Http/Requests/PiggyBankStoreRequest.php +++ b/app/Http/Requests/PiggyBankStoreRequest.php @@ -37,8 +37,6 @@ class PiggyBankStoreRequest extends FormRequest /** * Returns the data required by the controller. - * - * @return array */ public function getPiggyBankData(): array { @@ -55,8 +53,6 @@ class PiggyBankStoreRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/PiggyBankUpdateRequest.php b/app/Http/Requests/PiggyBankUpdateRequest.php index 34389570e4..d467552fd7 100644 --- a/app/Http/Requests/PiggyBankUpdateRequest.php +++ b/app/Http/Requests/PiggyBankUpdateRequest.php @@ -38,8 +38,6 @@ class PiggyBankUpdateRequest extends FormRequest /** * Returns the data required by the controller. - * - * @return array */ public function getPiggyBankData(): array { @@ -56,8 +54,6 @@ class PiggyBankUpdateRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/ProfileFormRequest.php b/app/Http/Requests/ProfileFormRequest.php index f643abb3b3..89749b119e 100644 --- a/app/Http/Requests/ProfileFormRequest.php +++ b/app/Http/Requests/ProfileFormRequest.php @@ -28,8 +28,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class ProfileFormRequest. - * - */ class ProfileFormRequest extends FormRequest { @@ -37,8 +35,6 @@ class ProfileFormRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/ReconciliationStoreRequest.php b/app/Http/Requests/ReconciliationStoreRequest.php index ac7215592e..0b095353cb 100644 --- a/app/Http/Requests/ReconciliationStoreRequest.php +++ b/app/Http/Requests/ReconciliationStoreRequest.php @@ -38,8 +38,6 @@ class ReconciliationStoreRequest extends FormRequest /** * Returns the data required by the controller. - * - * @return array */ public function getAll(): array { @@ -63,8 +61,6 @@ class ReconciliationStoreRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/RecurrenceFormRequest.php b/app/Http/Requests/RecurrenceFormRequest.php index f8334b0c84..f68a9a50c5 100644 --- a/app/Http/Requests/RecurrenceFormRequest.php +++ b/app/Http/Requests/RecurrenceFormRequest.php @@ -46,9 +46,9 @@ class RecurrenceFormRequest extends FormRequest /** * Get the data required by the controller. * - * @return array * @throws FireflyException * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function getAll(): array { @@ -94,7 +94,6 @@ class RecurrenceFormRequest extends FormRequest 'weekend' => $this->convertInteger('weekend'), ], ], - ]; // fill in foreign currency data @@ -131,6 +130,7 @@ class RecurrenceFormRequest extends FormRequest // replace category name with a new category: $factory = app(CategoryFactory::class); $factory->setUser(auth()->user()); + /** * @var int $index * @var array $transaction @@ -148,45 +148,8 @@ class RecurrenceFormRequest extends FormRequest return $return; } - /** - * Parses repetition data. - * - * @return array - */ - private function parseRepetitionData(): array - { - $value = $this->convertString('repetition_type'); - $return = [ - 'type' => '', - 'moment' => '', - ]; - - if ('daily' === $value) { - $return['type'] = $value; - } - //monthly,17 - //ndom,3,7 - if (in_array(substr($value, 0, 6), ['yearly', 'weekly'], true)) { - $return['type'] = substr($value, 0, 6); - $return['moment'] = substr($value, 7); - } - if (str_starts_with($value, 'monthly')) { - $return['type'] = substr($value, 0, 7); - $return['moment'] = substr($value, 8); - } - if (str_starts_with($value, 'ndom')) { - $return['type'] = substr($value, 0, 4); - $return['moment'] = substr($value, 5); - } - - return $return; - } - /** * The rules for this request. - * - * @return array - * */ public function rules(): array { @@ -195,7 +158,7 @@ class RecurrenceFormRequest extends FormRequest $rules = [ // mandatory info for recurrence. 'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title', - 'first_date' => 'required|date|after:' . $today->format('Y-m-d'), + 'first_date' => 'required|date|after:'.$today->format('Y-m-d'), 'repetition_type' => ['required', new ValidRecurrenceRepetitionValue(), new ValidRecurrenceRepetitionType(), 'between:1,20'], 'skip' => 'required|numeric|integer|gte:0|lte:31', @@ -239,7 +202,7 @@ class RecurrenceFormRequest extends FormRequest // if ends at date X, set another rule. if ('until_date' === $this->convertString('repetition_end')) { - $rules['repeat_until'] = 'required|date|after:' . $tomorrow->format('Y-m-d'); + $rules['repeat_until'] = 'required|date|after:'.$tomorrow->format('Y-m-d'); } // switch on type to expand rules for source and destination accounts: @@ -259,11 +222,11 @@ class RecurrenceFormRequest extends FormRequest } // update some rules in case the user is editing a post: - /** @var Recurrence|null $recurrence */ + /** @var null|Recurrence $recurrence */ $recurrence = $this->route()->parameter('recurrence'); if ($recurrence instanceof Recurrence) { $rules['id'] = 'required|numeric|exists:recurrences,id'; - $rules['title'] = 'required|between:1,255|uniqueObjectForUser:recurrences,title,' . $recurrence->id; + $rules['title'] = 'required|between:1,255|uniqueObjectForUser:recurrences,title,'.$recurrence->id; $rules['first_date'] = 'required|date'; } @@ -272,15 +235,11 @@ class RecurrenceFormRequest extends FormRequest /** * 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) { + function (Validator $validator): void { // validate all account info $this->validateAccountInformation($validator); } @@ -290,13 +249,12 @@ class RecurrenceFormRequest extends FormRequest /** * Validates the given account information. Switches on given transaction type. * - * @param Validator $validator - * * @throws FireflyException */ public function validateAccountInformation(Validator $validator): void { app('log')->debug('Now in validateAccountInformation (RecurrenceFormRequest)()'); + /** @var AccountValidator $accountValidator */ $accountValidator = app(AccountValidator::class); $data = $validator->getData(); @@ -314,29 +272,29 @@ class RecurrenceFormRequest extends FormRequest $throwError = true; if ('withdrawal' === $type) { $throwError = false; - $sourceId = (int)$data['source_id']; - $destinationId = (int)$data['withdrawal_destination_id']; + $sourceId = (int) $data['source_id']; + $destinationId = (int) $data['withdrawal_destination_id']; } if ('deposit' === $type) { $throwError = false; - $sourceId = (int)$data['deposit_source_id']; - $destinationId = (int)$data['destination_id']; + $sourceId = (int) $data['deposit_source_id']; + $destinationId = (int) $data['destination_id']; } if ('transfer' === $type) { $throwError = false; - $sourceId = (int)$data['source_id']; - $destinationId = (int)$data['destination_id']; + $sourceId = (int) $data['source_id']; + $destinationId = (int) $data['destination_id']; } if (true === $throwError) { throw new FireflyException(sprintf('Cannot handle transaction type "%s"', $this->convertString('transaction_type'))); } // validate source account. - $validSource = $accountValidator->validateSource(['id' => $sourceId,]); + $validSource = $accountValidator->validateSource(['id' => $sourceId]); // do something with result: if (false === $validSource) { - $message = (string)trans('validation.generic_invalid_source'); + $message = (string) trans('validation.generic_invalid_source'); $validator->errors()->add('source_id', $message); $validator->errors()->add('deposit_source_id', $message); @@ -344,12 +302,44 @@ class RecurrenceFormRequest extends FormRequest } // validate destination account - $validDestination = $accountValidator->validateDestination(['id' => $destinationId,]); + $validDestination = $accountValidator->validateDestination(['id' => $destinationId]); // do something with result: if (false === $validDestination) { - $message = (string)trans('validation.generic_invalid_destination'); + $message = (string) trans('validation.generic_invalid_destination'); $validator->errors()->add('destination_id', $message); $validator->errors()->add('withdrawal_destination_id', $message); } } + + /** + * Parses repetition data. + */ + private function parseRepetitionData(): array + { + $value = $this->convertString('repetition_type'); + $return = [ + 'type' => '', + 'moment' => '', + ]; + + if ('daily' === $value) { + $return['type'] = $value; + } + // monthly,17 + // ndom,3,7 + if (in_array(substr($value, 0, 6), ['yearly', 'weekly'], true)) { + $return['type'] = substr($value, 0, 6); + $return['moment'] = substr($value, 7); + } + if (str_starts_with($value, 'monthly')) { + $return['type'] = substr($value, 0, 7); + $return['moment'] = substr($value, 8); + } + if (str_starts_with($value, 'ndom')) { + $return['type'] = substr($value, 0, 4); + $return['moment'] = substr($value, 5); + } + + return $return; + } } diff --git a/app/Http/Requests/ReportFormRequest.php b/app/Http/Requests/ReportFormRequest.php index 2620ca025f..24f316fb1a 100644 --- a/app/Http/Requests/ReportFormRequest.php +++ b/app/Http/Requests/ReportFormRequest.php @@ -43,8 +43,6 @@ class ReportFormRequest extends FormRequest /** * Validate list of accounts. - * - * @return Collection */ public function getAccountList(): Collection { @@ -67,8 +65,6 @@ class ReportFormRequest extends FormRequest /** * Validate list of budgets. - * - * @return Collection */ public function getBudgetList(): Collection { @@ -90,8 +86,6 @@ class ReportFormRequest extends FormRequest /** * Validate list of categories. - * - * @return Collection */ public function getCategoryList(): Collection { @@ -113,8 +107,6 @@ class ReportFormRequest extends FormRequest /** * Validate list of accounts which exist twice in system. - * - * @return Collection */ public function getDoubleList(): Collection { @@ -137,8 +129,6 @@ class ReportFormRequest extends FormRequest /** * Validate end date. * - * @return Carbon - * * @throws FireflyException */ public function getEndDate(): Carbon @@ -151,29 +141,32 @@ class ReportFormRequest extends FormRequest // validate as date // if regex for YYYY-MM-DD: $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][\d]|3[01])$/'; - if (false !== preg_match($pattern, $string)) { + $result = preg_match($pattern, $string); + if (false !== $result && 0 !== $result) { try { $date = new Carbon($parts[1]); - } catch (Exception $e) { // intentional generic exception + } catch (\Exception $e) { // intentional generic exception $error = sprintf('"%s" is not a valid date range: %s', $range, $e->getMessage()); app('log')->error($error); app('log')->error($e->getTraceAsString()); + throw new FireflyException($error, 0, $e); } + return $date; } $error = sprintf('"%s" is not a valid date range: %s', $range, 'invalid format :('); app('log')->error($error); + throw new FireflyException($error, 0); } + return $date; } /** * Validate start date. * - * @return Carbon - * * @throws FireflyException */ public function getStartDate(): Carbon @@ -186,19 +179,23 @@ class ReportFormRequest extends FormRequest // validate as date // if regex for YYYY-MM-DD: $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][\d]|3[01])$/'; - if (false !== preg_match($pattern, $string)) { + $result = preg_match($pattern, $string); + if (false !== $result && 0 !== $result) { try { $date = new Carbon($parts[0]); - } catch (Exception $e) { // intentional generic exception + } catch (\Exception $e) { // intentional generic exception $error = sprintf('"%s" is not a valid date range: %s', $range, $e->getMessage()); app('log')->error($error); app('log')->error($e->getTraceAsString()); + throw new FireflyException($error, 0, $e); } + return $date; } $error = sprintf('"%s" is not a valid date range: %s', $range, 'invalid format :('); app('log')->error($error); + throw new FireflyException($error, 0); } @@ -207,8 +204,6 @@ class ReportFormRequest extends FormRequest /** * Validate list of tags. - * - * @return Collection */ public function getTagList(): Collection { @@ -221,6 +216,7 @@ class ReportFormRequest extends FormRequest } if (!is_array($set)) { app('log')->error(sprintf('Set is not an array! "%s"', $set)); + return $collection; } foreach ($set as $tagTag) { @@ -228,6 +224,7 @@ class ReportFormRequest extends FormRequest $tag = $repository->findByTag($tagTag); if (null !== $tag) { $collection->push($tag); + continue; } $tag = $repository->find((int)$tagTag); @@ -241,8 +238,6 @@ class ReportFormRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/RuleFormRequest.php b/app/Http/Requests/RuleFormRequest.php index 7a3fe63f29..c6314d37c7 100644 --- a/app/Http/Requests/RuleFormRequest.php +++ b/app/Http/Requests/RuleFormRequest.php @@ -40,9 +40,6 @@ class RuleFormRequest extends FormRequest /** * Get all data for controller. - * - * @return array - * */ public function getRuleData(): array { @@ -59,9 +56,69 @@ class RuleFormRequest extends FormRequest ]; } + public static function replaceAmountTrigger(array $array): array + { + // do some sneaky search and replace. + $amountFields = [ + 'amount_is', + 'amount', + 'amount_exactly', + 'amount_less', + 'amount_max', + 'amount_more', + 'amount_min', + 'foreign_amount_is', + 'foreign_amount', + 'foreign_amount_less', + 'foreign_amount_max', + 'foreign_amount_more', + 'foreign_amount_min', + ]; + if (in_array($array['type'], $amountFields, true) && '0' === $array['value']) { + $array['value'] = '0.00'; + } + + return $array; + } + /** - * @return array + * Rules for this request. */ + public function rules(): array + { + $validTriggers = $this->getTriggers(); + $validActions = array_keys(config('firefly.rule-actions')); + + // some actions require text (aka context): + $contextActions = implode(',', config('firefly.context-rule-actions')); + + // some triggers require text (aka context): + $contextTriggers = implode(',', $this->getTriggersWithContext()); + + // initial set of rules: + $rules = [ + 'title' => 'required|between:1,100|uniqueObjectForUser:rules,title', + 'description' => 'between:1,5000|nullable', + 'stop_processing' => 'boolean', + 'rule_group_id' => 'required|belongsToUser:rule_groups', + 'trigger' => 'required|in:store-journal,update-journal', + 'triggers.*.type' => 'required|in:'.implode(',', $validTriggers), + 'triggers.*.value' => sprintf('required_if:triggers.*.type,%s|max:1024|min:1|ruleTriggerValue', $contextTriggers), + 'actions.*.type' => 'required|in:'.implode(',', $validActions), + 'actions.*.value' => sprintf('required_if:actions.*.type,%s|min:0|max:1024|ruleActionValue', $contextActions), + 'strict' => 'in:0,1', + ]; + + /** @var null|Rule $rule */ + $rule = $this->route()->parameter('rule'); + + if (null !== $rule) { + $rules['title'] = 'required|between:1,100|uniqueObjectForUser:rules,title,'.$rule->id; + } + + return $rules; + } + private function getRuleTriggerData(): array { $return = []; @@ -84,38 +141,6 @@ class RuleFormRequest extends FormRequest return $return; } - /** - * @param array $array - * - * @return array - */ - public static function replaceAmountTrigger(array $array): array - { - // do some sneaky search and replace. - $amountFields = [ - 'amount_is', - 'amount', - 'amount_exactly', - 'amount_less', - 'amount_max', - 'amount_more', - 'amount_min', - 'foreign_amount_is', - 'foreign_amount', - 'foreign_amount_less', - 'foreign_amount_max', - 'foreign_amount_more', - 'foreign_amount_min', - ]; - if (in_array($array['type'], $amountFields, true) && '0' === $array['value']) { - $array['value'] = '0.00'; - } - return $array; - } - - /** - * @return array - */ private function getRuleActionData(): array { $return = []; @@ -133,44 +158,4 @@ class RuleFormRequest extends FormRequest return $return; } - - /** - * Rules for this request. - * - * @return array - */ - public function rules(): array - { - $validTriggers = $this->getTriggers(); - $validActions = array_keys(config('firefly.rule-actions')); - - // some actions require text (aka context): - $contextActions = implode(',', config('firefly.context-rule-actions')); - - // some triggers require text (aka context): - $contextTriggers = implode(',', $this->getTriggersWithContext()); - - // initial set of rules: - $rules = [ - 'title' => 'required|between:1,100|uniqueObjectForUser:rules,title', - 'description' => 'between:1,5000|nullable', - 'stop_processing' => 'boolean', - 'rule_group_id' => 'required|belongsToUser:rule_groups', - 'trigger' => 'required|in:store-journal,update-journal', - 'triggers.*.type' => 'required|in:' . implode(',', $validTriggers), - 'triggers.*.value' => sprintf('required_if:triggers.*.type,%s|max:1024|min:1|ruleTriggerValue', $contextTriggers), - 'actions.*.type' => 'required|in:' . implode(',', $validActions), - 'actions.*.value' => sprintf('required_if:actions.*.type,%s|min:0|max:1024|ruleActionValue', $contextActions), - 'strict' => 'in:0,1', - ]; - - /** @var Rule|null $rule */ - $rule = $this->route()->parameter('rule'); - - if (null !== $rule) { - $rules['title'] = 'required|between:1,100|uniqueObjectForUser:rules,title,' . $rule->id; - } - - return $rules; - } } diff --git a/app/Http/Requests/RuleGroupFormRequest.php b/app/Http/Requests/RuleGroupFormRequest.php index 60fbaa41af..ccea91076b 100644 --- a/app/Http/Requests/RuleGroupFormRequest.php +++ b/app/Http/Requests/RuleGroupFormRequest.php @@ -39,8 +39,6 @@ class RuleGroupFormRequest extends FormRequest /** * Get all data for controller. - * - * @return array */ public function getRuleGroupData(): array { @@ -58,18 +56,16 @@ class RuleGroupFormRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { $titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title'; - /** @var RuleGroup|null $ruleGroup */ + /** @var null|RuleGroup $ruleGroup */ $ruleGroup = $this->route()->parameter('ruleGroup'); if (null !== $ruleGroup) { - $titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title,' . $ruleGroup->id; + $titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title,'.$ruleGroup->id; } return [ diff --git a/app/Http/Requests/SelectTransactionsRequest.php b/app/Http/Requests/SelectTransactionsRequest.php index 17f5284ca1..c6641acc98 100644 --- a/app/Http/Requests/SelectTransactionsRequest.php +++ b/app/Http/Requests/SelectTransactionsRequest.php @@ -29,8 +29,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class SelectTransactionsRequest. - * - */ class SelectTransactionsRequest extends FormRequest { @@ -38,8 +36,6 @@ class SelectTransactionsRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { @@ -50,8 +46,8 @@ class SelectTransactionsRequest extends FormRequest $today = today(config('app.timezone'))->addDay()->format('Y-m-d'); return [ - 'start' => 'required|date|after:' . $first, - 'end' => 'required|date|before:' . $today, + 'start' => 'required|date|after:'.$first, + 'end' => 'required|date|before:'.$today, 'accounts' => 'required', 'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts', ]; diff --git a/app/Http/Requests/TagFormRequest.php b/app/Http/Requests/TagFormRequest.php index e631d35232..c2fd77886e 100644 --- a/app/Http/Requests/TagFormRequest.php +++ b/app/Http/Requests/TagFormRequest.php @@ -41,8 +41,6 @@ class TagFormRequest extends FormRequest /** * Get all data for controller. - * - * @return array */ public function collectTagData(): array { @@ -57,19 +55,17 @@ class TagFormRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { $idRule = ''; - /** @var Tag|null $tag */ + /** @var null|Tag $tag */ $tag = $this->route()->parameter('tag'); $tagRule = 'required|max:1024|min:1|uniqueObjectForUser:tags,tag'; if (null !== $tag) { $idRule = 'belongsToUser:tags'; - $tagRule = 'required|max:1024|min:1|uniqueObjectForUser:tags,tag,' . $tag->id; + $tagRule = 'required|max:1024|min:1|uniqueObjectForUser:tags,tag,'.$tag->id; } $rules = [ @@ -77,7 +73,6 @@ class TagFormRequest extends FormRequest 'id' => $idRule, 'description' => 'max:65536|min:1|nullable', 'date' => 'date|nullable', - ]; return Location::requestRules($rules); diff --git a/app/Http/Requests/TestRuleFormRequest.php b/app/Http/Requests/TestRuleFormRequest.php index b5166f1eea..7d215391fb 100644 --- a/app/Http/Requests/TestRuleFormRequest.php +++ b/app/Http/Requests/TestRuleFormRequest.php @@ -29,8 +29,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class TestRuleFormRequest. - * - */ class TestRuleFormRequest extends FormRequest { @@ -40,8 +38,6 @@ class TestRuleFormRequest extends FormRequest /** * Rules for this request. * TODO these rules are not valid anymore. - * - * @return array */ public function rules(): array { @@ -49,7 +45,7 @@ class TestRuleFormRequest extends FormRequest $validTriggers = $this->getTriggers(); return [ - 'rule-trigger.*' => 'required|max:1024|min:1|in:' . implode(',', $validTriggers), + 'rule-trigger.*' => 'required|max:1024|min:1|in:'.implode(',', $validTriggers), 'rule-trigger-value.*' => 'required|max:1024|min:1|ruleTriggerValue', ]; } diff --git a/app/Http/Requests/TokenFormRequest.php b/app/Http/Requests/TokenFormRequest.php index eedf49a616..10df54a479 100644 --- a/app/Http/Requests/TokenFormRequest.php +++ b/app/Http/Requests/TokenFormRequest.php @@ -28,8 +28,6 @@ use Illuminate\Foundation\Http\FormRequest; /** * Class TokenFormRequest. - * - */ class TokenFormRequest extends FormRequest { @@ -37,8 +35,6 @@ class TokenFormRequest extends FormRequest /** * Rules for this request. - * - * @return array */ public function rules(): array { diff --git a/app/Http/Requests/TriggerRecurrenceRequest.php b/app/Http/Requests/TriggerRecurrenceRequest.php index 640c338570..c09c4ad5a0 100644 --- a/app/Http/Requests/TriggerRecurrenceRequest.php +++ b/app/Http/Requests/TriggerRecurrenceRequest.php @@ -1,6 +1,5 @@ startOfDay(); + $this->date = $newDate; + } + /** - * @param AutoBudget $autoBudget - * * @throws FireflyException */ private function handleAutoBudget(AutoBudget $autoBudget): void @@ -158,9 +160,6 @@ class CreateAutoBudgetLimits implements ShouldQueue } /** - * @param AutoBudget $autoBudget - * - * @return bool * @throws FireflyException */ private function isMagicDay(AutoBudget $autoBudget): bool @@ -194,16 +193,10 @@ class CreateAutoBudgetLimits implements ShouldQueue return '01-01' === $value; } + throw new FireflyException(sprintf('isMagicDay() can\'t handle period "%s"', $autoBudget->period)); } - /** - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * - * @return BudgetLimit|null - */ private function findBudgetLimit(Budget $budget, Carbon $start, Carbon $end): ?BudgetLimit { app('log')->debug( @@ -217,16 +210,11 @@ class CreateAutoBudgetLimits implements ShouldQueue ); return $budget->budgetlimits() - ->where('start_date', $start->format('Y-m-d')) - ->where('end_date', $end->format('Y-m-d'))->first(); + ->where('start_date', $start->format('Y-m-d')) + ->where('end_date', $end->format('Y-m-d'))->first() + ; } - /** - * @param AutoBudget $autoBudget - * @param Carbon $start - * @param Carbon $end - * @param string|null $amount - */ private function createBudgetLimit(AutoBudget $autoBudget, Carbon $start, Carbon $end, ?string $amount = null): void { app('log')->debug(sprintf('No budget limit exist. Must create one for auto-budget #%d', $autoBudget->id)); @@ -247,8 +235,6 @@ class CreateAutoBudgetLimits implements ShouldQueue } /** - * @param AutoBudget $autoBudget - * * @throws FireflyException */ private function createRollover(AutoBudget $autoBudget): void @@ -311,11 +297,6 @@ class CreateAutoBudgetLimits implements ShouldQueue app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); } - /** - * @param AutoBudget $autoBudget - * - * @return void - */ private function createAdjustedLimit(AutoBudget $autoBudget): void { app('log')->debug(sprintf('Will now manage rollover for auto budget #%d', $autoBudget->id)); @@ -344,6 +325,7 @@ class CreateAutoBudgetLimits implements ShouldQueue app('log')->debug('No budget limit exists in previous period, so create one.'); // if not, create standard amount, and we're done. $this->createBudgetLimit($autoBudget, $start, $end); + return; } app('log')->debug('Budget limit exists for previous period.'); @@ -363,7 +345,6 @@ class CreateAutoBudgetLimits implements ShouldQueue $totalAmount = $autoBudget->amount; app('log')->debug(sprintf('Total amount available for current budget period is %s', $budgetAvailable)); - if (-1 !== bccomp($budgetAvailable, $totalAmount)) { app('log')->info(sprintf('There is no overspending, no need to adjust. Budget limit amount will be %s.', $budgetAvailable)); // create budget limit: @@ -381,14 +362,4 @@ class CreateAutoBudgetLimits implements ShouldQueue } app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); } - - /** - * @param Carbon $date - */ - public function setDate(Carbon $date): void - { - $newDate = clone $date; - $newDate->startOfDay(); - $this->date = $newDate; - } } diff --git a/app/Jobs/CreateRecurringTransactions.php b/app/Jobs/CreateRecurringTransactions.php index f9852517a7..74eafbaf96 100644 --- a/app/Jobs/CreateRecurringTransactions.php +++ b/app/Jobs/CreateRecurringTransactions.php @@ -45,7 +45,6 @@ use Illuminate\Support\Collection; /** * Class CreateRecurringTransactions. - * */ class CreateRecurringTransactions implements ShouldQueue { @@ -67,9 +66,6 @@ class CreateRecurringTransactions implements ShouldQueue /** * Create a new job instance. - * - * - * @param Carbon|null $date */ public function __construct(?Carbon $date) { @@ -95,9 +91,6 @@ class CreateRecurringTransactions implements ShouldQueue app('log')->debug(sprintf('Created new CreateRecurringTransactions("%s")', $this->date->format('Y-m-d'))); } - /** - * @return Collection - */ public function getGroups(): Collection { return $this->groups; @@ -127,6 +120,7 @@ class CreateRecurringTransactions implements ShouldQueue // filter recurrences: $filtered = $this->filterRecurrences($this->recurrences); app('log')->debug(sprintf('Left after filtering is %d', $filtered->count())); + /** @var Recurrence $recurrence */ foreach ($filtered as $recurrence) { if (!array_key_exists($recurrence->user_id, $result)) { @@ -143,7 +137,7 @@ class CreateRecurringTransactions implements ShouldQueue $createdReps = $this->handleRepetitions($recurrence); app('log')->debug(sprintf('Done with recurrence #%d', $recurrence->id)); $result[$recurrence->user_id] = $result[$recurrence->user_id]->merge($createdReps); - $this->executed++; + ++$this->executed; } app('log')->debug('Now running report thing.'); @@ -158,11 +152,23 @@ class CreateRecurringTransactions implements ShouldQueue app('preferences')->mark(); } - /** - * @param Collection $recurrences - * - * @return Collection - */ + public function setDate(Carbon $date): void + { + $newDate = clone $date; + $newDate->startOfDay(); + $this->date = $newDate; + } + + public function setForce(bool $force): void + { + $this->force = $force; + } + + public function setRecurrences(Collection $recurrences): void + { + $this->recurrences = $recurrences; + } + private function filterRecurrences(Collection $recurrences): Collection { return $recurrences->filter( @@ -174,11 +180,6 @@ class CreateRecurringTransactions implements ShouldQueue /** * Is the info in the recurrence valid? - * - * @param Recurrence $recurrence - * - * @return bool - * */ private function validRecurrence(Recurrence $recurrence): bool { @@ -239,10 +240,6 @@ class CreateRecurringTransactions implements ShouldQueue /** * Return recurring transaction is active. - * - * @param Recurrence $recurrence - * - * @return bool */ private function active(Recurrence $recurrence): bool { @@ -251,10 +248,6 @@ class CreateRecurringTransactions implements ShouldQueue /** * Return true if the $repeat_until date is in the past. - * - * @param Recurrence $recurrence - * - * @return bool */ private function repeatUntilHasPassed(Recurrence $recurrence): bool { @@ -264,10 +257,6 @@ class CreateRecurringTransactions implements ShouldQueue /** * Has the recurrence started yet? - * - * @param Recurrence $recurrence - * - * @return bool */ private function hasNotStartedYet(Recurrence $recurrence): bool { @@ -279,10 +268,6 @@ class CreateRecurringTransactions implements ShouldQueue /** * Get the start date of a recurrence. - * - * @param Recurrence $recurrence - * - * @return Carbon */ private function getStartDate(Recurrence $recurrence): Carbon { @@ -296,10 +281,6 @@ class CreateRecurringTransactions implements ShouldQueue /** * Has the recurrence fired today. - * - * @param Recurrence $recurrence - * - * @return bool */ private function hasFiredToday(Recurrence $recurrence): bool { @@ -310,15 +291,13 @@ class CreateRecurringTransactions implements ShouldQueue * Separate method that will loop all repetitions and do something with it. Will return * all created transaction journals. * - * @param Recurrence $recurrence - * - * @return Collection * @throws DuplicateTransactionException * @throws FireflyException */ private function handleRepetitions(Recurrence $recurrence): Collection { $collection = new Collection(); + /** @var RecurrenceRepetition $repetition */ foreach ($recurrence->recurrenceRepetitions as $repetition) { app('log')->debug( @@ -348,17 +327,13 @@ class CreateRecurringTransactions implements ShouldQueue /** * Check if the occurences should be executed. * - * @param Recurrence $recurrence - * @param RecurrenceRepetition $repetition - * @param array $occurrences - * - * @return Collection * @throws DuplicateTransactionException * @throws FireflyException */ private function handleOccurrences(Recurrence $recurrence, RecurrenceRepetition $repetition, array $occurrences): Collection { $collection = new Collection(); + /** @var Carbon $date */ foreach ($occurrences as $date) { $result = $this->handleOccurrence($recurrence, $repetition, $date); @@ -371,11 +346,6 @@ class CreateRecurringTransactions implements ShouldQueue } /** - * @param Recurrence $recurrence - * @param RecurrenceRepetition $repetition - * @param Carbon $date - * - * @return TransactionGroup|null * @throws DuplicateTransactionException * @throws FireflyException */ @@ -401,7 +371,6 @@ class CreateRecurringTransactions implements ShouldQueue return null; } - if ($journalCount > 0 && true === $this->force) { app('log')->warning(sprintf('Already created %d groups for date %s but FORCED to continue.', $journalCount, $date->format('Y-m-d'))); } @@ -411,7 +380,6 @@ class CreateRecurringTransactions implements ShouldQueue $count = $recurrence->recurrenceTransactions->count(); if ($count > 1) { /** @var RecurrenceTransaction $first */ - $first = $recurrence->recurrenceTransactions()->first(); $groupTitle = $first->description; } @@ -429,7 +397,7 @@ class CreateRecurringTransactions implements ShouldQueue /** @var TransactionGroup $group */ $group = $this->groupRepository->store($array); - $this->created++; + ++$this->created; app('log')->info(sprintf('Created new transaction group #%d', $group->id)); // trigger event: @@ -445,13 +413,6 @@ class CreateRecurringTransactions implements ShouldQueue /** * Get transaction information from a recurring transaction. - * - * @param Recurrence $recurrence - * @param RecurrenceRepetition $repetition - * @param Carbon $date - * - * @return array - * */ private function getTransactionData(Recurrence $recurrence, RecurrenceRepetition $repetition, Carbon $date): array { @@ -459,9 +420,11 @@ class CreateRecurringTransactions implements ShouldQueue $total = $this->repository->totalTransactions($recurrence, $repetition); $count = $this->repository->getJournalCount($recurrence) + 1; $transactions = $recurrence->recurrenceTransactions()->get(); + /** @var RecurrenceTransaction $first */ $first = $transactions->first(); $return = []; + /** @var RecurrenceTransaction $transaction */ foreach ($transactions as $index => $transaction) { $single = [ @@ -502,30 +465,4 @@ class CreateRecurringTransactions implements ShouldQueue return $return; } - - /** - * @param Carbon $date - */ - public function setDate(Carbon $date): void - { - $newDate = clone $date; - $newDate->startOfDay(); - $this->date = $newDate; - } - - /** - * @param bool $force - */ - public function setForce(bool $force): void - { - $this->force = $force; - } - - /** - * @param Collection $recurrences - */ - public function setRecurrences(Collection $recurrences): void - { - $this->recurrences = $recurrences; - } } diff --git a/app/Jobs/DownloadExchangeRates.php b/app/Jobs/DownloadExchangeRates.php index ae0d3ebc9a..524faaf83f 100644 --- a/app/Jobs/DownloadExchangeRates.php +++ b/app/Jobs/DownloadExchangeRates.php @@ -55,9 +55,6 @@ class DownloadExchangeRates implements ShouldQueue /** * Create a new job instance. - * - * - * @param Carbon|null $date */ public function __construct(?Carbon $date) { @@ -91,10 +88,14 @@ class DownloadExchangeRates implements ShouldQueue } } + public function setDate(Carbon $date): void + { + $newDate = clone $date; + $newDate->startOfDay(); + $this->date = $newDate; + } + /** - * @param TransactionCurrency $currency - * - * @return void * @throws GuzzleException */ private function downloadRates(TransactionCurrency $currency): void @@ -103,21 +104,25 @@ class DownloadExchangeRates implements ShouldQueue $base = sprintf('%s/%s/%s', (string)config('cer.url'), $this->date->year, $this->date->isoWeek); $client = new Client(); $url = sprintf('%s/%s.json', $base, $currency->code); + try { $res = $client->get($url); } catch (RequestException $e) { app('log')->warning(sprintf('Trying to grab "%s" resulted in error "%d".', $url, $e->getMessage())); + return; } $statusCode = $res->getStatusCode(); if (200 !== $statusCode) { app('log')->warning(sprintf('Trying to grab "%s" resulted in status code %d.', $url, $statusCode)); + return; } $body = (string)$res->getBody(); $json = json_decode($body, true); if (false === $json || null === $json) { app('log')->warning(sprintf('Trying to grab "%s" resulted in bad JSON.', $url)); + return; } $date = Carbon::createFromFormat('Y-m-d', $json['date'], config('app.timezone')); @@ -127,19 +132,13 @@ class DownloadExchangeRates implements ShouldQueue $this->saveRates($currency, $date, $json['rates']); } - /** - * @param TransactionCurrency $currency - * @param Carbon $date - * @param array $rates - * - * @return void - */ private function saveRates(TransactionCurrency $currency, Carbon $date, array $rates): void { foreach ($rates as $code => $rate) { $to = $this->getCurrency($code); if (null === $to) { app('log')->debug(sprintf('Currency %s is not in use, do not save rate.', $code)); + continue; } app('log')->debug(sprintf('Currency %s is in use.', $code)); @@ -147,16 +146,12 @@ class DownloadExchangeRates implements ShouldQueue } } - /** - * @param string $code - * - * @return TransactionCurrency|null - */ private function getCurrency(string $code): ?TransactionCurrency { // if we have it already, don't bother searching for it again. if (array_key_exists($code, $this->active)) { app('log')->debug(sprintf('Already know what the result is of searching for %s', $code)); + return $this->active[$code]; } // find it in the database. @@ -164,11 +159,13 @@ class DownloadExchangeRates implements ShouldQueue if (null === $currency) { app('log')->debug(sprintf('Did not find currency %s.', $code)); $this->active[$code] = null; + return null; } if (false === $currency->enabled) { app('log')->debug(sprintf('Currency %s is not enabled.', $code)); $this->active[$code] = null; + return null; } app('log')->debug(sprintf('Currency %s is enabled.', $code)); @@ -177,14 +174,6 @@ class DownloadExchangeRates implements ShouldQueue return $currency; } - /** - * @param TransactionCurrency $from - * @param TransactionCurrency $to - * @param Carbon $date - * @param float $rate - * - * @return void - */ private function saveRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date, float $rate): void { foreach ($this->users as $user) { @@ -196,14 +185,4 @@ class DownloadExchangeRates implements ShouldQueue } } } - - /** - * @param Carbon $date - */ - public function setDate(Carbon $date): void - { - $newDate = clone $date; - $newDate->startOfDay(); - $this->date = $newDate; - } } diff --git a/app/Jobs/MailError.php b/app/Jobs/MailError.php index 9d48ce949e..c06a617d03 100644 --- a/app/Jobs/MailError.php +++ b/app/Jobs/MailError.php @@ -23,18 +23,14 @@ declare(strict_types=1); namespace FireflyIII\Jobs; -use Exception; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Message; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use Mail; use Symfony\Component\Mailer\Exception\TransportException; /** * Class MailError. - * - */ class MailError extends Job implements ShouldQueue { @@ -48,11 +44,6 @@ class MailError extends Job implements ShouldQueue /** * MailError constructor. - * - * @param array $userData - * @param string $destination - * @param string $ipAddress - * @param array $exceptionData */ public function __construct(array $userData, string $destination, string $ipAddress, array $exceptionData) { @@ -61,14 +52,13 @@ class MailError extends Job implements ShouldQueue $this->ipAddress = $ipAddress; $this->exception = $exceptionData; $debug = $exceptionData; - unset($debug['stackTrace']); - unset($debug['headers']); + unset($debug['stackTrace'], $debug['headers']); + app('log')->error(sprintf('Exception is: %s', json_encode($debug))); } /** * Execute the job. - * */ public function handle(): void { @@ -78,25 +68,27 @@ class MailError extends Job implements ShouldQueue $args['user'] = $this->userData; $args['ip'] = $this->ipAddress; $args['token'] = config('firefly.ipinfo_token'); - if ($this->attempts() < 3 && $email !== '') { + if ($this->attempts() < 3 && '' !== $email) { try { - Mail::send( + \Mail::send( ['emails.error-html', 'emails.error-text'], $args, - static function (Message $message) use ($email) { + static function (Message $message) use ($email): void { if ('mail@example.com' !== $email) { $message->to($email, $email)->subject((string)trans('email.error_subject')); } } ); - } catch (Exception | TransportException $e) { // @phpstan-ignore-line + } catch (\Exception|TransportException $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { app('log')->warning('[Bcc] Could not email or log the error. Please validate your email settings, use the .env.example file as a guide.'); + return; } if (str_contains($message, 'RFC 2822')) { app('log')->warning('[RFC] Could not email or log the error. Please validate your email settings, use the .env.example file as a guide.'); + return; } app('log')->error($e->getMessage()); diff --git a/app/Jobs/SendWebhookMessage.php b/app/Jobs/SendWebhookMessage.php index 3539d74ed9..b656b58df3 100644 --- a/app/Jobs/SendWebhookMessage.php +++ b/app/Jobs/SendWebhookMessage.php @@ -46,8 +46,6 @@ class SendWebhookMessage implements ShouldQueue /** * Create a new job instance. - * - * @param WebhookMessage $message */ public function __construct(WebhookMessage $message) { @@ -56,8 +54,6 @@ class SendWebhookMessage implements ShouldQueue /** * Execute the job. - * - * @return void */ public function handle(): void { diff --git a/app/Jobs/WarnAboutBills.php b/app/Jobs/WarnAboutBills.php index 31fdcf0599..a127da6888 100644 --- a/app/Jobs/WarnAboutBills.php +++ b/app/Jobs/WarnAboutBills.php @@ -48,9 +48,6 @@ class WarnAboutBills implements ShouldQueue /** * Create a new job instance. - * - * - * @param Carbon|null $date */ public function __construct(?Carbon $date) { @@ -76,6 +73,7 @@ class WarnAboutBills implements ShouldQueue { app('log')->debug(sprintf('Now at start of WarnAboutBills() job for %s.', $this->date->format('D d M Y'))); $bills = Bill::all(); + /** @var Bill $bill */ foreach ($bills as $bill) { app('log')->debug(sprintf('Now checking bill #%d ("%s")', $bill->id, $bill->name)); @@ -94,73 +92,6 @@ class WarnAboutBills implements ShouldQueue app('preferences')->mark(); } - /** - * @param Bill $bill - * - * @return bool - */ - private function hasDateFields(Bill $bill): bool - { - if (false === $bill->active) { - app('log')->debug('Bill is not active.'); - return false; - } - if (null === $bill->end_date && null === $bill->extension_date) { - app('log')->debug('Bill has no date fields.'); - return false; - } - return true; - } - - /** - * @param Bill $bill - * @param string $field - * - * @return bool - */ - private function needsWarning(Bill $bill, string $field): bool - { - if (null === $bill->$field) { - return false; - } - $diff = $this->getDiff($bill, $field); - $list = config('firefly.bill_reminder_periods'); - app('log')->debug(sprintf('Difference in days for field "%s" ("%s") is %d day(s)', $field, $bill->$field->format('Y-m-d'), $diff)); - if (in_array($diff, $list, true)) { - return true; - } - return false; - } - - /** - * @param Bill $bill - * @param string $field - * - * @return int - */ - private function getDiff(Bill $bill, string $field): int - { - $today = clone $this->date; - $carbon = clone $bill->$field; - return $today->diffInDays($carbon, false); - } - - /** - * @param Bill $bill - * @param string $field - * - * @return void - */ - private function sendWarning(Bill $bill, string $field): void - { - $diff = $this->getDiff($bill, $field); - app('log')->debug('Will now send warning!'); - event(new WarnUserAboutBill($bill, $field, $diff)); - } - - /** - * @param Carbon $date - */ public function setDate(Carbon $date): void { $newDate = clone $date; @@ -168,11 +99,54 @@ class WarnAboutBills implements ShouldQueue $this->date = $newDate; } - /** - * @param bool $force - */ public function setForce(bool $force): void { $this->force = $force; } + + private function hasDateFields(Bill $bill): bool + { + if (false === $bill->active) { + app('log')->debug('Bill is not active.'); + + return false; + } + if (null === $bill->end_date && null === $bill->extension_date) { + app('log')->debug('Bill has no date fields.'); + + return false; + } + + return true; + } + + private function needsWarning(Bill $bill, string $field): bool + { + if (null === $bill->{$field}) { + return false; + } + $diff = $this->getDiff($bill, $field); + $list = config('firefly.bill_reminder_periods'); + app('log')->debug(sprintf('Difference in days for field "%s" ("%s") is %d day(s)', $field, $bill->{$field}->format('Y-m-d'), $diff)); + if (in_array($diff, $list, true)) { + return true; + } + + return false; + } + + private function getDiff(Bill $bill, string $field): int + { + $today = clone $this->date; + $carbon = clone $bill->{$field}; + + return $today->diffInDays($carbon, false); + } + + private function sendWarning(Bill $bill, string $field): void + { + $diff = $this->getDiff($bill, $field); + app('log')->debug('Will now send warning!'); + event(new WarnUserAboutBill($bill, $field, $diff)); + } } diff --git a/app/Mail/AccessTokenCreatedMail.php b/app/Mail/AccessTokenCreatedMail.php index 6a4f1db4bf..e908864838 100644 --- a/app/Mail/AccessTokenCreatedMail.php +++ b/app/Mail/AccessTokenCreatedMail.php @@ -29,8 +29,6 @@ use Illuminate\Queue\SerializesModels; /** * Class AccessTokenCreatedMail - * - */ class AccessTokenCreatedMail extends Mailable { @@ -51,6 +49,7 @@ class AccessTokenCreatedMail extends Mailable { return $this ->markdown('emails.token-created') - ->subject((string)trans('email.access_token_created_subject')); + ->subject((string)trans('email.access_token_created_subject')) + ; } } diff --git a/app/Mail/AdminTestMail.php b/app/Mail/AdminTestMail.php index e0861e5999..1971dce7dc 100644 --- a/app/Mail/AdminTestMail.php +++ b/app/Mail/AdminTestMail.php @@ -31,8 +31,6 @@ use Illuminate\Queue\SerializesModels; * Class AdminTestMail. * * Sends a test mail to administrators. - * - */ class AdminTestMail extends Mailable { @@ -55,6 +53,7 @@ class AdminTestMail extends Mailable { return $this ->markdown('emails.admin-test') - ->subject((string)trans('email.admin_test_subject')); + ->subject((string)trans('email.admin_test_subject')) + ; } } diff --git a/app/Mail/BillWarningMail.php b/app/Mail/BillWarningMail.php index e1811e9ebc..588772148f 100644 --- a/app/Mail/BillWarningMail.php +++ b/app/Mail/BillWarningMail.php @@ -43,10 +43,6 @@ class BillWarningMail extends Mailable /** * ConfirmEmailChangeMail constructor. - * - * @param Bill $bill - * @param string $field - * @param int $diff */ public function __construct(Bill $bill, string $field, int $diff) { @@ -69,6 +65,7 @@ class BillWarningMail extends Mailable return $this ->markdown('emails.bill-warning') - ->subject($subject); + ->subject($subject) + ; } } diff --git a/app/Mail/ConfirmEmailChangeMail.php b/app/Mail/ConfirmEmailChangeMail.php index 452ce6e4e7..d4346eff6c 100644 --- a/app/Mail/ConfirmEmailChangeMail.php +++ b/app/Mail/ConfirmEmailChangeMail.php @@ -31,8 +31,6 @@ use Illuminate\Queue\SerializesModels; * Class ConfirmEmailChangeMail * * Sends message to new address to confirm change. - * - */ class ConfirmEmailChangeMail extends Mailable { @@ -45,10 +43,6 @@ class ConfirmEmailChangeMail extends Mailable /** * ConfirmEmailChangeMail constructor. - * - * @param string $newEmail - * @param string $oldEmail - * @param string $url */ public function __construct(string $newEmail, string $oldEmail, string $url) { @@ -65,9 +59,10 @@ class ConfirmEmailChangeMail extends Mailable public function build(): self { return $this - //->view('emails.confirm-email-change-html') - //->text('emails.confirm-email-change-text') + // ->view('emails.confirm-email-change-html') + // ->text('emails.confirm-email-change-text') ->markdown('emails.confirm-email-change') - ->subject((string)trans('email.email_change_subject')); + ->subject((string)trans('email.email_change_subject')) + ; } } diff --git a/app/Mail/InvitationMail.php b/app/Mail/InvitationMail.php index f26184547b..151e7d423a 100644 --- a/app/Mail/InvitationMail.php +++ b/app/Mail/InvitationMail.php @@ -43,10 +43,6 @@ class InvitationMail extends Mailable /** * OAuthTokenCreatedMail constructor. - * - * @param string $invitee - * @param string $admin - * @param string $url */ public function __construct(string $invitee, string $admin, string $url) { @@ -65,6 +61,7 @@ class InvitationMail extends Mailable { return $this ->markdown('emails.invitation') - ->subject((string)trans('email.invite_user_subject')); + ->subject((string)trans('email.invite_user_subject')) + ; } } diff --git a/app/Mail/NewIPAddressWarningMail.php b/app/Mail/NewIPAddressWarningMail.php index a9da5a0ebb..e8fdf5ea86 100644 --- a/app/Mail/NewIPAddressWarningMail.php +++ b/app/Mail/NewIPAddressWarningMail.php @@ -43,8 +43,6 @@ class NewIPAddressWarningMail extends Mailable /** * OAuthTokenCreatedMail constructor. - * - * @param string $ipAddress */ public function __construct(string $ipAddress) { @@ -60,6 +58,7 @@ class NewIPAddressWarningMail extends Mailable { $this->time = now(config('app.timezone'))->isoFormat((string)trans('config.date_time_js')); $this->host = ''; + try { $hostName = app('steam')->getHostName($this->ipAddress); } catch (FireflyException $e) { @@ -72,6 +71,7 @@ class NewIPAddressWarningMail extends Mailable return $this ->markdown('emails.new-ip') - ->subject((string)trans('email.login_from_new_ip')); + ->subject((string)trans('email.login_from_new_ip')) + ; } } diff --git a/app/Mail/OAuthTokenCreatedMail.php b/app/Mail/OAuthTokenCreatedMail.php index 2c75fa3d36..9ccb95942d 100644 --- a/app/Mail/OAuthTokenCreatedMail.php +++ b/app/Mail/OAuthTokenCreatedMail.php @@ -30,8 +30,6 @@ use Laravel\Passport\Client; /** * Class OAuthTokenCreatedMail - * - */ class OAuthTokenCreatedMail extends Mailable { @@ -42,8 +40,6 @@ class OAuthTokenCreatedMail extends Mailable /** * OAuthTokenCreatedMail constructor. - * - * @param Client $client */ public function __construct(Client $client) { @@ -59,6 +55,7 @@ class OAuthTokenCreatedMail extends Mailable { return $this ->markdown('emails.oauth-client-created') - ->subject((string)trans('email.oauth_created_subject')); + ->subject((string)trans('email.oauth_created_subject')) + ; } } diff --git a/app/Mail/RegisteredUser.php b/app/Mail/RegisteredUser.php index d460047eed..8659c8e3f9 100644 --- a/app/Mail/RegisteredUser.php +++ b/app/Mail/RegisteredUser.php @@ -31,8 +31,6 @@ use Illuminate\Queue\SerializesModels; * Sends newly registered user an email message. * * Class RegisteredUser - * - */ class RegisteredUser extends Mailable { @@ -43,8 +41,6 @@ class RegisteredUser extends Mailable /** * Create a new message instance. - * - * @param string $address */ public function __construct(string $address) { @@ -60,6 +56,7 @@ class RegisteredUser extends Mailable { return $this ->markdown('emails.registered') - ->subject((string)trans('email.registered_subject')); + ->subject((string)trans('email.registered_subject')) + ; } } diff --git a/app/Mail/ReportNewJournalsMail.php b/app/Mail/ReportNewJournalsMail.php index 24820ab5b7..ff7515ff68 100644 --- a/app/Mail/ReportNewJournalsMail.php +++ b/app/Mail/ReportNewJournalsMail.php @@ -35,8 +35,6 @@ use Illuminate\Support\Collection; * Class ReportNewJournalsMail. * * Sends a list of newly created journals to the user. - * - */ class ReportNewJournalsMail extends Mailable { @@ -48,8 +46,6 @@ class ReportNewJournalsMail extends Mailable /** * ConfirmEmailChangeMail constructor. - * - * @param Collection $groups */ public function __construct(Collection $groups) { @@ -67,11 +63,11 @@ class ReportNewJournalsMail extends Mailable return $this ->markdown('emails.report-new-journals') - ->subject(trans_choice('email.new_journals_subject', $this->groups->count())); + ->subject(trans_choice('email.new_journals_subject', $this->groups->count())) + ; } /** - * @return void * @throws FireflyException */ private function transform(): void diff --git a/app/Mail/RequestedNewPassword.php b/app/Mail/RequestedNewPassword.php index f91c84ceef..0be46c5251 100644 --- a/app/Mail/RequestedNewPassword.php +++ b/app/Mail/RequestedNewPassword.php @@ -30,8 +30,6 @@ use Illuminate\Queue\SerializesModels; /** * Sends user link for new password. * Class RequestedNewPassword - * - */ class RequestedNewPassword extends Mailable { @@ -42,8 +40,6 @@ class RequestedNewPassword extends Mailable /** * RequestedNewPassword constructor. - * - * @param string $url */ public function __construct(string $url) { @@ -59,6 +55,7 @@ class RequestedNewPassword extends Mailable { return $this ->markdown('emails.password') - ->subject((string)trans('email.reset_pw_subject')); + ->subject((string)trans('email.reset_pw_subject')) + ; } } diff --git a/app/Mail/UndoEmailChangeMail.php b/app/Mail/UndoEmailChangeMail.php index 6f6a202997..70d243685a 100644 --- a/app/Mail/UndoEmailChangeMail.php +++ b/app/Mail/UndoEmailChangeMail.php @@ -29,8 +29,6 @@ use Illuminate\Queue\SerializesModels; /** * Class UndoEmailChangeMail - * - */ class UndoEmailChangeMail extends Mailable { @@ -43,10 +41,6 @@ class UndoEmailChangeMail extends Mailable /** * UndoEmailChangeMail constructor. - * - * @param string $newEmail - * @param string $oldEmail - * @param string $url */ public function __construct(string $newEmail, string $oldEmail, string $url) { @@ -64,6 +58,7 @@ class UndoEmailChangeMail extends Mailable { return $this ->markdown('emails.undo-email-change') - ->subject((string)trans('email.email_change_subject')); + ->subject((string)trans('email.email_change_subject')) + ; } } diff --git a/app/Models/Account.php b/app/Models/Account.php index 376090edf7..b1a7a7b3b0 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -44,40 +44,41 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class Account * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $user_id - * @property int $account_type_id - * @property string $name - * @property string $virtual_balance - * @property string|null $iban - * @property bool $active - * @property bool $encrypted - * @property int $order - * @property-read Collection|AccountMeta[] $accountMeta - * @property-read int|null $account_meta_count - * @property AccountType $accountType - * @property-read Collection|Attachment[] $attachments - * @property-read int|null $attachments_count - * @property-read string $account_number - * @property-read string $edit_name - * @property-read Collection|Location[] $locations - * @property-read int|null $locations_count - * @property-read Collection|Note[] $notes - * @property-read int|null $notes_count - * @property-read Collection|ObjectGroup[] $objectGroups - * @property-read int|null $object_groups_count - * @property-read Collection|PiggyBank[] $piggyBanks - * @property-read int|null $piggy_banks_count - * @property-read Collection|Transaction[] $transactions - * @property-read int|null $transactions_count - * @property-read User $user + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $user_id + * @property int $account_type_id + * @property string $name + * @property string $virtual_balance + * @property null|string $iban + * @property bool $active + * @property bool $encrypted + * @property int $order + * @property AccountMeta[]|Collection $accountMeta + * @property null|int $account_meta_count + * @property AccountType $accountType + * @property Attachment[]|Collection $attachments + * @property null|int $attachments_count + * @property string $account_number + * @property string $edit_name + * @property Collection|Location[] $locations + * @property null|int $locations_count + * @property Collection|Note[] $notes + * @property null|int $notes_count + * @property Collection|ObjectGroup[] $objectGroups + * @property null|int $object_groups_count + * @property Collection|PiggyBank[] $piggyBanks + * @property null|int $piggy_banks_count + * @property Collection|Transaction[] $transactions + * @property null|int $transactions_count + * @property User $user + * * @method static EloquentBuilder|Account accountTypeIn($types) * @method static EloquentBuilder|Account newModelQuery() * @method static EloquentBuilder|Account newQuery() - * @method static Builder|Account onlyTrashed() + * @method static Builder|Account onlyTrashed() * @method static EloquentBuilder|Account query() * @method static EloquentBuilder|Account whereAccountTypeId($value) * @method static EloquentBuilder|Account whereActive($value) @@ -91,21 +92,25 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static EloquentBuilder|Account whereUpdatedAt($value) * @method static EloquentBuilder|Account whereUserId($value) * @method static EloquentBuilder|Account whereVirtualBalance($value) - * @method static Builder|Account withTrashed() - * @method static Builder|Account withoutTrashed() - * @property Carbon $lastActivityDate - * @property string $startBalance - * @property string $endBalance - * @property string $difference - * @property string $interest - * @property string $interestPeriod - * @property string $accountTypeString - * @property Location $location - * @property string $liability_direction - * @property string $current_debt - * @property int $user_group_id + * @method static Builder|Account withTrashed() + * @method static Builder|Account withoutTrashed() + * + * @property Carbon $lastActivityDate + * @property string $startBalance + * @property string $endBalance + * @property string $difference + * @property string $interest + * @property string $interestPeriod + * @property string $accountTypeString + * @property Location $location + * @property string $liability_direction + * @property string $current_debt + * @property int $user_group_id + * * @method static EloquentBuilder|Account whereUserGroupId($value) - * @property-read UserGroup|null $userGroup + * + * @property null|UserGroup $userGroup + * * @mixin Eloquent */ class Account extends Model @@ -133,45 +138,36 @@ class Account extends Model /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return Account * @throws NotFoundHttpException */ public static function routeBinder(string $value): self { if (auth()->check()) { $accountId = (int)$value; + /** @var User $user */ $user = auth()->user(); - /** @var Account|null $account */ + + /** @var null|Account $account */ $account = $user->accounts()->with(['accountType'])->find($accountId); if (null !== $account) { return $account; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); } - /** - * @return BelongsTo - */ public function accountType(): BelongsTo { return $this->belongsTo(AccountType::class); } - /** - * @return MorphMany - */ public function attachments(): MorphMany { return $this->morphMany(Attachment::class, 'attachable'); @@ -179,30 +175,23 @@ class Account extends Model /** * Get the account number. - * - * @return string */ public function getAccountNumberAttribute(): string { - /** @var AccountMeta|null $metaValue */ + /** @var null|AccountMeta $metaValue */ $metaValue = $this->accountMeta() - ->where('name', 'account_number') - ->first(); + ->where('name', 'account_number') + ->first() + ; return null !== $metaValue ? $metaValue->data : ''; } - /** - * @return HasMany - */ public function accountMeta(): HasMany { return $this->hasMany(AccountMeta::class); } - /** - * @return string - */ public function getEditNameAttribute(): string { $name = $this->name; @@ -214,9 +203,6 @@ class Account extends Model return $name; } - /** - * @return MorphMany - */ public function locations(): MorphMany { return $this->morphMany(Location::class, 'locatable'); @@ -238,19 +224,11 @@ class Account extends Model return $this->morphToMany(ObjectGroup::class, 'object_groupable'); } - /** - * @return HasMany - */ public function piggyBanks(): HasMany { return $this->hasMany(PiggyBank::class); } - /** - * - * @param EloquentBuilder $query - * @param array $types - */ public function scopeAccountTypeIn(EloquentBuilder $query, array $types): void { if (false === $this->joinedAccountTypes) { @@ -260,12 +238,6 @@ class Account extends Model $query->whereIn('account_types.type', $types); } - /** - * - * @param mixed $value - * - - */ public function setVirtualBalanceAttribute(mixed $value): void { $value = (string)$value; @@ -275,64 +247,47 @@ class Account extends Model $this->attributes['virtual_balance'] = $value; } - /** - * @return HasMany - */ public function transactions(): HasMany { return $this->hasMany(Transaction::class); } - /** - * @return BelongsTo - */ public function userGroup(): BelongsTo { return $this->belongsTo(UserGroup::class); } - /** - * @return Attribute - */ protected function accountId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } /** * Get the user ID - * - * @return Attribute */ protected function accountTypeId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } /** * Get the virtual balance - * - * @return Attribute */ protected function virtualBalance(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } - } diff --git a/app/Models/AccountMeta.php b/app/Models/AccountMeta.php index 95f309ad8b..54b7b93f8b 100644 --- a/app/Models/AccountMeta.php +++ b/app/Models/AccountMeta.php @@ -33,13 +33,14 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; /** * Class AccountMeta * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property int $account_id - * @property string $name - * @property mixed $data - * @property-read Account $account + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property int $account_id + * @property string $name + * @property mixed $data + * @property Account $account + * * @method static Builder|AccountMeta newModelQuery() * @method static Builder|AccountMeta newQuery() * @method static Builder|AccountMeta query() @@ -49,6 +50,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; * @method static Builder|AccountMeta whereId($value) * @method static Builder|AccountMeta whereName($value) * @method static Builder|AccountMeta whereUpdatedAt($value) + * * @mixin Eloquent */ class AccountMeta extends Model @@ -62,33 +64,22 @@ class AccountMeta extends Model ]; protected $fillable = ['account_id', 'name', 'data']; + /** @var string The table to store the data in */ protected $table = 'account_meta'; - /** - * @return BelongsTo - */ public function account(): BelongsTo { return $this->belongsTo(Account::class); } - /** - * @param mixed $value - * - * @return string - */ public function getDataAttribute(mixed $value): string { return (string)json_decode($value, true); } - /** - * @param mixed $value - */ public function setDataAttribute(mixed $value): void { $this->attributes['data'] = json_encode($value); } - } diff --git a/app/Models/AccountType.php b/app/Models/AccountType.php index 09b279ff8b..2ccfe765b9 100644 --- a/app/Models/AccountType.php +++ b/app/Models/AccountType.php @@ -34,12 +34,13 @@ use Illuminate\Database\Eloquent\Relations\HasMany; /** * FireflyIII\Models\AccountType * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property string $type - * @property-read Collection|Account[] $accounts - * @property-read int|null $accounts_count + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property string $type + * @property Account[]|Collection $accounts + * @property null|int $accounts_count + * * @method static Builder|AccountType newModelQuery() * @method static Builder|AccountType newQuery() * @method static Builder|AccountType query() @@ -47,6 +48,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; * @method static Builder|AccountType whereId($value) * @method static Builder|AccountType whereType($value) * @method static Builder|AccountType whereUpdatedAt($value) + * * @mixin Eloquent */ class AccountType extends Model @@ -68,7 +70,6 @@ class AccountType extends Model public const string RECONCILIATION = 'Reconciliation account'; public const string REVENUE = 'Revenue account'; - protected $casts = [ 'created_at' => 'datetime', @@ -77,13 +78,8 @@ class AccountType extends Model protected $fillable = ['type']; - /** - * @return HasMany - */ public function accounts(): HasMany { return $this->hasMany(Account::class); } - - } diff --git a/app/Models/Attachment.php b/app/Models/Attachment.php index a22246ebb5..d4493d324c 100644 --- a/app/Models/Attachment.php +++ b/app/Models/Attachment.php @@ -41,29 +41,30 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\Attachment * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $user_id - * @property int $attachable_id - * @property string $attachable_type - * @property bool $file_exists - * @property string $md5 - * @property string $filename - * @property string|null $title - * @property string|null $description - * @property string $mime - * @property int|string $size - * @property bool $uploaded - * @property string $notes_text - * @property-read Model|Eloquent $attachable - * @property Collection|Note[] $notes - * @property-read int|null $notes_count - * @property-read User $user + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $user_id + * @property int $attachable_id + * @property string $attachable_type + * @property bool $file_exists + * @property string $md5 + * @property string $filename + * @property null|string $title + * @property null|string $description + * @property string $mime + * @property int|string $size + * @property bool $uploaded + * @property string $notes_text + * @property \Eloquent|Model $attachable + * @property Collection|Note[] $notes + * @property null|int $notes_count + * @property User $user + * * @method static \Illuminate\Database\Eloquent\Builder|Attachment newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|Attachment newQuery() - * @method static Builder|Attachment onlyTrashed() + * @method static Builder|Attachment onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Attachment query() * @method static \Illuminate\Database\Eloquent\Builder|Attachment whereAttachableId($value) * @method static \Illuminate\Database\Eloquent\Builder|Attachment whereAttachableType($value) @@ -79,10 +80,13 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|Attachment whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Attachment whereUploaded($value) * @method static \Illuminate\Database\Eloquent\Builder|Attachment whereUserId($value) - * @method static Builder|Attachment withTrashed() - * @method static Builder|Attachment withoutTrashed() - * @property int $user_group_id + * @method static Builder|Attachment withTrashed() + * @method static Builder|Attachment withoutTrashed() + * + * @property int $user_group_id + * * @method static \Illuminate\Database\Eloquent\Builder|Attachment whereUserGroupId($value) + * * @mixin Eloquent */ class Attachment extends Model @@ -104,29 +108,26 @@ class Attachment extends Model /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return Attachment * @throws NotFoundHttpException */ public static function routeBinder(string $value): self { if (auth()->check()) { $attachmentId = (int)$value; + /** @var User $user */ $user = auth()->user(); - /** @var Attachment|null $attachment */ + + /** @var null|Attachment $attachment */ $attachment = $user->attachments()->find($attachmentId); if (null !== $attachment) { return $attachment; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); @@ -134,9 +135,6 @@ class Attachment extends Model /** * Get all of the owning attachable models. - * - * - * @return MorphTo */ public function attachable(): MorphTo { @@ -145,8 +143,6 @@ class Attachment extends Model /** * Returns the expected filename for this attachment. - * - * @return string */ public function fileName(): string { @@ -161,14 +157,10 @@ class Attachment extends Model return $this->morphMany(Note::class, 'noteable'); } - /** - * @return Attribute - */ protected function attachableId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - } diff --git a/app/Models/AuditLogEntry.php b/app/Models/AuditLogEntry.php index e48d0ecaf6..cc2a93b2e4 100644 --- a/app/Models/AuditLogEntry.php +++ b/app/Models/AuditLogEntry.php @@ -36,25 +36,28 @@ use Illuminate\Database\Eloquent\SoftDeletes; /** * Class AuditLogEntry * - * @property-read Model|Eloquent $auditable - * @property-read Model|Eloquent $changer - * @method static Builder|AuditLogEntry newModelQuery() - * @method static Builder|AuditLogEntry newQuery() + * @property \Eloquent|Model $auditable + * @property \Eloquent|Model $changer + * + * @method static Builder|AuditLogEntry newModelQuery() + * @method static Builder|AuditLogEntry newQuery() * @method static \Illuminate\Database\Query\Builder|AuditLogEntry onlyTrashed() - * @method static Builder|AuditLogEntry query() + * @method static Builder|AuditLogEntry query() * @method static \Illuminate\Database\Query\Builder|AuditLogEntry withTrashed() * @method static \Illuminate\Database\Query\Builder|AuditLogEntry withoutTrashed() - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $auditable_id - * @property string $auditable_type - * @property int $changer_id - * @property string $changer_type - * @property string $action - * @property array|null $before - * @property array|null $after + * + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $auditable_id + * @property string $auditable_type + * @property int $changer_id + * @property string $changer_type + * @property string $action + * @property null|array $before + * @property null|array $after + * * @method static Builder|AuditLogEntry whereAction($value) * @method static Builder|AuditLogEntry whereAfter($value) * @method static Builder|AuditLogEntry whereAuditableId($value) @@ -66,6 +69,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @method static Builder|AuditLogEntry whereDeletedAt($value) * @method static Builder|AuditLogEntry whereId($value) * @method static Builder|AuditLogEntry whereUpdatedAt($value) + * * @mixin Eloquent */ class AuditLogEntry extends Model @@ -82,39 +86,27 @@ class AuditLogEntry extends Model 'deleted_at' => 'datetime', ]; - /** - */ public function auditable(): MorphTo { return $this->morphTo(); } - /** - */ public function changer(): MorphTo { return $this->morphTo(); } - /** - * @return Attribute - */ protected function auditableId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function changerId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - - } diff --git a/app/Models/AutoBudget.php b/app/Models/AutoBudget.php index ed02be17fe..cd95c02856 100644 --- a/app/Models/AutoBudget.php +++ b/app/Models/AutoBudget.php @@ -36,20 +36,21 @@ use Illuminate\Database\Query\Builder; /** * FireflyIII\Models\AutoBudget * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $budget_id - * @property int $transaction_currency_id - * @property int|string $auto_budget_type - * @property string $amount - * @property string $period - * @property-read Budget $budget - * @property-read TransactionCurrency $transactionCurrency + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $budget_id + * @property int $transaction_currency_id + * @property int|string $auto_budget_type + * @property string $amount + * @property string $period + * @property Budget $budget + * @property TransactionCurrency $transactionCurrency + * * @method static \Illuminate\Database\Eloquent\Builder|AutoBudget newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|AutoBudget newQuery() - * @method static Builder|AutoBudget onlyTrashed() + * @method static Builder|AutoBudget onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|AutoBudget query() * @method static \Illuminate\Database\Eloquent\Builder|AutoBudget whereAmount($value) * @method static \Illuminate\Database\Eloquent\Builder|AutoBudget whereAutoBudgetType($value) @@ -60,8 +61,9 @@ use Illuminate\Database\Query\Builder; * @method static \Illuminate\Database\Eloquent\Builder|AutoBudget wherePeriod($value) * @method static \Illuminate\Database\Eloquent\Builder|AutoBudget whereTransactionCurrencyId($value) * @method static \Illuminate\Database\Eloquent\Builder|AutoBudget whereUpdatedAt($value) - * @method static Builder|AutoBudget withTrashed() - * @method static Builder|AutoBudget withoutTrashed() + * @method static Builder|AutoBudget withTrashed() + * @method static Builder|AutoBudget withoutTrashed() + * * @mixin Eloquent */ class AutoBudget extends Model @@ -74,50 +76,34 @@ class AutoBudget extends Model public const int AUTO_BUDGET_ROLLOVER = 2; protected $fillable = ['budget_id', 'amount', 'period']; - /** - * @return BelongsTo - */ public function budget(): BelongsTo { return $this->belongsTo(Budget::class); } - /** - * @return BelongsTo - */ public function transactionCurrency(): BelongsTo { return $this->belongsTo(TransactionCurrency::class); } - /** - * @return Attribute - */ protected function amount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } - /** - * @return Attribute - */ protected function budgetId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - } diff --git a/app/Models/AvailableBudget.php b/app/Models/AvailableBudget.php index 182dd22c3e..939f054342 100644 --- a/app/Models/AvailableBudget.php +++ b/app/Models/AvailableBudget.php @@ -38,20 +38,21 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\AvailableBudget * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $user_id - * @property int $transaction_currency_id - * @property string $amount - * @property Carbon $start_date - * @property Carbon $end_date - * @property-read TransactionCurrency $transactionCurrency - * @property-read User $user + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $user_id + * @property int $transaction_currency_id + * @property string $amount + * @property Carbon $start_date + * @property Carbon $end_date + * @property TransactionCurrency $transactionCurrency + * @property User $user + * * @method static \Illuminate\Database\Eloquent\Builder|AvailableBudget newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|AvailableBudget newQuery() - * @method static Builder|AvailableBudget onlyTrashed() + * @method static Builder|AvailableBudget onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|AvailableBudget query() * @method static \Illuminate\Database\Eloquent\Builder|AvailableBudget whereAmount($value) * @method static \Illuminate\Database\Eloquent\Builder|AvailableBudget whereCreatedAt($value) @@ -62,10 +63,13 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|AvailableBudget whereTransactionCurrencyId($value) * @method static \Illuminate\Database\Eloquent\Builder|AvailableBudget whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|AvailableBudget whereUserId($value) - * @method static Builder|AvailableBudget withTrashed() - * @method static Builder|AvailableBudget withoutTrashed() - * @property int $user_group_id + * @method static Builder|AvailableBudget withTrashed() + * @method static Builder|AvailableBudget withoutTrashed() + * + * @property int $user_group_id + * * @method static \Illuminate\Database\Eloquent\Builder|AvailableBudget whereUserGroupId($value) + * * @mixin Eloquent */ class AvailableBudget extends Model @@ -89,60 +93,47 @@ class AvailableBudget extends Model /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return AvailableBudget * @throws NotFoundHttpException */ public static function routeBinder(string $value): self { if (auth()->check()) { $availableBudgetId = (int)$value; + /** @var User $user */ $user = auth()->user(); - /** @var AvailableBudget|null $availableBudget */ + + /** @var null|AvailableBudget $availableBudget */ $availableBudget = $user->availableBudgets()->find($availableBudgetId); if (null !== $availableBudget) { return $availableBudget; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); } - /** - * @return BelongsTo - */ public function transactionCurrency(): BelongsTo { return $this->belongsTo(TransactionCurrency::class); } - /** - * @return Attribute - */ protected function amount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } - /** - * @return Attribute - */ protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - } diff --git a/app/Models/Bill.php b/app/Models/Bill.php index 73bf79a971..10e5ff1c18 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -42,39 +42,40 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\Bill * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $user_id - * @property int $transaction_currency_id - * @property string $name - * @property string $match - * @property string $amount_min - * @property string $amount_max - * @property Carbon $date - * @property Carbon|null $end_date - * @property Carbon|null $extension_date - * @property string $repeat_freq - * @property int $skip - * @property bool $automatch - * @property bool $active - * @property bool $name_encrypted - * @property bool $match_encrypted - * @property int $order - * @property-read Collection|Attachment[] $attachments - * @property-read int|null $attachments_count - * @property-read Collection|Note[] $notes - * @property-read int|null $notes_count - * @property-read Collection|ObjectGroup[] $objectGroups - * @property-read int|null $object_groups_count - * @property-read TransactionCurrency|null $transactionCurrency - * @property-read Collection|TransactionJournal[] $transactionJournals - * @property-read int|null $transaction_journals_count - * @property-read User $user + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $user_id + * @property int $transaction_currency_id + * @property string $name + * @property string $match + * @property string $amount_min + * @property string $amount_max + * @property Carbon $date + * @property null|Carbon $end_date + * @property null|Carbon $extension_date + * @property string $repeat_freq + * @property int $skip + * @property bool $automatch + * @property bool $active + * @property bool $name_encrypted + * @property bool $match_encrypted + * @property int $order + * @property Attachment[]|Collection $attachments + * @property null|int $attachments_count + * @property Collection|Note[] $notes + * @property null|int $notes_count + * @property Collection|ObjectGroup[] $objectGroups + * @property null|int $object_groups_count + * @property null|TransactionCurrency $transactionCurrency + * @property Collection|TransactionJournal[] $transactionJournals + * @property null|int $transaction_journals_count + * @property User $user + * * @method static \Illuminate\Database\Eloquent\Builder|Bill newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|Bill newQuery() - * @method static Builder|Bill onlyTrashed() + * @method static Builder|Bill onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Bill query() * @method static \Illuminate\Database\Eloquent\Builder|Bill whereActive($value) * @method static \Illuminate\Database\Eloquent\Builder|Bill whereAmountMax($value) @@ -96,10 +97,13 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|Bill whereTransactionCurrencyId($value) * @method static \Illuminate\Database\Eloquent\Builder|Bill whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Bill whereUserId($value) - * @method static Builder|Bill withTrashed() - * @method static Builder|Bill withoutTrashed() - * @property int $user_group_id + * @method static Builder|Bill withTrashed() + * @method static Builder|Bill withoutTrashed() + * + * @property int $user_group_id + * * @method static \Illuminate\Database\Eloquent\Builder|Bill whereUserGroupId($value) + * * @mixin Eloquent */ class Bill extends Model @@ -123,7 +127,6 @@ class Bill extends Model 'match_encrypted' => 'boolean', ]; - protected $fillable = [ 'name', @@ -147,37 +150,31 @@ class Bill extends Model /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return Bill * @throws NotFoundHttpException */ public static function routeBinder(string $value): self { if (auth()->check()) { $billId = (int)$value; + /** @var User $user */ $user = auth()->user(); - /** @var Bill|null $bill */ + + /** @var null|Bill $bill */ $bill = $user->bills()->find($billId); if (null !== $bill) { return $bill; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); } - /** - * @return MorphMany - */ public function attachments(): MorphMany { return $this->morphMany(Attachment::class, 'attachable'); @@ -200,7 +197,6 @@ class Bill extends Model } /** - * * @param mixed $value */ public function setAmountMaxAttribute($value): void @@ -210,25 +206,17 @@ class Bill extends Model /** * @param mixed $value - * - */ public function setAmountMinAttribute($value): void { $this->attributes['amount_min'] = (string)$value; } - /** - * @return BelongsTo - */ public function transactionCurrency(): BelongsTo { return $this->belongsTo(TransactionCurrency::class); } - /** - * @return HasMany - */ public function transactionJournals(): HasMany { return $this->hasMany(TransactionJournal::class); @@ -236,59 +224,45 @@ class Bill extends Model /** * Get the max amount - * - * @return Attribute */ protected function amountMax(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } /** * Get the min amount - * - * @return Attribute */ protected function amountMin(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } - /** - * @return Attribute - */ protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } /** * Get the skip - * - * @return Attribute */ protected function skip(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - - } diff --git a/app/Models/Budget.php b/app/Models/Budget.php index 88168dc69e..62155710b8 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -42,29 +42,30 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\Budget * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $user_id - * @property string $name - * @property bool $active - * @property bool $encrypted - * @property int $order - * @property-read Collection|Attachment[] $attachments - * @property-read int|null $attachments_count - * @property-read Collection|AutoBudget[] $autoBudgets - * @property-read int|null $auto_budgets_count - * @property-read Collection|BudgetLimit[] $budgetlimits - * @property-read int|null $budgetlimits_count - * @property-read Collection|TransactionJournal[] $transactionJournals - * @property-read int|null $transaction_journals_count - * @property-read Collection|Transaction[] $transactions - * @property-read int|null $transactions_count - * @property-read User $user + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $user_id + * @property string $name + * @property bool $active + * @property bool $encrypted + * @property int $order + * @property Attachment[]|Collection $attachments + * @property null|int $attachments_count + * @property AutoBudget[]|Collection $autoBudgets + * @property null|int $auto_budgets_count + * @property BudgetLimit[]|Collection $budgetlimits + * @property null|int $budgetlimits_count + * @property Collection|TransactionJournal[] $transactionJournals + * @property null|int $transaction_journals_count + * @property Collection|Transaction[] $transactions + * @property null|int $transactions_count + * @property User $user + * * @method static \Illuminate\Database\Eloquent\Builder|Budget newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|Budget newQuery() - * @method static Builder|Budget onlyTrashed() + * @method static Builder|Budget onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Budget query() * @method static \Illuminate\Database\Eloquent\Builder|Budget whereActive($value) * @method static \Illuminate\Database\Eloquent\Builder|Budget whereCreatedAt($value) @@ -75,13 +76,17 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|Budget whereOrder($value) * @method static \Illuminate\Database\Eloquent\Builder|Budget whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Budget whereUserId($value) - * @method static Builder|Budget withTrashed() - * @method static Builder|Budget withoutTrashed() - * @property string $email - * @property int $user_group_id + * @method static Builder|Budget withTrashed() + * @method static Builder|Budget withoutTrashed() + * + * @property string $email + * @property int $user_group_id + * * @method static \Illuminate\Database\Eloquent\Builder|Budget whereUserGroupId($value) - * @property-read Collection|Note[] $notes - * @property-read int|null $notes_count + * + * @property Collection|Note[] $notes + * @property null|int $notes_count + * * @mixin Eloquent */ class Budget extends Model @@ -106,53 +111,41 @@ class Budget extends Model /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return Budget * @throws NotFoundHttpException */ public static function routeBinder(string $value): self { if (auth()->check()) { $budgetId = (int)$value; + /** @var User $user */ $user = auth()->user(); - /** @var Budget|null $budget */ + + /** @var null|Budget $budget */ $budget = $user->budgets()->find($budgetId); if (null !== $budget) { return $budget; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); } - /** - * @return MorphMany - */ public function attachments(): MorphMany { return $this->morphMany(Attachment::class, 'attachable'); } - /** - * @return HasMany - */ public function autoBudgets(): HasMany { return $this->hasMany(AutoBudget::class); } - /** - * @return HasMany - */ public function budgetlimits(): HasMany { return $this->hasMany(BudgetLimit::class); @@ -166,30 +159,20 @@ class Budget extends Model return $this->morphMany(Note::class, 'noteable'); } - /** - * @return BelongsToMany - */ public function transactionJournals(): BelongsToMany { return $this->belongsToMany(TransactionJournal::class, 'budget_transaction_journal', 'budget_id'); } - /** - * @return BelongsToMany - */ public function transactions(): BelongsToMany { return $this->belongsToMany(Transaction::class, 'budget_transaction', 'budget_id'); } - /** - * @return Attribute - */ protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - } diff --git a/app/Models/BudgetLimit.php b/app/Models/BudgetLimit.php index a228f7cd9c..9ffe1acc30 100644 --- a/app/Models/BudgetLimit.php +++ b/app/Models/BudgetLimit.php @@ -38,19 +38,20 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\BudgetLimit * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property int $budget_id - * @property int $transaction_currency_id - * @property Carbon $start_date - * @property Carbon|null $end_date - * @property string $amount - * @property string $spent - * @property string|null $period - * @property int|string $generated - * @property-read Budget $budget - * @property-read TransactionCurrency|null $transactionCurrency + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property int $budget_id + * @property int $transaction_currency_id + * @property Carbon $start_date + * @property null|Carbon $end_date + * @property string $amount + * @property string $spent + * @property null|string $period + * @property int|string $generated + * @property Budget $budget + * @property null|TransactionCurrency $transactionCurrency + * * @method static Builder|BudgetLimit newModelQuery() * @method static Builder|BudgetLimit newQuery() * @method static Builder|BudgetLimit query() @@ -64,6 +65,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static Builder|BudgetLimit whereStartDate($value) * @method static Builder|BudgetLimit whereTransactionCurrencyId($value) * @method static Builder|BudgetLimit whereUpdatedAt($value) + * * @mixin Eloquent */ class BudgetLimit extends Model @@ -90,9 +92,6 @@ class BudgetLimit extends Model /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return BudgetLimit * @throws NotFoundHttpException */ public static function routeBinder(string $value): self @@ -100,27 +99,23 @@ class BudgetLimit extends Model if (auth()->check()) { $budgetLimitId = (int)$value; $budgetLimit = self::where('budget_limits.id', $budgetLimitId) - ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') - ->where('budgets.user_id', auth()->user()->id) - ->first(['budget_limits.*']); + ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') + ->where('budgets.user_id', auth()->user()->id) + ->first(['budget_limits.*']) + ; if (null !== $budgetLimit) { return $budgetLimit; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function budget(): BelongsTo { return $this->belongsTo(Budget::class); } - /** - * @return BelongsTo - */ public function transactionCurrency(): BelongsTo { return $this->belongsTo(TransactionCurrency::class); @@ -128,35 +123,25 @@ class BudgetLimit extends Model /** * Get the amount - * - * @return Attribute */ protected function amount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } - /** - * @return Attribute - */ protected function budgetId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - - } diff --git a/app/Models/Category.php b/app/Models/Category.php index 76065276e0..905d53c484 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -40,26 +40,27 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\Category * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $user_id - * @property string $name - * @property Carbon $lastActivity - * @property bool $encrypted - * @property-read Collection|Attachment[] $attachments - * @property-read int|null $attachments_count - * @property-read Collection|Note[] $notes - * @property-read int|null $notes_count - * @property-read Collection|TransactionJournal[] $transactionJournals - * @property-read int|null $transaction_journals_count - * @property-read Collection|Transaction[] $transactions - * @property-read int|null $transactions_count - * @property-read User $user + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $user_id + * @property string $name + * @property Carbon $lastActivity + * @property bool $encrypted + * @property Attachment[]|Collection $attachments + * @property null|int $attachments_count + * @property Collection|Note[] $notes + * @property null|int $notes_count + * @property Collection|TransactionJournal[] $transactionJournals + * @property null|int $transaction_journals_count + * @property Collection|Transaction[] $transactions + * @property null|int $transactions_count + * @property User $user + * * @method static \Illuminate\Database\Eloquent\Builder|Category newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|Category newQuery() - * @method static Builder|Category onlyTrashed() + * @method static Builder|Category onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Category query() * @method static \Illuminate\Database\Eloquent\Builder|Category whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Category whereDeletedAt($value) @@ -68,10 +69,13 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|Category whereName($value) * @method static \Illuminate\Database\Eloquent\Builder|Category whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Category whereUserId($value) - * @method static Builder|Category withTrashed() - * @method static Builder|Category withoutTrashed() - * @property int $user_group_id + * @method static Builder|Category withTrashed() + * @method static Builder|Category withoutTrashed() + * + * @property int $user_group_id + * * @method static \Illuminate\Database\Eloquent\Builder|Category whereUserGroupId($value) + * * @mixin Eloquent */ class Category extends Model @@ -95,37 +99,31 @@ class Category extends Model /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return Category * @throws NotFoundHttpException */ public static function routeBinder(string $value): self { if (auth()->check()) { $categoryId = (int)$value; + /** @var User $user */ $user = auth()->user(); - /** @var Category|null $category */ + + /** @var null|Category $category */ $category = $user->categories()->find($categoryId); if (null !== $category) { return $category; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); } - /** - * @return MorphMany - */ public function attachments(): MorphMany { return $this->morphMany(Attachment::class, 'attachable'); @@ -139,20 +137,13 @@ class Category extends Model return $this->morphMany(Note::class, 'noteable'); } - /** - * @return BelongsToMany - */ public function transactionJournals(): BelongsToMany { return $this->belongsToMany(TransactionJournal::class, 'category_transaction_journal', 'category_id'); } - /** - * @return BelongsToMany - */ public function transactions(): BelongsToMany { return $this->belongsToMany(Transaction::class, 'category_transaction', 'category_id'); } - } diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 2613a32a89..bbb5d17225 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -34,14 +34,15 @@ use Illuminate\Database\Query\Builder; * FireflyIII\Models\Configuration * * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at * @property string $name * @property mixed $data + * * @method static \Illuminate\Database\Eloquent\Builder|Configuration newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|Configuration newQuery() - * @method static Builder|Configuration onlyTrashed() + * @method static Builder|Configuration onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Configuration query() * @method static \Illuminate\Database\Eloquent\Builder|Configuration whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Configuration whereData($value) @@ -49,8 +50,9 @@ use Illuminate\Database\Query\Builder; * @method static \Illuminate\Database\Eloquent\Builder|Configuration whereId($value) * @method static \Illuminate\Database\Eloquent\Builder|Configuration whereName($value) * @method static \Illuminate\Database\Eloquent\Builder|Configuration whereUpdatedAt($value) - * @method static Builder|Configuration withTrashed() - * @method static Builder|Configuration withoutTrashed() + * @method static Builder|Configuration withTrashed() + * @method static Builder|Configuration withoutTrashed() + * * @mixin Eloquent */ class Configuration extends Model @@ -58,20 +60,19 @@ class Configuration extends Model use ReturnsIntegerIdTrait; use SoftDeletes; - protected $casts = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', ]; + /** @var string The table to store the data in */ protected $table = 'configuration'; /** * TODO can be replaced with native laravel code. * - * * @param mixed $value * * @return mixed @@ -82,13 +83,10 @@ class Configuration extends Model } /** - * * @param mixed $value */ public function setDataAttribute($value): void { $this->attributes['data'] = json_encode($value); } - - } diff --git a/app/Models/CurrencyExchangeRate.php b/app/Models/CurrencyExchangeRate.php index 86b317bf97..c4cf0a4d49 100644 --- a/app/Models/CurrencyExchangeRate.php +++ b/app/Models/CurrencyExchangeRate.php @@ -37,19 +37,20 @@ use Illuminate\Database\Eloquent\SoftDeletes; /** * Class CurrencyExchangeRate * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property string|null $deleted_at - * @property int $user_id - * @property int $from_currency_id - * @property int $to_currency_id - * @property Carbon $date - * @property string $rate - * @property string $user_rate - * @property-read TransactionCurrency $fromCurrency - * @property-read TransactionCurrency $toCurrency - * @property-read User $user + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|string $deleted_at + * @property int $user_id + * @property int $from_currency_id + * @property int $to_currency_id + * @property Carbon $date + * @property string $rate + * @property string $user_rate + * @property TransactionCurrency $fromCurrency + * @property TransactionCurrency $toCurrency + * @property User $user + * * @method static Builder|CurrencyExchangeRate newModelQuery() * @method static Builder|CurrencyExchangeRate newQuery() * @method static Builder|CurrencyExchangeRate query() @@ -63,11 +64,14 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @method static Builder|CurrencyExchangeRate whereUpdatedAt($value) * @method static Builder|CurrencyExchangeRate whereUserId($value) * @method static Builder|CurrencyExchangeRate whereUserRate($value) - * @property int $user_group_id + * + * @property int $user_group_id + * * @method static Builder|CurrencyExchangeRate whereUserGroupId($value) * @method static Builder|CurrencyExchangeRate onlyTrashed() * @method static Builder|CurrencyExchangeRate withTrashed() * @method static Builder|CurrencyExchangeRate withoutTrashed() + * * @mixin Eloquent */ class CurrencyExchangeRate extends Model @@ -76,81 +80,57 @@ class CurrencyExchangeRate extends Model use ReturnsIntegerUserIdTrait; use SoftDeletes; - protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'user_id' => 'int', - 'from_currency_id' => 'int', - 'to_currency_id' => 'int', - 'date' => 'datetime', - ]; + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'user_id' => 'int', + 'from_currency_id' => 'int', + 'to_currency_id' => 'int', + 'date' => 'datetime', + ]; protected $fillable = ['user_id', 'from_currency_id', 'to_currency_id', 'date', 'rate']; - /** - * @return BelongsTo - */ public function fromCurrency(): BelongsTo { return $this->belongsTo(TransactionCurrency::class, 'from_currency_id'); } - /** - * @return BelongsTo - */ public function toCurrency(): BelongsTo { return $this->belongsTo(TransactionCurrency::class, 'to_currency_id'); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); } - /** - * @return Attribute - */ protected function fromCurrencyId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function rate(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } - /** - * @return Attribute - */ protected function toCurrencyId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function userRate(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } - - } diff --git a/app/Models/GroupMembership.php b/app/Models/GroupMembership.php index 9e1a3852b8..add3a01472 100644 --- a/app/Models/GroupMembership.php +++ b/app/Models/GroupMembership.php @@ -37,16 +37,17 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; /** * Class GroupMembership * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property string|null $deleted_at - * @property int $user_id - * @property int $user_group_id - * @property int $user_role_id - * @property-read User $user - * @property-read UserGroup $userGroup - * @property-read UserRole $userRole + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|string $deleted_at + * @property int $user_id + * @property int $user_group_id + * @property int $user_role_id + * @property User $user + * @property UserGroup $userGroup + * @property UserRole $userRole + * * @method static Builder|GroupMembership newModelQuery() * @method static Builder|GroupMembership newQuery() * @method static Builder|GroupMembership query() @@ -57,6 +58,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; * @method static Builder|GroupMembership whereUserGroupId($value) * @method static Builder|GroupMembership whereUserId($value) * @method static Builder|GroupMembership whereUserRoleId($value) + * * @mixin Eloquent */ class GroupMembership extends Model @@ -66,39 +68,25 @@ class GroupMembership extends Model protected $fillable = ['user_id', 'user_group_id', 'user_role_id']; - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); } - /** - * @return BelongsTo - */ public function userGroup(): BelongsTo { return $this->belongsTo(UserGroup::class); } - /** - * @return BelongsTo - */ public function userRole(): BelongsTo { return $this->belongsTo(UserRole::class); } - /** - * @return Attribute - */ protected function userRoleId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - - } diff --git a/app/Models/InvitedUser.php b/app/Models/InvitedUser.php index e3acc26f8a..124a4e2528 100644 --- a/app/Models/InvitedUser.php +++ b/app/Models/InvitedUser.php @@ -37,18 +37,21 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class InvitedUser * - * @property-read User $user + * @property User $user + * * @method static Builder|InvitedUser newModelQuery() * @method static Builder|InvitedUser newQuery() * @method static Builder|InvitedUser query() + * * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at + * @property null|Carbon $created_at + * @property null|Carbon $updated_at * @property int $user_id * @property string $email * @property string $invite_code * @property Carbon $expires * @property bool $redeemed + * * @method static Builder|InvitedUser whereCreatedAt($value) * @method static Builder|InvitedUser whereEmail($value) * @method static Builder|InvitedUser whereExpires($value) @@ -57,6 +60,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static Builder|InvitedUser whereRedeemed($value) * @method static Builder|InvitedUser whereUpdatedAt($value) * @method static Builder|InvitedUser whereUserId($value) + * * @mixin Eloquent */ class InvitedUser extends Model @@ -66,38 +70,31 @@ class InvitedUser extends Model protected $casts = [ - 'expires' => 'datetime', - 'redeemed' => 'boolean', - ]; + 'expires' => 'datetime', + 'redeemed' => 'boolean', + ]; protected $fillable = ['user_id', 'email', 'invite_code', 'expires', 'redeemed']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). - * - * @param string $value - * - * @return InvitedUser */ public static function routeBinder(string $value): self { if (auth()->check()) { $attemptId = (int)$value; - /** @var InvitedUser|null $attempt */ + + /** @var null|InvitedUser $attempt */ $attempt = self::find($attemptId); if (null !== $attempt) { return $attempt; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); } - - } diff --git a/app/Models/LinkType.php b/app/Models/LinkType.php index 3acbfdbf27..6a5c80077f 100644 --- a/app/Models/LinkType.php +++ b/app/Models/LinkType.php @@ -36,20 +36,21 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\LinkType * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property string $name - * @property string $outward - * @property string $inward - * @property int $journalCount - * @property bool $editable - * @property-read Collection|TransactionJournalLink[] $transactionJournalLinks - * @property-read int|null $transaction_journal_links_count + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property string $name + * @property string $outward + * @property string $inward + * @property int $journalCount + * @property bool $editable + * @property Collection|TransactionJournalLink[] $transactionJournalLinks + * @property null|int $transaction_journal_links_count + * * @method static \Illuminate\Database\Eloquent\Builder|LinkType newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|LinkType newQuery() - * @method static Builder|LinkType onlyTrashed() + * @method static Builder|LinkType onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|LinkType query() * @method static \Illuminate\Database\Eloquent\Builder|LinkType whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|LinkType whereDeletedAt($value) @@ -59,8 +60,9 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|LinkType whereName($value) * @method static \Illuminate\Database\Eloquent\Builder|LinkType whereOutward($value) * @method static \Illuminate\Database\Eloquent\Builder|LinkType whereUpdatedAt($value) - * @method static Builder|LinkType withTrashed() - * @method static Builder|LinkType withoutTrashed() + * @method static Builder|LinkType withTrashed() + * @method static Builder|LinkType withoutTrashed() + * * @mixin Eloquent */ class LinkType extends Model @@ -68,7 +70,6 @@ class LinkType extends Model use ReturnsIntegerIdTrait; use SoftDeletes; - protected $casts = [ 'created_at' => 'datetime', @@ -77,16 +78,11 @@ class LinkType extends Model 'editable' => 'boolean', ]; - protected $fillable = ['name', 'inward', 'outward', 'editable']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return LinkType - * * @throws NotFoundHttpException */ public static function routeBinder(string $value): self @@ -98,16 +94,12 @@ class LinkType extends Model return $linkType; } } + throw new NotFoundHttpException(); } - /** - * @return HasMany - */ public function transactionJournalLinks(): HasMany { return $this->hasMany(TransactionJournalLink::class); } - - } diff --git a/app/Models/Location.php b/app/Models/Location.php index c67085f2de..c6634cad71 100644 --- a/app/Models/Location.php +++ b/app/Models/Location.php @@ -37,18 +37,19 @@ use Illuminate\Database\Eloquent\Relations\MorphTo; /** * FireflyIII\Models\Location * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $locatable_id - * @property string $locatable_type - * @property float|null $latitude - * @property float|null $longitude - * @property int|null $zoom_level - * @property-read Collection|Account[] $accounts - * @property-read int|null $accounts_count - * @property-read Model|Eloquent $locatable + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $locatable_id + * @property string $locatable_type + * @property null|float $latitude + * @property null|float $longitude + * @property null|int $zoom_level + * @property Account[]|Collection $accounts + * @property null|int $accounts_count + * @property \Eloquent|Model $locatable + * * @method static Builder|Location newModelQuery() * @method static Builder|Location newQuery() * @method static Builder|Location query() @@ -61,6 +62,7 @@ use Illuminate\Database\Eloquent\Relations\MorphTo; * @method static Builder|Location whereLongitude($value) * @method static Builder|Location whereUpdatedAt($value) * @method static Builder|Location whereZoomLevel($value) + * * @mixin Eloquent */ class Location extends Model @@ -81,10 +83,6 @@ class Location extends Model /** * Add rules for locations. - * - * @param array $rules - * - * @return array */ public static function requestRules(array $rules): array { @@ -105,24 +103,16 @@ class Location extends Model /** * Get all the owning attachable models. - * - * - * @return MorphTo */ public function locatable(): MorphTo { return $this->morphTo(); } - /** - * @return Attribute - */ protected function locatableId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - - } diff --git a/app/Models/Note.php b/app/Models/Note.php index f4ef3f46e8..9a0699046a 100644 --- a/app/Models/Note.php +++ b/app/Models/Note.php @@ -35,18 +35,19 @@ use Illuminate\Database\Query\Builder; /** * FireflyIII\Models\Note * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $noteable_id - * @property string $noteable_type - * @property string|null $title - * @property string|null $text - * @property-read Model|Eloquent $noteable + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $noteable_id + * @property string $noteable_type + * @property null|string $title + * @property null|string $text + * @property \Eloquent|Model $noteable + * * @method static \Illuminate\Database\Eloquent\Builder|Note newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|Note newQuery() - * @method static Builder|Note onlyTrashed() + * @method static Builder|Note onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Note query() * @method static \Illuminate\Database\Eloquent\Builder|Note whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Note whereDeletedAt($value) @@ -56,8 +57,9 @@ use Illuminate\Database\Query\Builder; * @method static \Illuminate\Database\Eloquent\Builder|Note whereText($value) * @method static \Illuminate\Database\Eloquent\Builder|Note whereTitle($value) * @method static \Illuminate\Database\Eloquent\Builder|Note whereUpdatedAt($value) - * @method static Builder|Note withTrashed() - * @method static Builder|Note withoutTrashed() + * @method static Builder|Note withTrashed() + * @method static Builder|Note withoutTrashed() + * * @mixin Eloquent */ class Note extends Model @@ -65,7 +67,6 @@ class Note extends Model use ReturnsIntegerIdTrait; use SoftDeletes; - protected $casts = [ 'created_at' => 'datetime', @@ -76,7 +77,6 @@ class Note extends Model protected $fillable = ['title', 'text', 'noteable_id', 'noteable_type']; /** - * * Get all the owning noteable models. */ public function noteable(): MorphTo @@ -84,15 +84,10 @@ class Note extends Model return $this->morphTo(); } - /** - * @return Attribute - */ protected function noteableId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - - } diff --git a/app/Models/ObjectGroup.php b/app/Models/ObjectGroup.php index 8d0f610ec8..4f89dcb12f 100644 --- a/app/Models/ObjectGroup.php +++ b/app/Models/ObjectGroup.php @@ -40,20 +40,21 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\ObjectGroup * - * @property int $id - * @property int $user_id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property string $title - * @property int $order - * @property-read Collection|Account[] $accounts - * @property-read int|null $accounts_count - * @property-read Collection|Bill[] $bills - * @property-read int|null $bills_count - * @property-read Collection|PiggyBank[] $piggyBanks - * @property-read int|null $piggy_banks_count - * @property-read User $user + * @property int $id + * @property int $user_id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property string $title + * @property int $order + * @property Account[]|Collection $accounts + * @property null|int $accounts_count + * @property Bill[]|Collection $bills + * @property null|int $bills_count + * @property Collection|PiggyBank[] $piggyBanks + * @property null|int $piggy_banks_count + * @property User $user + * * @method static Builder|ObjectGroup newModelQuery() * @method static Builder|ObjectGroup newQuery() * @method static Builder|ObjectGroup query() @@ -64,8 +65,11 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static Builder|ObjectGroup whereTitle($value) * @method static Builder|ObjectGroup whereUpdatedAt($value) * @method static Builder|ObjectGroup whereUserId($value) - * @property int $user_group_id + * + * @property int $user_group_id + * * @method static Builder|ObjectGroup whereUserGroupId($value) + * * @mixin Eloquent */ class ObjectGroup extends Model @@ -75,38 +79,35 @@ class ObjectGroup extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'user_id' => 'integer', - 'deleted_at' => 'datetime', - ]; + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'user_id' => 'integer', + 'deleted_at' => 'datetime', + ]; protected $fillable = ['title', 'order', 'user_id', 'user_group_id']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return ObjectGroup * @throws NotFoundHttpException */ public static function routeBinder(string $value): self { if (auth()->check()) { $objectGroupId = (int)$value; - /** @var ObjectGroup|null $objectGroup */ + + /** @var null|ObjectGroup $objectGroup */ $objectGroup = self::where('object_groups.id', $objectGroupId) - ->where('object_groups.user_id', auth()->user()->id)->first(); + ->where('object_groups.user_id', auth()->user()->id)->first() + ; if (null !== $objectGroup) { return $objectGroup; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); @@ -136,15 +137,10 @@ class ObjectGroup extends Model return $this->morphedByMany(PiggyBank::class, 'object_groupable'); } - - /** - * @return Attribute - */ protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - } diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index 45e0b24088..7d07479778 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -40,32 +40,33 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\PiggyBank * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $account_id - * @property string $name - * @property string $targetamount - * @property Carbon|null $startdate - * @property Carbon|null $targetdate - * @property int $order - * @property bool $active - * @property bool $encrypted - * @property-read Account $account - * @property-read Collection|Attachment[] $attachments - * @property-read int|null $attachments_count - * @property-read Collection|Note[] $notes - * @property-read int|null $notes_count - * @property-read Collection|ObjectGroup[] $objectGroups - * @property-read int|null $object_groups_count - * @property-read Collection|PiggyBankEvent[] $piggyBankEvents - * @property-read int|null $piggy_bank_events_count - * @property-read Collection|PiggyBankRepetition[] $piggyBankRepetitions - * @property-read int|null $piggy_bank_repetitions_count + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $account_id + * @property string $name + * @property string $targetamount + * @property null|Carbon $startdate + * @property null|Carbon $targetdate + * @property int $order + * @property bool $active + * @property bool $encrypted + * @property Account $account + * @property Attachment[]|Collection $attachments + * @property null|int $attachments_count + * @property Collection|Note[] $notes + * @property null|int $notes_count + * @property Collection|ObjectGroup[] $objectGroups + * @property null|int $object_groups_count + * @property Collection|PiggyBankEvent[] $piggyBankEvents + * @property null|int $piggy_bank_events_count + * @property Collection|PiggyBankRepetition[] $piggyBankRepetitions + * @property null|int $piggy_bank_repetitions_count + * * @method static \Illuminate\Database\Eloquent\Builder|PiggyBank newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|PiggyBank newQuery() - * @method static Builder|PiggyBank onlyTrashed() + * @method static Builder|PiggyBank onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|PiggyBank query() * @method static \Illuminate\Database\Eloquent\Builder|PiggyBank whereAccountId($value) * @method static \Illuminate\Database\Eloquent\Builder|PiggyBank whereActive($value) @@ -79,8 +80,9 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|PiggyBank whereTargetamount($value) * @method static \Illuminate\Database\Eloquent\Builder|PiggyBank whereTargetdate($value) * @method static \Illuminate\Database\Eloquent\Builder|PiggyBank whereUpdatedAt($value) - * @method static Builder|PiggyBank withTrashed() - * @method static Builder|PiggyBank withoutTrashed() + * @method static Builder|PiggyBank withTrashed() + * @method static Builder|PiggyBank withoutTrashed() + * * @mixin Eloquent */ class PiggyBank extends Model @@ -107,9 +109,6 @@ class PiggyBank extends Model /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return PiggyBank * @throws NotFoundHttpException */ public static function routeBinder(string $value): self @@ -117,26 +116,22 @@ class PiggyBank extends Model if (auth()->check()) { $piggyBankId = (int)$value; $piggyBank = self::where('piggy_banks.id', $piggyBankId) - ->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') - ->where('accounts.user_id', auth()->user()->id)->first(['piggy_banks.*']); + ->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') + ->where('accounts.user_id', auth()->user()->id)->first(['piggy_banks.*']) + ; if (null !== $piggyBank) { return $piggyBank; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function account(): BelongsTo { return $this->belongsTo(Account::class); } - /** - * @return MorphMany - */ public function attachments(): MorphMany { return $this->morphMany(Attachment::class, 'attachable'); @@ -158,24 +153,17 @@ class PiggyBank extends Model return $this->morphToMany(ObjectGroup::class, 'object_groupable'); } - /** - * @return HasMany - */ public function piggyBankEvents(): HasMany { return $this->hasMany(PiggyBankEvent::class); } - /** - * @return HasMany - */ public function piggyBankRepetitions(): HasMany { return $this->hasMany(PiggyBankRepetition::class); } /** - * * @param mixed $value */ public function setTargetamountAttribute($value): void @@ -183,36 +171,27 @@ class PiggyBank extends Model $this->attributes['targetamount'] = (string)$value; } - /** - * @return Attribute - */ protected function accountId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } /** * Get the max amount - * - * @return Attribute */ protected function targetamount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } - } diff --git a/app/Models/PiggyBankEvent.php b/app/Models/PiggyBankEvent.php index 67cfd2fff3..f97a1c11fb 100644 --- a/app/Models/PiggyBankEvent.php +++ b/app/Models/PiggyBankEvent.php @@ -34,15 +34,16 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; /** * FireflyIII\Models\PiggyBankEvent * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property int $piggy_bank_id - * @property int|null $transaction_journal_id - * @property Carbon $date - * @property string $amount - * @property PiggyBank $piggyBank - * @property-read TransactionJournal|null $transactionJournal + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property int $piggy_bank_id + * @property null|int $transaction_journal_id + * @property Carbon $date + * @property string $amount + * @property PiggyBank $piggyBank + * @property null|TransactionJournal $transactionJournal + * * @method static Builder|PiggyBankEvent newModelQuery() * @method static Builder|PiggyBankEvent newQuery() * @method static Builder|PiggyBankEvent query() @@ -53,6 +54,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; * @method static Builder|PiggyBankEvent wherePiggyBankId($value) * @method static Builder|PiggyBankEvent whereTransactionJournalId($value) * @method static Builder|PiggyBankEvent whereUpdatedAt($value) + * * @mixin Eloquent */ class PiggyBankEvent extends Model @@ -70,16 +72,12 @@ class PiggyBankEvent extends Model protected $hidden = ['amount_encrypted']; - /** - * @return BelongsTo - */ public function piggyBank(): BelongsTo { return $this->belongsTo(PiggyBank::class); } /** - * * @param mixed $value */ public function setAmountAttribute($value): void @@ -87,9 +85,6 @@ class PiggyBankEvent extends Model $this->attributes['amount'] = (string)$value; } - /** - * @return BelongsTo - */ public function transactionJournal(): BelongsTo { return $this->belongsTo(TransactionJournal::class); @@ -97,23 +92,18 @@ class PiggyBankEvent extends Model /** * Get the amount - * - * @return Attribute */ protected function amount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } - /** - * @return Attribute - */ protected function piggyBankId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/PiggyBankRepetition.php b/app/Models/PiggyBankRepetition.php index 8e6568f7b5..1c11f8d264 100644 --- a/app/Models/PiggyBankRepetition.php +++ b/app/Models/PiggyBankRepetition.php @@ -34,14 +34,15 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; /** * FireflyIII\Models\PiggyBankRepetition * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property int $piggy_bank_id - * @property Carbon|null $startdate - * @property Carbon|null $targetdate - * @property string $currentamount - * @property-read PiggyBank $piggyBank + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property int $piggy_bank_id + * @property null|Carbon $startdate + * @property null|Carbon $targetdate + * @property string $currentamount + * @property PiggyBank $piggyBank + * * @method static EloquentBuilder|PiggyBankRepetition newModelQuery() * @method static EloquentBuilder|PiggyBankRepetition newQuery() * @method static EloquentBuilder|PiggyBankRepetition onDates(Carbon $start, Carbon $target) @@ -54,6 +55,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; * @method static EloquentBuilder|PiggyBankRepetition whereStartdate($value) * @method static EloquentBuilder|PiggyBankRepetition whereTargetdate($value) * @method static EloquentBuilder|PiggyBankRepetition whereUpdatedAt($value) + * * @mixin Eloquent */ class PiggyBankRepetition extends Model @@ -70,52 +72,37 @@ class PiggyBankRepetition extends Model protected $fillable = ['piggy_bank_id', 'startdate', 'targetdate', 'currentamount']; - /** - * @return BelongsTo - */ public function piggyBank(): BelongsTo { return $this->belongsTo(PiggyBank::class); } - /** - * - * @param EloquentBuilder $query - * @param Carbon $start - * @param Carbon $target - * - * @return EloquentBuilder - */ public function scopeOnDates(EloquentBuilder $query, Carbon $start, Carbon $target): EloquentBuilder { return $query->where('startdate', $start->format('Y-m-d'))->where('targetdate', $target->format('Y-m-d')); } /** - * - * @param EloquentBuilder $query - * @param Carbon $date - * * @return EloquentBuilder */ public function scopeRelevantOnDate(EloquentBuilder $query, Carbon $date) { return $query->where( - static function (EloquentBuilder $q) use ($date) { + static function (EloquentBuilder $q) use ($date): void { $q->where('startdate', '<=', $date->format('Y-m-d 00:00:00')); $q->orWhereNull('startdate'); } ) - ->where( - static function (EloquentBuilder $q) use ($date) { - $q->where('targetdate', '>=', $date->format('Y-m-d 00:00:00')); - $q->orWhereNull('targetdate'); - } - ); + ->where( + static function (EloquentBuilder $q) use ($date): void { + $q->where('targetdate', '>=', $date->format('Y-m-d 00:00:00')); + $q->orWhereNull('targetdate'); + } + ) + ; } /** - * * @param mixed $value */ public function setCurrentamountAttribute($value): void @@ -125,23 +112,18 @@ class PiggyBankRepetition extends Model /** * Get the amount - * - * @return Attribute */ protected function currentamount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } - /** - * @return Attribute - */ protected function piggyBankId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/Preference.php b/app/Models/Preference.php index f249f4ae1f..e5cfaf1a02 100644 --- a/app/Models/Preference.php +++ b/app/Models/Preference.php @@ -37,12 +37,13 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * FireflyIII\Models\Preference * * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at + * @property null|Carbon $created_at + * @property null|Carbon $updated_at * @property int $user_id * @property string $name - * @property int|string|array|null $data - * @property-read User $user + * @property null|array|int|string $data + * @property User $user + * * @method static Builder|Preference newModelQuery() * @method static Builder|Preference newQuery() * @method static Builder|Preference query() @@ -52,6 +53,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static Builder|Preference whereName($value) * @method static Builder|Preference whereUpdatedAt($value) * @method static Builder|Preference whereUserId($value) + * * @mixin Eloquent */ class Preference extends Model @@ -66,15 +68,11 @@ class Preference extends Model 'data' => 'array', ]; - protected $fillable = ['user_id', 'data', 'name']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return Preference * @throws NotFoundHttpException */ public static function routeBinder(string $value): self @@ -82,7 +80,8 @@ class Preference extends Model if (auth()->check()) { /** @var User $user */ $user = auth()->user(); - /** @var Preference|null $preference */ + + /** @var null|Preference $preference */ $preference = $user->preferences()->where('name', $value)->first(); if (null === $preference) { $preference = $user->preferences()->where('id', (int)$value)->first(); @@ -101,12 +100,10 @@ class Preference extends Model return $preference; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); diff --git a/app/Models/Recurrence.php b/app/Models/Recurrence.php index 678c9d984b..f5864f7071 100644 --- a/app/Models/Recurrence.php +++ b/app/Models/Recurrence.php @@ -41,36 +41,37 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\Recurrence * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $user_id - * @property int $transaction_type_id - * @property string $title - * @property string $description - * @property Carbon|null $first_date - * @property Carbon|null $repeat_until - * @property Carbon|null $latest_date - * @property int|string $repetitions - * @property bool $apply_rules - * @property bool $active - * @property-read Collection|Attachment[] $attachments - * @property-read int|null $attachments_count - * @property-read Collection|Note[] $notes - * @property-read int|null $notes_count - * @property-read Collection|RecurrenceMeta[] $recurrenceMeta - * @property-read int|null $recurrence_meta_count - * @property-read Collection|RecurrenceRepetition[] $recurrenceRepetitions - * @property-read int|null $recurrence_repetitions_count - * @property-read Collection|RecurrenceTransaction[] $recurrenceTransactions - * @property-read int|null $recurrence_transactions_count - * @property-read TransactionCurrency $transactionCurrency - * @property-read TransactionType $transactionType - * @property-read User $user + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $user_id + * @property int $transaction_type_id + * @property string $title + * @property string $description + * @property null|Carbon $first_date + * @property null|Carbon $repeat_until + * @property null|Carbon $latest_date + * @property int|string $repetitions + * @property bool $apply_rules + * @property bool $active + * @property Attachment[]|Collection $attachments + * @property null|int $attachments_count + * @property Collection|Note[] $notes + * @property null|int $notes_count + * @property Collection|RecurrenceMeta[] $recurrenceMeta + * @property null|int $recurrence_meta_count + * @property Collection|RecurrenceRepetition[] $recurrenceRepetitions + * @property null|int $recurrence_repetitions_count + * @property Collection|RecurrenceTransaction[] $recurrenceTransactions + * @property null|int $recurrence_transactions_count + * @property TransactionCurrency $transactionCurrency + * @property TransactionType $transactionType + * @property User $user + * * @method static \Illuminate\Database\Eloquent\Builder|Recurrence newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|Recurrence newQuery() - * @method static Builder|Recurrence onlyTrashed() + * @method static Builder|Recurrence onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Recurrence query() * @method static \Illuminate\Database\Eloquent\Builder|Recurrence whereActive($value) * @method static \Illuminate\Database\Eloquent\Builder|Recurrence whereApplyRules($value) @@ -86,10 +87,13 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|Recurrence whereTransactionTypeId($value) * @method static \Illuminate\Database\Eloquent\Builder|Recurrence whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Recurrence whereUserId($value) - * @method static Builder|Recurrence withTrashed() - * @method static Builder|Recurrence withoutTrashed() - * @property int $user_group_id + * @method static Builder|Recurrence withTrashed() + * @method static Builder|Recurrence withoutTrashed() + * + * @property int $user_group_id + * * @method static \Illuminate\Database\Eloquent\Builder|Recurrence whereUserGroupId($value) + * * @mixin Eloquent */ class Recurrence extends Model @@ -98,7 +102,6 @@ class Recurrence extends Model use ReturnsIntegerUserIdTrait; use SoftDeletes; - protected $casts = [ 'created_at' => 'datetime', @@ -117,43 +120,38 @@ class Recurrence extends Model protected $fillable = ['user_id', 'transaction_type_id', 'title', 'description', 'first_date', 'repeat_until', 'latest_date', 'repetitions', 'apply_rules', 'active']; + /** @var string The table to store the data in */ protected $table = 'recurrences'; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return Recurrence * @throws NotFoundHttpException */ public static function routeBinder(string $value): self { if (auth()->check()) { $recurrenceId = (int)$value; + /** @var User $user */ $user = auth()->user(); - /** @var Recurrence|null $recurrence */ + + /** @var null|Recurrence $recurrence */ $recurrence = $user->recurrences()->find($recurrenceId); if (null !== $recurrence) { return $recurrence; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); } - /** - * @return MorphMany - */ public function attachments(): MorphMany { return $this->morphMany(Attachment::class, 'attachable'); @@ -167,53 +165,35 @@ class Recurrence extends Model return $this->morphMany(Note::class, 'noteable'); } - /** - * @return HasMany - */ public function recurrenceMeta(): HasMany { return $this->hasMany(RecurrenceMeta::class); } - /** - * @return HasMany - */ public function recurrenceRepetitions(): HasMany { return $this->hasMany(RecurrenceRepetition::class); } - /** - * @return HasMany - */ public function recurrenceTransactions(): HasMany { return $this->hasMany(RecurrenceTransaction::class); } - /** - * @return BelongsTo - */ public function transactionCurrency(): BelongsTo { return $this->belongsTo(TransactionCurrency::class); } - /** - * @return BelongsTo - */ public function transactionType(): BelongsTo { return $this->belongsTo(TransactionType::class); } - /** - * @return Attribute - */ protected function transactionTypeId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/RecurrenceMeta.php b/app/Models/RecurrenceMeta.php index 7d7f333d97..d74db3261d 100644 --- a/app/Models/RecurrenceMeta.php +++ b/app/Models/RecurrenceMeta.php @@ -35,17 +35,18 @@ use Illuminate\Database\Query\Builder; /** * FireflyIII\Models\RecurrenceMeta * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $recurrence_id - * @property string $name - * @property mixed $value - * @property-read Recurrence $recurrence + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $recurrence_id + * @property string $name + * @property mixed $value + * @property Recurrence $recurrence + * * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceMeta newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceMeta newQuery() - * @method static Builder|RecurrenceMeta onlyTrashed() + * @method static Builder|RecurrenceMeta onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceMeta query() * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceMeta whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceMeta whereDeletedAt($value) @@ -54,8 +55,9 @@ use Illuminate\Database\Query\Builder; * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceMeta whereRecurrenceId($value) * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceMeta whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceMeta whereValue($value) - * @method static Builder|RecurrenceMeta withTrashed() - * @method static Builder|RecurrenceMeta withoutTrashed() + * @method static Builder|RecurrenceMeta withTrashed() + * @method static Builder|RecurrenceMeta withoutTrashed() + * * @mixin Eloquent */ class RecurrenceMeta extends Model @@ -63,7 +65,6 @@ class RecurrenceMeta extends Model use ReturnsIntegerIdTrait; use SoftDeletes; - protected $casts = [ 'created_at' => 'datetime', @@ -74,24 +75,19 @@ class RecurrenceMeta extends Model ]; protected $fillable = ['recurrence_id', 'name', 'value']; + /** @var string The table to store the data in */ protected $table = 'recurrences_meta'; - /** - * @return BelongsTo - */ public function recurrence(): BelongsTo { return $this->belongsTo(Recurrence::class); } - /** - * @return Attribute - */ protected function recurrenceId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/RecurrenceRepetition.php b/app/Models/RecurrenceRepetition.php index 0446b074a7..dfbaf72be3 100644 --- a/app/Models/RecurrenceRepetition.php +++ b/app/Models/RecurrenceRepetition.php @@ -35,19 +35,20 @@ use Illuminate\Database\Query\Builder; /** * FireflyIII\Models\RecurrenceRepetition * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $recurrence_id - * @property string $repetition_type - * @property string $repetition_moment - * @property int $repetition_skip - * @property int $weekend - * @property-read Recurrence $recurrence + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $recurrence_id + * @property string $repetition_type + * @property string $repetition_moment + * @property int $repetition_skip + * @property int $weekend + * @property Recurrence $recurrence + * * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceRepetition newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceRepetition newQuery() - * @method static Builder|RecurrenceRepetition onlyTrashed() + * @method static Builder|RecurrenceRepetition onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceRepetition query() * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceRepetition whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceRepetition whereDeletedAt($value) @@ -58,8 +59,9 @@ use Illuminate\Database\Query\Builder; * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceRepetition whereRepetitionType($value) * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceRepetition whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceRepetition whereWeekend($value) - * @method static Builder|RecurrenceRepetition withTrashed() - * @method static Builder|RecurrenceRepetition withoutTrashed() + * @method static Builder|RecurrenceRepetition withTrashed() + * @method static Builder|RecurrenceRepetition withoutTrashed() + * * @mixin Eloquent */ class RecurrenceRepetition extends Model @@ -72,7 +74,6 @@ class RecurrenceRepetition extends Model public const int WEEKEND_TO_FRIDAY = 3; public const int WEEKEND_TO_MONDAY = 4; - protected $casts = [ 'created_at' => 'datetime', @@ -85,44 +86,33 @@ class RecurrenceRepetition extends Model ]; protected $fillable = ['recurrence_id', 'weekend', 'repetition_type', 'repetition_moment', 'repetition_skip']; + /** @var string The table to store the data in */ protected $table = 'recurrences_repetitions'; - /** - * @return BelongsTo - */ public function recurrence(): BelongsTo { return $this->belongsTo(Recurrence::class); } - /** - * @return Attribute - */ protected function recurrenceId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function repetitionSkip(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function weekend(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/RecurrenceTransaction.php b/app/Models/RecurrenceTransaction.php index fce3ad652f..044fbc34ef 100644 --- a/app/Models/RecurrenceTransaction.php +++ b/app/Models/RecurrenceTransaction.php @@ -37,28 +37,29 @@ use Illuminate\Database\Query\Builder; /** * FireflyIII\Models\RecurrenceTransaction * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $recurrence_id - * @property int $transaction_currency_id - * @property int|string|null $foreign_currency_id - * @property int $source_id - * @property int $destination_id - * @property string $amount - * @property string $foreign_amount - * @property string $description - * @property-read Account $destinationAccount - * @property-read TransactionCurrency|null $foreignCurrency - * @property-read Recurrence $recurrence - * @property-read Collection|RecurrenceTransactionMeta[] $recurrenceTransactionMeta - * @property-read int|null $recurrence_transaction_meta_count - * @property-read Account $sourceAccount - * @property-read TransactionCurrency $transactionCurrency + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $recurrence_id + * @property int $transaction_currency_id + * @property null|int|string $foreign_currency_id + * @property int $source_id + * @property int $destination_id + * @property string $amount + * @property string $foreign_amount + * @property string $description + * @property Account $destinationAccount + * @property null|TransactionCurrency $foreignCurrency + * @property Recurrence $recurrence + * @property Collection|RecurrenceTransactionMeta[] $recurrenceTransactionMeta + * @property null|int $recurrence_transaction_meta_count + * @property Account $sourceAccount + * @property TransactionCurrency $transactionCurrency + * * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransaction newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransaction newQuery() - * @method static Builder|RecurrenceTransaction onlyTrashed() + * @method static Builder|RecurrenceTransaction onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransaction query() * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransaction whereAmount($value) * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransaction whereCreatedAt($value) @@ -72,11 +73,15 @@ use Illuminate\Database\Query\Builder; * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransaction whereSourceId($value) * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransaction whereTransactionCurrencyId($value) * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransaction whereUpdatedAt($value) - * @method static Builder|RecurrenceTransaction withTrashed() - * @method static Builder|RecurrenceTransaction withoutTrashed() - * @property int|null $transaction_type_id + * @method static Builder|RecurrenceTransaction withTrashed() + * @method static Builder|RecurrenceTransaction withoutTrashed() + * + * @property null|int $transaction_type_id + * * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransaction whereTransactionTypeId($value) - * @property-read TransactionType|null $transactionType + * + * @property null|TransactionType $transactionType + * * @mixin Eloquent */ class RecurrenceTransaction extends Model @@ -84,7 +89,6 @@ class RecurrenceTransaction extends Model use ReturnsIntegerIdTrait; use SoftDeletes; - protected $casts = [ 'created_at' => 'datetime', @@ -106,133 +110,91 @@ class RecurrenceTransaction extends Model 'foreign_amount', 'description', ]; + /** @var string The table to store the data in */ protected $table = 'recurrences_transactions'; - /** - * @return BelongsTo - */ public function destinationAccount(): BelongsTo { return $this->belongsTo(Account::class, 'destination_id'); } - /** - * @return BelongsTo - */ public function foreignCurrency(): BelongsTo { return $this->belongsTo(TransactionCurrency::class); } - /** - * @return BelongsTo - */ public function recurrence(): BelongsTo { return $this->belongsTo(Recurrence::class); } - /** - * @return HasMany - */ public function recurrenceTransactionMeta(): HasMany { return $this->hasMany(RecurrenceTransactionMeta::class, 'rt_id'); } - /** - * @return BelongsTo - */ public function sourceAccount(): BelongsTo { return $this->belongsTo(Account::class, 'source_id'); } - /** - * @return BelongsTo - */ public function transactionCurrency(): BelongsTo { return $this->belongsTo(TransactionCurrency::class); } - /** - * @return BelongsTo - */ public function transactionType(): BelongsTo { return $this->belongsTo(TransactionType::class); } - /** - * @return Attribute - */ protected function amount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } - /** - * @return Attribute - */ protected function destinationId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function foreignAmount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); - } - /** - * @return Attribute - */ protected function recurrenceId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function sourceId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function transactionCurrencyId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function userId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/RecurrenceTransactionMeta.php b/app/Models/RecurrenceTransactionMeta.php index 82c48fb642..4500f71d05 100644 --- a/app/Models/RecurrenceTransactionMeta.php +++ b/app/Models/RecurrenceTransactionMeta.php @@ -35,17 +35,18 @@ use Illuminate\Database\Query\Builder; /** * FireflyIII\Models\RecurrenceTransactionMeta * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int|string $rt_id - * @property string $name - * @property mixed $value - * @property-read RecurrenceTransaction $recurrenceTransaction + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int|string $rt_id + * @property string $name + * @property mixed $value + * @property RecurrenceTransaction $recurrenceTransaction + * * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransactionMeta newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransactionMeta newQuery() - * @method static Builder|RecurrenceTransactionMeta onlyTrashed() + * @method static Builder|RecurrenceTransactionMeta onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransactionMeta query() * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransactionMeta whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransactionMeta whereDeletedAt($value) @@ -54,8 +55,9 @@ use Illuminate\Database\Query\Builder; * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransactionMeta whereRtId($value) * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransactionMeta whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransactionMeta whereValue($value) - * @method static Builder|RecurrenceTransactionMeta withTrashed() - * @method static Builder|RecurrenceTransactionMeta withoutTrashed() + * @method static Builder|RecurrenceTransactionMeta withTrashed() + * @method static Builder|RecurrenceTransactionMeta withoutTrashed() + * * @mixin Eloquent */ class RecurrenceTransactionMeta extends Model @@ -63,7 +65,6 @@ class RecurrenceTransactionMeta extends Model use ReturnsIntegerIdTrait; use SoftDeletes; - protected $casts = [ 'created_at' => 'datetime', @@ -74,24 +75,19 @@ class RecurrenceTransactionMeta extends Model ]; protected $fillable = ['rt_id', 'name', 'value']; + /** @var string The table to store the data in */ protected $table = 'rt_meta'; - /** - * @return BelongsTo - */ public function recurrenceTransaction(): BelongsTo { return $this->belongsTo(RecurrenceTransaction::class, 'rt_id'); } - /** - * @return Attribute - */ protected function rtId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/Role.php b/app/Models/Role.php index b417d10001..eee321ec09 100644 --- a/app/Models/Role.php +++ b/app/Models/Role.php @@ -35,14 +35,15 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany; /** * FireflyIII\Models\Role * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property string $name - * @property string|null $display_name - * @property string|null $description - * @property-read Collection|User[] $users - * @property-read int|null $users_count + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property string $name + * @property null|string $display_name + * @property null|string $description + * @property Collection|User[] $users + * @property null|int $users_count + * * @method static Builder|Role newModelQuery() * @method static Builder|Role newQuery() * @method static Builder|Role query() @@ -52,6 +53,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany; * @method static Builder|Role whereId($value) * @method static Builder|Role whereName($value) * @method static Builder|Role whereUpdatedAt($value) + * * @mixin Eloquent */ class Role extends Model @@ -64,12 +66,8 @@ class Role extends Model 'updated_at' => 'datetime', ]; - protected $fillable = ['name', 'display_name', 'description']; - /** - * @return BelongsToMany - */ public function users(): BelongsToMany { return $this->belongsToMany(User::class); diff --git a/app/Models/Rule.php b/app/Models/Rule.php index d3f4aeef49..c154383062 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -40,28 +40,29 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\Rule * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $user_id - * @property int $rule_group_id - * @property string $title - * @property string|null $description - * @property int $order - * @property bool $active - * @property bool $stop_processing - * @property bool $strict - * @property-read string $action_value - * @property-read Collection|RuleAction[] $ruleActions - * @property-read int|null $rule_actions_count - * @property-read RuleGroup $ruleGroup - * @property Collection|RuleTrigger[] $ruleTriggers - * @property-read int|null $rule_triggers_count - * @property-read User $user + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $user_id + * @property int $rule_group_id + * @property string $title + * @property null|string $description + * @property int $order + * @property bool $active + * @property bool $stop_processing + * @property bool $strict + * @property string $action_value + * @property Collection|RuleAction[] $ruleActions + * @property null|int $rule_actions_count + * @property RuleGroup $ruleGroup + * @property Collection|RuleTrigger[] $ruleTriggers + * @property null|int $rule_triggers_count + * @property User $user + * * @method static \Illuminate\Database\Eloquent\Builder|Rule newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|Rule newQuery() - * @method static Builder|Rule onlyTrashed() + * @method static Builder|Rule onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Rule query() * @method static \Illuminate\Database\Eloquent\Builder|Rule whereActive($value) * @method static \Illuminate\Database\Eloquent\Builder|Rule whereCreatedAt($value) @@ -75,11 +76,15 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|Rule whereTitle($value) * @method static \Illuminate\Database\Eloquent\Builder|Rule whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Rule whereUserId($value) - * @method static Builder|Rule withTrashed() - * @method static Builder|Rule withoutTrashed() - * @property int $user_group_id + * @method static Builder|Rule withTrashed() + * @method static Builder|Rule withoutTrashed() + * + * @property int $user_group_id + * * @method static \Illuminate\Database\Eloquent\Builder|Rule whereUserGroupId($value) - * @property-read UserGroup|null $userGroup + * + * @property null|UserGroup $userGroup + * * @mixin Eloquent */ class Rule extends Model @@ -88,7 +93,6 @@ class Rule extends Model use ReturnsIntegerUserIdTrait; use SoftDeletes; - protected $casts = [ 'created_at' => 'datetime', @@ -106,53 +110,41 @@ class Rule extends Model /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return Rule * @throws NotFoundHttpException */ public static function routeBinder(string $value): self { if (auth()->check()) { $ruleId = (int)$value; + /** @var User $user */ $user = auth()->user(); - /** @var Rule|null $rule */ + + /** @var null|Rule $rule */ $rule = $user->rules()->find($ruleId); if (null !== $rule) { return $rule; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); } - /** - * @return HasMany - */ public function ruleActions(): HasMany { return $this->hasMany(RuleAction::class); } - /** - * @return BelongsTo - */ public function ruleGroup(): BelongsTo { return $this->belongsTo(RuleGroup::class); } - /** - * @return HasMany - */ public function ruleTriggers(): HasMany { return $this->hasMany(RuleTrigger::class); @@ -160,39 +152,28 @@ class Rule extends Model /** * @param mixed $value - * - */ public function setDescriptionAttribute($value): void { $this->attributes['description'] = e($value); } - /** - * @return BelongsTo - */ public function userGroup(): BelongsTo { return $this->belongsTo(UserGroup::class); } - /** - * @return Attribute - */ protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function ruleGroupId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/RuleAction.php b/app/Models/RuleAction.php index 217462bfa4..0750c47f14 100644 --- a/app/Models/RuleAction.php +++ b/app/Models/RuleAction.php @@ -35,15 +35,16 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; * FireflyIII\Models\RuleAction * * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at + * @property null|Carbon $created_at + * @property null|Carbon $updated_at * @property int $rule_id - * @property string|null $action_type - * @property string|null $action_value + * @property null|string $action_type + * @property null|string $action_value * @property int $order * @property bool $active * @property bool $stop_processing - * @property-read Rule $rule + * @property Rule $rule + * * @method static Builder|RuleAction newModelQuery() * @method static Builder|RuleAction newQuery() * @method static Builder|RuleAction query() @@ -56,6 +57,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; * @method static Builder|RuleAction whereRuleId($value) * @method static Builder|RuleAction whereStopProcessing($value) * @method static Builder|RuleAction whereUpdatedAt($value) + * * @mixin Eloquent */ class RuleAction extends Model @@ -71,35 +73,24 @@ class RuleAction extends Model 'stop_processing' => 'boolean', ]; - protected $fillable = ['rule_id', 'action_type', 'action_value', 'order', 'active', 'stop_processing']; - /** - * @return BelongsTo - */ public function rule(): BelongsTo { return $this->belongsTo(Rule::class); } - /** - * @return Attribute - */ protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function ruleId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - } diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php index 6d6fe9026d..74ba367200 100644 --- a/app/Models/RuleGroup.php +++ b/app/Models/RuleGroup.php @@ -41,21 +41,22 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * FireflyIII\Models\RuleGroup * * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at * @property int $user_id - * @property string|null $title - * @property string|null $description + * @property null|string $title + * @property null|string $description * @property int $order * @property bool $active * @property bool $stop_processing * @property Collection|Rule[] $rules - * @property-read int|null $rules_count - * @property-read User $user + * @property null|int $rules_count + * @property User $user + * * @method static \Illuminate\Database\Eloquent\Builder|RuleGroup newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|RuleGroup newQuery() - * @method static Builder|RuleGroup onlyTrashed() + * @method static Builder|RuleGroup onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|RuleGroup query() * @method static \Illuminate\Database\Eloquent\Builder|RuleGroup whereActive($value) * @method static \Illuminate\Database\Eloquent\Builder|RuleGroup whereCreatedAt($value) @@ -67,10 +68,13 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|RuleGroup whereTitle($value) * @method static \Illuminate\Database\Eloquent\Builder|RuleGroup whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|RuleGroup whereUserId($value) - * @method static Builder|RuleGroup withTrashed() - * @method static Builder|RuleGroup withoutTrashed() - * @property int $user_group_id + * @method static Builder|RuleGroup withTrashed() + * @method static Builder|RuleGroup withoutTrashed() + * + * @property int $user_group_id + * * @method static \Illuminate\Database\Eloquent\Builder|RuleGroup whereUserGroupId($value) + * * @mixin Eloquent */ class RuleGroup extends Model @@ -79,7 +83,6 @@ class RuleGroup extends Model use ReturnsIntegerUserIdTrait; use SoftDeletes; - protected $casts = [ 'created_at' => 'datetime', @@ -90,55 +93,45 @@ class RuleGroup extends Model 'order' => 'int', ]; - protected $fillable = ['user_id', 'user_group_id', 'stop_processing', 'order', 'title', 'description', 'active']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return RuleGroup * @throws NotFoundHttpException */ public static function routeBinder(string $value): self { if (auth()->check()) { $ruleGroupId = (int)$value; + /** @var User $user */ $user = auth()->user(); - /** @var RuleGroup|null $ruleGroup */ + + /** @var null|RuleGroup $ruleGroup */ $ruleGroup = $user->ruleGroups()->find($ruleGroupId); if (null !== $ruleGroup) { return $ruleGroup; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); } - /** - * @return HasMany - */ public function rules(): HasMany { return $this->hasMany(Rule::class); } - /** - * @return Attribute - */ protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/RuleTrigger.php b/app/Models/RuleTrigger.php index 8194592e41..cc88693cc5 100644 --- a/app/Models/RuleTrigger.php +++ b/app/Models/RuleTrigger.php @@ -35,15 +35,16 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; * FireflyIII\Models\RuleTrigger * * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at + * @property null|Carbon $created_at + * @property null|Carbon $updated_at * @property int $rule_id - * @property string|null $trigger_type - * @property string|null $trigger_value + * @property null|string $trigger_type + * @property null|string $trigger_value * @property int $order * @property bool $active * @property bool $stop_processing - * @property-read Rule $rule + * @property Rule $rule + * * @method static Builder|RuleTrigger newModelQuery() * @method static Builder|RuleTrigger newQuery() * @method static Builder|RuleTrigger query() @@ -56,6 +57,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; * @method static Builder|RuleTrigger whereTriggerType($value) * @method static Builder|RuleTrigger whereTriggerValue($value) * @method static Builder|RuleTrigger whereUpdatedAt($value) + * * @mixin Eloquent */ class RuleTrigger extends Model @@ -71,35 +73,24 @@ class RuleTrigger extends Model 'stop_processing' => 'boolean', ]; - protected $fillable = ['rule_id', 'trigger_type', 'trigger_value', 'order', 'active', 'stop_processing']; - /** - * @return BelongsTo - */ public function rule(): BelongsTo { return $this->belongsTo(Rule::class); } - /** - * @return Attribute - */ protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function ruleId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - } diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 9b5d35f1d8..e851b2e862 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -40,28 +40,29 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\Tag * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $user_id - * @property string $tag - * @property string $tagMode - * @property Carbon|null $date - * @property string|null $description - * @property float|null $latitude - * @property float|null $longitude - * @property int|null $zoomLevel - * @property-read Collection|Attachment[] $attachments - * @property-read int|null $attachments_count - * @property-read Collection|Location[] $locations - * @property-read int|null $locations_count - * @property-read Collection|TransactionJournal[] $transactionJournals - * @property-read int|null $transaction_journals_count - * @property-read User $user + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $user_id + * @property string $tag + * @property string $tagMode + * @property null|Carbon $date + * @property null|string $description + * @property null|float $latitude + * @property null|float $longitude + * @property null|int $zoomLevel + * @property Attachment[]|Collection $attachments + * @property null|int $attachments_count + * @property Collection|Location[] $locations + * @property null|int $locations_count + * @property Collection|TransactionJournal[] $transactionJournals + * @property null|int $transaction_journals_count + * @property User $user + * * @method static \Illuminate\Database\Eloquent\Builder|Tag newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|Tag newQuery() - * @method static Builder|Tag onlyTrashed() + * @method static Builder|Tag onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Tag query() * @method static \Illuminate\Database\Eloquent\Builder|Tag whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Tag whereDate($value) @@ -75,10 +76,13 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|Tag whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Tag whereUserId($value) * @method static \Illuminate\Database\Eloquent\Builder|Tag whereZoomLevel($value) - * @method static Builder|Tag withTrashed() - * @method static Builder|Tag withoutTrashed() - * @property int $user_group_id + * @method static Builder|Tag withTrashed() + * @method static Builder|Tag withoutTrashed() + * + * @property int $user_group_id + * * @method static \Illuminate\Database\Eloquent\Builder|Tag whereUserGroupId($value) + * * @mixin Eloquent */ class Tag extends Model @@ -87,7 +91,6 @@ class Tag extends Model use ReturnsIntegerUserIdTrait; use SoftDeletes; - protected $casts = [ 'created_at' => 'datetime', @@ -106,53 +109,41 @@ class Tag extends Model /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return Tag * @throws NotFoundHttpException */ public static function routeBinder(string $value): self { if (auth()->check()) { $tagId = (int)$value; + /** @var User $user */ $user = auth()->user(); - /** @var Tag|null $tag */ + + /** @var null|Tag $tag */ $tag = $user->tags()->find($tagId); if (null !== $tag) { return $tag; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); } - /** - * @return MorphMany - */ public function attachments(): MorphMany { return $this->morphMany(Attachment::class, 'attachable'); } - /** - * @return MorphMany - */ public function locations(): MorphMany { return $this->morphMany(Location::class, 'locatable'); } - /** - * @return BelongsToMany - */ public function transactionJournals(): BelongsToMany { return $this->belongsToMany(TransactionJournal::class); diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index 88f78c51dc..70eb2e187b 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -38,54 +38,57 @@ use Illuminate\Database\Eloquent\SoftDeletes; /** * FireflyIII\Models\Transaction * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property bool $reconciled - * @property int $account_id - * @property int $transaction_journal_id - * @property string|null $description - * @property int|null $transaction_currency_id - * @property string|int|null $modified - * @property string|int|null $modified_foreign - * @property string $date - * @property string $max_date - * @property string $amount - * @property string|null $foreign_amount - * @property int|null $foreign_currency_id - * @property int $identifier - * @property-read Account $account - * @property-read Collection|Budget[] $budgets - * @property-read int|null $budgets_count - * @property-read Collection|Category[] $categories - * @property-read int|null $categories_count - * @property-read TransactionCurrency|null $foreignCurrency - * @property-read TransactionCurrency|null $transactionCurrency - * @property-read TransactionJournal $transactionJournal - * @method static Builder|Transaction after(Carbon $date) - * @method static Builder|Transaction before(Carbon $date) - * @method static Builder|Transaction newModelQuery() - * @method static Builder|Transaction newQuery() + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property bool $reconciled + * @property int $account_id + * @property int $transaction_journal_id + * @property null|string $description + * @property null|int $transaction_currency_id + * @property null|int|string $modified + * @property null|int|string $modified_foreign + * @property string $date + * @property string $max_date + * @property string $amount + * @property null|string $foreign_amount + * @property null|int $foreign_currency_id + * @property int $identifier + * @property Account $account + * @property Budget[]|Collection $budgets + * @property null|int $budgets_count + * @property Category[]|Collection $categories + * @property null|int $categories_count + * @property null|TransactionCurrency $foreignCurrency + * @property null|TransactionCurrency $transactionCurrency + * @property TransactionJournal $transactionJournal + * + * @method static Builder|Transaction after(Carbon $date) + * @method static Builder|Transaction before(Carbon $date) + * @method static Builder|Transaction newModelQuery() + * @method static Builder|Transaction newQuery() * @method static \Illuminate\Database\Query\Builder|Transaction onlyTrashed() - * @method static Builder|Transaction query() - * @method static Builder|Transaction transactionTypes($types) - * @method static Builder|Transaction whereAccountId($value) - * @method static Builder|Transaction whereAmount($value) - * @method static Builder|Transaction whereCreatedAt($value) - * @method static Builder|Transaction whereDeletedAt($value) - * @method static Builder|Transaction whereDescription($value) - * @method static Builder|Transaction whereForeignAmount($value) - * @method static Builder|Transaction whereForeignCurrencyId($value) - * @method static Builder|Transaction whereId($value) - * @method static Builder|Transaction whereIdentifier($value) - * @method static Builder|Transaction whereReconciled($value) - * @method static Builder|Transaction whereTransactionCurrencyId($value) - * @method static Builder|Transaction whereTransactionJournalId($value) - * @method static Builder|Transaction whereUpdatedAt($value) + * @method static Builder|Transaction query() + * @method static Builder|Transaction transactionTypes($types) + * @method static Builder|Transaction whereAccountId($value) + * @method static Builder|Transaction whereAmount($value) + * @method static Builder|Transaction whereCreatedAt($value) + * @method static Builder|Transaction whereDeletedAt($value) + * @method static Builder|Transaction whereDescription($value) + * @method static Builder|Transaction whereForeignAmount($value) + * @method static Builder|Transaction whereForeignCurrencyId($value) + * @method static Builder|Transaction whereId($value) + * @method static Builder|Transaction whereIdentifier($value) + * @method static Builder|Transaction whereReconciled($value) + * @method static Builder|Transaction whereTransactionCurrencyId($value) + * @method static Builder|Transaction whereTransactionJournalId($value) + * @method static Builder|Transaction whereUpdatedAt($value) * @method static \Illuminate\Database\Query\Builder|Transaction withTrashed() * @method static \Illuminate\Database\Query\Builder|Transaction withoutTrashed() - * @property int|string $the_count + * + * @property int|string $the_count + * * @mixin Eloquent */ class Transaction extends Model @@ -94,7 +97,6 @@ class Transaction extends Model use ReturnsIntegerIdTrait; use SoftDeletes; - protected $casts = [ 'created_at' => 'datetime', @@ -123,8 +125,6 @@ class Transaction extends Model /** * Get the account this object belongs to. - * - * @return BelongsTo */ public function account(): BelongsTo { @@ -133,8 +133,6 @@ class Transaction extends Model /** * Get the budget(s) this object belongs to. - * - * @return BelongsToMany */ public function budgets(): BelongsToMany { @@ -143,8 +141,6 @@ class Transaction extends Model /** * Get the category(ies) this object belongs to. - * - * @return BelongsToMany */ public function categories(): BelongsToMany { @@ -153,8 +149,6 @@ class Transaction extends Model /** * Get the currency this object belongs to. - * - * @return BelongsTo */ public function foreignCurrency(): BelongsTo { @@ -163,10 +157,6 @@ class Transaction extends Model /** * Check for transactions AFTER a specified date. - * - * - * @param Builder $query - * @param Carbon $date */ public function scopeAfter(Builder $query, Carbon $date): void { @@ -178,11 +168,6 @@ class Transaction extends Model /** * Check if a table is joined. - * - * @param Builder $query - * @param string $table - * - * @return bool */ public static function isJoined(Builder $query, string $table): bool { @@ -199,10 +184,6 @@ class Transaction extends Model /** * Check for transactions BEFORE the specified date. - * - * - * @param Builder $query - * @param Carbon $date */ public function scopeBefore(Builder $query, Carbon $date): void { @@ -212,11 +193,6 @@ class Transaction extends Model $query->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')); } - /** - * - * @param Builder $query - * @param array $types - */ public function scopeTransactionTypes(Builder $query, array $types): void { if (!self::isJoined($query, 'transaction_journals')) { @@ -230,7 +206,6 @@ class Transaction extends Model } /** - * * @param mixed $value */ public function setAmountAttribute($value): void @@ -238,63 +213,47 @@ class Transaction extends Model $this->attributes['amount'] = (string)$value; } - /** - * @return BelongsTo - */ public function transactionCurrency(): BelongsTo { return $this->belongsTo(TransactionCurrency::class); } - /** - * @return BelongsTo - */ public function transactionJournal(): BelongsTo { return $this->belongsTo(TransactionJournal::class); } - /** - * @return Attribute - */ protected function accountId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } /** * Get the amount - * - * @return Attribute */ protected function amount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } /** * Get the foreign amount - * - * @return Attribute */ protected function foreignAmount(): Attribute { return Attribute::make( - get: static fn($value) => (string)$value, + get: static fn ($value) => (string)$value, ); } - /** - * @return Attribute - */ protected function transactionJournalId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/TransactionCurrency.php b/app/Models/TransactionCurrency.php index abbd1576a2..b59bc18acb 100644 --- a/app/Models/TransactionCurrency.php +++ b/app/Models/TransactionCurrency.php @@ -39,26 +39,27 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\TransactionCurrency * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property bool $enabled - * @property bool|null $userGroupDefault - * @property bool|null $userGroupEnabled - * @property string $code - * @property string $name - * @property string $symbol - * @property int $decimal_places - * @property-read Collection|BudgetLimit[] $budgetLimits - * @property-read int|null $budget_limits_count - * @property-read Collection|TransactionJournal[] $transactionJournals - * @property-read int|null $transaction_journals_count - * @property-read Collection|Transaction[] $transactions - * @property-read int|null $transactions_count + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property bool $enabled + * @property null|bool $userGroupDefault + * @property null|bool $userGroupEnabled + * @property string $code + * @property string $name + * @property string $symbol + * @property int $decimal_places + * @property BudgetLimit[]|Collection $budgetLimits + * @property null|int $budget_limits_count + * @property Collection|TransactionJournal[] $transactionJournals + * @property null|int $transaction_journals_count + * @property Collection|Transaction[] $transactions + * @property null|int $transactions_count + * * @method static \Illuminate\Database\Eloquent\Builder|TransactionCurrency newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|TransactionCurrency newQuery() - * @method static Builder|TransactionCurrency onlyTrashed() + * @method static Builder|TransactionCurrency onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|TransactionCurrency query() * @method static \Illuminate\Database\Eloquent\Builder|TransactionCurrency whereCode($value) * @method static \Illuminate\Database\Eloquent\Builder|TransactionCurrency whereCreatedAt($value) @@ -69,12 +70,14 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|TransactionCurrency whereName($value) * @method static \Illuminate\Database\Eloquent\Builder|TransactionCurrency whereSymbol($value) * @method static \Illuminate\Database\Eloquent\Builder|TransactionCurrency whereUpdatedAt($value) - * @method static Builder|TransactionCurrency withTrashed() - * @method static Builder|TransactionCurrency withoutTrashed() - * @property-read Collection $userGroups - * @property-read int|null $user_groups_count - * @property-read Collection $users - * @property-read int|null $users_count + * @method static Builder|TransactionCurrency withTrashed() + * @method static Builder|TransactionCurrency withoutTrashed() + * + * @property Collection $userGroups + * @property null|int $user_groups_count + * @property Collection $users + * @property null|int $users_count + * * @mixin Eloquent */ class TransactionCurrency extends Model @@ -98,9 +101,6 @@ class TransactionCurrency extends Model /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return TransactionCurrency * @throws NotFoundHttpException */ public static function routeBinder(string $value): self @@ -110,18 +110,15 @@ class TransactionCurrency extends Model $currency = self::find($currencyId); if (null !== $currency) { $currency->refreshForUser(auth()->user()); + return $currency; } } + throw new NotFoundHttpException(); } - /** - * @param User $user - * - * @return void - */ - public function refreshForUser(User $user) + public function refreshForUser(User $user): void { $current = $user->userGroup->currencies()->where('transaction_currencies.id', $this->id)->first(); $default = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); @@ -129,25 +126,16 @@ class TransactionCurrency extends Model $this->userGroupEnabled = null !== $current; } - /** - * @return HasMany - */ public function budgetLimits(): HasMany { return $this->hasMany(BudgetLimit::class); } - /** - * @return HasMany - */ public function transactionJournals(): HasMany { return $this->hasMany(TransactionJournal::class); } - /** - * @return HasMany - */ public function transactions(): HasMany { return $this->hasMany(Transaction::class); @@ -155,8 +143,6 @@ class TransactionCurrency extends Model /** * Link to user groups - * - * @return BelongsToMany */ public function userGroups(): BelongsToMany { @@ -165,21 +151,16 @@ class TransactionCurrency extends Model /** * Link to users - * - * @return BelongsToMany */ public function users(): BelongsToMany { return $this->belongsToMany(User::class)->withTimestamps()->withPivot('user_default'); } - /** - * @return Attribute - */ protected function decimalPlaces(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/TransactionGroup.php b/app/Models/TransactionGroup.php index 5cf4a599f6..fe63cc8a08 100644 --- a/app/Models/TransactionGroup.php +++ b/app/Models/TransactionGroup.php @@ -39,18 +39,19 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\TransactionGroup * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $user_id - * @property string|null $title - * @property-read Collection|TransactionJournal[] $transactionJournals - * @property-read int|null $transaction_journals_count - * @property-read User $user + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $user_id + * @property null|string $title + * @property Collection|TransactionJournal[] $transactionJournals + * @property null|int $transaction_journals_count + * @property User $user + * * @method static \Illuminate\Database\Eloquent\Builder|TransactionGroup newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|TransactionGroup newQuery() - * @method static Builder|TransactionGroup onlyTrashed() + * @method static Builder|TransactionGroup onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|TransactionGroup query() * @method static \Illuminate\Database\Eloquent\Builder|TransactionGroup whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|TransactionGroup whereDeletedAt($value) @@ -58,11 +59,15 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|TransactionGroup whereTitle($value) * @method static \Illuminate\Database\Eloquent\Builder|TransactionGroup whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|TransactionGroup whereUserId($value) - * @method static Builder|TransactionGroup withTrashed() - * @method static Builder|TransactionGroup withoutTrashed() - * @property int $user_group_id + * @method static Builder|TransactionGroup withTrashed() + * @method static Builder|TransactionGroup withoutTrashed() + * + * @property int $user_group_id + * * @method static \Illuminate\Database\Eloquent\Builder|TransactionGroup whereUserGroupId($value) - * @property-read UserGroup|null $userGroup + * + * @property null|UserGroup $userGroup + * * @mixin Eloquent */ class TransactionGroup extends Model @@ -71,7 +76,6 @@ class TransactionGroup extends Model use ReturnsIntegerUserIdTrait; use SoftDeletes; - protected $casts = [ 'id' => 'integer', @@ -82,15 +86,11 @@ class TransactionGroup extends Model 'date' => 'datetime', ]; - protected $fillable = ['user_id', 'user_group_id', 'title']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return TransactionGroup * @throws NotFoundHttpException */ public static function routeBinder(string $value): self @@ -98,15 +98,19 @@ class TransactionGroup extends Model app('log')->debug(sprintf('Now in %s("%s")', __METHOD__, $value)); if (auth()->check()) { $groupId = (int)$value; + /** @var User $user */ $user = auth()->user(); app('log')->debug(sprintf('User authenticated as %s', $user->email)); - /** @var TransactionGroup|null $group */ + + /** @var null|TransactionGroup $group */ $group = $user->transactionGroups() - ->with(['transactionJournals', 'transactionJournals.transactions']) - ->where('transaction_groups.id', $groupId)->first(['transaction_groups.*']); + ->with(['transactionJournals', 'transactionJournals.transactions']) + ->where('transaction_groups.id', $groupId)->first(['transaction_groups.*']) + ; if (null !== $group) { app('log')->debug(sprintf('Found group #%d.', $group->id)); + return $group; } } @@ -115,25 +119,16 @@ class TransactionGroup extends Model throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); } - /** - * @return HasMany - */ public function transactionJournals(): HasMany { return $this->hasMany(TransactionJournal::class); } - /** - * @return BelongsTo - */ public function userGroup(): BelongsTo { return $this->belongsTo(UserGroup::class); diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 0a85d38b6e..315bd11f4b 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -44,84 +44,89 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\TransactionJournal * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $user_id - * @property int $transaction_type_id - * @property int|string|null $transaction_group_id - * @property int|string|null $bill_id - * @property int|string|null $transaction_currency_id - * @property string|null $description - * @property Carbon $date - * @property Carbon|null $interest_date - * @property Carbon|null $book_date - * @property Carbon|null $process_date - * @property int $order - * @property int $tag_count - * @property string $transaction_type_type - * @property bool $encrypted - * @property bool $completed - * @property-read Collection|Attachment[] $attachments - * @property-read int|null $attachments_count - * @property-read Bill|null $bill - * @property-read Collection|Budget[] $budgets - * @property-read int|null $budgets_count - * @property-read Collection|Category[] $categories - * @property-read int|null $categories_count - * @property-read Collection|TransactionJournalLink[] $destJournalLinks - * @property-read int|null $dest_journal_links_count - * @property-read Collection|Note[] $notes - * @property-read int|null $notes_count - * @property-read Collection|PiggyBankEvent[] $piggyBankEvents - * @property-read int|null $piggy_bank_events_count - * @property-read Collection|TransactionJournalLink[] $sourceJournalLinks - * @property-read int|null $source_journal_links_count - * @property-read Collection|Tag[] $tags - * @property-read int|null $tags_count - * @property-read TransactionCurrency|null $transactionCurrency - * @property-read TransactionGroup|null $transactionGroup - * @property-read Collection|TransactionJournalMeta[] $transactionJournalMeta - * @property-read int|null $transaction_journal_meta_count - * @property-read TransactionType $transactionType - * @property-read Collection|Transaction[] $transactions - * @property-read int|null $transactions_count - * @property-read User $user - * @method static EloquentBuilder|TransactionJournal after(Carbon $date) - * @method static EloquentBuilder|TransactionJournal before(Carbon $date) - * @method static EloquentBuilder|TransactionJournal newModelQuery() - * @method static EloquentBuilder|TransactionJournal newQuery() + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $user_id + * @property int $transaction_type_id + * @property null|int|string $transaction_group_id + * @property null|int|string $bill_id + * @property null|int|string $transaction_currency_id + * @property null|string $description + * @property Carbon $date + * @property null|Carbon $interest_date + * @property null|Carbon $book_date + * @property null|Carbon $process_date + * @property int $order + * @property int $tag_count + * @property string $transaction_type_type + * @property bool $encrypted + * @property bool $completed + * @property Attachment[]|Collection $attachments + * @property null|int $attachments_count + * @property null|Bill $bill + * @property Budget[]|Collection $budgets + * @property null|int $budgets_count + * @property Category[]|Collection $categories + * @property null|int $categories_count + * @property Collection|TransactionJournalLink[] $destJournalLinks + * @property null|int $dest_journal_links_count + * @property Collection|Note[] $notes + * @property null|int $notes_count + * @property Collection|PiggyBankEvent[] $piggyBankEvents + * @property null|int $piggy_bank_events_count + * @property Collection|TransactionJournalLink[] $sourceJournalLinks + * @property null|int $source_journal_links_count + * @property Collection|Tag[] $tags + * @property null|int $tags_count + * @property null|TransactionCurrency $transactionCurrency + * @property null|TransactionGroup $transactionGroup + * @property Collection|TransactionJournalMeta[] $transactionJournalMeta + * @property null|int $transaction_journal_meta_count + * @property TransactionType $transactionType + * @property Collection|Transaction[] $transactions + * @property null|int $transactions_count + * @property User $user + * + * @method static EloquentBuilder|TransactionJournal after(Carbon $date) + * @method static EloquentBuilder|TransactionJournal before(Carbon $date) + * @method static EloquentBuilder|TransactionJournal newModelQuery() + * @method static EloquentBuilder|TransactionJournal newQuery() * @method static \Illuminate\Database\Query\Builder|TransactionJournal onlyTrashed() - * @method static EloquentBuilder|TransactionJournal query() - * @method static EloquentBuilder|TransactionJournal transactionTypes($types) - * @method static EloquentBuilder|TransactionJournal whereBillId($value) - * @method static EloquentBuilder|TransactionJournal whereBookDate($value) - * @method static EloquentBuilder|TransactionJournal whereCompleted($value) - * @method static EloquentBuilder|TransactionJournal whereCreatedAt($value) - * @method static EloquentBuilder|TransactionJournal whereDate($value) - * @method static EloquentBuilder|TransactionJournal whereDeletedAt($value) - * @method static EloquentBuilder|TransactionJournal whereDescription($value) - * @method static EloquentBuilder|TransactionJournal whereEncrypted($value) - * @method static EloquentBuilder|TransactionJournal whereId($value) - * @method static EloquentBuilder|TransactionJournal whereInterestDate($value) - * @method static EloquentBuilder|TransactionJournal whereOrder($value) - * @method static EloquentBuilder|TransactionJournal whereProcessDate($value) - * @method static EloquentBuilder|TransactionJournal whereTagCount($value) - * @method static EloquentBuilder|TransactionJournal whereTransactionCurrencyId($value) - * @method static EloquentBuilder|TransactionJournal whereTransactionGroupId($value) - * @method static EloquentBuilder|TransactionJournal whereTransactionTypeId($value) - * @method static EloquentBuilder|TransactionJournal whereUpdatedAt($value) - * @method static EloquentBuilder|TransactionJournal whereUserId($value) + * @method static EloquentBuilder|TransactionJournal query() + * @method static EloquentBuilder|TransactionJournal transactionTypes($types) + * @method static EloquentBuilder|TransactionJournal whereBillId($value) + * @method static EloquentBuilder|TransactionJournal whereBookDate($value) + * @method static EloquentBuilder|TransactionJournal whereCompleted($value) + * @method static EloquentBuilder|TransactionJournal whereCreatedAt($value) + * @method static EloquentBuilder|TransactionJournal whereDate($value) + * @method static EloquentBuilder|TransactionJournal whereDeletedAt($value) + * @method static EloquentBuilder|TransactionJournal whereDescription($value) + * @method static EloquentBuilder|TransactionJournal whereEncrypted($value) + * @method static EloquentBuilder|TransactionJournal whereId($value) + * @method static EloquentBuilder|TransactionJournal whereInterestDate($value) + * @method static EloquentBuilder|TransactionJournal whereOrder($value) + * @method static EloquentBuilder|TransactionJournal whereProcessDate($value) + * @method static EloquentBuilder|TransactionJournal whereTagCount($value) + * @method static EloquentBuilder|TransactionJournal whereTransactionCurrencyId($value) + * @method static EloquentBuilder|TransactionJournal whereTransactionGroupId($value) + * @method static EloquentBuilder|TransactionJournal whereTransactionTypeId($value) + * @method static EloquentBuilder|TransactionJournal whereUpdatedAt($value) + * @method static EloquentBuilder|TransactionJournal whereUserId($value) * @method static \Illuminate\Database\Query\Builder|TransactionJournal withTrashed() * @method static \Illuminate\Database\Query\Builder|TransactionJournal withoutTrashed() - * @property-read Collection|Location[] $locations - * @property-read int|null $locations_count - * @property int|string $the_count - * @property int $user_group_id + * + * @property Collection|Location[] $locations + * @property null|int $locations_count + * @property int|string $the_count + * @property int $user_group_id + * * @method static EloquentBuilder|TransactionJournal whereUserGroupId($value) - * @property-read Collection $auditLogEntries - * @property-read int|null $audit_log_entries_count + * + * @property Collection $auditLogEntries + * @property null|int $audit_log_entries_count + * * @mixin Eloquent */ class TransactionJournal extends Model @@ -131,7 +136,6 @@ class TransactionJournal extends Model use ReturnsIntegerUserIdTrait; use SoftDeletes; - protected $casts = [ 'created_at' => 'datetime', @@ -147,7 +151,6 @@ class TransactionJournal extends Model 'completed' => 'boolean', ]; - protected $fillable = [ 'user_id', @@ -167,18 +170,17 @@ class TransactionJournal extends Model /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return TransactionJournal * @throws NotFoundHttpException */ public static function routeBinder(string $value): self { if (auth()->check()) { $journalId = (int)$value; + /** @var User $user */ $user = auth()->user(); - /** @var TransactionJournal|null $journal */ + + /** @var null|TransactionJournal $journal */ $journal = $user->transactionJournals()->where('transaction_journals.id', $journalId)->first(['transaction_journals.*']); if (null !== $journal) { return $journal; @@ -188,65 +190,41 @@ class TransactionJournal extends Model throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); } - /** - * @return MorphMany - */ public function attachments(): MorphMany { return $this->morphMany(Attachment::class, 'attachable'); } - /** - * @return MorphMany - */ public function auditLogEntries(): MorphMany { return $this->morphMany(AuditLogEntry::class, 'auditable'); } - /** - * @return BelongsTo - */ public function bill(): BelongsTo { return $this->belongsTo(Bill::class); } - /** - * @return BelongsToMany - */ public function budgets(): BelongsToMany { return $this->belongsToMany(Budget::class); } - /** - * @return BelongsToMany - */ public function categories(): BelongsToMany { return $this->belongsToMany(Category::class); } - /** - * @return HasMany - */ public function destJournalLinks(): HasMany { return $this->hasMany(TransactionJournalLink::class, 'destination_id'); } - /** - * @return bool - */ public function isTransfer(): bool { if (null !== $this->transaction_type_type) { @@ -256,9 +234,6 @@ class TransactionJournal extends Model return $this->transactionType->isTransfer(); } - /** - * @return MorphMany - */ public function locations(): MorphMany { return $this->morphMany(Location::class, 'locatable'); @@ -272,43 +247,21 @@ class TransactionJournal extends Model return $this->morphMany(Note::class, 'noteable'); } - /** - * @return HasMany - */ public function piggyBankEvents(): HasMany { return $this->hasMany(PiggyBankEvent::class); } - /** - * - * @param EloquentBuilder $query - * @param Carbon $date - * - * @return EloquentBuilder - */ public function scopeAfter(EloquentBuilder $query, Carbon $date): EloquentBuilder { return $query->where('transaction_journals.date', '>=', $date->format('Y-m-d 00:00:00')); } - /** - * - * @param EloquentBuilder $query - * @param Carbon $date - * - * @return EloquentBuilder - */ public function scopeBefore(EloquentBuilder $query, Carbon $date): EloquentBuilder { return $query->where('transaction_journals.date', '<=', $date->format('Y-m-d 00:00:00')); } - /** - * - * @param EloquentBuilder $query - * @param array $types - */ public function scopeTransactionTypes(EloquentBuilder $query, array $types): void { if (!self::isJoined($query, 'transaction_types')) { @@ -321,12 +274,6 @@ class TransactionJournal extends Model /** * Checks if tables are joined. - * - * - * @param Builder $query - * @param string $table - * - * @return bool */ public static function isJoined(Builder $query, string $table): bool { @@ -340,79 +287,52 @@ class TransactionJournal extends Model return false; } - /** - * @return HasMany - */ public function sourceJournalLinks(): HasMany { return $this->hasMany(TransactionJournalLink::class, 'source_id'); } - /** - * @return BelongsToMany - */ public function tags(): BelongsToMany { return $this->belongsToMany(Tag::class); } - /** - * @return BelongsTo - */ public function transactionCurrency(): BelongsTo { return $this->belongsTo(TransactionCurrency::class); } - /** - * @return BelongsTo - */ public function transactionGroup(): BelongsTo { return $this->belongsTo(TransactionGroup::class); } - /** - * @return HasMany - */ public function transactionJournalMeta(): HasMany { return $this->hasMany(TransactionJournalMeta::class); } - /** - * @return BelongsTo - */ public function transactionType(): BelongsTo { return $this->belongsTo(TransactionType::class); } - /** - * @return HasMany - */ public function transactions(): HasMany { return $this->hasMany(Transaction::class); } - /** - * @return Attribute - */ protected function order(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function transactionTypeId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/TransactionJournalLink.php b/app/Models/TransactionJournalLink.php index 6f5dfe0f91..f284fbfb44 100644 --- a/app/Models/TransactionJournalLink.php +++ b/app/Models/TransactionJournalLink.php @@ -37,20 +37,21 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\TransactionJournalLink * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property int $link_type_id - * @property int $source_id - * @property int $destination_id - * @property string|null $comment - * @property-read TransactionJournal $destination - * @property-read LinkType $linkType - * @property-read Collection|Note[] $notes - * @property-read int|null $notes_count - * @property-read TransactionJournal $source - * @property-read string $inward - * @property-read string $outward + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property int $link_type_id + * @property int $source_id + * @property int $destination_id + * @property null|string $comment + * @property TransactionJournal $destination + * @property LinkType $linkType + * @property Collection|Note[] $notes + * @property null|int $notes_count + * @property TransactionJournal $source + * @property string $inward + * @property string $outward + * * @method static Builder|TransactionJournalLink newModelQuery() * @method static Builder|TransactionJournalLink newQuery() * @method static Builder|TransactionJournalLink query() @@ -61,6 +62,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static Builder|TransactionJournalLink whereLinkTypeId($value) * @method static Builder|TransactionJournalLink whereSourceId($value) * @method static Builder|TransactionJournalLink whereUpdatedAt($value) + * * @mixin Eloquent */ class TransactionJournalLink extends Model @@ -72,16 +74,13 @@ class TransactionJournalLink extends Model 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; + /** @var string The table to store the data in */ protected $table = 'journal_links'; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return TransactionJournalLink - * * @throws NotFoundHttpException */ public static function routeBinder(string $value): self @@ -89,29 +88,25 @@ class TransactionJournalLink extends Model if (auth()->check()) { $linkId = (int)$value; $link = self::where('journal_links.id', $linkId) - ->leftJoin('transaction_journals as t_a', 't_a.id', '=', 'source_id') - ->leftJoin('transaction_journals as t_b', 't_b.id', '=', 'destination_id') - ->where('t_a.user_id', auth()->user()->id) - ->where('t_b.user_id', auth()->user()->id) - ->first(['journal_links.*']); + ->leftJoin('transaction_journals as t_a', 't_a.id', '=', 'source_id') + ->leftJoin('transaction_journals as t_b', 't_b.id', '=', 'destination_id') + ->where('t_a.user_id', auth()->user()->id) + ->where('t_b.user_id', auth()->user()->id) + ->first(['journal_links.*']) + ; if (null !== $link) { return $link; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function destination(): BelongsTo { return $this->belongsTo(TransactionJournal::class, 'destination_id'); } - /** - * @return BelongsTo - */ public function linkType(): BelongsTo { return $this->belongsTo(LinkType::class); @@ -125,41 +120,29 @@ class TransactionJournalLink extends Model return $this->morphMany(Note::class, 'noteable'); } - /** - * @return BelongsTo - */ public function source(): BelongsTo { return $this->belongsTo(TransactionJournal::class, 'source_id'); } - /** - * @return Attribute - */ protected function destinationId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function linkTypeId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function sourceId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/TransactionJournalMeta.php b/app/Models/TransactionJournalMeta.php index 03b209d2a3..378e81e3e1 100644 --- a/app/Models/TransactionJournalMeta.php +++ b/app/Models/TransactionJournalMeta.php @@ -35,18 +35,19 @@ use Illuminate\Database\Query\Builder; /** * FireflyIII\Models\TransactionJournalMeta * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property int $transaction_journal_id - * @property string $name - * @property mixed $data - * @property string $hash - * @property Carbon|null $deleted_at - * @property-read TransactionJournal $transactionJournal + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property int $transaction_journal_id + * @property string $name + * @property mixed $data + * @property string $hash + * @property null|Carbon $deleted_at + * @property TransactionJournal $transactionJournal + * * @method static \Illuminate\Database\Eloquent\Builder|TransactionJournalMeta newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|TransactionJournalMeta newQuery() - * @method static Builder|TransactionJournalMeta onlyTrashed() + * @method static Builder|TransactionJournalMeta onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|TransactionJournalMeta query() * @method static \Illuminate\Database\Eloquent\Builder|TransactionJournalMeta whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|TransactionJournalMeta whereData($value) @@ -56,8 +57,9 @@ use Illuminate\Database\Query\Builder; * @method static \Illuminate\Database\Eloquent\Builder|TransactionJournalMeta whereName($value) * @method static \Illuminate\Database\Eloquent\Builder|TransactionJournalMeta whereTransactionJournalId($value) * @method static \Illuminate\Database\Eloquent\Builder|TransactionJournalMeta whereUpdatedAt($value) - * @method static Builder|TransactionJournalMeta withTrashed() - * @method static Builder|TransactionJournalMeta withoutTrashed() + * @method static Builder|TransactionJournalMeta withTrashed() + * @method static Builder|TransactionJournalMeta withoutTrashed() + * * @mixin Eloquent */ class TransactionJournalMeta extends Model @@ -65,7 +67,6 @@ class TransactionJournalMeta extends Model use ReturnsIntegerIdTrait; use SoftDeletes; - protected $casts = [ 'created_at' => 'datetime', @@ -74,11 +75,11 @@ class TransactionJournalMeta extends Model ]; protected $fillable = ['transaction_journal_id', 'name', 'data', 'hash']; + /** @var string The table to store the data in */ protected $table = 'journal_meta'; /** - * * @param mixed $value * * @return mixed @@ -89,7 +90,6 @@ class TransactionJournalMeta extends Model } /** - * * @param mixed $value */ public function setDataAttribute($value): void @@ -99,21 +99,15 @@ class TransactionJournalMeta extends Model $this->attributes['hash'] = hash('sha256', (string)$data); } - /** - * @return BelongsTo - */ public function transactionJournal(): BelongsTo { return $this->belongsTo(TransactionJournal::class); } - /** - * @return Attribute - */ protected function transactionJournalId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/TransactionType.php b/app/Models/TransactionType.php index 64c9414ef9..14d1c9d5a1 100644 --- a/app/Models/TransactionType.php +++ b/app/Models/TransactionType.php @@ -36,24 +36,26 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\TransactionType * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property string $type - * @property-read Collection|TransactionJournal[] $transactionJournals - * @property-read int|null $transaction_journals_count + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property string $type + * @property Collection|TransactionJournal[] $transactionJournals + * @property null|int $transaction_journals_count + * * @method static \Illuminate\Database\Eloquent\Builder|TransactionType newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|TransactionType newQuery() - * @method static Builder|TransactionType onlyTrashed() + * @method static Builder|TransactionType onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|TransactionType query() * @method static \Illuminate\Database\Eloquent\Builder|TransactionType whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|TransactionType whereDeletedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|TransactionType whereId($value) * @method static \Illuminate\Database\Eloquent\Builder|TransactionType whereType($value) * @method static \Illuminate\Database\Eloquent\Builder|TransactionType whereUpdatedAt($value) - * @method static Builder|TransactionType withTrashed() - * @method static Builder|TransactionType withoutTrashed() + * @method static Builder|TransactionType withTrashed() + * @method static Builder|TransactionType withoutTrashed() + * * @mixin Eloquent */ class TransactionType extends Model @@ -71,18 +73,15 @@ class TransactionType extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - ]; + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + ]; protected $fillable = ['type']; /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $type - * - * @return TransactionType * @throws NotFoundHttpException */ public static function routeBinder(string $type): self @@ -94,44 +93,30 @@ class TransactionType extends Model if (null !== $transactionType) { return $transactionType; } + throw new NotFoundHttpException(); } - /** - * @return bool - */ public function isDeposit(): bool { return self::DEPOSIT === $this->type; } - /** - * @return bool - */ public function isOpeningBalance(): bool { return self::OPENING_BALANCE === $this->type; } - /** - * @return bool - */ public function isTransfer(): bool { return self::TRANSFER === $this->type; } - /** - * @return bool - */ public function isWithdrawal(): bool { return self::WITHDRAWAL === $this->type; } - /** - * @return HasMany - */ public function transactionJournals(): HasMany { return $this->hasMany(TransactionJournal::class); diff --git a/app/Models/UserGroup.php b/app/Models/UserGroup.php index bff29b6ab0..614767c7d2 100644 --- a/app/Models/UserGroup.php +++ b/app/Models/UserGroup.php @@ -40,13 +40,14 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class UserGroup * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property string|null $deleted_at - * @property string $title - * @property-read Collection|GroupMembership[] $groupMemberships - * @property-read int|null $group_memberships_count + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|string $deleted_at + * @property string $title + * @property Collection|GroupMembership[] $groupMemberships + * @property null|int $group_memberships_count + * * @method static Builder|UserGroup newModelQuery() * @method static Builder|UserGroup newQuery() * @method static Builder|UserGroup query() @@ -55,40 +56,42 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static Builder|UserGroup whereId($value) * @method static Builder|UserGroup whereTitle($value) * @method static Builder|UserGroup whereUpdatedAt($value) - * @property-read Collection $accounts - * @property-read int|null $accounts_count - * @property-read Collection $availableBudgets - * @property-read int|null $available_budgets_count - * @property-read Collection $bills - * @property-read int|null $bills_count - * @property-read Collection $budgets - * @property-read int|null $budgets_count - * @property-read Collection $piggyBanks - * @property-read int|null $piggy_banks_count - * @property-read Collection $transactionJournals - * @property-read int|null $transaction_journals_count - * @property-read Collection $attachments - * @property-read int|null $attachments_count - * @property-read Collection $categories - * @property-read int|null $categories_count - * @property-read Collection $currencyExchangeRates - * @property-read int|null $currency_exchange_rates_count - * @property-read Collection $objectGroups - * @property-read int|null $object_groups_count - * @property-read Collection $recurrences - * @property-read int|null $recurrences_count - * @property-read Collection $ruleGroups - * @property-read int|null $rule_groups_count - * @property-read Collection $rules - * @property-read int|null $rules_count - * @property-read Collection $tags - * @property-read int|null $tags_count - * @property-read Collection $transactionGroups - * @property-read int|null $transaction_groups_count - * @property-read Collection $webhooks - * @property-read int|null $webhooks_count - * @property-read Collection $currencies - * @property-read int|null $currencies_count + * + * @property Collection $accounts + * @property null|int $accounts_count + * @property Collection $availableBudgets + * @property null|int $available_budgets_count + * @property Collection $bills + * @property null|int $bills_count + * @property Collection $budgets + * @property null|int $budgets_count + * @property Collection $piggyBanks + * @property null|int $piggy_banks_count + * @property Collection $transactionJournals + * @property null|int $transaction_journals_count + * @property Collection $attachments + * @property null|int $attachments_count + * @property Collection $categories + * @property null|int $categories_count + * @property Collection $currencyExchangeRates + * @property null|int $currency_exchange_rates_count + * @property Collection $objectGroups + * @property null|int $object_groups_count + * @property Collection $recurrences + * @property null|int $recurrences_count + * @property Collection $ruleGroups + * @property null|int $rule_groups_count + * @property Collection $rules + * @property null|int $rules_count + * @property Collection $tags + * @property null|int $tags_count + * @property Collection $transactionGroups + * @property null|int $transaction_groups_count + * @property Collection $webhooks + * @property null|int $webhooks_count + * @property Collection $currencies + * @property null|int $currencies_count + * * @mixin Eloquent */ class UserGroup extends Model @@ -100,18 +103,17 @@ class UserGroup extends Model /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return UserGroup * @throws NotFoundHttpException */ public static function routeBinder(string $value): self { if (auth()->check()) { $userGroupId = (int)$value; + /** @var User $user */ $user = auth()->user(); - /** @var UserGroup|null $userGroup */ + + /** @var null|UserGroup $userGroup */ $userGroup = self::find($userGroupId); if (null === $userGroup) { throw new NotFoundHttpException(); @@ -123,13 +125,12 @@ class UserGroup extends Model return $userGroup; } } + throw new NotFoundHttpException(); } /** * Link to accounts. - * - * @return HasMany */ public function accounts(): HasMany { @@ -138,8 +139,6 @@ class UserGroup extends Model /** * Link to attachments. - * - * @return HasMany */ public function attachments(): HasMany { @@ -148,8 +147,6 @@ class UserGroup extends Model /** * Link to bills. - * - * @return HasMany */ public function availableBudgets(): HasMany { @@ -158,8 +155,6 @@ class UserGroup extends Model /** * Link to bills. - * - * @return HasMany */ public function bills(): HasMany { @@ -168,8 +163,6 @@ class UserGroup extends Model /** * Link to budgets. - * - * @return HasMany */ public function budgets(): HasMany { @@ -178,8 +171,6 @@ class UserGroup extends Model /** * Link to categories. - * - * @return HasMany */ public function categories(): HasMany { @@ -188,8 +179,6 @@ class UserGroup extends Model /** * Link to currencies - * - * @return BelongsToMany */ public function currencies(): BelongsToMany { @@ -198,26 +187,17 @@ class UserGroup extends Model /** * Link to exchange rates. - * - * @return HasMany */ public function currencyExchangeRates(): HasMany { return $this->hasMany(CurrencyExchangeRate::class); } - /** - * - * @return HasMany - */ public function groupMemberships(): HasMany { return $this->hasMany(GroupMembership::class); } - /** - * @return HasMany - */ public function objectGroups(): HasMany { return $this->hasMany(ObjectGroup::class); @@ -225,49 +205,32 @@ class UserGroup extends Model /** * Link to piggy banks. - * - * @return HasManyThrough */ public function piggyBanks(): HasManyThrough { return $this->hasManyThrough(PiggyBank::class, Account::class); } - /** - * @return HasMany - */ public function recurrences(): HasMany { return $this->hasMany(Recurrence::class); } - /** - * @return HasMany - */ public function ruleGroups(): HasMany { return $this->hasMany(RuleGroup::class); } - /** - * @return HasMany - */ public function rules(): HasMany { return $this->hasMany(Rule::class); } - /** - * @return HasMany - */ public function tags(): HasMany { return $this->hasMany(Tag::class); } - /** - * @return HasMany - */ public function transactionGroups(): HasMany { return $this->hasMany(TransactionGroup::class); @@ -275,17 +238,12 @@ class UserGroup extends Model /** * Link to transaction journals. - * - * @return HasMany */ public function transactionJournals(): HasMany { return $this->hasMany(TransactionJournal::class); } - /** - * @return HasMany - */ public function webhooks(): HasMany { return $this->hasMany(Webhook::class); diff --git a/app/Models/UserRole.php b/app/Models/UserRole.php index 7c902a3c61..3b18b0262d 100644 --- a/app/Models/UserRole.php +++ b/app/Models/UserRole.php @@ -35,13 +35,14 @@ use Illuminate\Database\Eloquent\Relations\HasMany; /** * Class UserRole * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property string|null $deleted_at - * @property string $title - * @property-read Collection|GroupMembership[] $groupMemberships - * @property-read int|null $group_memberships_count + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|string $deleted_at + * @property string $title + * @property Collection|GroupMembership[] $groupMemberships + * @property null|int $group_memberships_count + * * @method static Builder|UserRole newModelQuery() * @method static Builder|UserRole newQuery() * @method static Builder|UserRole query() @@ -50,6 +51,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; * @method static Builder|UserRole whereId($value) * @method static Builder|UserRole whereTitle($value) * @method static Builder|UserRole whereUpdatedAt($value) + * * @mixin Eloquent */ class UserRole extends Model @@ -58,10 +60,6 @@ class UserRole extends Model protected $fillable = ['title']; - /** - * - * @return HasMany - */ public function groupMemberships(): HasMany { return $this->hasMany(GroupMembership::class); diff --git a/app/Models/Webhook.php b/app/Models/Webhook.php index 9d060a42cd..19e344b05d 100644 --- a/app/Models/Webhook.php +++ b/app/Models/Webhook.php @@ -42,41 +42,47 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\Webhook * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $user_id - * @property bool $active - * @property int $trigger - * @property int $response - * @property int $delivery - * @property string $url - * @property-read User $user - * @property-read Collection|WebhookMessage[] $webhookMessages - * @property-read int|null $webhook_messages_count - * @method static Builder|Webhook newModelQuery() - * @method static Builder|Webhook newQuery() + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|Carbon $deleted_at + * @property int $user_id + * @property bool $active + * @property int $trigger + * @property int $response + * @property int $delivery + * @property string $url + * @property User $user + * @property Collection|WebhookMessage[] $webhookMessages + * @property null|int $webhook_messages_count + * + * @method static Builder|Webhook newModelQuery() + * @method static Builder|Webhook newQuery() * @method static \Illuminate\Database\Query\Builder|Webhook onlyTrashed() - * @method static Builder|Webhook query() - * @method static Builder|Webhook whereActive($value) - * @method static Builder|Webhook whereCreatedAt($value) - * @method static Builder|Webhook whereDeletedAt($value) - * @method static Builder|Webhook whereDelivery($value) - * @method static Builder|Webhook whereId($value) - * @method static Builder|Webhook whereResponse($value) - * @method static Builder|Webhook whereTrigger($value) - * @method static Builder|Webhook whereUpdatedAt($value) - * @method static Builder|Webhook whereUrl($value) - * @method static Builder|Webhook whereUserId($value) + * @method static Builder|Webhook query() + * @method static Builder|Webhook whereActive($value) + * @method static Builder|Webhook whereCreatedAt($value) + * @method static Builder|Webhook whereDeletedAt($value) + * @method static Builder|Webhook whereDelivery($value) + * @method static Builder|Webhook whereId($value) + * @method static Builder|Webhook whereResponse($value) + * @method static Builder|Webhook whereTrigger($value) + * @method static Builder|Webhook whereUpdatedAt($value) + * @method static Builder|Webhook whereUrl($value) + * @method static Builder|Webhook whereUserId($value) * @method static \Illuminate\Database\Query\Builder|Webhook withTrashed() * @method static \Illuminate\Database\Query\Builder|Webhook withoutTrashed() - * @property string $title - * @property string $secret + * + * @property string $title + * @property string $secret + * * @method static Builder|Webhook whereSecret($value) * @method static Builder|Webhook whereTitle($value) - * @property int $user_group_id + * + * @property int $user_group_id + * * @method static Builder|Webhook whereUserGroupId($value) + * * @mixin Eloquent */ class Webhook extends Model @@ -87,16 +93,13 @@ class Webhook extends Model protected $casts = [ - 'active' => 'boolean', - 'trigger' => 'integer', - 'response' => 'integer', - 'delivery' => 'integer', - ]; + 'active' => 'boolean', + 'trigger' => 'integer', + 'response' => 'integer', + 'delivery' => 'integer', + ]; protected $fillable = ['active', 'trigger', 'response', 'delivery', 'user_id', 'user_group_id', 'url', 'title', 'secret']; - /** - * @return array - */ public static function getDeliveries(): array { $array = []; @@ -104,12 +107,10 @@ class Webhook extends Model foreach ($set as $item) { $array[$item->value] = $item->name; } + return $array; } - /** - * @return array - */ public static function getDeliveriesForValidation(): array { $array = []; @@ -118,12 +119,10 @@ class Webhook extends Model $array[$item->name] = $item->value; $array[$item->value] = $item->value; } + return $array; } - /** - * @return array - */ public static function getResponses(): array { $array = []; @@ -131,12 +130,10 @@ class Webhook extends Model foreach ($set as $item) { $array[$item->value] = $item->name; } + return $array; } - /** - * @return array - */ public static function getResponsesForValidation(): array { $array = []; @@ -145,12 +142,10 @@ class Webhook extends Model $array[$item->name] = $item->value; $array[$item->value] = $item->value; } + return $array; } - /** - * @return array - */ public static function getTriggers(): array { $array = []; @@ -158,12 +153,10 @@ class Webhook extends Model foreach ($set as $item) { $array[$item->value] = $item->name; } + return $array; } - /** - * @return array - */ public static function getTriggersForValidation(): array { $array = []; @@ -172,43 +165,38 @@ class Webhook extends Model $array[$item->name] = $item->value; $array[$item->value] = $item->value; } + return $array; } /** * Route binder. Converts the key in the URL to the specified object (or throw 404). * - * @param string $value - * - * @return Webhook * @throws NotFoundHttpException */ public static function routeBinder(string $value): self { if (auth()->check()) { $webhookId = (int)$value; + /** @var User $user */ $user = auth()->user(); - /** @var Webhook|null $webhook */ + + /** @var null|Webhook $webhook */ $webhook = $user->webhooks()->find($webhookId); if (null !== $webhook) { return $webhook; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function user(): BelongsTo { return $this->belongsTo(User::class); } - /** - * @return HasMany - */ public function webhookMessages(): HasMany { return $this->hasMany(WebhookMessage::class); diff --git a/app/Models/WebhookAttempt.php b/app/Models/WebhookAttempt.php index c591bd469e..46194902db 100644 --- a/app/Models/WebhookAttempt.php +++ b/app/Models/WebhookAttempt.php @@ -37,15 +37,16 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class WebhookAttempt * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property string|null $deleted_at - * @property int $webhook_message_id - * @property int|string $status_code - * @property string|null $logs - * @property string|null $response - * @property-read WebhookMessage $webhookMessage + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|string $deleted_at + * @property int $webhook_message_id + * @property int|string $status_code + * @property null|string $logs + * @property null|string $response + * @property 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() @@ -57,9 +58,10 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @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) - * @method static Builder|WebhookAttempt onlyTrashed() - * @method static Builder|WebhookAttempt withTrashed() - * @method static Builder|WebhookAttempt withoutTrashed() + * @method static Builder|WebhookAttempt onlyTrashed() + * @method static Builder|WebhookAttempt withTrashed() + * @method static Builder|WebhookAttempt withoutTrashed() + * * @mixin Eloquent */ class WebhookAttempt extends Model @@ -70,41 +72,35 @@ class WebhookAttempt extends Model /** * 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): self { if (auth()->check()) { $attemptId = (int)$value; + /** @var User $user */ $user = auth()->user(); - /** @var WebhookAttempt|null $attempt */ + + /** @var null|WebhookAttempt $attempt */ $attempt = self::find($attemptId); if (null !== $attempt && $attempt->webhookMessage->webhook->user_id === $user->id) { return $attempt; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function webhookMessage(): BelongsTo { return $this->belongsTo(WebhookMessage::class); } - /** - * @return Attribute - */ protected function webhookMessageId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Models/WebhookMessage.php b/app/Models/WebhookMessage.php index 20c88b0da1..cc9d9e6da2 100644 --- a/app/Models/WebhookMessage.php +++ b/app/Models/WebhookMessage.php @@ -38,18 +38,19 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\WebhookMessage * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property string|null $deleted_at - * @property int $webhook_id - * @property bool $sent - * @property bool $errored - * @property int $attempts - * @property string $uuid - * @property array $message - * @property array|null $logs - * @property-read Webhook $webhook + * @property int $id + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|string $deleted_at + * @property int $webhook_id + * @property bool $sent + * @property bool $errored + * @property int $attempts + * @property string $uuid + * @property array $message + * @property null|array $logs + * @property Webhook $webhook + * * @method static Builder|WebhookMessage newModelQuery() * @method static Builder|WebhookMessage newQuery() * @method static Builder|WebhookMessage query() @@ -64,8 +65,10 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static Builder|WebhookMessage whereUpdatedAt($value) * @method static Builder|WebhookMessage whereUuid($value) * @method static Builder|WebhookMessage whereWebhookId($value) - * @property-read Collection|WebhookAttempt[] $webhookAttempts - * @property-read int|null $webhook_attempts_count + * + * @property Collection|WebhookAttempt[] $webhookAttempts + * @property null|int $webhook_attempts_count + * * @mixin Eloquent */ class WebhookMessage extends Model @@ -84,37 +87,31 @@ class WebhookMessage extends Model /** * 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): self { if (auth()->check()) { $messageId = (int)$value; + /** @var User $user */ $user = auth()->user(); - /** @var WebhookMessage|null $message */ + + /** @var null|WebhookMessage $message */ $message = self::find($messageId); if (null !== $message && $message->webhook->user_id === $user->id) { return $message; } } + throw new NotFoundHttpException(); } - /** - * @return BelongsTo - */ public function webhook(): BelongsTo { return $this->belongsTo(Webhook::class); } - /** - * @return HasMany - */ public function webhookAttempts(): HasMany { return $this->hasMany(WebhookAttempt::class); @@ -122,23 +119,18 @@ class WebhookMessage extends Model /** * Get the amount - * - * @return Attribute */ protected function sent(): Attribute { return Attribute::make( - get: static fn($value) => (bool)$value, + get: static fn ($value) => (bool)$value, ); } - /** - * @return Attribute - */ protected function webhookId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Notifications/Admin/TestNotification.php b/app/Notifications/Admin/TestNotification.php index 3d3a276092..03b52e93d6 100644 --- a/app/Notifications/Admin/TestNotification.php +++ b/app/Notifications/Admin/TestNotification.php @@ -41,8 +41,6 @@ class TestNotification extends Notification /** * Create a new notification instance. - * - * @return void */ public function __construct(string $address) { @@ -53,6 +51,7 @@ class TestNotification extends Notification * Get the array representation of the notification. * * @param mixed $notifiable + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return array @@ -60,7 +59,6 @@ class TestNotification extends Notification public function toArray($notifiable) { return [ - // ]; } @@ -68,6 +66,7 @@ class TestNotification extends Notification * Get the mail representation of the notification. * * @param mixed $notifiable + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return MailMessage @@ -76,13 +75,15 @@ class TestNotification extends Notification { return (new MailMessage()) ->markdown('emails.admin-test', ['email' => $this->address]) - ->subject((string)trans('email.admin_test_subject')); + ->subject((string)trans('email.admin_test_subject')) + ; } /** * Get the Slack representation of the notification. * * @param mixed $notifiable + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return SlackMessage @@ -94,6 +95,7 @@ class TestNotification extends Notification /** * Get the notification's delivery channels. + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @param mixed $notifiable @@ -106,6 +108,7 @@ class TestNotification extends Notification if (UrlValidator::isValidWebhookURL($slackUrl)) { return ['mail', 'slack']; } + return ['mail']; } } diff --git a/app/Notifications/Admin/UserInvitation.php b/app/Notifications/Admin/UserInvitation.php index a9f05e7014..15d97d1aa2 100644 --- a/app/Notifications/Admin/UserInvitation.php +++ b/app/Notifications/Admin/UserInvitation.php @@ -42,8 +42,6 @@ class UserInvitation extends Notification /** * Create a new notification instance. - * - * @return void */ public function __construct(InvitedUser $invitee) { @@ -56,12 +54,12 @@ class UserInvitation extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { return [ - // ]; } @@ -71,13 +69,15 @@ class UserInvitation extends Notification * @param mixed $notifiable * * @return MailMessage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { return (new MailMessage()) ->markdown('emails.invitation-created', ['email' => $this->invitee->user->email, 'invitee' => $this->invitee->email]) - ->subject((string)trans('email.invitation_created_subject')); + ->subject((string)trans('email.invitation_created_subject')) + ; } /** @@ -86,6 +86,7 @@ class UserInvitation extends Notification * @param mixed $notifiable * * @return SlackMessage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toSlack($notifiable) @@ -101,6 +102,7 @@ class UserInvitation extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) @@ -109,6 +111,7 @@ class UserInvitation extends Notification if (UrlValidator::isValidWebhookURL($slackUrl)) { return ['mail', 'slack']; } + return ['mail']; } } diff --git a/app/Notifications/Admin/UserRegistration.php b/app/Notifications/Admin/UserRegistration.php index cccb74cb8a..759e200d82 100644 --- a/app/Notifications/Admin/UserRegistration.php +++ b/app/Notifications/Admin/UserRegistration.php @@ -42,8 +42,6 @@ class UserRegistration extends Notification /** * Create a new notification instance. - * - * @return void */ public function __construct(User $user) { @@ -56,12 +54,12 @@ class UserRegistration extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { return [ - // ]; } @@ -71,13 +69,15 @@ class UserRegistration extends Notification * @param mixed $notifiable * * @return MailMessage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { return (new MailMessage()) ->markdown('emails.registered-admin', ['email' => $this->user->email, 'id' => $this->user->id]) - ->subject((string)trans('email.registered_subject_admin')); + ->subject((string)trans('email.registered_subject_admin')) + ; } /** @@ -86,6 +86,7 @@ class UserRegistration extends Notification * @param mixed $notifiable * * @return SlackMessage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toSlack($notifiable) @@ -99,6 +100,7 @@ class UserRegistration extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) @@ -107,6 +109,7 @@ class UserRegistration extends Notification if (UrlValidator::isValidWebhookURL($slackUrl)) { return ['mail', 'slack']; } + return ['mail']; } } diff --git a/app/Notifications/Admin/VersionCheckResult.php b/app/Notifications/Admin/VersionCheckResult.php index 49dfaffa66..7ee75e8933 100644 --- a/app/Notifications/Admin/VersionCheckResult.php +++ b/app/Notifications/Admin/VersionCheckResult.php @@ -32,7 +32,6 @@ use Illuminate\Notifications\Notification; /** * Class VersionCheckResult - * */ class VersionCheckResult extends Notification { @@ -42,8 +41,6 @@ class VersionCheckResult extends Notification /** * Create a new notification instance. - * - * @return void */ public function __construct(string $message) { @@ -56,12 +53,12 @@ class VersionCheckResult extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { return [ - // ]; } @@ -71,13 +68,15 @@ class VersionCheckResult extends Notification * @param mixed $notifiable * * @return MailMessage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { return (new MailMessage()) ->markdown('emails.new-version', ['message' => $this->message]) - ->subject((string)trans('email.new_version_email_subject')); + ->subject((string)trans('email.new_version_email_subject')) + ; } /** @@ -86,14 +85,16 @@ class VersionCheckResult extends Notification * @param mixed $notifiable * * @return SlackMessage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toSlack($notifiable) { return (new SlackMessage())->content($this->message) - ->attachment(static function ($attachment) { - $attachment->title('Firefly III @ GitHub', 'https://github.com/firefly-iii/firefly-iii/releases'); - }); + ->attachment(static function ($attachment): void { + $attachment->title('Firefly III @ GitHub', 'https://github.com/firefly-iii/firefly-iii/releases'); + }) + ; } /** @@ -102,6 +103,7 @@ class VersionCheckResult extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) @@ -110,6 +112,7 @@ class VersionCheckResult extends Notification if (UrlValidator::isValidWebhookURL($slackUrl)) { return ['mail', 'slack']; } + return ['mail']; } } diff --git a/app/Notifications/User/BillReminder.php b/app/Notifications/User/BillReminder.php index d017273a77..abadbbc6cb 100644 --- a/app/Notifications/User/BillReminder.php +++ b/app/Notifications/User/BillReminder.php @@ -45,8 +45,6 @@ class BillReminder extends Notification /** * Create a new notification instance. - * - * @return void */ public function __construct(Bill $bill, string $field, int $diff) { @@ -61,12 +59,12 @@ class BillReminder extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { return [ - // ]; } @@ -76,6 +74,7 @@ class BillReminder extends Notification * @param mixed $notifiable * * @return MailMessage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) @@ -87,7 +86,8 @@ class BillReminder extends Notification return (new MailMessage()) ->markdown('emails.bill-warning', ['field' => $this->field, 'diff' => $this->diff, 'bill' => $this->bill]) - ->subject($subject); + ->subject($subject) + ; } /** @@ -96,6 +96,7 @@ class BillReminder extends Notification * @param mixed $notifiable * * @return SlackMessage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toSlack($notifiable) @@ -106,12 +107,14 @@ class BillReminder extends Notification } $bill = $this->bill; $url = route('bills.show', [$bill->id]); + return (new SlackMessage()) ->warning() - ->attachment(static function ($attachment) use ($bill, $url) { + ->attachment(static function ($attachment) use ($bill, $url): void { $attachment->title((string)trans('firefly.visit_bill', ['name' => $bill->name]), $url); }) - ->content($message); + ->content($message) + ; } /** @@ -120,11 +123,12 @@ class BillReminder extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) { - /** @var User|null $user */ + /** @var null|User $user */ $user = auth()->user(); $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; if (is_array($slackUrl)) { @@ -133,6 +137,7 @@ class BillReminder extends Notification if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { return ['mail', 'slack']; } + return ['mail']; } } diff --git a/app/Notifications/User/NewAccessToken.php b/app/Notifications/User/NewAccessToken.php index 6ee9e4ce80..b32192d089 100644 --- a/app/Notifications/User/NewAccessToken.php +++ b/app/Notifications/User/NewAccessToken.php @@ -40,8 +40,6 @@ class NewAccessToken extends Notification /** * Create a new notification instance. - * - * @return void */ public function __construct() {} @@ -51,12 +49,12 @@ class NewAccessToken extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { return [ - // ]; } @@ -66,13 +64,15 @@ class NewAccessToken extends Notification * @param mixed $notifiable * * @return MailMessage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { return (new MailMessage()) ->markdown('emails.token-created') - ->subject((string)trans('email.access_token_created_subject')); + ->subject((string)trans('email.access_token_created_subject')) + ; } /** @@ -81,6 +81,7 @@ class NewAccessToken extends Notification * @param mixed $notifiable * * @return SlackMessage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toSlack($notifiable) @@ -94,11 +95,12 @@ class NewAccessToken extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) { - /** @var User|null $user */ + /** @var null|User $user */ $user = auth()->user(); $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; if (is_array($slackUrl)) { @@ -107,6 +109,7 @@ class NewAccessToken extends Notification if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { return ['mail', 'slack']; } + return ['mail']; } } diff --git a/app/Notifications/User/RuleActionFailed.php b/app/Notifications/User/RuleActionFailed.php index 4f4ec4e48a..067fe79fef 100644 --- a/app/Notifications/User/RuleActionFailed.php +++ b/app/Notifications/User/RuleActionFailed.php @@ -45,8 +45,6 @@ class RuleActionFailed extends Notification /** * Create a new notification instance. - * - * @return void */ public function __construct(array $params) { @@ -56,8 +54,6 @@ class RuleActionFailed extends Notification $this->groupLink = $groupLink; $this->ruleTitle = $ruleTitle; $this->ruleLink = $ruleLink; - - } /** @@ -66,12 +62,12 @@ class RuleActionFailed extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { return [ - // ]; } @@ -81,6 +77,7 @@ class RuleActionFailed extends Notification * @param mixed $notifiable * * @return SlackMessage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toSlack($notifiable) @@ -90,9 +87,9 @@ class RuleActionFailed extends Notification $ruleTitle = $this->ruleTitle; $ruleLink = $this->ruleLink; - return (new SlackMessage())->content($this->message)->attachment(static function ($attachment) use ($groupTitle, $groupLink) { + return (new SlackMessage())->content($this->message)->attachment(static function ($attachment) use ($groupTitle, $groupLink): void { $attachment->title((string)trans('rules.inspect_transaction', ['title' => $groupTitle]), $groupLink); - })->attachment(static function ($attachment) use ($ruleTitle, $ruleLink) { + })->attachment(static function ($attachment) use ($ruleTitle, $ruleLink): void { $attachment->title((string)trans('rules.inspect_rule', ['title' => $ruleTitle]), $ruleLink); }); } @@ -103,11 +100,12 @@ class RuleActionFailed extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) { - /** @var User|null $user */ + /** @var null|User $user */ $user = auth()->user(); $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; if (is_array($slackUrl)) { @@ -115,9 +113,11 @@ class RuleActionFailed extends Notification } if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { app('log')->debug('Will send ruleActionFailed through Slack!'); + return ['slack']; } app('log')->debug('Will NOT send ruleActionFailed through Slack'); + return []; } } diff --git a/app/Notifications/User/TransactionCreation.php b/app/Notifications/User/TransactionCreation.php index 5eefc7717c..fcf9f95fc0 100644 --- a/app/Notifications/User/TransactionCreation.php +++ b/app/Notifications/User/TransactionCreation.php @@ -39,8 +39,6 @@ class TransactionCreation extends Notification /** * Create a new notification instance. - * - * @return void */ public function __construct(array $collection) { @@ -53,12 +51,12 @@ class TransactionCreation extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { return [ - // ]; } @@ -68,13 +66,15 @@ class TransactionCreation extends Notification * @param mixed $notifiable * * @return MailMessage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { return (new MailMessage()) ->markdown('emails.report-new-journals', ['transformed' => $this->collection]) - ->subject(trans_choice('email.new_journals_subject', count($this->collection))); + ->subject(trans_choice('email.new_journals_subject', count($this->collection))) + ; } /** @@ -83,6 +83,7 @@ class TransactionCreation extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) diff --git a/app/Notifications/User/UserLogin.php b/app/Notifications/User/UserLogin.php index 5f0a22728e..e7fc4e8a53 100644 --- a/app/Notifications/User/UserLogin.php +++ b/app/Notifications/User/UserLogin.php @@ -43,8 +43,6 @@ class UserLogin extends Notification /** * Create a new notification instance. - * - * @return void */ public function __construct(string $ip) { @@ -57,12 +55,12 @@ class UserLogin extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { return [ - // ]; } @@ -72,12 +70,14 @@ class UserLogin extends Notification * @param mixed $notifiable * * @return MailMessage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { $time = now(config('app.timezone'))->isoFormat((string)trans('config.date_time_js')); $host = ''; + try { $hostName = app('steam')->getHostName($this->ip); } catch (FireflyException $e) { @@ -90,7 +90,8 @@ class UserLogin extends Notification return (new MailMessage()) ->markdown('emails.new-ip', ['time' => $time, 'ipAddress' => $this->ip, 'host' => $host]) - ->subject((string)trans('email.login_from_new_ip')); + ->subject((string)trans('email.login_from_new_ip')) + ; } /** @@ -99,11 +100,13 @@ class UserLogin extends Notification * @param mixed $notifiable * * @return SlackMessage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toSlack($notifiable) { $host = ''; + try { $hostName = app('steam')->getHostName($this->ip); } catch (FireflyException $e) { @@ -123,11 +126,12 @@ class UserLogin extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) { - /** @var User|null $user */ + /** @var null|User $user */ $user = auth()->user(); $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; if (is_array($slackUrl)) { @@ -136,6 +140,7 @@ class UserLogin extends Notification if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { return ['mail', 'slack']; } + return ['mail']; } } diff --git a/app/Notifications/User/UserNewPassword.php b/app/Notifications/User/UserNewPassword.php index bf0516fdb7..e4090984d5 100644 --- a/app/Notifications/User/UserNewPassword.php +++ b/app/Notifications/User/UserNewPassword.php @@ -39,8 +39,6 @@ class UserNewPassword extends Notification /** * Create a new notification instance. - * - * @return void */ public function __construct(string $url) { @@ -53,12 +51,12 @@ class UserNewPassword extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { return [ - // ]; } @@ -68,13 +66,15 @@ class UserNewPassword extends Notification * @param mixed $notifiable * * @return MailMessage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { return (new MailMessage()) ->markdown('emails.password', ['url' => $this->url]) - ->subject((string)trans('email.reset_pw_subject')); + ->subject((string)trans('email.reset_pw_subject')) + ; } /** @@ -83,6 +83,7 @@ class UserNewPassword extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) diff --git a/app/Notifications/User/UserRegistration.php b/app/Notifications/User/UserRegistration.php index 526065bec6..50aae568a3 100644 --- a/app/Notifications/User/UserRegistration.php +++ b/app/Notifications/User/UserRegistration.php @@ -37,8 +37,6 @@ class UserRegistration extends Notification /** * Create a new notification instance. - * - * @return void */ public function __construct() {} @@ -48,12 +46,12 @@ class UserRegistration extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { return [ - // ]; } @@ -63,13 +61,15 @@ class UserRegistration extends Notification * @param mixed $notifiable * * @return MailMessage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { return (new MailMessage()) ->markdown('emails.registered', ['address' => route('index')]) - ->subject((string)trans('email.registered_subject')); + ->subject((string)trans('email.registered_subject')) + ; } /** @@ -78,6 +78,7 @@ class UserRegistration extends Notification * @param mixed $notifiable * * @return array + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 5094305b08..49c6271e8d 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -36,8 +36,6 @@ class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. - * - * @return void */ public function boot(): void { @@ -47,19 +45,19 @@ class AppServiceProvider extends ServiceProvider 'Cache-Control' => 'no-store', ]; $uuid = (string)request()->header('X-Trace-Id'); - if ('' !== trim($uuid) && (preg_match('/^[a-f\d]{8}(-[a-f\d]{4}){4}[a-f\d]{8}$/i', trim($uuid)) === 1)) { + if ('' !== trim($uuid) && (1 === preg_match('/^[a-f\d]{8}(-[a-f\d]{4}){4}[a-f\d]{8}$/i', trim($uuid)))) { $headers['X-Trace-Id'] = $uuid; } + return response() ->json($value) - ->withHeaders($headers); + ->withHeaders($headers) + ; }); } /** * Register any application services. - * - * @return void */ public function register(): void { diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 9c7dfe117e..6ad1fc165b 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -41,9 +41,8 @@ class AuthServiceProvider extends ServiceProvider /** * Register any authentication / authorization services. - * @SuppressWarnings(PHPMD.UnusedFormalParameter) * - * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function boot(): void { diff --git a/app/Providers/BudgetServiceProvider.php b/app/Providers/BudgetServiceProvider.php index c48674fd7f..1c31ea805f 100644 --- a/app/Providers/BudgetServiceProvider.php +++ b/app/Providers/BudgetServiceProvider.php @@ -54,6 +54,8 @@ class BudgetServiceProvider extends ServiceProvider /** * Register the application services. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function register(): void { diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 8df7fe412a..6893d62c6b 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -32,7 +32,6 @@ use FireflyIII\Events\Model\BudgetLimit\Created; use FireflyIII\Events\Model\BudgetLimit\Deleted; use FireflyIII\Events\Model\BudgetLimit\Updated; use FireflyIII\Events\Model\PiggyBank\ChangedAmount; -use FireflyIII\Events\Model\PiggyBank\ChangedPiggyBankAmount; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject; use FireflyIII\Events\NewVersionAvailable; @@ -87,7 +86,7 @@ use Laravel\Passport\Events\AccessTokenCreated; /** * Class EventServiceProvider. * - + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class EventServiceProvider extends ServiceProvider { @@ -206,7 +205,6 @@ class EventServiceProvider extends ServiceProvider RuleActionFailedOnObject::class => [ 'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnObject', ], - ]; /** @@ -217,9 +215,6 @@ class EventServiceProvider extends ServiceProvider $this->registerObservers(); } - /** - * @return void - */ private function registerObservers(): void { Attachment::observe(new AttachmentObserver()); diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index d4accbf037..3f304c33da 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -72,13 +72,11 @@ use FireflyIII\TransactionRules\Engine\SearchRuleEngine; use FireflyIII\Validation\FireflyValidator; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; -use Validator; /** - * * Class FireflyServiceProvider. * - * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class FireflyServiceProvider extends ServiceProvider { @@ -87,7 +85,7 @@ class FireflyServiceProvider extends ServiceProvider */ public function boot(): void { - Validator::resolver( + \Validator::resolver( static function ($translator, $data, $rules, $messages) { return new FireflyValidator($translator, $data, $rules, $messages); } @@ -97,6 +95,7 @@ class FireflyServiceProvider extends ServiceProvider /** * Register stuff. * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function register(): void { diff --git a/app/Providers/JournalServiceProvider.php b/app/Providers/JournalServiceProvider.php index 50235ad37e..a70532dc81 100644 --- a/app/Providers/JournalServiceProvider.php +++ b/app/Providers/JournalServiceProvider.php @@ -137,9 +137,6 @@ class JournalServiceProvider extends ServiceProvider ); } - /** - * - */ private function registerGroupCollector(): void { $this->app->bind( diff --git a/app/Providers/PiggyBankServiceProvider.php b/app/Providers/PiggyBankServiceProvider.php index 309650aaf8..ec3c9653b4 100644 --- a/app/Providers/PiggyBankServiceProvider.php +++ b/app/Providers/PiggyBankServiceProvider.php @@ -66,6 +66,7 @@ class PiggyBankServiceProvider extends ServiceProvider if ($app->auth->check()) { // @phpstan-ignore-line (phpstan does not understand the reference to auth) $repository->setUser(auth()->user()); } + return $repository; } ); diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index ebe9eb2c0f..f13e19afd8 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -39,20 +39,23 @@ class RouteServiceProvider extends ServiceProvider */ public function boot(): void { - $this->routes(function () { + $this->routes(function (): void { Route::prefix('api') - ->middleware('api') - ->namespace($this->namespace) - ->group(base_path('routes/api.php')); + ->middleware('api') + ->namespace($this->namespace) + ->group(base_path('routes/api.php')) + ; Route::prefix('api/v1/cron') - ->middleware('api_basic') - ->namespace($this->namespace) - ->group(base_path('routes/api-noauth.php')); + ->middleware('api_basic') + ->namespace($this->namespace) + ->group(base_path('routes/api-noauth.php')) + ; Route::middleware('web') - ->namespace($this->namespace) - ->group(base_path('routes/web.php')); + ->namespace($this->namespace) + ->group(base_path('routes/web.php')) + ; }); } } diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index d41fbe3bc6..c521d0c3cb 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -42,12 +42,9 @@ use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Collection; -use JsonException; -use Storage; /** * Class AccountRepository. - * */ class AccountRepository implements AccountRepositoryInterface { @@ -55,13 +52,6 @@ class AccountRepository implements AccountRepositoryInterface /** * Moved here from account CRUD. - * - * @param Account $account - * @param Account|null $moveTo - * - * @return bool - * - */ public function destroy(Account $account, ?Account $moveTo): bool { @@ -74,29 +64,30 @@ class AccountRepository implements AccountRepositoryInterface /** * Find account with same name OR same IBAN or both, but not the same type or ID. - * - * @param Collection $accounts - * - * @return Collection */ public function expandWithDoubles(Collection $accounts): Collection { $result = new Collection(); + /** @var Account $account */ foreach ($accounts as $account) { $byName = $this->user->accounts()->where('name', $account->name) - ->where('id', '!=', $account->id)->first(); + ->where('id', '!=', $account->id)->first() + ; if (null !== $byName) { $result->push($account); $result->push($byName); + continue; } if (null !== $account->iban) { $byIban = $this->user->accounts()->where('iban', $account->iban) - ->where('id', '!=', $account->id)->first(); + ->where('id', '!=', $account->id)->first() + ; if (null !== $byIban) { $result->push($account); $result->push($byIban); + continue; } } @@ -106,9 +97,6 @@ class AccountRepository implements AccountRepositoryInterface return $result; } - /** - * @inheritDoc - */ public function findByAccountNumber(string $number, array $types): ?Account { $dbQuery = $this->user @@ -116,27 +104,23 @@ class AccountRepository implements AccountRepositoryInterface ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') ->where('accounts.active', true) ->where( - static function (EloquentBuilder $q1) use ($number) { // @phpstan-ignore-line + static function (EloquentBuilder $q1) use ($number): void { // @phpstan-ignore-line $json = json_encode($number); $q1->where('account_meta.name', '=', 'account_number'); $q1->where('account_meta.data', '=', $json); } - ); + ) + ; if (0 !== count($types)) { $dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); $dbQuery->whereIn('account_types.type', $types); } - /** @var Account|null */ + + // @var Account|null return $dbQuery->first(['accounts.*']); } - /** - * @param string $iban - * @param array $types - * - * @return Account|null - */ public function findByIbanNull(string $iban, array $types): ?Account { $query = $this->user->accounts()->where('iban', '!=', '')->whereNotNull('iban'); @@ -146,16 +130,10 @@ class AccountRepository implements AccountRepositoryInterface $query->whereIn('account_types.type', $types); } - /** @var Account|null */ + // @var Account|null return $query->where('iban', $iban)->first(['accounts.*']); } - /** - * @param string $name - * @param array $types - * - * @return Account|null - */ public function findByName(string $name, array $types): ?Account { $query = $this->user->accounts(); @@ -167,7 +145,8 @@ class AccountRepository implements AccountRepositoryInterface app('log')->debug(sprintf('Searching for account named "%s" (of user #%d) of the following type(s)', $name, $this->user->id), ['types' => $types]); $query->where('accounts.name', $name); - /** @var Account|null $account */ + + /** @var null|Account $account */ $account = $query->first(['accounts.*']); if (null === $account) { app('log')->debug(sprintf('There is no account with name "%s" of types', $name), $types); @@ -181,21 +160,12 @@ class AccountRepository implements AccountRepositoryInterface /** * Return account type or null if not found. - * - * @param string $type - * - * @return AccountType|null */ public function getAccountTypeByType(string $type): ?AccountType { return AccountType::whereType(ucfirst($type))->first(); } - /** - * @param array $accountIds - * - * @return Collection - */ public function getAccountsById(array $accountIds): Collection { $query = $this->user->accounts(); @@ -210,16 +180,11 @@ class AccountRepository implements AccountRepositoryInterface return $query->get(['accounts.*']); } - /** - * @param array $types - * - * @return Collection - */ public function getActiveAccountsByType(array $types): Collection { $query = $this->user->accounts()->with( [ - 'accountmeta' => static function (HasMany $query) { + 'accountmeta' => static function (HasMany $query): void { $query->where('name', 'account_role'); }, 'attachments', @@ -236,15 +201,12 @@ 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'); + /** @var \Storage $disk */ + $disk = \Storage::disk('upload'); return $set->each( static function (Attachment $attachment) use ($disk) { @@ -258,15 +220,13 @@ class AccountRepository implements AccountRepositoryInterface } /** - * @return Account - * * @throws FireflyException - * @throws JsonException */ public function getCashAccount(): Account { /** @var AccountType $type */ $type = AccountType::where('type', AccountType::CASH)->first(); + /** @var AccountFactory $factory */ $factory = app(AccountFactory::class); $factory->setUser($this->user); @@ -274,38 +234,29 @@ class AccountRepository implements AccountRepositoryInterface return $factory->findOrCreate('Cash account', $type->type); } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * @inheritDoc - */ public function getCreditTransactionGroup(Account $account): ?TransactionGroup { $journal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transactions.account_id', $account->id) - ->transactionTypes([TransactionType::LIABILITY_CREDIT]) - ->first(['transaction_journals.*']); + ->where('transactions.account_id', $account->id) + ->transactionTypes([TransactionType::LIABILITY_CREDIT]) + ->first(['transaction_journals.*']) + ; + return $journal?->transactionGroup; } - /** - * @param array $types - * - * @return Collection - */ public function getInactiveAccountsByType(array $types): Collection { $query = $this->user->accounts()->with( [ - 'accountmeta' => static function (HasMany $query) { + 'accountmeta' => static function (HasMany $query): void { $query->where('name', 'account_role'); }, ] @@ -321,21 +272,14 @@ class AccountRepository implements AccountRepositoryInterface return $query->get(['accounts.*']); } - /** - * @inheritDoc - */ public function getLocation(Account $account): ?Location { - /** @var Location|null */ + // @var Location|null return $account->locations()->first(); } /** * Get note text or null. - * - * @param Account $account - * - * @return null|string */ public function getNoteText(Account $account): ?string { @@ -344,17 +288,14 @@ class AccountRepository implements AccountRepositoryInterface /** * Returns the amount of the opening balance for this account. - * - * @param Account $account - * - * @return string|null */ public function getOpeningBalanceAmount(Account $account): ?string { $journal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transactions.account_id', $account->id) - ->transactionTypes([TransactionType::OPENING_BALANCE, TransactionType::LIABILITY_CREDIT]) - ->first(['transaction_journals.*']); + ->where('transactions.account_id', $account->id) + ->transactionTypes([TransactionType::OPENING_BALANCE, TransactionType::LIABILITY_CREDIT]) + ->first(['transaction_journals.*']) + ; if (null === $journal) { return null; } @@ -368,24 +309,16 @@ class AccountRepository implements AccountRepositoryInterface /** * Return date of opening balance as string or null. - * - * @param Account $account - * - * @return null|string */ public function getOpeningBalanceDate(Account $account): ?string { return TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transactions.account_id', $account->id) - ->transactionTypes([TransactionType::OPENING_BALANCE, TransactionType::LIABILITY_CREDIT]) - ->first(['transaction_journals.*'])?->date->format('Y-m-d H:i:s'); + ->where('transactions.account_id', $account->id) + ->transactionTypes([TransactionType::OPENING_BALANCE, TransactionType::LIABILITY_CREDIT]) + ->first(['transaction_journals.*'])?->date->format('Y-m-d H:i:s') + ; } - /** - * @param Account $account - * - * @return TransactionGroup|null - */ public function getOpeningBalanceGroup(Account $account): ?TransactionGroup { $journal = $this->getOpeningBalance($account); @@ -393,36 +326,22 @@ class AccountRepository implements AccountRepositoryInterface return $journal?->transactionGroup; } - /** - * @param Account $account - * - * @return TransactionJournal|null - */ public function getOpeningBalance(Account $account): ?TransactionJournal { return TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transactions.account_id', $account->id) - ->transactionTypes([TransactionType::OPENING_BALANCE]) - ->first(['transaction_journals.*']); + ->where('transactions.account_id', $account->id) + ->transactionTypes([TransactionType::OPENING_BALANCE]) + ->first(['transaction_journals.*']) + ; } - /** - * @param Account $account - * - * @return Collection - */ public function getPiggyBanks(Account $account): Collection { return $account->piggyBanks()->get(); } /** - * @param Account $account - * - * @return Account|null - * * @throws FireflyException - * @throws JsonException */ public function getReconciliation(Account $account): ?Account { @@ -435,10 +354,11 @@ class AccountRepository implements AccountRepositoryInterface /** @var AccountType $type */ $type = AccountType::where('type', AccountType::RECONCILIATION)->first(); - /** @var Account|null $current */ + /** @var null|Account $current */ $current = $this->user->accounts()->where('account_type_id', $type->id) - ->where('name', $name) - ->first(); + ->where('name', $name) + ->first() + ; if (null !== $current) { return $current; @@ -460,11 +380,6 @@ class AccountRepository implements AccountRepositoryInterface return $factory->create($data); } - /** - * @param Account $account - * - * @return TransactionCurrency|null - */ public function getAccountCurrency(Account $account): ?TransactionCurrency { $type = $account->accountType->type; @@ -484,11 +399,6 @@ class AccountRepository implements AccountRepositoryInterface /** * Return meta value for account. Null if not found. - * - * @param Account $account - * @param string $field - * - * @return null|string */ public function getMetaValue(Account $account, string $field): ?string { @@ -507,29 +417,16 @@ class AccountRepository implements AccountRepositoryInterface return null; } - /** - * @param array $types - * - * @return int - */ public function count(array $types): int { return $this->user->accounts()->accountTypeIn($types)->count(); } - /** - * @param int $accountId - * - * @return Account|null - */ public function find(int $accountId): ?Account { return $this->user->accounts()->find($accountId); } - /** - * @inheritDoc - */ public function getUsedCurrencies(Account $account): Collection { $info = $account->transactions()->get(['transaction_currency_id', 'foreign_currency_id'])->toArray(); @@ -543,19 +440,11 @@ class AccountRepository implements AccountRepositoryInterface return TransactionCurrency::whereIn('id', $currencyIds)->get(); } - /** - * @param Account $account - * - * @return bool - */ public function isLiability(Account $account): bool { return in_array($account->accountType->type, [AccountType::CREDITCARD, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE], true); } - /** - * @inheritDoc - */ public function maxOrder(string $type): int { $sets = [ @@ -580,12 +469,6 @@ class AccountRepository implements AccountRepositoryInterface return $order; } - /** - * @param array $types - * @param array|null $sort - * - * @return Collection - */ public function getAccountsByType(array $types, ?array $sort = []): Collection { $res = array_intersect([AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT], $types); @@ -614,10 +497,6 @@ class AccountRepository implements AccountRepositoryInterface /** * Returns the date of the very first transaction in this account. - * - * @param Account $account - * - * @return Carbon|null */ public function oldestJournalDate(Account $account): ?Carbon { @@ -628,21 +507,18 @@ class AccountRepository implements AccountRepositoryInterface /** * Returns the date of the very first transaction in this account. - * - * @param Account $account - * - * @return TransactionJournal|null */ public function oldestJournal(Account $account): ?TransactionJournal { - /** @var TransactionJournal|null $first */ + /** @var null|TransactionJournal $first */ $first = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->orderBy('transaction_journals.date', 'ASC') - ->orderBy('transaction_journals.order', 'DESC') - ->where('transaction_journals.user_id', $this->user->id) - ->orderBy('transaction_journals.id', 'ASC') - ->first(['transaction_journals.id']); + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->orderBy('transaction_journals.date', 'ASC') + ->orderBy('transaction_journals.order', 'DESC') + ->where('transaction_journals.user_id', $this->user->id) + ->orderBy('transaction_journals.id', 'ASC') + ->first(['transaction_journals.id']) + ; if (null !== $first) { return TransactionJournal::find($first->id); } @@ -650,17 +526,14 @@ class AccountRepository implements AccountRepositoryInterface return null; } - /** - * @inheritDoc - */ public function resetAccountOrder(): void { $sets = [ [AccountType::DEFAULT, AccountType::ASSET], - //[AccountType::EXPENSE, AccountType::BENEFICIARY], - //[AccountType::REVENUE], + // [AccountType::EXPENSE, AccountType::BENEFICIARY], + // [AccountType::REVENUE], [AccountType::LOAN, AccountType::DEBT, AccountType::CREDITCARD, AccountType::MORTGAGE], - //[AccountType::CASH, AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION], + // [AccountType::CASH, AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION], ]; foreach ($sets as $set) { $list = $this->getAccountsByType($set); @@ -668,6 +541,7 @@ class AccountRepository implements AccountRepositoryInterface foreach ($list as $account) { if (false === $account->active) { $account->order = 0; + continue; } if ($index !== (int)$account->order) { @@ -675,26 +549,20 @@ class AccountRepository implements AccountRepositoryInterface $account->order = $index; $account->save(); } - $index++; + ++$index; } } } - /** - * @param string $query - * @param array $types - * @param int $limit - * - * @return Collection - */ public function searchAccount(string $query, array $types, int $limit): Collection { $dbQuery = $this->user->accounts() - ->where('active', true) - ->orderBy('accounts.order', 'ASC') - ->orderBy('accounts.account_type_id', 'ASC') - ->orderBy('accounts.name', 'ASC') - ->with(['accountType']); + ->where('active', true) + ->orderBy('accounts.order', 'ASC') + ->orderBy('accounts.account_type_id', 'ASC') + ->orderBy('accounts.name', 'ASC') + ->with(['accountType']) + ; if ('' !== $query) { // split query on spaces just in case: $parts = explode(' ', $query); @@ -711,28 +579,26 @@ 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', true) - ->orderBy('accounts.order', 'ASC') - ->orderBy('accounts.account_type_id', 'ASC') - ->orderBy('accounts.name', 'ASC') - ->with(['accountType', 'accountMeta']); + ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') + ->where('accounts.active', true) + ->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( - static function (EloquentBuilder $q1) use ($search) { // @phpstan-ignore-line + static function (EloquentBuilder $q1) use ($search): void { // @phpstan-ignore-line $q1->where('accounts.iban', 'LIKE', $search); $q1->orWhere( - static function (EloquentBuilder $q2) use ($search) { + static function (EloquentBuilder $q2) use ($search): void { $q2->where('account_meta.name', '=', 'account_number'); $q2->where('account_meta.data', 'LIKE', $search); } @@ -750,11 +616,7 @@ class AccountRepository implements AccountRepositoryInterface } /** - * @param array $data - * - * @return Account * @throws FireflyException - * @throws JsonException */ public function store(array $data): Account { @@ -766,12 +628,7 @@ class AccountRepository implements AccountRepositoryInterface } /** - * @param Account $account - * @param array $data - * - * @return Account * @throws FireflyException - * @throws JsonException */ public function update(Account $account, array $data): Account { diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php index 3c2b7d8d92..8b301a7621 100644 --- a/app/Repositories/Account/AccountRepositoryInterface.php +++ b/app/Repositories/Account/AccountRepositoryInterface.php @@ -41,241 +41,100 @@ interface AccountRepositoryInterface { /** * Moved here from account CRUD. - * - * @param array $types - * - * @return int */ public function count(array $types): int; - /** * Moved here from account CRUD. - * - * @param Account $account - * @param Account|null $moveTo - * - * @return bool */ public function destroy(Account $account, ?Account $moveTo): bool; /** * Find account with same name OR same IBAN or both, but not the same type or ID. - * - * @param Collection $accounts - * - * @return Collection */ public function expandWithDoubles(Collection $accounts): Collection; - /** - * @param int $accountId - * - * @return Account|null - */ public function find(int $accountId): ?Account; - /** - * @param string $number - * @param array $types - * - * @return Account|null - */ public function findByAccountNumber(string $number, array $types): ?Account; - /** - * @param string $iban - * @param array $types - * - * @return Account|null - */ public function findByIbanNull(string $iban, array $types): ?Account; - /** - * @param string $name - * @param array $types - * - * @return Account|null - */ public function findByName(string $name, array $types): ?Account; - /** - * @param Account $account - * - * @return TransactionCurrency|null - */ public function getAccountCurrency(Account $account): ?TransactionCurrency; /** * Return account type or null if not found. - * - * @param string $type - * - * @return AccountType|null */ public function getAccountTypeByType(string $type): ?AccountType; - /** - * @param array $accountIds - * - * @return Collection - */ public function getAccountsById(array $accountIds): Collection; /** * @param array $types - * @param array|null $sort - * - * @return Collection */ public function getAccountsByType(array $types, ?array $sort = []): Collection; - /** - * @param array $types - * - * @return Collection - */ public function getActiveAccountsByType(array $types): Collection; - /** - * @param Account $account - * - * @return Collection - */ public function getAttachments(Account $account): Collection; - /** - * @return Account - */ public function getCashAccount(): Account; - /** - * @param Account $account - * - * @return TransactionGroup|null - */ public function getCreditTransactionGroup(Account $account): ?TransactionGroup; - /** - * @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 - * - * @return null|string */ public function getMetaValue(Account $account, string $field): ?string; /** * Get note text or null. - * - * @param Account $account - * - * @return null|string */ public function getNoteText(Account $account): ?string; - /** - * @param Account $account - * - * @return TransactionJournal|null - * - */ public function getOpeningBalance(Account $account): ?TransactionJournal; /** * Returns the amount of the opening balance for this account. - * - * @param Account $account - * - * @return string|null */ public function getOpeningBalanceAmount(Account $account): ?string; /** * Return date of opening balance as string or null. - * - * @param Account $account - * - * @return null|string */ public function getOpeningBalanceDate(Account $account): ?string; - /** - * @param Account $account - * - * @return TransactionGroup|null - */ public function getOpeningBalanceGroup(Account $account): ?TransactionGroup; - /** - * @param Account $account - * - * @return Collection - */ public function getPiggyBanks(Account $account): Collection; /** * Find or create the opposing reconciliation account. - * - * @param Account $account - * - * @return Account|null */ public function getReconciliation(Account $account): ?Account; - /** - * @param Account $account - * - * @return Collection - */ public function getUsedCurrencies(Account $account): Collection; - /** - * @param Account $account - * - * @return bool - */ public function isLiability(Account $account): bool; - /** - * @param string $type - * - * @return int - */ public function maxOrder(string $type): int; /** * Returns the date of the very first transaction in this account. - * - * @param Account $account - * - * @return TransactionJournal|null */ public function oldestJournal(Account $account): ?TransactionJournal; /** * Returns the date of the very first transaction in this account. - * - * @param Account $account - * - * @return Carbon|null */ public function oldestJournalDate(Account $account): ?Carbon; @@ -284,41 +143,13 @@ interface AccountRepositoryInterface */ public function resetAccountOrder(): void; - /** - * @param string $query - * @param array $types - * @param int $limit - * - * @return Collection - */ public function searchAccount(string $query, array $types, int $limit): Collection; - /** - * @param string $query - * @param array $types - * @param int $limit - * - * @return Collection - */ public function searchAccountNr(string $query, array $types, int $limit): Collection; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; - /** - * @param array $data - * - * @return Account - */ public function store(array $data): Account; - /** - * @param Account $account - * @param array $data - * - * @return Account - */ public function update(Account $account, array $data): Account; } diff --git a/app/Repositories/Account/AccountTasker.php b/app/Repositories/Account/AccountTasker.php index 45df92a6cb..47cd075aca 100644 --- a/app/Repositories/Account/AccountTasker.php +++ b/app/Repositories/Account/AccountTasker.php @@ -32,7 +32,6 @@ use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use JsonException; /** * Class AccountTasker. @@ -42,13 +41,7 @@ class AccountTasker implements AccountTaskerInterface private User $user; /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array * @throws FireflyException - * @throws JsonException */ public function getAccountReport(Collection $accounts, Carbon $start, Carbon $end): array { @@ -115,13 +108,7 @@ class AccountTasker implements AccountTaskerInterface } /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return array * @throws FireflyException - * @throws JsonException */ public function getExpenseReport(Carbon $start, Carbon $end, Collection $accounts): array { @@ -152,18 +139,50 @@ class AccountTasker implements AccountTaskerInterface } /** - * @param array $array - * - * @return array * @throws FireflyException - * @throws JsonException + */ + public function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): array + { + // get all incomes for the given accounts in the given period! + // also transfers! + // get all transactions: + + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setDestinationAccounts($accounts)->setRange($start, $end); + $collector->excludeSourceAccounts($accounts); + $collector->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])->withAccountInformation(); + $report = $this->groupIncomeBySource($collector->getExtractedJournals()); + + // sort the result + // Obtain a list of columns + $sum = []; + foreach ($report['accounts'] as $accountId => $row) { + $sum[$accountId] = (float)$row['sum']; // intentional float + } + + array_multisort($sum, SORT_DESC, $report['accounts']); + + return $report; + } + + public function setUser(null|Authenticatable|User $user): void + { + if ($user instanceof User) { + $this->user = $user; + } + } + + /** + * @throws FireflyException */ private function groupExpenseByDestination(array $array): array { $defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); + /** @var CurrencyRepositoryInterface $currencyRepos */ $currencyRepos = app(CurrencyRepositoryInterface::class); - $currencies = [$defaultCurrency->id => $defaultCurrency,]; + $currencies = [$defaultCurrency->id => $defaultCurrency]; $report = [ 'accounts' => [], 'sums' => [], @@ -215,52 +234,15 @@ class AccountTasker implements AccountTaskerInterface } /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return array * @throws FireflyException - * @throws JsonException - */ - public function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): array - { - // get all incomes for the given accounts in the given period! - // also transfers! - // get all transactions: - - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setDestinationAccounts($accounts)->setRange($start, $end); - $collector->excludeSourceAccounts($accounts); - $collector->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])->withAccountInformation(); - $report = $this->groupIncomeBySource($collector->getExtractedJournals()); - - // sort the result - // Obtain a list of columns - $sum = []; - foreach ($report['accounts'] as $accountId => $row) { - $sum[$accountId] = (float)$row['sum']; // intentional float - } - - array_multisort($sum, SORT_DESC, $report['accounts']); - - return $report; - } - - /** - * @param array $array - * - * @return array - * @throws FireflyException - * @throws JsonException */ private function groupIncomeBySource(array $array): array { $defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); + /** @var CurrencyRepositoryInterface $currencyRepos */ $currencyRepos = app(CurrencyRepositoryInterface::class); - $currencies = [$defaultCurrency->id => $defaultCurrency,]; + $currencies = [$defaultCurrency->id => $defaultCurrency]; $report = [ 'accounts' => [], 'sums' => [], @@ -309,14 +291,4 @@ class AccountTasker implements AccountTaskerInterface return $report; } - - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void - { - if ($user instanceof User) { - $this->user = $user; - } - } } diff --git a/app/Repositories/Account/AccountTaskerInterface.php b/app/Repositories/Account/AccountTaskerInterface.php index 7bbb1c0c75..74438f7882 100644 --- a/app/Repositories/Account/AccountTaskerInterface.php +++ b/app/Repositories/Account/AccountTaskerInterface.php @@ -33,35 +33,11 @@ use Illuminate\Support\Collection; */ interface AccountTaskerInterface { - /** - * @param Collection $accounts - * @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 Collection $accounts - * - * @return array - */ public function getExpenseReport(Carbon $start, Carbon $end, Collection $accounts): array; - /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return array - */ public function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): array; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; } diff --git a/app/Repositories/Account/OperationsRepository.php b/app/Repositories/Account/OperationsRepository.php index 6fa75b60bc..b63ea91817 100644 --- a/app/Repositories/Account/OperationsRepository.php +++ b/app/Repositories/Account/OperationsRepository.php @@ -32,7 +32,6 @@ use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; /** - * * Class OperationsRepository */ class OperationsRepository implements OperationsRepositoryInterface @@ -43,12 +42,6 @@ class OperationsRepository implements OperationsRepositoryInterface * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period * 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 Collection $accounts - * - * @return array */ public function listExpenses(Carbon $start, Carbon $end, Collection $accounts): array { @@ -57,15 +50,124 @@ class OperationsRepository implements OperationsRepositoryInterface return $this->sortByCurrency($journals, 'negative'); } + public function setUser(null|Authenticatable|User $user): void + { + if ($user instanceof User) { + $this->user = $user; + } + } + + /** + * This method returns a list of all the deposit transaction journals (as arrays) set in that period + * which have the specified accounts. It's grouped per currency, with as few details in the array + * as possible. Amounts are always positive. + */ + public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null): array + { + $journals = $this->getTransactions($start, $end, $accounts, TransactionType::DEPOSIT); + + return $this->sortByCurrency($journals, 'positive'); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function sumExpenses( + Carbon $start, + Carbon $end, + ?Collection $accounts = null, + ?Collection $expense = null, + ?TransactionCurrency $currency = null + ): array { + $journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency); + + return $this->groupByCurrency($journals, 'negative'); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function sumExpensesByDestination( + Carbon $start, + Carbon $end, + ?Collection $accounts = null, + ?Collection $expense = null, + ?TransactionCurrency $currency = null + ): array { + $journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency); + + return $this->groupByDirection($journals, 'destination', 'negative'); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function sumExpensesBySource( + Carbon $start, + Carbon $end, + ?Collection $accounts = null, + ?Collection $expense = null, + ?TransactionCurrency $currency = null + ): array { + $journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency); + + return $this->groupByDirection($journals, 'source', 'negative'); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function sumIncome( + Carbon $start, + Carbon $end, + ?Collection $accounts = null, + ?Collection $revenue = null, + ?TransactionCurrency $currency = null + ): array { + $journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency); + + return $this->groupByCurrency($journals, 'positive'); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function sumIncomeByDestination( + Carbon $start, + Carbon $end, + ?Collection $accounts = null, + ?Collection $revenue = null, + ?TransactionCurrency $currency = null + ): array { + $journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency); + + return $this->groupByDirection($journals, 'destination', 'positive'); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function sumIncomeBySource( + Carbon $start, + Carbon $end, + ?Collection $accounts = null, + ?Collection $revenue = null, + ?TransactionCurrency $currency = null + ): array { + $journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency); + + return $this->groupByDirection($journals, 'source', 'positive'); + } + + public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array + { + $journals = $this->getTransactionsForSum(TransactionType::TRANSFER, $start, $end, $accounts, null, $currency); + + return $this->groupByEither($journals); + } + /** * Collect transactions with some parameters - * - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * @param string $type - * - * @return array */ private function getTransactions(Carbon $start, Carbon $end, Collection $accounts, string $type): array { @@ -78,22 +180,6 @@ class OperationsRepository implements OperationsRepositoryInterface return $collector->getExtractedJournals(); } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void - { - if ($user instanceof User) { - $this->user = $user; - } - } - - /** - * @param array $journals - * @param string $direction - * - * @return array - */ private function sortByCurrency(array $journals, string $direction): array { $array = []; @@ -101,7 +187,6 @@ class OperationsRepository implements OperationsRepositoryInterface $currencyId = (int)$journal['currency_id']; $journalId = (int)$journal['transaction_journal_id']; $array[$currencyId] ??= [ - 'currency_id' => $journal['currency_id'], 'currency_name' => $journal['currency_name'], 'currency_symbol' => $journal['currency_symbol'], @@ -111,7 +196,7 @@ class OperationsRepository implements OperationsRepositoryInterface ]; $array[$currencyId]['transaction_journals'][$journalId] = [ - 'amount' => app('steam')->$direction((string)$journal['amount']), // @phpstan-ignore-line + 'amount' => app('steam')->{$direction}((string)$journal['amount']), // @phpstan-ignore-line 'date' => $journal['date'], 'transaction_journal_id' => $journalId, 'budget_name' => $journal['budget_name'], @@ -132,50 +217,8 @@ class OperationsRepository implements OperationsRepositoryInterface } /** - * This method returns a list of all the deposit transaction journals (as arrays) set in that period - * which have the specified accounts. It's grouped per currency, with as few details in the array - * as possible. Amounts are always positive. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * - * @return array - */ - public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null): array - { - $journals = $this->getTransactions($start, $end, $accounts, TransactionType::DEPOSIT); - - return $this->sortByCurrency($journals, 'positive'); - } - - /** - * @inheritDoc - * @SuppressWarnings(PHPMD.ExcessiveParameterList) - */ - public function sumExpenses( - Carbon $start, - Carbon $end, - ?Collection $accounts = null, - ?Collection $expense = null, - ?TransactionCurrency $currency = null - ): array - { - $journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency); - - return $this->groupByCurrency($journals, 'negative'); - } - - /** - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $opposing - * @param TransactionCurrency|null $currency - * @param string $type - * - * @return array * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * @SuppressWarnings(PHPMD.NPathComplexity) */ private function getTransactionsForSum( string $type, @@ -184,8 +227,7 @@ class OperationsRepository implements OperationsRepositoryInterface ?Collection $accounts = null, ?Collection $opposing = null, ?TransactionCurrency $currency = null - ): array - { + ): array { $start->startOfDay(); $end->endOfDay(); @@ -225,7 +267,8 @@ class OperationsRepository implements OperationsRepositoryInterface /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setUser($this->user)->setRange($start, $end)->setTypes([$type])->withAccountInformation() - ->setForeignCurrency($currency); + ->setForeignCurrency($currency) + ; if (TransactionType::WITHDRAWAL === $type) { if (null !== $accounts) { $collector->setSourceAccounts($accounts); @@ -252,12 +295,6 @@ class OperationsRepository implements OperationsRepositoryInterface return $journals; } - /** - * @param array $journals - * @param string $direction - * - * @return array - */ private function groupByCurrency(array $journals, string $direction): array { $array = []; @@ -272,7 +309,7 @@ class OperationsRepository implements OperationsRepositoryInterface 'currency_code' => $journal['currency_code'], 'currency_decimal_places' => $journal['currency_decimal_places'], ]; - $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->$direction($journal['amount'])); // @phpstan-ignore-line + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->{$direction}($journal['amount'])); // @phpstan-ignore-line // also do foreign amount: $foreignId = (int)$journal['foreign_currency_id']; @@ -285,37 +322,13 @@ class OperationsRepository implements OperationsRepositoryInterface 'currency_code' => $journal['foreign_currency_code'], 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], ]; - $array[$foreignId]['sum'] = bcadd($array[$foreignId]['sum'], app('steam')->$direction($journal['foreign_amount']));// @phpstan-ignore-line + $array[$foreignId]['sum'] = bcadd($array[$foreignId]['sum'], app('steam')->{$direction}($journal['foreign_amount'])); // @phpstan-ignore-line } } return $array; } - /** - * @inheritDoc - * @SuppressWarnings(PHPMD.ExcessiveParameterList) - */ - public function sumExpensesByDestination( - Carbon $start, - Carbon $end, - ?Collection $accounts = null, - ?Collection $expense = null, - ?TransactionCurrency $currency = null - ): array - { - $journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency); - - return $this->groupByDirection($journals, 'destination', 'negative'); - } - - /** - * @param array $journals - * @param string $direction - * @param string $method - * - * @return array - */ private function groupByDirection(array $journals, string $direction, string $method): array { $array = []; @@ -334,7 +347,7 @@ class OperationsRepository implements OperationsRepositoryInterface 'currency_code' => $journal['currency_code'], 'currency_decimal_places' => $journal['currency_decimal_places'], ]; - $array[$key]['sum'] = bcadd($array[$key]['sum'], app('steam')->$method((string)$journal['amount']));// @phpstan-ignore-line + $array[$key]['sum'] = bcadd($array[$key]['sum'], app('steam')->{$method}((string)$journal['amount'])); // @phpstan-ignore-line // also do foreign amount: if (0 !== (int)$journal['foreign_currency_id']) { @@ -349,99 +362,17 @@ class OperationsRepository implements OperationsRepositoryInterface 'currency_code' => $journal['foreign_currency_code'], 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], ]; - $array[$key]['sum'] = bcadd($array[$key]['sum'], app('steam')->$method((string)$journal['foreign_amount']));// @phpstan-ignore-line + $array[$key]['sum'] = bcadd($array[$key]['sum'], app('steam')->{$method}((string)$journal['foreign_amount'])); // @phpstan-ignore-line } } return $array; } - /** - * @inheritDoc - * @SuppressWarnings(PHPMD.ExcessiveParameterList) - */ - public function sumExpensesBySource( - Carbon $start, - Carbon $end, - ?Collection $accounts = null, - ?Collection $expense = null, - ?TransactionCurrency $currency = null - ): array - { - $journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency); - - return $this->groupByDirection($journals, 'source', 'negative'); - } - - /** - * @inheritDoc - * @SuppressWarnings(PHPMD.ExcessiveParameterList) - */ - public function sumIncome( - Carbon $start, - Carbon $end, - ?Collection $accounts = null, - ?Collection $revenue = null, - ?TransactionCurrency $currency = null - ): array - { - $journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency); - - return $this->groupByCurrency($journals, 'positive'); - } - - /** - * @inheritDoc - * @SuppressWarnings(PHPMD.ExcessiveParameterList) - */ - public function sumIncomeByDestination( - Carbon $start, - Carbon $end, - ?Collection $accounts = null, - ?Collection $revenue = null, - ?TransactionCurrency $currency = null - ): array - { - $journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency); - - return $this->groupByDirection($journals, 'destination', 'positive'); - } - - /** - * @inheritDoc - * @SuppressWarnings(PHPMD.ExcessiveParameterList) - */ - public function sumIncomeBySource( - Carbon $start, - Carbon $end, - ?Collection $accounts = null, - ?Collection $revenue = null, - ?TransactionCurrency $currency = null - ): array - { - $journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency); - - return $this->groupByDirection($journals, 'source', 'positive'); - } - - /** - * @inheritDoc - */ - public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array - { - $journals = $this->getTransactionsForSum(TransactionType::TRANSFER, $start, $end, $accounts, null, $currency); - - return $this->groupByEither($journals); - } - - /** - * @param array $journals - * - * @return array - */ private function groupByEither(array $journals): array { $return = []; + /** @var array $journal */ foreach ($journals as $journal) { $return = $this->groupByEitherJournal($return, $journal); @@ -457,12 +388,6 @@ class OperationsRepository implements OperationsRepositoryInterface return $final; } - /** - * @param array $return - * @param array $journal - * - * @return array - */ private function groupByEitherJournal(array $return, array $journal): array { $sourceId = $journal['source_account_id']; diff --git a/app/Repositories/Account/OperationsRepositoryInterface.php b/app/Repositories/Account/OperationsRepositoryInterface.php index 43c6d65905..b1867da02b 100644 --- a/app/Repositories/Account/OperationsRepositoryInterface.php +++ b/app/Repositories/Account/OperationsRepositoryInterface.php @@ -38,12 +38,6 @@ interface OperationsRepositoryInterface * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period * 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 Collection $accounts - * - * @return array */ public function listExpenses(Carbon $start, Carbon $end, Collection $accounts): array; @@ -51,30 +45,14 @@ interface OperationsRepositoryInterface * This method returns a list of all the deposit transaction journals (as arrays) set in that period * which have the specified accounts. It's grouped per currency, with as few details in the array * as possible. Amounts are always positive. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * - * @return array */ public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null): array; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; /** * 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 Collection|null $expense - * @param TransactionCurrency|null $currency - * - * @return array * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumExpenses( @@ -89,13 +67,6 @@ interface OperationsRepositoryInterface * Sum of withdrawal journals in period for a set of accounts, grouped per destination / currency. Amounts are * always negative. * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $expense - * @param TransactionCurrency|null $currency - * - * @return array * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumExpensesByDestination( @@ -110,13 +81,6 @@ interface OperationsRepositoryInterface * Sum of withdrawal journals in period for a set of accounts, grouped per source / currency. Amounts are always * negative. * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $expense - * @param TransactionCurrency|null $currency - * - * @return array * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumExpensesBySource( @@ -130,13 +94,6 @@ interface OperationsRepositoryInterface /** * 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 Collection|null $revenue - * @param TransactionCurrency|null $currency - * - * @return array * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumIncome( @@ -151,13 +108,6 @@ interface OperationsRepositoryInterface * Sum of income journals in period for a set of accounts, grouped per destination + currency. Amounts are always * positive. * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $revenue - * @param TransactionCurrency|null $currency - * - * @return array * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumIncomeByDestination( @@ -172,13 +122,6 @@ interface OperationsRepositoryInterface * Sum of income journals in period for a set of accounts, grouped per source + currency. Amounts are always * positive. * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $revenue - * @param TransactionCurrency|null $currency - * - * @return array * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumIncomeBySource( @@ -191,13 +134,6 @@ interface OperationsRepositoryInterface /** * 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 e3c0392007..13bd2b1638 100644 --- a/app/Repositories/Attachment/AttachmentRepository.php +++ b/app/Repositories/Attachment/AttachmentRepository.php @@ -23,8 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Attachment; -use Crypt; -use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\AttachmentFactory; use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; @@ -36,11 +34,9 @@ use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Storage; use League\Flysystem\UnableToDeleteFile; -use LogicException; /** * Class AttachmentRepository. - * */ class AttachmentRepository implements AttachmentRepositoryInterface { @@ -48,10 +44,7 @@ class AttachmentRepository implements AttachmentRepositoryInterface private $user; /** - * @param Attachment $attachment - * - * @return bool - * @throws Exception + * @throws \Exception */ public function destroy(Attachment $attachment): bool { @@ -59,6 +52,7 @@ class AttachmentRepository implements AttachmentRepositoryInterface $helper = app(AttachmentHelperInterface::class); $path = $helper->getAttachmentLocation($attachment); + try { Storage::disk('upload')->delete($path); } catch (UnableToDeleteFile $e) { @@ -69,11 +63,6 @@ class AttachmentRepository implements AttachmentRepositoryInterface return true; } - /** - * @param Attachment $attachment - * - * @return string - */ public function getContent(Attachment $attachment): string { // create a disk. @@ -83,8 +72,9 @@ class AttachmentRepository implements AttachmentRepositoryInterface if ($disk->exists($file)) { $encryptedContent = (string)$disk->get($file); + try { - $unencryptedContent = Crypt::decrypt($encryptedContent); // verified + $unencryptedContent = \Crypt::decrypt($encryptedContent); // verified } catch (DecryptException $e) { app('log')->debug(sprintf('Could not decrypt attachment #%d but this is fine: %s', $attachment->id, $e->getMessage())); $unencryptedContent = $encryptedContent; @@ -94,11 +84,6 @@ class AttachmentRepository implements AttachmentRepositoryInterface return $unencryptedContent; } - /** - * @param Attachment $attachment - * - * @return bool - */ public function exists(Attachment $attachment): bool { /** @var Storage $disk */ @@ -107,9 +92,6 @@ class AttachmentRepository implements AttachmentRepositoryInterface return $disk->exists($attachment->fileName()); } - /** - * @return Collection - */ public function get(): Collection { return $this->user->attachments()->get(); @@ -117,10 +99,6 @@ class AttachmentRepository implements AttachmentRepositoryInterface /** * Get attachment note text or empty string. - * - * @param Attachment $attachment - * - * @return string|null */ public function getNoteText(Attachment $attachment): ?string { @@ -133,9 +111,6 @@ class AttachmentRepository implements AttachmentRepositoryInterface } /** - * @param array $data - * - * @return Attachment * @throws FireflyException */ public function store(array $data): Attachment @@ -151,22 +126,13 @@ class AttachmentRepository implements AttachmentRepositoryInterface return $result; } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * @param Attachment $attachment - * @param array $data - * - * @return Attachment - */ public function update(Attachment $attachment, array $data): Attachment { if (array_key_exists('title', $data)) { @@ -192,12 +158,6 @@ class AttachmentRepository implements AttachmentRepositoryInterface return $attachment; } - /** - * @param Attachment $attachment - * @param string $note - * - * @return bool - */ public function updateNote(Attachment $attachment, string $note): bool { if ('' === $note) { @@ -205,7 +165,7 @@ class AttachmentRepository implements AttachmentRepositoryInterface if (null !== $dbNote) { try { $dbNote->delete(); - } catch (LogicException $e) { + } catch (\LogicException $e) { app('log')->error($e->getMessage()); } } diff --git a/app/Repositories/Attachment/AttachmentRepositoryInterface.php b/app/Repositories/Attachment/AttachmentRepositoryInterface.php index b3a8b879aa..e48a8df323 100644 --- a/app/Repositories/Attachment/AttachmentRepositoryInterface.php +++ b/app/Repositories/Attachment/AttachmentRepositoryInterface.php @@ -34,59 +34,25 @@ use Illuminate\Support\Collection; */ interface AttachmentRepositoryInterface { - /** - * @param Attachment $attachment - * - * @return bool - */ public function destroy(Attachment $attachment): bool; - /** - * @param Attachment $attachment - * - * @return bool - */ public function exists(Attachment $attachment): bool; - /** - * @return Collection - */ public function get(): Collection; - /** - * @param Attachment $attachment - * - * @return string - */ public function getContent(Attachment $attachment): string; /** * Get attachment note text or empty string. - * - * @param Attachment $attachment - * - * @return string|null */ public function getNoteText(Attachment $attachment): ?string; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; /** - * @param array $data - * - * @return Attachment * @throws FireflyException */ public function store(array $data): Attachment; - /** - * @param Attachment $attachment - * @param array $attachmentData - * - * @return Attachment - */ public function update(Attachment $attachment, array $attachmentData): Attachment; } diff --git a/app/Repositories/AuditLogEntry/ALERepository.php b/app/Repositories/AuditLogEntry/ALERepository.php index 8e3af7ca94..f97d16639e 100644 --- a/app/Repositories/AuditLogEntry/ALERepository.php +++ b/app/Repositories/AuditLogEntry/ALERepository.php @@ -33,18 +33,12 @@ use Illuminate\Support\Collection; */ class ALERepository implements ALERepositoryInterface { - /** - * @inheritDoc - */ public function getForObject(Model $model): Collection { // all Models have an ID. return AuditLogEntry::where('auditable_id', $model->id)->where('auditable_type', get_class($model))->get(); // @phpstan-ignore-line } - /** - * @inheritDoc - */ public function store(array $data): AuditLogEntry { $auditLogEntry = new AuditLogEntry(); @@ -55,6 +49,7 @@ class ALERepository implements ALERepositoryInterface $auditLogEntry->before = $data['before']; $auditLogEntry->after = $data['after']; $auditLogEntry->save(); + return $auditLogEntry; } } diff --git a/app/Repositories/AuditLogEntry/ALERepositoryInterface.php b/app/Repositories/AuditLogEntry/ALERepositoryInterface.php index d0153beb83..84b48b8ad9 100644 --- a/app/Repositories/AuditLogEntry/ALERepositoryInterface.php +++ b/app/Repositories/AuditLogEntry/ALERepositoryInterface.php @@ -33,17 +33,7 @@ use Illuminate\Support\Collection; */ interface ALERepositoryInterface { - /** - * @param Model $model - * - * @return Collection - */ public function getForObject(Model $model): Collection; - /** - * @param array $data - * - * @return AuditLogEntry - */ public function store(array $data): AuditLogEntry; } diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 356542d0b2..2e5479b9dd 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Bill; use Carbon\Carbon; -use DB; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\BillFactory; use FireflyIII\Models\Attachment; @@ -43,12 +42,9 @@ use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Query\JoinClause; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; -use JsonException; -use Storage; /** * Class BillRepository. - * */ class BillRepository implements BillRepositoryInterface { @@ -56,9 +52,6 @@ class BillRepository implements BillRepositoryInterface private User $user; - /** - * @inheritDoc - */ public function billEndsWith(string $query, int $limit): Collection { $search = $this->user->bills(); @@ -66,14 +59,12 @@ class BillRepository implements BillRepositoryInterface $search->where('name', 'LIKE', sprintf('%%%s', $query)); } $search->orderBy('name', 'ASC') - ->where('active', true); + ->where('active', true) + ; return $search->take($limit)->get(); } - /** - * @inheritDoc - */ public function billStartsWith(string $query, int $limit): Collection { $search = $this->user->bills(); @@ -81,7 +72,8 @@ class BillRepository implements BillRepositoryInterface $search->where('name', 'LIKE', sprintf('%s%%', $query)); } $search->orderBy('name', 'ASC') - ->where('active', true); + ->where('active', true) + ; return $search->take($limit)->get(); } @@ -98,17 +90,10 @@ class BillRepository implements BillRepositoryInterface $bill->order = $current; $bill->save(); } - $current++; + ++$current; } } - /** - * @param Bill $bill - * - * @return bool - * - - */ public function destroy(Bill $bill): bool { /** @var BillDestroyService $service */ @@ -118,9 +103,6 @@ class BillRepository implements BillRepositoryInterface return true; } - /** - * @inheritDoc - */ public function destroyAll(): void { $this->user->bills()->delete(); @@ -128,11 +110,6 @@ class BillRepository implements BillRepositoryInterface /** * Find bill by parameters. - * - * @param int|null $billId - * @param string|null $billName - * - * @return Bill|null */ public function findBill(?int $billId, ?string $billName): ?Bill { @@ -159,10 +136,6 @@ class BillRepository implements BillRepositoryInterface /** * Find a bill by ID. - * - * @param int $billId - * - * @return Bill|null */ public function find(int $billId): ?Bill { @@ -171,10 +144,6 @@ class BillRepository implements BillRepositoryInterface /** * Find a bill by name. - * - * @param string $name - * - * @return Bill|null */ public function findByName(string $name): ?Bill { @@ -183,17 +152,13 @@ class BillRepository implements BillRepositoryInterface /** * Get all attachments. - * - * @param Bill $bill - * - * @return Collection */ public function getAttachments(Bill $bill): Collection { $set = $bill->attachments()->get(); - /** @var Storage $disk */ - $disk = Storage::disk('upload'); + /** @var \Storage $disk */ + $disk = \Storage::disk('upload'); return $set->each( static function (Attachment $attachment) use ($disk) { @@ -206,22 +171,15 @@ class BillRepository implements BillRepositoryInterface ); } - /** - * @return Collection - */ public function getBills(): Collection { return $this->user->bills() - ->orderBy('order', 'ASC') - ->orderBy('active', 'DESC') - ->orderBy('name', 'ASC')->get(); + ->orderBy('order', 'ASC') + ->orderBy('active', 'DESC') + ->orderBy('name', 'ASC')->get() + ; } - /** - * @param Collection $accounts - * - * @return Collection - */ public function getBillsForAccounts(Collection $accounts): Collection { $fields = [ @@ -243,32 +201,29 @@ class BillRepository implements BillRepositoryInterface $ids = $accounts->pluck('id')->toArray(); return $this->user->bills() - ->leftJoin( - 'transaction_journals', - static function (JoinClause $join) { - $join->on('transaction_journals.bill_id', '=', 'bills.id')->whereNull('transaction_journals.deleted_at'); - } - ) - ->leftJoin( - 'transactions', - static function (JoinClause $join) { - $join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0); - } - ) - ->whereIn('transactions.account_id', $ids) - ->whereNull('transaction_journals.deleted_at') - ->orderBy('bills.active', 'DESC') - ->orderBy('bills.name', 'ASC') - ->groupBy($fields) - ->get($fields); + ->leftJoin( + 'transaction_journals', + static function (JoinClause $join): void { + $join->on('transaction_journals.bill_id', '=', 'bills.id')->whereNull('transaction_journals.deleted_at'); + } + ) + ->leftJoin( + 'transactions', + static function (JoinClause $join): void { + $join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0); + } + ) + ->whereIn('transactions.account_id', $ids) + ->whereNull('transaction_journals.deleted_at') + ->orderBy('bills.active', 'DESC') + ->orderBy('bills.name', 'ASC') + ->groupBy($fields) + ->get($fields) + ; } /** * Get all bills with these ID's. - * - * @param array $billIds - * - * @return Collection */ public function getByIds(array $billIds): Collection { @@ -277,24 +232,15 @@ class BillRepository implements BillRepositoryInterface /** * Get text or return empty string. - * - * @param Bill $bill - * - * @return string */ public function getNoteText(Bill $bill): string { - /** @var Note|null $note */ + /** @var null|Note $note */ $note = $bill->notes()->first(); - return (string)$note?->text; + return (string) $note?->text; } - /** - * @param Bill $bill - * - * @return array - */ public function getOverallAverage(Bill $bill): array { /** @var JournalRepositoryInterface $repos */ @@ -309,7 +255,7 @@ class BillRepository implements BillRepositoryInterface foreach ($journals as $journal) { /** @var Transaction $transaction */ $transaction = $journal->transactions()->where('amount', '<', 0)->first(); - $currencyId = (int)$journal->transaction_currency_id; + $currencyId = (int) $journal->transaction_currency_id; $currency = $journal->transactionCurrency; $result[$currencyId] ??= [ 'sum' => '0', @@ -321,7 +267,7 @@ class BillRepository implements BillRepositoryInterface 'currency_decimal_places' => $currency->decimal_places, ]; $result[$currencyId]['sum'] = bcadd($result[$currencyId]['sum'], $transaction->amount); - $result[$currencyId]['count']++; + ++$result[$currencyId]['count']; } // after loop, re-loop for avg. @@ -330,71 +276,56 @@ class BillRepository implements BillRepositoryInterface * @var array $arr */ foreach ($result as $currencyId => $arr) { - $result[$currencyId]['avg'] = bcdiv($arr['sum'], (string)$arr['count']); + $result[$currencyId]['avg'] = bcdiv($arr['sum'], (string) $arr['count']); } return $result; } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * @param int $size - * - * @return LengthAwarePaginator - */ public function getPaginator(int $size): LengthAwarePaginator { return $this->user->bills() - ->orderBy('active', 'DESC') - ->orderBy('name', 'ASC')->paginate($size); + ->orderBy('active', 'DESC') + ->orderBy('name', 'ASC')->paginate($size) + ; } /** * The "paid dates" list is a list of dates of transaction journals that are linked to this bill. - * - * @param Bill $bill - * @param Carbon $start - * @param Carbon $end - * - * @return Collection */ public function getPaidDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection { - //app('log')->debug('Now in getPaidDatesInRange()'); + // app('log')->debug('Now in getPaidDatesInRange()'); return $bill->transactionJournals() - ->before($end)->after($start)->get( - [ + ->before($end)->after($start)->get( + [ 'transaction_journals.id', 'transaction_journals.date', 'transaction_journals.transaction_group_id', ] - ); + ) + ; } /** * Return all rules for one bill - * - * @param Bill $bill - * - * @return Collection */ public function getRulesForBill(Bill $bill): Collection { return $this->user->rules() - ->leftJoin('rule_actions', 'rule_actions.rule_id', '=', 'rules.id') - ->where('rule_actions.action_type', 'link_to_bill') - ->where('rule_actions.action_value', $bill->name) - ->get(['rules.*']); + ->leftJoin('rule_actions', 'rule_actions.rule_id', '=', 'rules.id') + ->where('rule_actions.action_type', 'link_to_bill') + ->where('rule_actions.action_value', $bill->name) + ->get(['rules.*']) + ; } /** @@ -402,18 +333,16 @@ class BillRepository implements BillRepositoryInterface * 5= billid * * 5 => [['id' => 1, 'title' => 'Some rule'],['id' => 2, 'title' => 'Some other rule']] - * - * @param Collection $collection - * - * @return array */ public function getRulesForBills(Collection $collection): array { $rules = $this->user->rules() - ->leftJoin('rule_actions', 'rule_actions.rule_id', '=', 'rules.id') - ->where('rule_actions.action_type', 'link_to_bill') - ->get(['rules.id', 'rules.title', 'rule_actions.action_value', 'rules.active']); + ->leftJoin('rule_actions', 'rule_actions.rule_id', '=', 'rules.id') + ->where('rule_actions.action_type', 'link_to_bill') + ->get(['rules.id', 'rules.title', 'rule_actions.action_value', 'rules.active']) + ; $array = []; + /** @var Rule $rule */ foreach ($rules as $rule) { $array[$rule->action_value] ??= []; @@ -427,12 +356,6 @@ class BillRepository implements BillRepositoryInterface return $return; } - /** - * @param Bill $bill - * @param Carbon $date - * - * @return array - */ public function getYearAverage(Bill $bill, Carbon $date): array { /** @var JournalRepositoryInterface $repos */ @@ -443,18 +366,19 @@ class BillRepository implements BillRepositoryInterface $result = []; $journals = $bill->transactionJournals() - ->where('date', '>=', $date->year . '-01-01 00:00:00') - ->where('date', '<=', $date->year . '-12-31 23:59:59') - ->get(); + ->where('date', '>=', $date->year.'-01-01 00:00:00') + ->where('date', '<=', $date->year.'-12-31 23:59:59') + ->get() + ; /** @var TransactionJournal $journal */ foreach ($journals as $journal) { - /** @var Transaction|null $transaction */ + /** @var null|Transaction $transaction */ $transaction = $journal->transactions()->where('amount', '<', 0)->first(); if (null === $transaction) { continue; } - $currencyId = (int)$journal->transaction_currency_id; + $currencyId = (int) $journal->transaction_currency_id; $currency = $journal->transactionCurrency; $result[$currencyId] ??= [ 'sum' => '0', @@ -466,7 +390,7 @@ class BillRepository implements BillRepositoryInterface 'currency_decimal_places' => $currency->decimal_places, ]; $result[$currencyId]['sum'] = bcadd($result[$currencyId]['sum'], $transaction->amount); - $result[$currencyId]['count']++; + ++$result[$currencyId]['count']; } // after loop, re-loop for avg. @@ -475,7 +399,7 @@ class BillRepository implements BillRepositoryInterface * @var array $arr */ foreach ($result as $currencyId => $arr) { - $result[$currencyId]['avg'] = bcdiv($arr['sum'], (string)$arr['count']); + $result[$currencyId]['avg'] = bcdiv($arr['sum'], (string) $arr['count']); } return $result; @@ -483,15 +407,12 @@ class BillRepository implements BillRepositoryInterface /** * Link a set of journals to a bill. - * - * @param Bill $bill - * @param array $transactions */ public function linkCollectionToBill(Bill $bill, array $transactions): void { /** @var Transaction $transaction */ foreach ($transactions as $transaction) { - $journal = $bill->user->transactionJournals()->find((int)$transaction['transaction_journal_id']); + $journal = $bill->user->transactionJournals()->find((int) $transaction['transaction_journal_id']); $journal->bill_id = $bill->id; $journal->save(); app('log')->debug(sprintf('Linked journal #%d to bill #%d', $journal->id, $bill->id)); @@ -500,12 +421,6 @@ class BillRepository implements BillRepositoryInterface /** * Given the date in $date, this method will return a moment in the future where the bill is expected to be paid. - * - * @param Bill $bill - * @param Carbon $date - * - * @return Carbon - * @throws JsonException */ public function nextExpectedMatch(Bill $bill, Carbon $date): Carbon { @@ -518,12 +433,12 @@ class BillRepository implements BillRepositoryInterface } // find the most recent date for this bill NOT in the future. Cache this date: $start = clone $bill->date; - app('log')->debug('nextExpectedMatch: Start is ' . $start->format('Y-m-d')); + app('log')->debug('nextExpectedMatch: Start is '.$start->format('Y-m-d')); while ($start < $date) { app('log')->debug(sprintf('$start (%s) < $date (%s)', $start->format('Y-m-d'), $date->format('Y-m-d'))); $start = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip); - app('log')->debug('Start is now ' . $start->format('Y-m-d')); + app('log')->debug('Start is now '.$start->format('Y-m-d')); } $end = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip); @@ -537,8 +452,8 @@ class BillRepository implements BillRepositoryInterface $start = clone $end; $end = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip); } - app('log')->debug('nextExpectedMatch: Final start is ' . $start->format('Y-m-d')); - app('log')->debug('nextExpectedMatch: Matching end is ' . $end->format('Y-m-d')); + app('log')->debug('nextExpectedMatch: Final start is '.$start->format('Y-m-d')); + app('log')->debug('nextExpectedMatch: Matching end is '.$end->format('Y-m-d')); $cache->store($start); @@ -546,11 +461,7 @@ class BillRepository implements BillRepositoryInterface } /** - * @param array $data - * - * @return Bill * @throws FireflyException - * @throws JsonException */ public function store(array $data): Bill { @@ -561,9 +472,6 @@ class BillRepository implements BillRepositoryInterface return $factory->create($data); } - /** - * @inheritDoc - */ public function removeObjectGroup(Bill $bill): Bill { $bill->objectGroups()->sync([]); @@ -571,12 +479,6 @@ class BillRepository implements BillRepositoryInterface return $bill; } - /** - * @param string $query - * @param int $limit - * - * @return Collection - */ public function searchBill(string $query, int $limit): Collection { $query = sprintf('%%%s%%', $query); @@ -584,9 +486,6 @@ 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); @@ -597,22 +496,17 @@ class BillRepository implements BillRepositoryInterface return $bill; } - /** - * @inheritDoc - */ public function setOrder(Bill $bill, int $order): void { $bill->order = $order; $bill->save(); } - /** - * @inheritDoc - */ public function sumPaidInRange(Carbon $start, Carbon $end): array { $bills = $this->getActiveBills(); $return = []; + /** @var Bill $bill */ foreach ($bills as $bill) { /** @var Collection $set */ @@ -620,7 +514,7 @@ class BillRepository implements BillRepositoryInterface $currency = $bill->transactionCurrency; $return[$currency->id] ??= [ - 'id' => (string)$currency->id, + 'id' => (string) $currency->id, 'name' => $currency->name, 'symbol' => $currency->symbol, 'code' => $currency->code, @@ -630,61 +524,58 @@ class BillRepository implements BillRepositoryInterface /** @var TransactionJournal $transactionJournal */ foreach ($set as $transactionJournal) { - /** @var Transaction|null $sourceTransaction */ + /** @var null|Transaction $sourceTransaction */ $sourceTransaction = $transactionJournal->transactions()->where('amount', '<', 0)->first(); if (null !== $sourceTransaction) { $amount = $sourceTransaction->amount; - if ((int)$sourceTransaction->foreign_currency_id === $currency->id) { + if ((int) $sourceTransaction->foreign_currency_id === $currency->id) { // use foreign amount instead! - $amount = (string)$sourceTransaction->foreign_amount; + $amount = (string) $sourceTransaction->foreign_amount; } $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $amount); } } } + return $return; } - /** - * @return Collection - */ public function getActiveBills(): Collection { return $this->user->bills() - ->where('active', true) - ->orderBy('bills.name', 'ASC') - ->get(['bills.*', DB::raw('((bills.amount_min + bills.amount_max) / 2) AS expectedAmount'),]); // @phpstan-ignore-line + ->where('active', true) + ->orderBy('bills.name', 'ASC') + ->get(['bills.*', \DB::raw('((bills.amount_min + bills.amount_max) / 2) AS expectedAmount')]) // @phpstan-ignore-line + ; } - /** - * @inheritDoc - */ public function sumUnpaidInRange(Carbon $start, Carbon $end): array { app('log')->debug(sprintf('Now in sumUnpaidInRange("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'))); $bills = $this->getActiveBills(); $return = []; + /** @var Bill $bill */ foreach ($bills as $bill) { - app('log')->debug(sprintf('Processing bill #%d ("%s")', $bill->id, $bill->name)); + // app('log')->debug(sprintf('Processing bill #%d ("%s")', $bill->id, $bill->name)); $dates = $this->getPayDatesInRange($bill, $start, $end); $count = $bill->transactionJournals()->after($start)->before($end)->count(); $total = $dates->count() - $count; - app('log')->debug(sprintf('Pay dates: %d, count: %d, left: %d', $dates->count(), $count, $total)); - app('log')->debug('dates', $dates->toArray()); + // app('log')->debug(sprintf('Pay dates: %d, count: %d, left: %d', $dates->count(), $count, $total)); + // app('log')->debug('dates', $dates->toArray()); if ($total > 0) { $currency = $bill->transactionCurrency; $average = bcdiv(bcadd($bill->amount_max, $bill->amount_min), '2'); $return[$currency->id] ??= [ - 'id' => (string)$currency->id, + 'id' => (string) $currency->id, 'name' => $currency->name, 'symbol' => $currency->symbol, 'code' => $currency->code, 'decimal_places' => $currency->decimal_places, 'sum' => '0', ]; - $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], bcmul($average, (string)$total)); + $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], bcmul($average, (string) $total)); } } @@ -693,32 +584,26 @@ class BillRepository implements BillRepositoryInterface /** * Between start and end, tells you on which date(s) the bill is expected to hit. - * - * @param Bill $bill - * @param Carbon $start - * @param Carbon $end - * - * @return Collection */ public function getPayDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection { $set = new Collection(); $currentStart = clone $start; - //app('log')->debug(sprintf('Now at bill "%s" (%s)', $bill->name, $bill->repeat_freq)); - //app('log')->debug(sprintf('First currentstart is %s', $currentStart->format('Y-m-d'))); + // app('log')->debug(sprintf('Now at bill "%s" (%s)', $bill->name, $bill->repeat_freq)); + // app('log')->debug(sprintf('First currentstart is %s', $currentStart->format('Y-m-d'))); while ($currentStart <= $end) { - //app('log')->debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d'))); + // app('log')->debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d'))); $nextExpectedMatch = $this->nextDateMatch($bill, $currentStart); - //app('log')->debug(sprintf('Next Date match after %s is %s', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); + // app('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 break; } $set->push(clone $nextExpectedMatch); - //app('log')->debug(sprintf('Now %d dates in set.', $set->count())); + // app('log')->debug(sprintf('Now %d dates in set.', $set->count())); $nextExpectedMatch->addDay(); - //app('log')->debug(sprintf('Currentstart (%s) has become %s.', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); + // app('log')->debug(sprintf('Currentstart (%s) has become %s.', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); $currentStart = clone $nextExpectedMatch; } @@ -729,11 +614,6 @@ class BillRepository implements BillRepositoryInterface /** * Given a bill and a date, this method will tell you at which moment this bill expects its next * transaction. Whether or not it is there already, is not relevant. - * - * @param Bill $bill - * @param Carbon $date - * - * @return Carbon */ public function nextDateMatch(Bill $bill, Carbon $date): Carbon { @@ -755,21 +635,13 @@ class BillRepository implements BillRepositoryInterface return $start; } - /** - * @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 - * - * @return Bill * @throws FireflyException - * @throws JsonException */ public function update(Bill $bill, array $data): Bill { diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php index 6232bec837..c74467cc31 100644 --- a/app/Repositories/Bill/BillRepositoryInterface.php +++ b/app/Repositories/Bill/BillRepositoryInterface.php @@ -36,20 +36,8 @@ use Illuminate\Support\Collection; */ interface BillRepositoryInterface { - /** - * @param string $query - * @param int $limit - * - * @return Collection - */ public function billEndsWith(string $query, int $limit): Collection; - /** - * @param string $query - * @param int $limit - * - * @return Collection - */ public function billStartsWith(string $query, int $limit): Collection; /** @@ -57,132 +45,62 @@ interface BillRepositoryInterface */ public function correctOrder(): void; - /** - * @param Bill $bill - * - * @return bool - */ public function destroy(Bill $bill): bool; - /** - * - */ public function destroyAll(): void; /** * Find a bill by ID. - * - * @param int $billId - * - * @return Bill|null */ public function find(int $billId): ?Bill; /** * Find bill by parameters. - * - * @param int|null $billId - * @param string|null $billName - * - * @return Bill|null */ public function findBill(?int $billId, ?string $billName): ?Bill; /** * Find a bill by name. - * - * @param string $name - * - * @return Bill|null */ public function findByName(string $name): ?Bill; - /** - * @return Collection - */ public function getActiveBills(): Collection; /** * Get all attachments. - * - * @param Bill $bill - * - * @return Collection */ public function getAttachments(Bill $bill): Collection; - /** - * @return Collection - */ public function getBills(): Collection; /** * Gets the bills which have some kind of relevance to the accounts mentioned. - * - * @param Collection $accounts - * - * @return Collection */ public function getBillsForAccounts(Collection $accounts): Collection; /** * Get all bills with these ID's. - * - * @param array $billIds - * - * @return Collection */ public function getByIds(array $billIds): Collection; /** * Get text or return empty string. - * - * @param Bill $bill - * - * @return string */ public function getNoteText(Bill $bill): string; - /** - * @param Bill $bill - * - * @return array - */ public function getOverallAverage(Bill $bill): array; - /** - * @param int $size - * - * @return LengthAwarePaginator - */ public function getPaginator(int $size): LengthAwarePaginator; - /** - * @param Bill $bill - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ public function getPaidDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection; /** * Between start and end, tells you on which date(s) the bill is expected to hit. - * - * @param Bill $bill - * @param Carbon $start - * @param Carbon $end - * - * @return Collection */ public function getPayDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection; /** * Return all rules for one bill - * - * @param Bill $bill - * - * @return Collection */ public function getRulesForBill(Bill $bill): Collection; @@ -191,122 +109,53 @@ interface BillRepositoryInterface * 5= billid * * 5 => [['id' => 1, 'title' => 'Some rule'],['id' => 2, 'title' => 'Some other rule']] - * - * @param Collection $collection - * - * @return array */ public function getRulesForBills(Collection $collection): array; - /** - * @param Bill $bill - * @param Carbon $date - * - * @return array - */ public function getYearAverage(Bill $bill, Carbon $date): array; /** * Link a set of journals to a bill. - * - * @param Bill $bill - * @param array $transactions */ public function linkCollectionToBill(Bill $bill, array $transactions): void; /** * Given a bill and a date, this method will tell you at which moment this bill expects its next * transaction. Whether or not it is there already, is not relevant. - * - * @param Bill $bill - * @param Carbon $date - * - * @return Carbon */ public function nextDateMatch(Bill $bill, Carbon $date): Carbon; - /** - * @param Bill $bill - * @param Carbon $date - * - * @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 - * - * @return Collection - */ 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|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; /** - * @param array $data - * - * @return Bill * @throws FireflyException */ public function store(array $data): Bill; /** * Collect multi-currency of sum of bills already paid. - * - * @param Carbon $start - * @param Carbon $end - * - * @return array */ public function sumPaidInRange(Carbon $start, Carbon $end): array; /** * Collect multi-currency of sum of bills yet to pay. - * - * @param Carbon $start - * @param Carbon $end - * - * @return array */ public function sumUnpaidInRange(Carbon $start, Carbon $end): array; - /** - * @param Bill $bill - */ public function unlinkAll(Bill $bill): void; - /** - * @param Bill $bill - * @param array $data - * - * @return Bill - */ public function update(Bill $bill, array $data): Bill; } diff --git a/app/Repositories/Budget/AvailableBudgetRepository.php b/app/Repositories/Budget/AvailableBudgetRepository.php index 7a4d4e6f10..a98f454556 100644 --- a/app/Repositories/Budget/AvailableBudgetRepository.php +++ b/app/Repositories/Budget/AvailableBudgetRepository.php @@ -32,20 +32,17 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Collection; /** - * * Class AvailableBudgetRepository */ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface { private User $user; - /** - * @inheritDoc - */ public function cleanup(): void { $exists = []; $availableBudgets = $this->user->availableBudgets()->get(); + /** @var AvailableBudget $availableBudget */ foreach ($availableBudgets as $availableBudget) { $start = $availableBudget->start_date->format('Y-m-d'); @@ -61,18 +58,13 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface /** * Return a list of all available budgets (in all currencies) (for the selected period). - * - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return Collection */ public function get(?Carbon $start = null, ?Carbon $end = null): Collection { $query = $this->user->availableBudgets()->with(['transactionCurrency']); if (null !== $start && null !== $end) { $query->where( - static function (Builder $q1) use ($start, $end) { // @phpstan-ignore-line + static function (Builder $q1) use ($start, $end): void { // @phpstan-ignore-line $q1->where('start_date', '=', $start->format('Y-m-d')); $q1->where('end_date', '=', $end->format('Y-m-d')); } @@ -90,17 +82,11 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface $this->user->availableBudgets()->delete(); } - /** - * @param AvailableBudget $availableBudget - */ public function destroyAvailableBudget(AvailableBudget $availableBudget): void { $availableBudget->delete(); } - /** - * @inheritDoc - */ public function findById(int $id): ?AvailableBudget { return $this->user->availableBudgets->find($id); @@ -108,37 +94,27 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface /** * Find existing AB. - * - * @param TransactionCurrency $currency - * @param Carbon $start - * @param Carbon $end - * - * @return AvailableBudget|null */ public function find(TransactionCurrency $currency, Carbon $start, Carbon $end): ?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(); + ->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 - * @param Carbon $end - * - * @return string - */ public function getAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end): string { $amount = '0'; - /** @var AvailableBudget|null $availableBudget */ + + /** @var null|AvailableBudget $availableBudget */ $availableBudget = $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(); + ->where('transaction_currency_id', $currency->id) + ->where('start_date', $start->format('Y-m-d')) + ->where('end_date', $end->format('Y-m-d'))->first() + ; if (null !== $availableBudget) { $amount = $availableBudget->amount; } @@ -146,18 +122,14 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface return $amount; } - /** - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ public function getAvailableBudgetWithCurrency(Carbon $start, Carbon $end): array { $return = []; $availableBudgets = $this->user->availableBudgets() - ->where('start_date', $start->format('Y-m-d')) - ->where('end_date', $end->format('Y-m-d'))->get(); + ->where('start_date', $start->format('Y-m-d')) + ->where('end_date', $end->format('Y-m-d'))->get() + ; + /** @var AvailableBudget $availableBudget */ foreach ($availableBudgets as $availableBudget) { $return[$availableBudget->transaction_currency_id] = $availableBudget->amount; @@ -168,10 +140,6 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface /** * Returns all available budget objects. - * - * @param TransactionCurrency $currency - * - * @return Collection */ public function getAvailableBudgetsByCurrency(TransactionCurrency $currency): Collection { @@ -180,12 +148,6 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface /** * Returns all available budget objects. - * - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return Collection - * */ public function getAvailableBudgetsByDate(?Carbon $start, ?Carbon $end): Collection { @@ -203,48 +165,36 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface /** * Returns all available budget objects. - * - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - * */ public function getAvailableBudgetsByExactDate(Carbon $start, Carbon $end): Collection { return $this->user->availableBudgets() - ->where('start_date', '=', $start->format('Y-m-d')) - ->where('end_date', '=', $end->format('Y-m-d')) - ->get(); + ->where('start_date', '=', $start->format('Y-m-d')) + ->where('end_date', '=', $end->format('Y-m-d')) + ->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(); + ->where('end_date', $end->format('Y-m-d'))->first() + ; } /** - * @param TransactionCurrency $currency - * @param Carbon $start - * @param Carbon $end - * @param string $amount - * - * @return AvailableBudget * @deprecated */ public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): AvailableBudget { $availableBudget = $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(); + ->where('transaction_currency_id', $currency->id) + ->where('start_date', $start->format('Y-m-d')) + ->where('end_date', $end->format('Y-m-d'))->first() + ; if (null === $availableBudget) { $availableBudget = new AvailableBudget(); $availableBudget->user()->associate($this->user); @@ -258,21 +208,13 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface return $availableBudget; } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * @param array $data - * - * @return AvailableBudget|null - */ public function store(array $data): ?AvailableBudget { $start = $data['start']; @@ -292,17 +234,10 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface 'amount' => $data['amount'], 'start_date' => $start, 'end_date' => $end, - ] ); } - /** - * @param AvailableBudget $availableBudget - * @param array $data - * - * @return AvailableBudget - */ public function update(AvailableBudget $availableBudget, array $data): AvailableBudget { if (array_key_exists('amount', $data)) { @@ -313,12 +248,6 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface return $availableBudget; } - /** - * @param AvailableBudget $availableBudget - * @param array $data - * - * @return AvailableBudget - */ public function updateAvailableBudget(AvailableBudget $availableBudget, array $data): AvailableBudget { if (array_key_exists('start', $data)) { diff --git a/app/Repositories/Budget/AvailableBudgetRepositoryInterface.php b/app/Repositories/Budget/AvailableBudgetRepositoryInterface.php index fd2c004e6e..80a23307cf 100644 --- a/app/Repositories/Budget/AvailableBudgetRepositoryInterface.php +++ b/app/Repositories/Budget/AvailableBudgetRepositoryInterface.php @@ -35,9 +35,6 @@ use Illuminate\Support\Collection; */ interface AvailableBudgetRepositoryInterface { - /** - * @return void - */ public function cleanup(): void; /** @@ -45,132 +42,54 @@ interface AvailableBudgetRepositoryInterface */ public function destroyAll(): void; - /** - * @param AvailableBudget $availableBudget - */ public function destroyAvailableBudget(AvailableBudget $availableBudget): void; /** * Find existing AB. - * - * @param TransactionCurrency $currency - * @param Carbon $start - * @param Carbon $end - * - * @return AvailableBudget|null */ public function find(TransactionCurrency $currency, Carbon $start, Carbon $end): ?AvailableBudget; - /** - * @param int $id - * - * @return AvailableBudget|null - */ public function findById(int $id): ?AvailableBudget; /** * Return a list of all available budgets (in all currencies) (for the selected period). - * - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return Collection */ public function get(?Carbon $start = null, ?Carbon $end = null): Collection; /** - * @param TransactionCurrency $currency - * @param Carbon $start - * @param Carbon $end - * - * @return string * @deprecated */ public function getAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end): string; - /** - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ public function getAvailableBudgetWithCurrency(Carbon $start, Carbon $end): array; /** * Returns all available budget objects. - * - * @param TransactionCurrency $currency - * - * @return Collection */ public function getAvailableBudgetsByCurrency(TransactionCurrency $currency): Collection; /** * Returns all available budget objects. - * - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return Collection - * */ public function getAvailableBudgetsByDate(?Carbon $start, ?Carbon $end): Collection; - /** - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ public function getAvailableBudgetsByExactDate(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 - * @param Carbon $end - * @param string $amount - * - * @return AvailableBudget * @deprecated */ public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): AvailableBudget; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; - /** - * @param array $data - * - * @return AvailableBudget|null - */ public function store(array $data): ?AvailableBudget; - /** - * @param AvailableBudget $availableBudget - * @param array $data - * - * @return AvailableBudget - */ public function update(AvailableBudget $availableBudget, array $data): AvailableBudget; - /** - * @param AvailableBudget $availableBudget - * @param array $data - * - * @return AvailableBudget - */ public function updateAvailableBudget(AvailableBudget $availableBudget, array $data): AvailableBudget; } diff --git a/app/Repositories/Budget/BudgetLimitRepository.php b/app/Repositories/Budget/BudgetLimitRepository.php index 8efc508858..ce882676aa 100644 --- a/app/Repositories/Budget/BudgetLimitRepository.php +++ b/app/Repositories/Budget/BudgetLimitRepository.php @@ -33,10 +33,8 @@ use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Collection; -use JsonException; /** - * * Class BudgetLimitRepository */ class BudgetLimitRepository implements BudgetLimitRepositoryInterface @@ -46,56 +44,53 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface /** * Tells you which amount has been budgeted (for the given budgets) * in the selected query. Returns a positive amount as a string. - * - * @param Carbon $start - * @param Carbon $end - * @param TransactionCurrency $currency - * @param Collection|null $budgets - * - * @return string */ public function budgeted(Carbon $start, Carbon $end, TransactionCurrency $currency, ?Collection $budgets = null): string { $query = BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') // same complex where query as below. - ->where( - static function (Builder $q5) use ($start, $end) { + ->where( + static function (Builder $q5) use ($start, $end): void { $q5->where( - static function (Builder $q1) use ($start, $end) { + static function (Builder $q1) use ($start, $end): void { $q1->where( - static function (Builder $q2) use ($start, $end) { + static function (Builder $q2) use ($start, $end): void { $q2->where('budget_limits.end_date', '>=', $start->format('Y-m-d')); $q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d')); } ) - ->orWhere( - static function (Builder $q3) use ($start, $end) { - $q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d')); - $q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d')); - } - ); + ->orWhere( + static function (Builder $q3) use ($start, $end): void { + $q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d')); + $q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d')); + } + ) + ; } ) - ->orWhere( - static function (Builder $q4) use ($start, $end) { - // or start is before start AND end is after end. - $q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d')); - $q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d')); - } - ); + ->orWhere( + static function (Builder $q4) use ($start, $end): void { + // or start is before start AND end is after end. + $q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d')); + $q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d')); + } + ) + ; } - ) - ->where('budget_limits.transaction_currency_id', $currency->id) - ->whereNull('budgets.deleted_at') - ->where('budgets.active', true) - ->where('budgets.user_id', $this->user->id); + ) + ->where('budget_limits.transaction_currency_id', $currency->id) + ->whereNull('budgets.deleted_at') + ->where('budgets.active', true) + ->where('budgets.user_id', $this->user->id) + ; if (null !== $budgets && $budgets->count() > 0) { $query->whereIn('budget_limits.budget_id', $budgets->pluck('id')->toArray()); } $set = $query->get(['budget_limits.*']); $result = '0'; + /** @var BudgetLimit $budgetLimit */ foreach ($set as $budgetLimit) { $result = bcadd($budgetLimit->amount, $result); @@ -110,6 +105,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface public function destroyAll(): void { $budgets = $this->user->budgets()->get(); + /** @var Budget $budget */ foreach ($budgets as $budget) { $budget->budgetlimits()->delete(); @@ -118,21 +114,12 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface /** * Destroy a budget limit. - * - * @param BudgetLimit $budgetLimit */ public function destroyBudgetLimit(BudgetLimit $budgetLimit): void { $budgetLimit->delete(); } - /** - * @param TransactionCurrency $currency - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return Collection - */ public function getAllBudgetLimitsByCurrency(TransactionCurrency $currency, Carbon $start = null, Carbon $end = null): Collection { return $this->getAllBudgetLimits($start, $end)->filter( @@ -142,28 +129,24 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface ); } - /** - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return Collection - */ public function getAllBudgetLimits(Carbon $start = null, Carbon $end = null): Collection { // both are NULL: if (null === $start && null === $end) { return BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') - ->with(['budget']) - ->where('budgets.user_id', $this->user->id) - ->whereNull('budgets.deleted_at') - ->get(['budget_limits.*']); + ->with(['budget']) + ->where('budgets.user_id', $this->user->id) + ->whereNull('budgets.deleted_at') + ->get(['budget_limits.*']) + ; } // one of the two is NULL. if (null === $start xor null === $end) { $query = BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') - ->with(['budget']) - ->whereNull('budgets.deleted_at') - ->where('budgets.user_id', $this->user->id); + ->with(['budget']) + ->whereNull('budgets.deleted_at') + ->where('budgets.user_id', $this->user->id) + ; if (null !== $end) { // end date must be before $end. $query->where('end_date', '<=', $end->format('Y-m-d 00:00:00')); @@ -178,45 +161,41 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface // neither are NULL: return BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') - ->with(['budget']) - ->where('budgets.user_id', $this->user->id) - ->whereNull('budgets.deleted_at') - ->where( - static function (Builder $q5) use ($start, $end) { - $q5->where( - static function (Builder $q1) use ($start, $end) { - $q1->where( - static function (Builder $q2) use ($start, $end) { - $q2->where('budget_limits.end_date', '>=', $start->format('Y-m-d')); - $q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d')); - } - ) - ->orWhere( - static function (Builder $q3) use ($start, $end) { - $q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d')); - $q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d')); - } - ); - } - ) - ->orWhere( - static function (Builder $q4) use ($start, $end) { - // or start is before start AND end is after end. - $q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d')); - $q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d')); - } - ); - } - )->get(['budget_limits.*']); + ->with(['budget']) + ->where('budgets.user_id', $this->user->id) + ->whereNull('budgets.deleted_at') + ->where( + static function (Builder $q5) use ($start, $end): void { + $q5->where( + static function (Builder $q1) use ($start, $end): void { + $q1->where( + static function (Builder $q2) use ($start, $end): void { + $q2->where('budget_limits.end_date', '>=', $start->format('Y-m-d')); + $q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d')); + } + ) + ->orWhere( + static function (Builder $q3) use ($start, $end): void { + $q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d')); + $q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d')); + } + ) + ; + } + ) + ->orWhere( + static function (Builder $q4) use ($start, $end): void { + // or start is before start AND end is after end. + $q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d')); + $q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d')); + } + ) + ; + } + )->get(['budget_limits.*']) + ; } - /** - * @param Budget $budget - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return Collection - */ public function getBudgetLimits(Budget $budget, Carbon $start = null, Carbon $end = null): Collection { if (null === $end && null === $start) { @@ -239,41 +218,41 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface // when both dates are set: return $budget->budgetlimits() - ->where( - static function (Builder $q5) use ($start, $end) { // @phpstan-ignore-line - $q5->where( - static function (Builder $q1) use ($start, $end) { - // budget limit ends within period - $q1->where( - static function (Builder $q2) use ($start, $end) { - $q2->where('budget_limits.end_date', '>=', $start->format('Y-m-d 00:00:00')); - $q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d 23:59:59')); - } - ) - // budget limit start within period - ->orWhere( - static function (Builder $q3) use ($start, $end) { - $q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d 00:00:00')); - $q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d 23:59:59')); - } - ); - } - ) - ->orWhere( - static function (Builder $q4) use ($start, $end) { - // or start is before start AND end is after end. - $q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d 23:59:59')); - $q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d 00:00:00')); - } - ); - } - )->orderBy('budget_limits.start_date', 'DESC')->get(['budget_limits.*']); + ->where( + static function (Builder $q5) use ($start, $end): void { // @phpstan-ignore-line + $q5->where( + static function (Builder $q1) use ($start, $end): void { + // budget limit ends within period + $q1->where( + static function (Builder $q2) use ($start, $end): void { + $q2->where('budget_limits.end_date', '>=', $start->format('Y-m-d 00:00:00')); + $q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d 23:59:59')); + } + ) + // budget limit start within period + ->orWhere( + static function (Builder $q3) use ($start, $end): void { + $q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d 00:00:00')); + $q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d 23:59:59')); + } + ) + ; + } + ) + ->orWhere( + static function (Builder $q4) use ($start, $end): void { + // or start is before start AND end is after end. + $q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d 23:59:59')); + $q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d 00:00:00')); + } + ) + ; + } + )->orderBy('budget_limits.start_date', 'DESC')->get(['budget_limits.*']) + ; } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; @@ -281,11 +260,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface } /** - * @param array $data - * - * @return BudgetLimit * @throws FireflyException - * @throws JsonException */ public function store(array $data): BudgetLimit { @@ -307,10 +282,11 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface // find limit with same date range and currency. $limit = $budget->budgetlimits() - ->where('budget_limits.start_date', $data['start_date']->format('Y-m-d')) - ->where('budget_limits.end_date', $data['end_date']->format('Y-m-d')) - ->where('budget_limits.transaction_currency_id', $currency->id) - ->first(['budget_limits.*']); + ->where('budget_limits.start_date', $data['start_date']->format('Y-m-d')) + ->where('budget_limits.end_date', $data['end_date']->format('Y-m-d')) + ->where('budget_limits.transaction_currency_id', $currency->id) + ->first(['budget_limits.*']) + ; if (null !== $limit) { throw new FireflyException('200027: Budget limit already exists.'); } @@ -329,29 +305,17 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface return $limit; } - /** - * @param Budget $budget - * @param TransactionCurrency $currency - * @param Carbon $start - * @param Carbon $end - * - * @return BudgetLimit|null - */ public function find(Budget $budget, TransactionCurrency $currency, Carbon $start, Carbon $end): ?BudgetLimit { return $budget->budgetlimits() - ->where('transaction_currency_id', $currency->id) - ->where('start_date', $start->format('Y-m-d')) - ->where('end_date', $end->format('Y-m-d'))->first(); + ->where('transaction_currency_id', $currency->id) + ->where('start_date', $start->format('Y-m-d')) + ->where('end_date', $end->format('Y-m-d'))->first() + ; } /** - * @param BudgetLimit $budgetLimit - * @param array $data - * - * @return BudgetLimit * @throws FireflyException - * @throws JsonException */ public function update(BudgetLimit $budgetLimit, array $data): BudgetLimit { @@ -382,38 +346,32 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface return $budgetLimit; } - /** - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * @param string $amount - * - * @return BudgetLimit|null - * - */ public function updateLimitAmount(Budget $budget, Carbon $start, Carbon $end, string $amount): ?BudgetLimit { // count the limits: $limits = $budget->budgetlimits() - ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) - ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) - ->count('budget_limits.*'); + ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) + ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) + ->count('budget_limits.*') + ; app('log')->debug(sprintf('Found %d budget limits.', $limits)); // there might be a budget limit for these dates: - /** @var BudgetLimit|null $limit */ + /** @var null|BudgetLimit $limit */ $limit = $budget->budgetlimits() - ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) - ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) - ->first(['budget_limits.*']); + ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) + ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) + ->first(['budget_limits.*']) + ; // if more than 1 limit found, delete the others: if ($limits > 1 && null !== $limit) { app('log')->debug(sprintf('Found more than 1, delete all except #%d', $limit->id)); $budget->budgetlimits() - ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) - ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) - ->where('budget_limits.id', '!=', $limit->id)->delete(); + ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) + ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) + ->where('budget_limits.id', '!=', $limit->id)->delete() + ; } // delete if amount is zero. diff --git a/app/Repositories/Budget/BudgetLimitRepositoryInterface.php b/app/Repositories/Budget/BudgetLimitRepositoryInterface.php index 7ddd85cadf..eaee27707b 100644 --- a/app/Repositories/Budget/BudgetLimitRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetLimitRepositoryInterface.php @@ -39,13 +39,6 @@ interface BudgetLimitRepositoryInterface /** * Tells you which amount has been budgeted (for the given budgets) * in the selected query. Returns a positive amount as a string. - * - * @param Carbon $start - * @param Carbon $end - * @param TransactionCurrency $currency - * @param Collection|null $budgets - * - * @return string */ public function budgeted(Carbon $start, Carbon $end, TransactionCurrency $currency, ?Collection $budgets = null): string; @@ -56,76 +49,25 @@ interface BudgetLimitRepositoryInterface /** * Destroy a budget limit. - * - * @param BudgetLimit $budgetLimit */ public function destroyBudgetLimit(BudgetLimit $budgetLimit): void; - /** - * @param Budget $budget - * @param TransactionCurrency $currency - * @param Carbon $start - * @param Carbon $end - * - * @return BudgetLimit|null - */ public function find(Budget $budget, TransactionCurrency $currency, Carbon $start, Carbon $end): ?BudgetLimit; /** * TODO this method is not multi currency aware. - * - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return Collection */ public function getAllBudgetLimits(Carbon $start = null, Carbon $end = null): Collection; - /** - * @param TransactionCurrency $currency - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return Collection - */ public function getAllBudgetLimitsByCurrency(TransactionCurrency $currency, Carbon $start = null, Carbon $end = null): Collection; - /** - * @param Budget $budget - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return Collection - */ public function getBudgetLimits(Budget $budget, Carbon $start = null, Carbon $end = null): Collection; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; - /** - * @param array $data - * - * @return BudgetLimit - */ public function store(array $data): BudgetLimit; - /** - * @param BudgetLimit $budgetLimit - * @param array $data - * - * @return BudgetLimit - */ public function update(BudgetLimit $budgetLimit, array $data): BudgetLimit; - /** - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * @param string $amount - * - * @return BudgetLimit|null - */ public function updateLimitAmount(Budget $budget, Carbon $start, Carbon $end, string $amount): ?BudgetLimit; } diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index ceb7a89a71..fd032b705f 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Budget; use Carbon\Carbon; -use DB; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Account; @@ -44,20 +43,14 @@ use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\QueryException; use Illuminate\Support\Collection; -use JsonException; -use Storage; /** * Class BudgetRepository. - * */ class BudgetRepository implements BudgetRepositoryInterface { private User $user; - /** - * @inheritDoc - */ public function budgetEndsWith(string $query, int $limit): Collection { $search = $this->user->budgets(); @@ -65,14 +58,12 @@ class BudgetRepository implements BudgetRepositoryInterface $search->where('name', 'LIKE', sprintf('%%%s', $query)); } $search->orderBy('order', 'ASC') - ->orderBy('name', 'ASC')->where('active', true); + ->orderBy('name', 'ASC')->where('active', true) + ; return $search->take($limit)->get(); } - /** - * @inheritDoc - */ public function budgetStartsWith(string $query, int $limit): Collection { $search = $this->user->budgets(); @@ -80,32 +71,33 @@ class BudgetRepository implements BudgetRepositoryInterface $search->where('name', 'LIKE', sprintf('%s%%', $query)); } $search->orderBy('order', 'ASC') - ->orderBy('name', 'ASC')->where('active', true); + ->orderBy('name', 'ASC')->where('active', true) + ; return $search->take($limit)->get(); } - /** - * @inheritDoc - */ public function budgetedInPeriod(Carbon $start, Carbon $end): array { app('log')->debug(sprintf('Now in budgetedInPeriod("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'))); $return = []; + /** @var BudgetLimitRepository $limitRepository */ $limitRepository = app(BudgetLimitRepository::class); $limitRepository->setUser($this->user); $budgets = $this->getActiveBudgets(); + /** @var Budget $budget */ foreach ($budgets as $budget) { app('log')->debug(sprintf('Budget #%d: "%s"', $budget->id, $budget->name)); $limits = $limitRepository->getBudgetLimits($budget, $start, $end); + /** @var BudgetLimit $limit */ foreach ($limits as $limit) { app('log')->debug(sprintf('Budget limit #%d', $limit->id)); $currency = $limit->transactionCurrency; $return[$currency->id] ??= [ - 'id' => (string)$currency->id, + 'id' => (string) $currency->id, 'name' => $currency->name, 'symbol' => $currency->symbol, 'code' => $currency->code, @@ -116,22 +108,24 @@ class BudgetRepository implements BudgetRepositoryInterface if ($limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end)) { $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $limit->amount); app('log')->debug(sprintf('Add full amount [1]: %s', $limit->amount)); + continue; } // limit is inside of date range if ($start->lte($limit->start_date) && $end->gte($limit->end_date)) { $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $limit->amount); app('log')->debug(sprintf('Add full amount [2]: %s', $limit->amount)); + continue; } $total = $limit->start_date->diffInDays($limit->end_date) + 1; // include the day itself. $days = $this->daysInOverlap($limit, $start, $end); - $amount = bcmul(bcdiv($limit->amount, (string)$total), (string)$days); + $amount = bcmul(bcdiv($limit->amount, (string) $total), (string) $days); $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $amount); app('log')->debug( sprintf( 'Amount per day: %s (%s over %d days). Total amount for %d days: %s', - bcdiv($limit->amount, (string)$total), + bcdiv($limit->amount, (string) $total), $limit->amount, $total, $days, @@ -140,88 +134,44 @@ class BudgetRepository implements BudgetRepositoryInterface ); } } + return $return; } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * @return Collection - */ public function getActiveBudgets(): Collection { return $this->user->budgets()->where('active', true) - ->orderBy('order', 'ASC') - ->orderBy('name', 'ASC') - ->get(); + ->orderBy('order', 'ASC') + ->orderBy('name', 'ASC') + ->get() + ; } - /** - * How many days of this budget limit are between start and end? - * - * @param BudgetLimit $limit - * @param Carbon $start - * @param Carbon $end - * - * @return int - */ - private function daysInOverlap(BudgetLimit $limit, Carbon $start, Carbon $end): int - { - // start1 = $start - // start2 = $limit->start_date - // start1 = $end - // start2 = $limit->end_date - - // limit is larger than start and end (inclusive) - // |-----------| - // |----------------| - if ($start->gte($limit->start_date) && $end->lte($limit->end_date)) { - return $start->diffInDays($end) + 1; // add one day - } - // limit starts earlier and limit ends first: - // |-----------| - // |-------| - if ($limit->start_date->lte($start) && $limit->end_date->lte($end)) { - // return days in the range $start-$limit_end - return $start->diffInDays($limit->end_date) + 1; // add one day, the day itself - } - // limit starts later and limit ends earlier - // |-----------| - // |-------| - if ($limit->start_date->gte($start) && $limit->end_date->gte($end)) { - // return days in the range $limit_start - $end - return $limit->start_date->diffInDays($end) + 1; // add one day, the day itself - } - return 0; - } - - /** - * @inheritDoc - */ public function budgetedInPeriodForBudget(Budget $budget, Carbon $start, Carbon $end): array { app('log')->debug(sprintf('Now in budgetedInPeriod(#%d, "%s", "%s")', $budget->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); $return = []; + /** @var BudgetLimitRepository $limitRepository */ $limitRepository = app(BudgetLimitRepository::class); $limitRepository->setUser($this->user); app('log')->debug(sprintf('Budget #%d: "%s"', $budget->id, $budget->name)); $limits = $limitRepository->getBudgetLimits($budget, $start, $end); + /** @var BudgetLimit $limit */ foreach ($limits as $limit) { app('log')->debug(sprintf('Budget limit #%d', $limit->id)); $currency = $limit->transactionCurrency; $return[$currency->id] ??= [ - 'id' => (string)$currency->id, + 'id' => (string) $currency->id, 'name' => $currency->name, 'symbol' => $currency->symbol, 'code' => $currency->code, @@ -232,22 +182,24 @@ class BudgetRepository implements BudgetRepositoryInterface if ($limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end)) { $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $limit->amount); app('log')->debug(sprintf('Add full amount [1]: %s', $limit->amount)); + continue; } // limit is inside of date range if ($start->lte($limit->start_date) && $end->gte($limit->end_date)) { $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $limit->amount); app('log')->debug(sprintf('Add full amount [2]: %s', $limit->amount)); + continue; } $total = $limit->start_date->diffInDays($limit->end_date) + 1; // include the day itself. $days = $this->daysInOverlap($limit, $start, $end); - $amount = bcmul(bcdiv($limit->amount, (string)$total), (string)$days); + $amount = bcmul(bcdiv($limit->amount, (string) $total), (string) $days); $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $amount); app('log')->debug( sprintf( 'Amount per day: %s (%s over %d days). Total amount for %d days: %s', - bcdiv($limit->amount, (string)$total), + bcdiv($limit->amount, (string) $total), $limit->amount, $total, $days, @@ -255,17 +207,16 @@ class BudgetRepository implements BudgetRepositoryInterface ) ); } + return $return; } - /** - * @return bool - */ public function cleanupBudgets(): bool { // delete limits with amount 0: BudgetLimit::where('amount', 0)->delete(); $budgets = $this->getActiveBudgets(); + /** * @var int $index * @var Budget $budget @@ -281,12 +232,7 @@ class BudgetRepository implements BudgetRepositoryInterface } /** - * @param Budget $budget - * @param array $data - * - * @return Budget * @throws FireflyException - * @throws JsonException */ public function update(Budget $budget, array $data): Budget { @@ -302,7 +248,7 @@ class BudgetRepository implements BudgetRepositoryInterface $budget->active = $data['active']; } if (array_key_exists('notes', $data)) { - $this->setNoteText($budget, (string)$data['notes']); + $this->setNoteText($budget, (string) $data['notes']); } $budget->save(); @@ -329,149 +275,21 @@ class BudgetRepository implements BudgetRepositoryInterface 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.*']); - app('log')->debug(sprintf('Found %d actions to update.', $actions->count())); - /** @var RuleAction $action */ - foreach ($actions as $action) { - $action->action_value = $newName; - $action->save(); - app('log')->debug(sprintf('Updated action %d: %s', $action->id, $action->action_value)); - } - } - - /** - * @param string $oldName - * @param string $newName - */ - private function updateRuleTriggers(string $oldName, string $newName): void - { - $types = ['budget_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.*']); - app('log')->debug(sprintf('Found %d triggers to update.', $triggers->count())); - /** @var RuleTrigger $trigger */ - foreach ($triggers as $trigger) { - $trigger->trigger_value = $newName; - $trigger->save(); - app('log')->debug(sprintf('Updated trigger %d: %s', $trigger->id, $trigger->trigger_value)); - } - } - - /** - * @param Budget $budget - * @param string $text - * - * @return void - */ - private function setNoteText(Budget $budget, string $text): void - { - $dbNote = $budget->notes()->first(); - if ('' !== $text) { - if (null === $dbNote) { - $dbNote = new Note(); - $dbNote->noteable()->associate($budget); - } - $dbNote->text = trim($text); - $dbNote->save(); - - return; - } - if (null !== $dbNote) { - $dbNote->delete(); - } - } - - /** - * @inheritDoc - */ public function getAutoBudget(Budget $budget): ?AutoBudget { return $budget->autoBudgets()->first(); } - /** - * @param Budget $budget - * @param array $data - * - * @throws FireflyException - * @throws JsonException - */ - private function updateAutoBudget(Budget $budget, array $data): void - { - // update or create auto-budget: - $autoBudget = $this->getAutoBudget($budget); - - // grab default currency: - $currency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); - - if (null === $autoBudget) { - // at this point it's a blind assumption auto_budget_type is 1 or 2. - $autoBudget = new AutoBudget(); - $autoBudget->auto_budget_type = $data['auto_budget_type']; - $autoBudget->budget_id = $budget->id; - $autoBudget->transaction_currency_id = $currency->id; - } - - // set or update the currency. - if (array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) { - /** @var CurrencyRepositoryInterface $repos */ - $repos = app(CurrencyRepositoryInterface::class); - $currencyId = (int)($data['currency_id'] ?? 0); - $currencyCode = (string)($data['currency_code'] ?? ''); - $currency = $repos->find($currencyId); - if (null === $currency) { - $currency = $repos->findByCode($currencyCode); - } - if (null !== $currency) { - $autoBudget->transaction_currency_id = $currency->id; - } - } - - // change values if submitted or presented: - if (array_key_exists('auto_budget_type', $data)) { - $autoBudget->auto_budget_type = $data['auto_budget_type']; - } - 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']; - } - - $autoBudget->save(); - } - /** * Find a budget or return NULL * - * @param int|null $budgetId |null - * - * @return Budget|null + * @param null|int $budgetId |null */ public function find(int $budgetId = null): ?Budget { return $this->user->budgets()->find($budgetId); } - /** - * @param Budget $budget - * - * @return bool - */ public function destroy(Budget $budget): bool { /** @var BudgetDestroyService $service */ @@ -487,28 +305,24 @@ class BudgetRepository implements BudgetRepositoryInterface 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', (string)$budget->id)->delete(); - RuleAction::where('action_type', 'set_budget')->where('action_value', (string)$budget->id)->delete(); + \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', (string) $budget->id)->delete(); + RuleAction::where('action_type', 'set_budget')->where('action_value', (string) $budget->id)->delete(); $budget->delete(); } } - /** - * @return Collection - */ public function getBudgets(): Collection { return $this->user->budgets()->orderBy('order', 'ASC') - ->orderBy('name', 'ASC')->get(); + ->orderBy('name', 'ASC')->get() + ; } - /** - * @inheritDoc - */ public function destroyAutoBudget(Budget $budget): void { /** @var AutoBudget $autoBudget */ @@ -517,17 +331,11 @@ class BudgetRepository implements BudgetRepositoryInterface } } - /** - * @param int|null $budgetId - * @param string|null $budgetName - * - * @return Budget|null - */ public function findBudget(?int $budgetId, ?string $budgetName): ?Budget { app('log')->debug('Now in findBudget()'); app('log')->debug(sprintf('Searching for budget with ID #%d...', $budgetId)); - $result = $this->find((int)$budgetId); + $result = $this->find((int) $budgetId); if (null === $result && null !== $budgetName && '' !== $budgetName) { app('log')->debug(sprintf('Searching for budget with name %s...', $budgetName)); $result = $this->findByName($budgetName); @@ -542,10 +350,6 @@ class BudgetRepository implements BudgetRepositoryInterface /** * Find budget by name. - * - * @param string|null $name - * - * @return Budget|null */ public function findByName(?string $name): ?Budget { @@ -560,10 +364,6 @@ class BudgetRepository implements BudgetRepositoryInterface /** * This method returns the oldest journal or transaction date known to this budget. * Will cache result. - * - * @param Budget $budget - * - * @return Carbon|null */ public function firstUseDate(Budget $budget): ?Carbon { @@ -575,15 +375,12 @@ class BudgetRepository implements BudgetRepositoryInterface return null; } - /** - * @inheritDoc - */ public function getAttachments(Budget $budget): Collection { $set = $budget->attachments()->get(); - /** @var Storage $disk */ - $disk = Storage::disk('upload'); + /** @var \Storage $disk */ + $disk = \Storage::disk('upload'); return $set->each( static function (Attachment $attachment) use ($disk) { @@ -598,29 +395,20 @@ class BudgetRepository implements BudgetRepositoryInterface /** * Get all budgets with these ID's. - * - * @param array $budgetIds - * - * @return Collection */ public function getByIds(array $budgetIds): Collection { return $this->user->budgets()->whereIn('id', $budgetIds)->get(); } - /** - * @return Collection - */ public function getInactiveBudgets(): Collection { return $this->user->budgets() - ->orderBy('order', 'ASC') - ->orderBy('name', 'ASC')->where('active', 0)->get(); + ->orderBy('order', 'ASC') + ->orderBy('name', 'ASC')->where('active', 0)->get() + ; } - /** - * @inheritDoc - */ public function getNoteText(Budget $budget): ?string { $note = $budget->notes()->first(); @@ -631,12 +419,6 @@ class BudgetRepository implements BudgetRepositoryInterface return $note->text; } - /** - * @param string $query - * @param int $limit - * - * @return Collection - */ public function searchBudget(string $query, int $limit): Collection { $search = $this->user->budgets(); @@ -644,24 +426,18 @@ class BudgetRepository implements BudgetRepositoryInterface $search->where('name', 'LIKE', sprintf('%%%s%%', $query)); } $search->orderBy('order', 'ASC') - ->orderBy('name', 'ASC')->where('active', true); + ->orderBy('name', 'ASC')->where('active', true) + ; return $search->take($limit)->get(); } - /** - * @param Budget $budget - * @param int $order - */ public function setBudgetOrder(Budget $budget, int $order): void { $budget->order = $order; $budget->save(); } - /** - * @inheritDoc - */ public function spentInPeriod(Carbon $start, Carbon $end): array { app('log')->debug(sprintf('Now in %s', __METHOD__)); @@ -673,6 +449,7 @@ class BudgetRepository implements BudgetRepositoryInterface $repository->setUser($this->user); $subset = $repository->getAccountsByType(config('firefly.valid_liabilities')); $selection = new Collection(); + /** @var Account $account */ foreach ($subset as $account) { if ('credit' === $repository->getMetaValue($account, 'liability_direction')) { @@ -684,18 +461,19 @@ class BudgetRepository implements BudgetRepositoryInterface /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setUser($this->user) - ->setRange($start, $end) - ->excludeDestinationAccounts($selection) - ->setTypes([TransactionType::WITHDRAWAL]) - ->setBudgets($this->getActiveBudgets()); + ->setRange($start, $end) + ->excludeDestinationAccounts($selection) + ->setTypes([TransactionType::WITHDRAWAL]) + ->setBudgets($this->getActiveBudgets()) + ; $journals = $collector->getExtractedJournals(); $array = []; foreach ($journals as $journal) { - $currencyId = (int)$journal['currency_id']; + $currencyId = (int) $journal['currency_id']; $array[$currencyId] ??= [ - 'id' => (string)$currencyId, + 'id' => (string) $currencyId, 'name' => $journal['currency_name'], 'symbol' => $journal['currency_symbol'], 'code' => $journal['currency_code'], @@ -705,10 +483,10 @@ class BudgetRepository implements BudgetRepositoryInterface $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->negative($journal['amount'])); // also do foreign amount: - $foreignId = (int)$journal['foreign_currency_id']; + $foreignId = (int) $journal['foreign_currency_id']; if (0 !== $foreignId) { $array[$foreignId] ??= [ - 'id' => (string)$foreignId, + 'id' => (string) $foreignId, 'name' => $journal['foreign_currency_name'], 'symbol' => $journal['foreign_currency_symbol'], 'code' => $journal['foreign_currency_code'], @@ -722,9 +500,6 @@ class BudgetRepository implements BudgetRepositoryInterface return $array; } - /** - * @inheritDoc - */ public function spentInPeriodForBudget(Budget $budget, Carbon $start, Carbon $end): array { app('log')->debug(sprintf('Now in %s', __METHOD__)); @@ -736,6 +511,7 @@ class BudgetRepository implements BudgetRepositoryInterface $repository->setUser($this->user); $subset = $repository->getAccountsByType(config('firefly.valid_liabilities')); $selection = new Collection(); + /** @var Account $account */ foreach ($subset as $account) { if ('credit' === $repository->getMetaValue($account, 'liability_direction')) { @@ -747,18 +523,19 @@ class BudgetRepository implements BudgetRepositoryInterface /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setUser($this->user) - ->setRange($start, $end) - ->excludeDestinationAccounts($selection) - ->setTypes([TransactionType::WITHDRAWAL]) - ->setBudget($budget); + ->setRange($start, $end) + ->excludeDestinationAccounts($selection) + ->setTypes([TransactionType::WITHDRAWAL]) + ->setBudget($budget) + ; $journals = $collector->getExtractedJournals(); $array = []; foreach ($journals as $journal) { - $currencyId = (int)$journal['currency_id']; + $currencyId = (int) $journal['currency_id']; $array[$currencyId] ??= [ - 'id' => (string)$currencyId, + 'id' => (string) $currencyId, 'name' => $journal['currency_name'], 'symbol' => $journal['currency_symbol'], 'code' => $journal['currency_code'], @@ -768,10 +545,10 @@ class BudgetRepository implements BudgetRepositoryInterface $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->negative($journal['amount'])); // also do foreign amount: - $foreignId = (int)$journal['foreign_currency_id']; + $foreignId = (int) $journal['foreign_currency_id']; if (0 !== $foreignId) { $array[$foreignId] ??= [ - 'id' => (string)$foreignId, + 'id' => (string) $foreignId, 'name' => $journal['foreign_currency_name'], 'symbol' => $journal['foreign_currency_symbol'], 'code' => $journal['foreign_currency_code'], @@ -786,15 +563,14 @@ class BudgetRepository implements BudgetRepositoryInterface } /** - * @param array $data - * - * @return Budget * @throws FireflyException - * @throws JsonException + * + * @SuppressWarnings(PHPMD.NPathComplexity) */ public function store(array $data): Budget { $order = $this->getMaxOrder(); + try { $newBudget = Budget::create( [ @@ -808,12 +584,13 @@ class BudgetRepository implements BudgetRepositoryInterface } catch (QueryException $e) { app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); + throw new FireflyException('400002: Could not store budget.', 0, $e); } // set notes if (array_key_exists('notes', $data)) { - $this->setNoteText($newBudget, (string)$data['notes']); + $this->setNoteText($newBudget, (string) $data['notes']); } if (!array_key_exists('auto_budget_type', $data) || !array_key_exists('auto_budget_amount', $data) || !array_key_exists('auto_budget_period', $data)) { @@ -841,10 +618,10 @@ class BudgetRepository implements BudgetRepositoryInterface $repos = app(CurrencyRepositoryInterface::class); $currency = null; if (array_key_exists('currency_id', $data)) { - $currency = $repos->find((int)$data['currency_id']); + $currency = $repos->find((int) $data['currency_id']); } if (array_key_exists('currency_code', $data)) { - $currency = $repos->findByCode((string)$data['currency_code']); + $currency = $repos->findByCode((string) $data['currency_code']); } if (null === $currency) { $currency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); @@ -878,11 +655,146 @@ class BudgetRepository implements BudgetRepositoryInterface return $newBudget; } - /** - * @return int - */ public function getMaxOrder(): int { - return (int)$this->user->budgets()->max('order'); + return (int) $this->user->budgets()->max('order'); + } + + /** + * How many days of this budget limit are between start and end? + */ + private function daysInOverlap(BudgetLimit $limit, Carbon $start, Carbon $end): int + { + // start1 = $start + // start2 = $limit->start_date + // start1 = $end + // start2 = $limit->end_date + + // limit is larger than start and end (inclusive) + // |-----------| + // |----------------| + if ($start->gte($limit->start_date) && $end->lte($limit->end_date)) { + return $start->diffInDays($end) + 1; // add one day + } + // limit starts earlier and limit ends first: + // |-----------| + // |-------| + if ($limit->start_date->lte($start) && $limit->end_date->lte($end)) { + // return days in the range $start-$limit_end + return $start->diffInDays($limit->end_date) + 1; // add one day, the day itself + } + // limit starts later and limit ends earlier + // |-----------| + // |-------| + if ($limit->start_date->gte($start) && $limit->end_date->gte($end)) { + // return days in the range $limit_start - $end + return $limit->start_date->diffInDays($end) + 1; // add one day, the day itself + } + + return 0; + } + + 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.*']) + ; + app('log')->debug(sprintf('Found %d actions to update.', $actions->count())); + + /** @var RuleAction $action */ + foreach ($actions as $action) { + $action->action_value = $newName; + $action->save(); + app('log')->debug(sprintf('Updated action %d: %s', $action->id, $action->action_value)); + } + } + + private function updateRuleTriggers(string $oldName, string $newName): void + { + $types = ['budget_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.*']) + ; + app('log')->debug(sprintf('Found %d triggers to update.', $triggers->count())); + + /** @var RuleTrigger $trigger */ + foreach ($triggers as $trigger) { + $trigger->trigger_value = $newName; + $trigger->save(); + app('log')->debug(sprintf('Updated trigger %d: %s', $trigger->id, $trigger->trigger_value)); + } + } + + private function setNoteText(Budget $budget, string $text): void + { + $dbNote = $budget->notes()->first(); + if ('' !== $text) { + if (null === $dbNote) { + $dbNote = new Note(); + $dbNote->noteable()->associate($budget); + } + $dbNote->text = trim($text); + $dbNote->save(); + + return; + } + if (null !== $dbNote) { + $dbNote->delete(); + } + } + + /** + * @throws FireflyException + */ + private function updateAutoBudget(Budget $budget, array $data): void + { + // update or create auto-budget: + $autoBudget = $this->getAutoBudget($budget); + + // grab default currency: + $currency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); + + if (null === $autoBudget) { + // at this point it's a blind assumption auto_budget_type is 1 or 2. + $autoBudget = new AutoBudget(); + $autoBudget->auto_budget_type = $data['auto_budget_type']; + $autoBudget->budget_id = $budget->id; + $autoBudget->transaction_currency_id = $currency->id; + } + + // set or update the currency. + if (array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) { + /** @var CurrencyRepositoryInterface $repos */ + $repos = app(CurrencyRepositoryInterface::class); + $currencyId = (int) ($data['currency_id'] ?? 0); + $currencyCode = (string) ($data['currency_code'] ?? ''); + $currency = $repos->find($currencyId); + if (null === $currency) { + $currency = $repos->findByCode($currencyCode); + } + if (null !== $currency) { + $autoBudget->transaction_currency_id = $currency->id; + } + } + + // change values if submitted or presented: + if (array_key_exists('auto_budget_type', $data)) { + $autoBudget->auto_budget_type = $data['auto_budget_type']; + } + 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']; + } + + $autoBudget->save(); } } diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index fcfaaed4ba..1f5b1df804 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -36,53 +36,22 @@ use Illuminate\Support\Collection; */ interface BudgetRepositoryInterface { - /** - * @param string $query - * @param int $limit - * - * @return Collection - */ public function budgetEndsWith(string $query, int $limit): Collection; - /** - * @param string $query - * @param int $limit - * - * @return Collection - */ public function budgetStartsWith(string $query, int $limit): Collection; /** * Returns the amount that is budgeted in a period. - * - * @param Carbon $start - * @param Carbon $end - * - * @return array */ public function budgetedInPeriod(Carbon $start, Carbon $end): array; /** * Returns the amount that is budgeted in a period. - * - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * - * @return array */ public function budgetedInPeriodForBudget(Budget $budget, Carbon $start, Carbon $end): array; - /** - * @return bool - */ public function cleanupBudgets(): bool; - /** - * @param Budget $budget - * - * @return bool - */ public function destroy(Budget $budget): bool; /** @@ -90,145 +59,62 @@ interface BudgetRepositoryInterface */ public function destroyAll(): void; - /** - * @param Budget $budget - */ public function destroyAutoBudget(Budget $budget): void; - /** - * - * @param int|null $budgetId - * - * @return Budget|null - */ public function find(int $budgetId = null): ?Budget; - /** - * @param int|null $budgetId - * @param string|null $budgetName - * - * @return Budget|null - */ public function findBudget(?int $budgetId, ?string $budgetName): ?Budget; /** * Find budget by name. - * - * @param string|null $name - * - * @return Budget|null */ public function findByName(?string $name): ?Budget; /** * This method returns the oldest journal or transaction date known to this budget. * Will cache result. - * - * @param Budget $budget - * - * @return Carbon|null */ public function firstUseDate(Budget $budget): ?Carbon; - /** - * @return Collection - */ 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 - */ public function getBudgets(): Collection; /** * Get all budgets with these ID's. - * - * @param array $budgetIds - * - * @return Collection */ public function getByIds(array $budgetIds): Collection; - /** - * @return Collection - */ public function getInactiveBudgets(): Collection; - /** - * @return int - */ public function getMaxOrder(): int; - /** - * @param Budget $budget - * - * @return string|null - */ public function getNoteText(Budget $budget): ?string; - /** - * @param string $query - * @param int $limit - * - * @return Collection - */ public function searchBudget(string $query, int $limit): Collection; - /** - * @param Budget $budget - * @param int $order - */ public function setBudgetOrder(Budget $budget, int $order): void; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; /** * Used in the v2 API to calculate the amount of money spent in all active budgets. - * - * @param Carbon $start - * @param Carbon $end - * - * @return array */ public function spentInPeriod(Carbon $start, Carbon $end): array; /** * Used in the v2 API to calculate the amount of money spent in a single budget.. - * - * */ public function spentInPeriodForBudget(Budget $budget, Carbon $start, Carbon $end): array; /** - * @param array $data - * - * @return Budget * @throws FireflyException */ public function store(array $data): Budget; - /** - * @param Budget $budget - * @param array $data - * - * @return Budget - */ public function update(Budget $budget, array $data): Budget; } diff --git a/app/Repositories/Budget/NoBudgetRepository.php b/app/Repositories/Budget/NoBudgetRepository.php index 69ee28cfc0..73f0bc2deb 100644 --- a/app/Repositories/Budget/NoBudgetRepository.php +++ b/app/Repositories/Budget/NoBudgetRepository.php @@ -32,7 +32,6 @@ use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; /** - * * Class NoBudgetRepository */ class NoBudgetRepository implements NoBudgetRepositoryInterface @@ -40,13 +39,6 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface /** @var User */ private $user; - /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ public function getNoBudgetPeriodReport(Collection $accounts, Carbon $start, Carbon $end): array { $carbonFormat = app('navigation')->preferredCarbonFormat($start, $end); @@ -87,11 +79,6 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface } /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array * @deprecated */ public function spentInPeriodWoBudgetMc(Collection $accounts, Carbon $start, Carbon $end): array @@ -109,6 +96,7 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface $return = []; $total = []; $currencies = []; + /** @var array $journal */ foreach ($journals as $journal) { $code = $journal['currency_code']; @@ -138,10 +126,7 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface return $return; } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; @@ -151,13 +136,6 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface /** * TODO this method does not include multi currency. It just counts. * TODO this probably also applies to the other "sumExpenses" methods. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param TransactionCurrency|null $currency - * - * @return array */ public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array { diff --git a/app/Repositories/Budget/NoBudgetRepositoryInterface.php b/app/Repositories/Budget/NoBudgetRepositoryInterface.php index 579d37eec2..79e30d1867 100644 --- a/app/Repositories/Budget/NoBudgetRepositoryInterface.php +++ b/app/Repositories/Budget/NoBudgetRepositoryInterface.php @@ -35,37 +35,16 @@ use Illuminate\Support\Collection; interface NoBudgetRepositoryInterface { /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array * @deprecated */ public function getNoBudgetPeriodReport(Collection $accounts, Carbon $start, Carbon $end): array; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array * @deprecated */ public function spentInPeriodWoBudgetMc(Collection $accounts, Carbon $start, Carbon $end): array; - /** - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param TransactionCurrency|null $currency - * - * @return array - */ public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array; } diff --git a/app/Repositories/Budget/OperationsRepository.php b/app/Repositories/Budget/OperationsRepository.php index 893eca39c3..0e0bec4b0d 100644 --- a/app/Repositories/Budget/OperationsRepository.php +++ b/app/Repositories/Budget/OperationsRepository.php @@ -35,7 +35,6 @@ use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; /** - * * Class OperationsRepository */ class OperationsRepository implements OperationsRepositoryInterface @@ -45,10 +44,6 @@ class OperationsRepository implements OperationsRepositoryInterface /** * A method that returns the amount of money budgeted per day for this budget, * on average. - * - * @param Budget $budget - * - * @return string */ public function budgetedPerDay(Budget $budget): string { @@ -61,7 +56,7 @@ class OperationsRepository implements OperationsRepositoryInterface $amount = $limit->amount; $perDay = bcdiv($amount, (string)$diff); $total = bcadd($total, $perDay); - $count++; + ++$count; app('log')->debug(sprintf('Found %d budget limits. Per day is %s, total is %s', $count, $perDay, $total)); } $avg = $total; @@ -77,18 +72,13 @@ class OperationsRepository implements OperationsRepositoryInterface * This method is being used to generate the budget overview in the year/multi-year report. Its used * in both the year/multi-year budget overview AND in the accompanying chart. * - * @param Collection $budgets - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array * @deprecated */ public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array { $carbonFormat = app('navigation')->preferredCarbonFormat($start, $end); $data = []; + // get all transactions: /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); @@ -127,13 +117,6 @@ class OperationsRepository implements OperationsRepositoryInterface * 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 { @@ -199,10 +182,7 @@ class OperationsRepository implements OperationsRepositoryInterface return $array; } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; @@ -210,24 +190,6 @@ class OperationsRepository implements OperationsRepositoryInterface } /** - * @return Collection - */ - private function getBudgets(): Collection - { - /** @var BudgetRepositoryInterface $repos */ - $repos = app(BudgetRepositoryInterface::class); - - return $repos->getActiveBudgets(); - } - - /** - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $budgets - * @param TransactionCurrency|null $currency - * - * @return array * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumExpenses( @@ -236,9 +198,8 @@ class OperationsRepository implements OperationsRepositoryInterface ?Collection $accounts = null, ?Collection $budgets = null, ?TransactionCurrency $currency = null - ): array - { - //app('log')->debug(sprintf('Now in %s', __METHOD__)); + ): array { + // app('log')->debug(sprintf('Now in %s', __METHOD__)); $start->startOfDay(); $end->endOfDay(); @@ -251,6 +212,7 @@ class OperationsRepository implements OperationsRepositoryInterface $repository->setUser($this->user); $subset = $repository->getAccountsByType(config('firefly.valid_liabilities')); $selection = new Collection(); + /** @var Account $account */ foreach ($subset as $account) { if ('credit' === $repository->getMetaValue($account, 'liability_direction')) { @@ -258,13 +220,13 @@ class OperationsRepository implements OperationsRepositoryInterface } } - /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setUser($this->user) - ->setRange($start, $end) - ->excludeDestinationAccounts($selection) - ->setTypes([TransactionType::WITHDRAWAL]); + ->setRange($start, $end) + ->excludeDestinationAccounts($selection) + ->setTypes([TransactionType::WITHDRAWAL]) + ; if (null !== $accounts) { $collector->setAccounts($accounts); @@ -280,17 +242,18 @@ class OperationsRepository implements OperationsRepositoryInterface // same but for foreign currencies: if (null !== $currency) { - //app('log')->debug(sprintf('Currency is "%s".', $currency->name)); + // app('log')->debug(sprintf('Currency is "%s".', $currency->name)); /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]) - ->setForeignCurrency($currency)->setBudgets($budgets); + ->setForeignCurrency($currency)->setBudgets($budgets) + ; if (null !== $accounts) { $collector->setAccounts($accounts); } $result = $collector->getExtractedJournals(); - //app('log')->debug(sprintf('Found %d journals with currency %s.', count($result), $currency->code)); + // app('log')->debug(sprintf('Found %d journals with currency %s.', count($result), $currency->code)); // do not use array_merge because you want keys to overwrite (otherwise you get double results): $journals = $result + $journals; } @@ -326,4 +289,11 @@ class OperationsRepository implements OperationsRepositoryInterface return $array; } + 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 bed08af39f..01c5da7560 100644 --- a/app/Repositories/Budget/OperationsRepositoryInterface.php +++ b/app/Repositories/Budget/OperationsRepositoryInterface.php @@ -38,20 +38,10 @@ interface OperationsRepositoryInterface /** * A method that returns the amount of money budgeted per day for this budget, * on average. - * - * @param Budget $budget - * - * @return string */ public function budgetedPerDay(Budget $budget): string; /** - * @param Collection $budgets - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array * @deprecated */ public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array; @@ -60,29 +50,12 @@ interface OperationsRepositoryInterface * 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|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; /** - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $budgets - * @param TransactionCurrency|null $currency - * - * @return array * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumExpenses( diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 6a05ef73bb..70605f9d20 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -24,8 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Category; use Carbon\Carbon; -use DB; -use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\CategoryFactory; use FireflyIII\Models\Attachment; @@ -38,7 +36,6 @@ use FireflyIII\Services\Internal\Update\CategoryUpdateService; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use Storage; /** * Class CategoryRepository. @@ -47,9 +44,6 @@ class CategoryRepository implements CategoryRepositoryInterface { private User $user; - /** - * @inheritDoc - */ public function categoryEndsWith(string $query, int $limit): Collection { $search = $this->user->categories(); @@ -60,9 +54,6 @@ class CategoryRepository implements CategoryRepositoryInterface return $search->take($limit)->get(); } - /** - * @inheritDoc - */ public function categoryStartsWith(string $query, int $limit): Collection { $search = $this->user->categories(); @@ -73,13 +64,6 @@ class CategoryRepository implements CategoryRepositoryInterface return $search->take($limit)->get(); } - /** - * @param Category $category - * - * @return bool - * - - */ public function destroy(Category $category): bool { /** @var CategoryDestroyService $service */ @@ -95,10 +79,11 @@ class CategoryRepository implements CategoryRepositoryInterface 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(); + \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(); @@ -107,8 +92,6 @@ class CategoryRepository implements CategoryRepositoryInterface /** * Returns a list of all the categories belonging to a user. - * - * @return Collection */ public function getCategories(): Collection { @@ -116,10 +99,6 @@ class CategoryRepository implements CategoryRepositoryInterface } /** - * @param int|null $categoryId - * @param string|null $categoryName - * - * @return Category|null * @throws FireflyException */ public function findCategory(?int $categoryId, ?string $categoryName): ?Category @@ -145,10 +124,6 @@ class CategoryRepository implements CategoryRepositoryInterface /** * Find a category or return NULL - * - * @param int $categoryId - * - * @return Category|null */ public function find(int $categoryId): ?Category { @@ -157,10 +132,6 @@ class CategoryRepository implements CategoryRepositoryInterface /** * Find a category. - * - * @param string $name - * - * @return Category|null */ public function findByName(string $name): ?Category { @@ -168,9 +139,6 @@ class CategoryRepository implements CategoryRepositoryInterface } /** - * @param array $data - * - * @return Category * @throws FireflyException */ public function store(array $data): Category @@ -195,27 +163,18 @@ class CategoryRepository implements CategoryRepositoryInterface return $category; } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * @param Category $category - */ public function removeNotes(Category $category): void { $category->notes()->delete(); } - /** - * @inheritDoc - */ public function updateNotes(Category $category, string $notes): void { $dbNote = $category->notes()->first(); @@ -227,12 +186,6 @@ class CategoryRepository implements CategoryRepositoryInterface $dbNote->save(); } - /** - * @param Category $category - * - * @return Carbon|null - * - */ public function firstUseDate(Category $category): ?Carbon { $firstJournalDate = $this->getFirstJournalDate($category); @@ -255,52 +208,12 @@ class CategoryRepository implements CategoryRepositoryInterface return $firstJournalDate; } - /** - * @param Category $category - * - * @return Carbon|null - */ - private function getFirstJournalDate(Category $category): ?Carbon - { - $query = $category->transactionJournals()->orderBy('date', 'ASC'); - $result = $query->first(['transaction_journals.*']); - - if (null !== $result) { - return $result->date; - } - - return null; - } - - /** - * @param Category $category - * - * @return Carbon|null - */ - private function getFirstTransactionDate(Category $category): ?Carbon - { - // check transactions: - $query = $category->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->orderBy('transaction_journals.date', 'ASC'); - - $lastTransaction = $query->first(['transaction_journals.*']); - if (null !== $lastTransaction) { - return new Carbon($lastTransaction->date); - } - - return null; - } - - /** - * @inheritDoc - */ public function getAttachments(Category $category): Collection { $set = $category->attachments()->get(); - /** @var Storage $disk */ - $disk = Storage::disk('upload'); + /** @var \Storage $disk */ + $disk = \Storage::disk('upload'); return $set->each( static function (Attachment $attachment) use ($disk) { @@ -315,19 +228,12 @@ class CategoryRepository implements CategoryRepositoryInterface /** * Get all categories with ID's. - * - * @param array $categoryIds - * - * @return Collection */ public function getByIds(array $categoryIds): Collection { return $this->user->categories()->whereIn('id', $categoryIds)->get(); } - /** - * @inheritDoc - */ public function getNoteText(Category $category): ?string { $dbNote = $category->notes()->first(); @@ -339,11 +245,7 @@ class CategoryRepository implements CategoryRepositoryInterface } /** - * @param Category $category - * @param Collection $accounts - * - * @return Carbon|null - * @throws Exception + * @throws \Exception */ public function lastUseDate(Category $category, Collection $accounts): ?Carbon { @@ -367,12 +269,56 @@ class CategoryRepository implements CategoryRepositoryInterface return $lastJournalDate; } + public function searchCategory(string $query, int $limit): Collection + { + $search = $this->user->categories(); + if ('' !== $query) { + $search->where('name', 'LIKE', sprintf('%%%s%%', $query)); + } + + return $search->take($limit)->get(); + } + /** - * @param Category $category - * @param Collection $accounts - * - * @return Carbon|null + * @throws \Exception */ + public function update(Category $category, array $data): Category + { + /** @var CategoryUpdateService $service */ + $service = app(CategoryUpdateService::class); + $service->setUser($this->user); + + return $service->update($category, $data); + } + + private function getFirstJournalDate(Category $category): ?Carbon + { + $query = $category->transactionJournals()->orderBy('date', 'ASC'); + $result = $query->first(['transaction_journals.*']); + + if (null !== $result) { + return $result->date; + } + + return null; + } + + private function getFirstTransactionDate(Category $category): ?Carbon + { + // check transactions: + $query = $category->transactions() + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->orderBy('transaction_journals.date', 'ASC') + ; + + $lastTransaction = $query->first(['transaction_journals.*']); + if (null !== $lastTransaction) { + return new Carbon($lastTransaction->date); + } + + return null; + } + private function getLastJournalDate(Category $category, Collection $accounts): ?Carbon { $query = $category->transactionJournals()->orderBy('date', 'DESC'); @@ -392,18 +338,15 @@ class CategoryRepository implements CategoryRepositoryInterface } /** - * @param Category $category - * @param Collection $accounts - * - * @return Carbon|null - * @throws Exception + * @throws \Exception */ private function getLastTransactionDate(Category $category, Collection $accounts): ?Carbon { // check transactions: $query = $category->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->orderBy('transaction_journals.date', 'DESC'); + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->orderBy('transaction_journals.date', 'DESC') + ; if ($accounts->count() > 0) { // filter journals: $query->whereIn('transactions.account_id', $accounts->pluck('id')->toArray()); @@ -416,36 +359,4 @@ class CategoryRepository implements CategoryRepositoryInterface return null; } - - /** - * @param string $query - * @param int $limit - * - * @return Collection - */ - public function searchCategory(string $query, int $limit): Collection - { - $search = $this->user->categories(); - if ('' !== $query) { - $search->where('name', 'LIKE', sprintf('%%%s%%', $query)); - } - - return $search->take($limit)->get(); - } - - /** - * @param Category $category - * @param array $data - * - * @return Category - * @throws Exception - */ - public function update(Category $category, array $data): Category - { - /** @var CategoryUpdateService $service */ - $service = app(CategoryUpdateService::class); - $service->setUser($this->user); - - return $service->update($category, $data); - } } diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 4f4d2e0afe..94e7bd7c48 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -35,27 +35,10 @@ use Illuminate\Support\Collection; */ interface CategoryRepositoryInterface { - /** - * @param string $query - * @param int $limit - * - * @return Collection - */ public function categoryEndsWith(string $query, int $limit): Collection; - /** - * @param string $query - * @param int $limit - * - * @return Collection - */ public function categoryStartsWith(string $query, int $limit): Collection; - /** - * @param Category $category - * - * @return bool - */ public function destroy(Category $category): bool; /** @@ -65,116 +48,52 @@ interface CategoryRepositoryInterface /** * Find a category or return NULL - * - * @param int $categoryId - * - * @return Category|null */ public function find(int $categoryId): ?Category; /** * Find a category. - * - * @param string $name - * - * @return Category|null */ public function findByName(string $name): ?Category; - /** - * @param int|null $categoryId - * @param string|null $categoryName - * - * @return Category|null - */ public function findCategory(?int $categoryId, ?string $categoryName): ?Category; - /** - * @param Category $category - * - * @return Carbon|null - */ public function firstUseDate(Category $category): ?Carbon; - /** - * @param Category $category - * - * @return Collection - */ public function getAttachments(Category $category): Collection; /** * Get all categories with ID's. - * - * @param array $categoryIds - * - * @return Collection */ public function getByIds(array $categoryIds): Collection; /** * Returns a list of all the categories belonging to a user. - * - * @return Collection */ 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. - * - * @param Category $category - * @param Collection $accounts - * - * @return Carbon|null */ 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 - * - * @return Collection - */ public function searchCategory(string $query, int $limit): Collection; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; /** - * @param array $data - * - * @return Category * @throws FireflyException */ public function store(array $data): Category; - /** - * @param Category $category - * @param array $data - * - * @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 533b3d93dd..e5dfd6a164 100644 --- a/app/Repositories/Category/NoCategoryRepository.php +++ b/app/Repositories/Category/NoCategoryRepository.php @@ -31,7 +31,6 @@ use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; /** - * * Class NoCategoryRepository */ class NoCategoryRepository implements NoCategoryRepositoryInterface @@ -42,12 +41,6 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period * which have no category 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 - * - * @return array */ public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null): array { @@ -82,18 +75,15 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface $journalId = (int)$journal['transaction_journal_id']; $array[$currencyId]['categories'][0]['transaction_journals'][$journalId] = [ - 'amount' => app('steam')->negative($journal['amount']), - 'date' => $journal['date'], - ]; + 'amount' => app('steam')->negative($journal['amount']), + 'date' => $journal['date'], + ]; } return $array; } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; @@ -104,12 +94,6 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface * This method returns a list of all the deposit transaction journals (as arrays) set in that period * which have no category set to them. It's grouped per currency, with as few details in the array * as possible. Amounts are always positive. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * - * @return array */ public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null): array { @@ -144,9 +128,9 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface $journalId = (int)$journal['transaction_journal_id']; $array[$currencyId]['categories'][0]['transaction_journals'][$journalId] = [ - 'amount' => app('steam')->positive($journal['amount']), - 'date' => $journal['date'], - ]; + 'amount' => app('steam')->positive($journal['amount']), + 'date' => $journal['date'], + ]; } return $array; @@ -154,12 +138,6 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface /** * Sum of withdrawal journals in period without a category, grouped per currency. Amounts are always negative. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * - * @return array */ public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null): array { @@ -191,12 +169,6 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface /** * Sum of income journals 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 sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null): array { @@ -226,9 +198,6 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface return $array; } - /** - * @inheritDoc - */ public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null): array { /** @var GroupCollectorInterface $collector */ diff --git a/app/Repositories/Category/NoCategoryRepositoryInterface.php b/app/Repositories/Category/NoCategoryRepositoryInterface.php index a227c8262a..58bc06446f 100644 --- a/app/Repositories/Category/NoCategoryRepositoryInterface.php +++ b/app/Repositories/Category/NoCategoryRepositoryInterface.php @@ -30,7 +30,6 @@ use Illuminate\Support\Collection; /** * Interface NoCategoryRepositoryInterface - * */ interface NoCategoryRepositoryInterface { @@ -38,12 +37,6 @@ interface NoCategoryRepositoryInterface * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period * which have no category 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 - * - * @return array */ public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null): array; @@ -51,50 +44,23 @@ interface NoCategoryRepositoryInterface * This method returns a list of all the deposit transaction journals (as arrays) set in that period * which have no category set to them. It's grouped per currency, with as few details in the array * as possible. Amounts are always positive. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * - * @return array */ public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null): array; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; /** * Sum of withdrawal journals in period without a category, grouped per currency. Amounts are always negative. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * - * @return array */ public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null): array; /** * Sum of income journals 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 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 f33dde6db6..823bcf5598 100644 --- a/app/Repositories/Category/OperationsRepository.php +++ b/app/Repositories/Category/OperationsRepository.php @@ -31,7 +31,6 @@ use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; /** - * * Class OperationsRepository */ class OperationsRepository implements OperationsRepositoryInterface @@ -44,13 +43,6 @@ class OperationsRepository implements OperationsRepositoryInterface * as possible. Amounts are always negative. * * First currency, then categories. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $categories - * - * @return array */ public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array { @@ -116,37 +108,17 @@ class OperationsRepository implements OperationsRepositoryInterface return $array; } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * Returns a list of all the categories belonging to a user. - * - * @return Collection - */ - private function getCategories(): Collection - { - return $this->user->categories()->get(); - } - /** * This method returns a list of all the deposit transaction journals (as arrays) set in that period * which have the specified category set to them. It's grouped per currency, with as few details in the array * as possible. Amounts are always positive. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $categories - * - * @return array */ public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array { @@ -211,15 +183,13 @@ class OperationsRepository implements OperationsRepositoryInterface return $array; } - /** - * @inheritDoc - */ public function listTransferredIn(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array { /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER]) - ->setDestinationAccounts($accounts)->excludeSourceAccounts($accounts); + ->setDestinationAccounts($accounts)->excludeSourceAccounts($accounts) + ; if (null !== $categories && $categories->count() > 0) { $collector->setCategories($categories); } @@ -276,15 +246,13 @@ class OperationsRepository implements OperationsRepositoryInterface return $array; } - /** - * @inheritDoc - */ public function listTransferredOut(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array { /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER]) - ->setSourceAccounts($accounts)->excludeDestinationAccounts($accounts); + ->setSourceAccounts($accounts)->excludeDestinationAccounts($accounts) + ; if (null !== $categories && $categories->count() > 0) { $collector->setCategories($categories); } @@ -343,20 +311,14 @@ class OperationsRepository implements OperationsRepositoryInterface /** * Sum of withdrawal journals in period for a set of categories, grouped per currency. Amounts are always negative. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $categories - * - * @return array */ public function sumExpenses(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::WITHDRAWAL]); + ->setTypes([TransactionType::WITHDRAWAL]) + ; if (null !== $accounts && $accounts->count() > 0) { $collector->setAccounts($accounts); @@ -387,20 +349,14 @@ class OperationsRepository implements OperationsRepositoryInterface /** * 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 sumIncome(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::DEPOSIT]); + ->setTypes([TransactionType::DEPOSIT]) + ; if (null !== $accounts && $accounts->count() > 0) { $collector->setAccounts($accounts); @@ -430,20 +386,14 @@ class OperationsRepository implements OperationsRepositoryInterface /** * 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]); + ->setTypes([TransactionType::TRANSFER]) + ; if (null !== $accounts && $accounts->count() > 0) { $collector->setAccounts($accounts); @@ -470,4 +420,12 @@ class OperationsRepository implements OperationsRepositoryInterface return $array; } + + /** + * Returns a list of all the categories belonging to a user. + */ + private function getCategories(): Collection + { + return $this->user->categories()->get(); + } } diff --git a/app/Repositories/Category/OperationsRepositoryInterface.php b/app/Repositories/Category/OperationsRepositoryInterface.php index 2738df0d6a..3eb38cc3c3 100644 --- a/app/Repositories/Category/OperationsRepositoryInterface.php +++ b/app/Repositories/Category/OperationsRepositoryInterface.php @@ -30,7 +30,6 @@ use Illuminate\Support\Collection; /** * Interface OperationsRepositoryInterface - * */ interface OperationsRepositoryInterface { @@ -38,13 +37,6 @@ interface OperationsRepositoryInterface * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period * which have the specified category 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 $categories - * - * @return array */ public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array; @@ -52,13 +44,6 @@ interface OperationsRepositoryInterface * This method returns a list of all the deposit transaction journals (as arrays) set in that period * which have the specified category set to them. It's grouped per currency, with as few details in the array * as possible. Amounts are always positive. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $categories - * - * @return array */ public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array; @@ -67,13 +52,6 @@ interface OperationsRepositoryInterface * which have the specified category set to them, transferred INTO the listed accounts. * It excludes any transfers between the listed 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 Collection $accounts - * @param Collection|null $categories - * - * @return array */ public function listTransferredIn(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array; @@ -82,54 +60,23 @@ interface OperationsRepositoryInterface * which have the specified category set to them, transferred FROM the listed accounts. * It excludes any transfers between the listed 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 Collection $accounts - * @param Collection|null $categories - * - * @return array */ public function listTransferredOut(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; /** * Sum of withdrawal journals in period for a set of categories, grouped per currency. Amounts are always negative. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $categories - * - * @return array */ public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): 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 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 e02219e249..725a5741ad 100644 --- a/app/Repositories/Currency/CurrencyRepository.php +++ b/app/Repositories/Currency/CurrencyRepository.php @@ -37,55 +37,42 @@ class CurrencyRepository implements CurrencyRepositoryInterface { private User $user; - /** * Find by currency code, return NULL if unfound. - * - * @param string $currencyCode - * - * @return TransactionCurrency|null */ public function findByCode(string $currencyCode): ?TransactionCurrency { return TransactionCurrency::where('code', $currencyCode)->first(); } - /** * Returns the complete set of transactions but needs * no user object. - * - * @return Collection */ public function getCompleteSet(): Collection { return TransactionCurrency::orderBy('code', 'ASC')->get(); } - /** * Get currency exchange rate. - * - * @param TransactionCurrency $fromCurrency - * @param TransactionCurrency $toCurrency - * @param Carbon $date - * - * @return CurrencyExchangeRate|null */ public function getExchangeRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date): ?CurrencyExchangeRate { if ($fromCurrency->id === $toCurrency->id) { $rate = new CurrencyExchangeRate(); - $rate->rate = "1"; + $rate->rate = '1'; $rate->id = 0; return $rate; } - /** @var CurrencyExchangeRate|null $rate */ + + /** @var null|CurrencyExchangeRate $rate */ $rate = $this->user->currencyExchangeRates() - ->where('from_currency_id', $fromCurrency->id) - ->where('to_currency_id', $toCurrency->id) - ->where('date', $date->format('Y-m-d'))->first(); + ->where('from_currency_id', $fromCurrency->id) + ->where('to_currency_id', $toCurrency->id) + ->where('date', $date->format('Y-m-d'))->first() + ; if (null !== $rate) { app('log')->debug(sprintf('Found cached exchange rate in database for %s to %s on %s', $fromCurrency->code, $toCurrency->code, $date->format('Y-m-d'))); @@ -97,13 +84,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface /** * TODO must be a factory - * - * @param TransactionCurrency $fromCurrency - * @param TransactionCurrency $toCurrency - * @param Carbon $date - * @param float $rate - * - * @return CurrencyExchangeRate */ public function setExchangeRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date, float $rate): CurrencyExchangeRate { @@ -118,14 +98,10 @@ class CurrencyRepository implements CurrencyRepositoryInterface ); } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - } diff --git a/app/Repositories/Currency/CurrencyRepositoryInterface.php b/app/Repositories/Currency/CurrencyRepositoryInterface.php index 3202ee18a3..e714977abb 100644 --- a/app/Repositories/Currency/CurrencyRepositoryInterface.php +++ b/app/Repositories/Currency/CurrencyRepositoryInterface.php @@ -39,10 +39,6 @@ interface CurrencyRepositoryInterface * Find by currency code, return NULL if unfound. * * Used in the download exchange rates cron job. Does not require user object. - * - * @param string $currencyCode - * - * @return TransactionCurrency|null */ public function findByCode(string $currencyCode): ?TransactionCurrency; @@ -51,8 +47,6 @@ interface CurrencyRepositoryInterface * no user object. * * Used by the download exchange rate cron job. - * - * @return Collection */ public function getCompleteSet(): Collection; @@ -60,12 +54,6 @@ interface CurrencyRepositoryInterface * Get currency exchange rate. * * Used in the download exchange rate cron job. Needs the user object! - * - * @param TransactionCurrency $fromCurrency - * @param TransactionCurrency $toCurrency - * @param Carbon $date - * - * @return CurrencyExchangeRate|null */ public function getExchangeRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date): ?CurrencyExchangeRate; @@ -73,19 +61,8 @@ interface CurrencyRepositoryInterface * Set currency exchange rate. * * Used in download exchange rate cron job. Needs the user object! - * - * @param TransactionCurrency $fromCurrency - * @param TransactionCurrency $toCurrency - * @param Carbon $date - * @param float $rate - * - * @return CurrencyExchangeRate */ public function setExchangeRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date, float $rate): CurrencyExchangeRate; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; - + public function setUser(null|Authenticatable|User $user): void; } diff --git a/app/Repositories/Journal/JournalAPIRepository.php b/app/Repositories/Journal/JournalAPIRepository.php index 55b3020092..9ded9dade4 100644 --- a/app/Repositories/Journal/JournalAPIRepository.php +++ b/app/Repositories/Journal/JournalAPIRepository.php @@ -30,7 +30,6 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use Storage; /** * Class JournalAPIRepository @@ -41,34 +40,27 @@ class JournalAPIRepository implements JournalAPIRepositoryInterface /** * Returns transaction by ID. Used to validate attachments. - * - * @param int $transactionId - * - * @return Transaction|null */ 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.*']) + ; } /** * TODO pretty sure method duplicated. * * Return all attachments for journal. - * - * @param TransactionJournal $journal - * - * @return Collection */ public function getAttachments(TransactionJournal $journal): Collection { $set = $journal->attachments; - /** @var Storage $disk */ - $disk = Storage::disk('upload'); + /** @var \Storage $disk */ + $disk = \Storage::disk('upload'); return $set->each( static function (Attachment $attachment) use ($disk) { @@ -81,9 +73,6 @@ class JournalAPIRepository implements JournalAPIRepositoryInterface ); } - /** - * @inheritDoc - */ public function getJournalLinks(TransactionJournal $journal): Collection { $collection = $journal->destJournalLinks()->get(); @@ -93,16 +82,12 @@ class JournalAPIRepository implements JournalAPIRepositoryInterface /** * Get all piggy bank events for a journal. - * - * @param TransactionJournal $journal - * - * @return Collection */ public function getPiggyBankEvents(TransactionJournal $journal): Collection { $events = $journal->piggyBankEvents()->get(); $events->each( - static function (PiggyBankEvent $event) { + static function (PiggyBankEvent $event): void { $event->piggyBank = $event->piggyBank()->withTrashed()->first(); } ); @@ -110,10 +95,7 @@ class JournalAPIRepository implements JournalAPIRepositoryInterface return $events; } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; diff --git a/app/Repositories/Journal/JournalAPIRepositoryInterface.php b/app/Repositories/Journal/JournalAPIRepositoryInterface.php index 30a1f0b3fd..66dae77d0c 100644 --- a/app/Repositories/Journal/JournalAPIRepositoryInterface.php +++ b/app/Repositories/Journal/JournalAPIRepositoryInterface.php @@ -36,42 +36,23 @@ interface JournalAPIRepositoryInterface { /** * Returns transaction by ID. Used to validate attachments. - * - * @param int $transactionId - * - * @return Transaction|null */ public function findTransaction(int $transactionId): ?Transaction; /** * Return all attachments for journal. - * - * @param TransactionJournal $journal - * - * @return Collection */ public function getAttachments(TransactionJournal $journal): Collection; /** * Return all journal links for journal. - * - * @param TransactionJournal $journal - * - * @return Collection */ public function getJournalLinks(TransactionJournal $journal): Collection; /** * Get all piggy bank events for a journal. - * - * @param TransactionJournal $journal - * - * @return Collection */ public function getPiggyBankEvents(TransactionJournal $journal): Collection; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; } diff --git a/app/Repositories/Journal/JournalCLIRepository.php b/app/Repositories/Journal/JournalCLIRepository.php index 186cc4189b..c690ed71bd 100644 --- a/app/Repositories/Journal/JournalCLIRepository.php +++ b/app/Repositories/Journal/JournalCLIRepository.php @@ -24,13 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Journal; use Carbon\Carbon; -use DB; use FireflyIII\Models\TransactionJournal; use FireflyIII\Support\CacheProperties; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use stdClass; /** * Class JournalCLIRepository @@ -39,25 +37,18 @@ class JournalCLIRepository implements JournalCLIRepositoryInterface { /** * Get all transaction journals with a specific type, regardless of user. - * - * @param array $types - * - * @return Collection */ public function getAllJournals(array $types): Collection { return TransactionJournal::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') - ->whereIn('transaction_types.type', $types) - ->with(['user', 'transactionType', 'transactionCurrency', 'transactions', 'transactions.account']) - ->get(['transaction_journals.*']); + ->whereIn('transaction_types.type', $types) + ->with(['user', 'transactionType', 'transactionCurrency', 'transactions', 'transactions.account']) + ->get(['transaction_journals.*']) + ; } /** * Return the ID of the budget linked to the journal (if any) or the transactions (if any). - * - * @param TransactionJournal $journal - * - * @return int */ public function getJournalBudgetId(TransactionJournal $journal): int { @@ -75,10 +66,6 @@ class JournalCLIRepository implements JournalCLIRepositoryInterface /** * Return the ID of the category linked to the journal (if any) or to the transactions (if any). - * - * @param TransactionJournal $journal - * - * @return int */ public function getJournalCategoryId(TransactionJournal $journal): int { @@ -96,8 +83,6 @@ class JournalCLIRepository implements JournalCLIRepositoryInterface /** * Return all journals without a group, used in an upgrade routine. - * - * @return array */ public function getJournalsWithoutGroup(): array { @@ -106,11 +91,6 @@ class JournalCLIRepository implements JournalCLIRepositoryInterface /** * 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 { @@ -135,11 +115,6 @@ class JournalCLIRepository implements JournalCLIRepositoryInterface /** * Return value of a meta field (or NULL) as a string. - * - * @param TransactionJournal $journal - * @param string $field - * - * @return null|string */ public function getMetaField(TransactionJournal $journal, string $field): ?string { @@ -175,10 +150,6 @@ class JournalCLIRepository implements JournalCLIRepositoryInterface /** * Return text of a note attached to journal, or NULL - * - * @param TransactionJournal $journal - * - * @return string|null */ public function getNoteText(TransactionJournal $journal): ?string { @@ -193,16 +164,16 @@ class JournalCLIRepository implements JournalCLIRepositoryInterface /** * 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 { $query = TransactionJournal::leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->groupBy('transaction_journals.id'); - $result = $query->get(['transaction_journals.id as id', DB::raw('count(transactions.id) as transaction_count')]); // @phpstan-ignore-line + ->groupBy('transaction_journals.id') + ; + $result = $query->get(['transaction_journals.id as id', \DB::raw('count(transactions.id) as transaction_count')]); // @phpstan-ignore-line $journalIds = []; - /** @var stdClass $row */ + + /** @var \stdClass $row */ foreach ($result as $row) { if ((int)$row->transaction_count > 2) { $journalIds[] = (int)$row->id; @@ -211,25 +182,19 @@ class JournalCLIRepository implements JournalCLIRepositoryInterface $journalIds = array_unique($journalIds); return TransactionJournal::with(['transactions']) - ->whereIn('id', $journalIds)->get(); + ->whereIn('id', $journalIds)->get() + ; } /** * Return all tags as strings in an array. - * - * @param TransactionJournal $journal - * - * @return array */ public function getTags(TransactionJournal $journal): array { return $journal->tags()->get()->pluck('tag')->toArray(); } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { // empty } diff --git a/app/Repositories/Journal/JournalCLIRepositoryInterface.php b/app/Repositories/Journal/JournalCLIRepositoryInterface.php index ca43bb908e..c0dc10fd54 100644 --- a/app/Repositories/Journal/JournalCLIRepositoryInterface.php +++ b/app/Repositories/Journal/JournalCLIRepositoryInterface.php @@ -36,86 +36,49 @@ interface JournalCLIRepositoryInterface { /** * Get all transaction journals with a specific type, regardless of user. - * - * @param array $types - * - * @return Collection */ public function getAllJournals(array $types): Collection; /** * Return the ID of the budget linked to the journal (if any) or the transactions (if any). - * - * @param TransactionJournal $journal - * - * @return int */ public function getJournalBudgetId(TransactionJournal $journal): int; /** * Return the ID of the category linked to the journal (if any) or to the transactions (if any). - * - * @param TransactionJournal $journal - * - * @return int */ 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|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; } diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index 5f5abbdb8e..a4ffd85881 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -45,13 +45,8 @@ use Illuminate\Support\Collection; */ class JournalRepository implements JournalRepositoryInterface { - /** @var User */ private User $user; - /** - * @param TransactionGroup $transactionGroup - * - */ public function destroyGroup(TransactionGroup $transactionGroup): void { /** @var TransactionGroupDestroyService $service */ @@ -59,10 +54,6 @@ class JournalRepository implements JournalRepositoryInterface $service->destroy($transactionGroup); } - /** - * @param TransactionJournal $journal - * - */ public function destroyJournal(TransactionJournal $journal): void { /** @var JournalDestroyService $service */ @@ -70,26 +61,22 @@ 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.*']); + ->get(['transaction_journals.*']) + ; } /** * Get users first transaction journal or NULL. - * - * @return TransactionJournal|null */ public function firstNull(): ?TransactionJournal { - /** @var TransactionJournal|null $entry */ + /** @var null|TransactionJournal $entry */ $entry = $this->user->transactionJournals()->orderBy('date', 'ASC')->first(['transaction_journals.*']); $result = null; if (null !== $entry) { @@ -99,12 +86,9 @@ class JournalRepository implements JournalRepositoryInterface return $result; } - /** - * @inheritDoc - */ public function getDestinationAccount(TransactionJournal $journal): Account { - /** @var Transaction|null $transaction */ + /** @var null|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)); @@ -115,10 +99,6 @@ class JournalRepository implements JournalRepositoryInterface /** * Return total amount of journal. Is always positive. - * - * @param TransactionJournal $journal - * - * @return string */ public function getJournalTotal(TransactionJournal $journal): string { @@ -137,12 +117,9 @@ class JournalRepository implements JournalRepositoryInterface return $amount; } - /** - * @return TransactionJournal|null - */ public function getLast(): ?TransactionJournal { - /** @var TransactionJournal|null $entry */ + /** @var null|TransactionJournal $entry */ $entry = $this->user->transactionJournals()->orderBy('date', 'DESC')->first(['transaction_journals.*']); $result = null; if (null !== $entry) { @@ -152,25 +129,16 @@ class JournalRepository implements JournalRepositoryInterface return $result; } - /** - * @param TransactionJournalLink $link - * - * @return string - */ public function getLinkNoteText(TransactionJournalLink $link): string { - /** @var Note|null $note */ + /** @var null|Note $note */ $note = $link->notes()->first(); + return (string)$note?->text; } /** * 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 { @@ -183,7 +151,8 @@ class JournalRepository implements JournalRepositoryInterface return new Carbon($cache->get()); } $entry = TransactionJournalMeta::where('transaction_journal_id', $journalId) - ->where('name', $field)->first(); + ->where('name', $field)->first() + ; if (null === $entry) { return null; } @@ -193,12 +162,9 @@ class JournalRepository implements JournalRepositoryInterface return $value; } - /** - * @inheritDoc - */ public function getSourceAccount(TransactionJournal $journal): Account { - /** @var Transaction|null $transaction */ + /** @var null|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 source transaction.', $journal->id)); @@ -207,22 +173,15 @@ class JournalRepository implements JournalRepositoryInterface return $transaction->account; } - /** - * @param int $journalId - */ public function reconcileById(int $journalId): void { - /** @var TransactionJournal|null $journal */ + /** @var null|TransactionJournal $journal */ $journal = $this->user->transactionJournals()->find($journalId); $journal?->transactions()->update(['reconciled' => true]); } /** * Find a specific journal. - * - * @param int $journalId - * - * @return TransactionJournal|null */ public function find(int $journalId): ?TransactionJournal { @@ -231,16 +190,12 @@ class JournalRepository implements JournalRepositoryInterface /** * 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'); + ->orderBy('date', 'DESC') + ; if ('' !== $search) { $query->where('description', 'LIKE', sprintf('%%%s%%', $search)); } @@ -248,33 +203,22 @@ class JournalRepository implements JournalRepositoryInterface return $query->take($limit)->get(); } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * @inheritDoc - */ public function unreconcileById(int $journalId): void { - /** @var TransactionJournal|null $journal */ + /** @var null|TransactionJournal $journal */ $journal = $this->user->transactionJournals()->find($journalId); $journal?->transactions()->update(['reconciled' => false]); } /** * Update budget for a journal. - * - * @param TransactionJournal $journal - * @param int $budgetId - * - * @return TransactionJournal */ public function updateBudget(TransactionJournal $journal, int $budgetId): TransactionJournal { @@ -295,11 +239,6 @@ class JournalRepository implements JournalRepositoryInterface /** * Update category for a journal. - * - * @param TransactionJournal $journal - * @param string $category - * - * @return TransactionJournal */ public function updateCategory(TransactionJournal $journal, string $category): TransactionJournal { @@ -319,11 +258,6 @@ class JournalRepository implements JournalRepositoryInterface /** * Update tag(s) for a journal. - * - * @param TransactionJournal $journal - * @param array $tags - * - * @return TransactionJournal */ public function updateTags(TransactionJournal $journal, array $tags): TransactionJournal { diff --git a/app/Repositories/Journal/JournalRepositoryInterface.php b/app/Repositories/Journal/JournalRepositoryInterface.php index a188d40c47..055af9b5f5 100644 --- a/app/Repositories/Journal/JournalRepositoryInterface.php +++ b/app/Repositories/Journal/JournalRepositoryInterface.php @@ -40,148 +40,83 @@ interface JournalRepositoryInterface { /** * Deletes a transaction group. - * - * @param TransactionGroup $transactionGroup */ public function destroyGroup(TransactionGroup $transactionGroup): void; /** * Deletes a journal. - * - * @param TransactionJournal $journal */ public function destroyJournal(TransactionJournal $journal): void; /** * Find a specific journal. - * - * @param int $journalId - * - * @return TransactionJournal|null */ public function find(int $journalId): ?TransactionJournal; - /** - * @param array $types - * - * @return Collection - */ public function findByType(array $types): Collection; /** * Get users very first transaction journal. - * - * @return TransactionJournal|null */ public function firstNull(): ?TransactionJournal; /** * 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; - /** - * @return TransactionJournal|null - */ public function getLast(): ?TransactionJournal; - /** - * @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. * - * @param TransactionJournal $journal - * - * @return Account * @throws FireflyException */ public function getSourceAccount(TransactionJournal $journal): Account; /** * TODO Maybe to account repository? Do this wen reconcile is API only. - * - * @param int $journalId */ 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|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; /** * TODO Maybe to account repository? Do this wen reconcile is API only. - * - * @param int $journalId */ public function unreconcileById(int $journalId): void; /** * Update budget for a journal. - * - * @param TransactionJournal $journal - * @param int $budgetId - * - * @return TransactionJournal */ public function updateBudget(TransactionJournal $journal, int $budgetId): TransactionJournal; /** * Update category for a journal. - * - * @param TransactionJournal $journal - * @param string $category - * - * @return TransactionJournal */ public function updateCategory(TransactionJournal $journal, string $category): TransactionJournal; /** * Update tag(s) for a journal. - * - * @param TransactionJournal $journal - * @param array $tags - * - * @return TransactionJournal */ public function updateTags(TransactionJournal $journal, array $tags): TransactionJournal; } diff --git a/app/Repositories/LinkType/LinkTypeRepository.php b/app/Repositories/LinkType/LinkTypeRepository.php index cdb6dd0331..b2ff6d012e 100644 --- a/app/Repositories/LinkType/LinkTypeRepository.php +++ b/app/Repositories/LinkType/LinkTypeRepository.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Repositories\LinkType; -use Exception; use FireflyIII\Events\DestroyedTransactionLink; use FireflyIII\Models\LinkType; use FireflyIII\Models\Note; @@ -35,28 +34,16 @@ use Illuminate\Support\Collection; /** * Class LinkTypeRepository. - * */ class LinkTypeRepository implements LinkTypeRepositoryInterface { private User $user; - /** - * @param LinkType $linkType - * - * @return int - */ public function countJournals(LinkType $linkType): int { return $linkType->transactionJournalLinks()->count(); } - /** - * @param LinkType $linkType - * @param LinkType|null $moveTo - * - * @return bool - */ public function destroy(LinkType $linkType, LinkType $moveTo = null): bool { if (null !== $moveTo) { @@ -67,12 +54,6 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface return true; } - /** - * @param LinkType $linkType - * @param array $data - * - * @return LinkType - */ public function update(LinkType $linkType, array $data): LinkType { if (array_key_exists('name', $data) && '' !== (string)$data['name']) { @@ -90,10 +71,7 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface } /** - * @param TransactionJournalLink $link - * - * @return bool - * @throws Exception + * @throws \Exception */ public function destroyLink(TransactionJournalLink $link): bool { @@ -105,11 +83,6 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface /** * Check if link exists between journals. - * - * @param TransactionJournal $one - * @param TransactionJournal $two - * - * @return bool */ public function findLink(TransactionJournal $one, TransactionJournal $two): bool { @@ -122,10 +95,6 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface /** * Return array of all journal ID's for this type of link. - * - * @param LinkType $linkType - * - * @return array */ public function getJournalIds(LinkType $linkType): array { @@ -136,9 +105,6 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface return array_unique(array_merge($sources, $destinations)); } - /** - * @return Collection - */ public function get(): Collection { return LinkType::orderBy('name', 'ASC')->get(); @@ -146,20 +112,17 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface /** * Returns all the journal links (of a specific type). - * - * @param LinkType|null $linkType - * - * @return Collection */ public function getJournalLinks(LinkType $linkType = null): Collection { $query = TransactionJournalLink::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) - ->where('dest_journals.user_id', $this->user->id) - ->whereNull('source_journals.deleted_at') - ->whereNull('dest_journals.deleted_at'); + ->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) + ->where('dest_journals.user_id', $this->user->id) + ->whereNull('source_journals.deleted_at') + ->whereNull('dest_journals.deleted_at') + ; if (null !== $linkType) { $query->where('journal_links.link_type_id', $linkType->id); @@ -168,12 +131,6 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface return $query->get(['journal_links.*']); } - /** - * @param TransactionJournal $one - * @param TransactionJournal $two - * - * @return TransactionJournalLink|null - */ public function getLink(TransactionJournal $one, TransactionJournal $two): ?TransactionJournalLink { $left = TransactionJournalLink::whereDestinationId($one->id)->whereSourceId($two->id)->first(); @@ -186,10 +143,6 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface /** * Return list of existing connections. - * - * @param TransactionJournal $journal - * - * @return Collection */ public function getLinks(TransactionJournal $journal): Collection { @@ -204,21 +157,13 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface ); } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * @param array $data - * - * @return LinkType - */ public function store(array $data): LinkType { $linkType = new LinkType(); @@ -234,12 +179,7 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface /** * Store link between two journals. * - * @param array $information - * @param TransactionJournal $inward - * @param TransactionJournal $outward - * - * @return TransactionJournalLink|null - * @throws Exception + * @throws \Exception */ public function storeLink(array $information, TransactionJournal $inward, TransactionJournal $outward): ?TransactionJournalLink { @@ -280,21 +220,11 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface return $link; } - /** - * @param int $linkTypeId - * - * @return LinkType|null - */ public function find(int $linkTypeId): ?LinkType { return LinkType::find($linkTypeId); } - /** - * @param string|null $name - * - * @return LinkType|null - */ public function findByName(string $name = null): ?LinkType { if (null === $name) { @@ -306,48 +236,18 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface /** * See if such a link already exists (and get it). - * - * @param LinkType $linkType - * @param TransactionJournal $inward - * @param TransactionJournal $outward - * - * @return TransactionJournalLink|null */ public function findSpecificLink(LinkType $linkType, TransactionJournal $inward, TransactionJournal $outward): ?TransactionJournalLink { return TransactionJournalLink::where('link_type_id', $linkType->id) - ->where('source_id', $inward->id) - ->where('destination_id', $outward->id)->first(); + ->where('source_id', $inward->id) + ->where('destination_id', $outward->id)->first() + ; } - /** - * @param TransactionJournalLink $link - * @param string $text - * - * @throws Exception - */ - private function setNoteText(TransactionJournalLink $link, string $text): void - { - $dbNote = $link->notes()->first(); - if ('' !== $text) { - if (null === $dbNote) { - $dbNote = new Note(); - $dbNote->noteable()->associate($link); - } - $dbNote->text = trim($text); - $dbNote->save(); - - return; - } - $dbNote?->delete(); - } - - /** - * @inheritDoc - */ public function switchLinkById(int $linkId): bool { - /** @var TransactionJournalLink|null $link */ + /** @var null|TransactionJournalLink $link */ $link = TransactionJournalLink::find($linkId); if (null !== $link && $link->source->user->id === $this->user->id) { $this->switchLink($link); @@ -356,11 +256,6 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface return true; } - /** - * @param TransactionJournalLink $link - * - * @return bool - */ public function switchLink(TransactionJournalLink $link): bool { $source = $link->source_id; @@ -374,11 +269,7 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface /** * Update an existing transaction journal link. * - * @param TransactionJournalLink $journalLink - * @param array $data - * - * @return TransactionJournalLink - * @throws Exception + * @throws \Exception */ public function updateLink(TransactionJournalLink $journalLink, array $data): TransactionJournalLink { @@ -402,4 +293,23 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface return $journalLink; } + + /** + * @throws \Exception + */ + private function setNoteText(TransactionJournalLink $link, string $text): void + { + $dbNote = $link->notes()->first(); + if ('' !== $text) { + if (null === $dbNote) { + $dbNote = new Note(); + $dbNote->noteable()->associate($link); + } + $dbNote->text = trim($text); + $dbNote->save(); + + return; + } + $dbNote?->delete(); + } } diff --git a/app/Repositories/LinkType/LinkTypeRepositoryInterface.php b/app/Repositories/LinkType/LinkTypeRepositoryInterface.php index b2941331f8..a8741f607c 100644 --- a/app/Repositories/LinkType/LinkTypeRepositoryInterface.php +++ b/app/Repositories/LinkType/LinkTypeRepositoryInterface.php @@ -35,147 +35,60 @@ use Illuminate\Support\Collection; */ interface LinkTypeRepositoryInterface { - /** - * @param LinkType $linkType - * - * @return int - */ public function countJournals(LinkType $linkType): int; - /** - * @param LinkType $linkType - * @param LinkType|null $moveTo - * - * @return bool - */ public function destroy(LinkType $linkType, LinkType $moveTo = null): bool; - /** - * @param TransactionJournalLink $link - * - * @return bool - */ public function destroyLink(TransactionJournalLink $link): bool; - /** - * @param int $linkTypeId - * - * @return LinkType|null - */ public function find(int $linkTypeId): ?LinkType; /** * Find link type by name. - * - * @param string|null $name - * - * @return LinkType|null */ public function findByName(string $name = null): ?LinkType; /** * Check if link exists between journals. - * - * @param TransactionJournal $one - * @param TransactionJournal $two - * - * @return bool */ public function findLink(TransactionJournal $one, TransactionJournal $two): bool; /** * See if such a link already exists (and get it). - * - * @param LinkType $linkType - * @param TransactionJournal $inward - * @param TransactionJournal $outward - * - * @return TransactionJournalLink|null */ public function findSpecificLink(LinkType $linkType, TransactionJournal $inward, TransactionJournal $outward): ?TransactionJournalLink; - /** - * @return Collection - */ public function get(): Collection; /** * Return array of all journal ID's for this type of link. - * - * @param LinkType $linkType - * - * @return array */ public function getJournalIds(LinkType $linkType): array; - /** - * @param LinkType|null $linkType - * - * @return Collection - */ public function getJournalLinks(LinkType $linkType = null): Collection; /** * Return list of existing connections. - * - * @param TransactionJournal $journal - * - * @return Collection */ public function getLinks(TransactionJournal $journal): Collection; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; - /** - * @param array $data - * - * @return LinkType - */ public function store(array $data): LinkType; /** * Store link between two journals. - * - * @param array $information - * @param TransactionJournal $inward - * @param TransactionJournal $outward - * - * @return TransactionJournalLink|null */ public function storeLink(array $information, TransactionJournal $inward, TransactionJournal $outward): ?TransactionJournalLink; - /** - * @param TransactionJournalLink $link - * - * @return bool - */ public function switchLink(TransactionJournalLink $link): bool; - /** - * @param int $linkId - * - * @return bool - */ public function switchLinkById(int $linkId): bool; - /** - * @param LinkType $linkType - * @param array $data - * - * @return LinkType - */ public function update(LinkType $linkType, array $data): LinkType; /** * Update an existing transaction journal link. - * - * @param TransactionJournalLink $journalLink - * @param array $data - * - * @return TransactionJournalLink */ public function updateLink(TransactionJournalLink $journalLink, array $data): TransactionJournalLink; } diff --git a/app/Repositories/ObjectGroup/CreatesObjectGroups.php b/app/Repositories/ObjectGroup/CreatesObjectGroups.php index 5258479fbf..dc843cc2c2 100644 --- a/app/Repositories/ObjectGroup/CreatesObjectGroups.php +++ b/app/Repositories/ObjectGroup/CreatesObjectGroups.php @@ -31,21 +31,11 @@ use FireflyIII\Models\ObjectGroup; */ trait CreatesObjectGroups { - /** - * @param int $groupId - * - * @return ObjectGroup|null - */ protected function findObjectGroupById(int $groupId): ?ObjectGroup { return $this->user->objectGroups()->where('id', $groupId)->first(); } - /** - * @param string $title - * - * @return ObjectGroup|null - */ protected function findOrCreateObjectGroup(string $title): ?ObjectGroup { $title = substr($title, 0, 255); @@ -64,29 +54,16 @@ trait CreatesObjectGroups return $this->findObjectGroup($title); } - /** - * @return int - */ protected function getObjectGroupMaxOrder(): int { return (int)$this->user->objectGroups()->max('order'); } - /** - * @param string $title - * - * @return bool - */ protected function hasObjectGroup(string $title): bool { 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 6da8b643c2..2d60e7eace 100644 --- a/app/Repositories/ObjectGroup/ObjectGroupRepository.php +++ b/app/Repositories/ObjectGroup/ObjectGroupRepository.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Repositories\ObjectGroup; -use DB; use FireflyIII\Models\ObjectGroup; use FireflyIII\Models\PiggyBank; use FireflyIII\User; @@ -38,12 +37,10 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface { private User $user; - /** - * @inheritDoc - */ public function deleteAll(): void { $all = $this->get(); + /** @var ObjectGroup $group */ foreach ($all as $group) { $group->piggyBanks()->sync([]); @@ -52,38 +49,32 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface } } - /** - * @inheritDoc - */ public function get(): Collection { return $this->user->objectGroups() - ->with(['piggyBanks', 'bills']) - ->orderBy('order', 'ASC') - ->orderBy('title', 'ASC')->get(); + ->with(['piggyBanks', 'bills']) + ->orderBy('order', 'ASC') + ->orderBy('title', 'ASC')->get() + ; } - /** - * @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(); + $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([]); @@ -92,30 +83,22 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface $objectGroup->delete(); } - /** - * @inheritDoc - */ public function getBills(ObjectGroup $objectGroup): Collection { return $objectGroup->bills; } - /** - * @inheritDoc - */ public function getPiggyBanks(ObjectGroup $objectGroup): Collection { return $objectGroup->piggyBanks; } - /** - * @inheritDoc - */ public function resetOrder(): void { app('log')->debug('Now in resetOrder'); $list = $this->get(); $index = 1; + /** @var ObjectGroup $objectGroup */ foreach ($list as $objectGroup) { if ($index !== $objectGroup->order) { @@ -125,16 +108,10 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface $objectGroup->order = $index; $objectGroup->save(); } - $index++; + ++$index; } } - /** - * @param string $query - * @param int $limit - * - * @return Collection - */ public function search(string $query, int $limit): Collection { $dbQuery = $this->user->objectGroups()->orderBy('order', 'ASC')->orderBy('title', 'ASC'); @@ -150,19 +127,13 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface return $dbQuery->take($limit)->get(['object_groups.*']); } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * @inheritDoc - */ public function update(ObjectGroup $objectGroup, array $data): ObjectGroup { if (array_key_exists('title', $data)) { @@ -178,25 +149,24 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface return $objectGroup; } - /** - * @inheritDoc - */ public function setOrder(ObjectGroup $objectGroup, int $newOrder): ObjectGroup { $oldOrder = $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'); + ->where('object_groups.id', '!=', $objectGroup->id) + ->decrement('object_groups.order') + ; $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'); + ->where('object_groups.id', '!=', $objectGroup->id) + ->increment('object_groups.order') + ; $objectGroup->order = $newOrder; $objectGroup->save(); diff --git a/app/Repositories/ObjectGroup/ObjectGroupRepositoryInterface.php b/app/Repositories/ObjectGroup/ObjectGroupRepositoryInterface.php index 6f0c2382c0..0232de5ef6 100644 --- a/app/Repositories/ObjectGroup/ObjectGroupRepositoryInterface.php +++ b/app/Repositories/ObjectGroup/ObjectGroupRepositoryInterface.php @@ -44,28 +44,12 @@ interface ObjectGroupRepositoryInterface */ public function deleteEmpty(): void; - /** - * @param ObjectGroup $objectGroup - */ 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 - * - * @return Collection - */ public function getPiggyBanks(ObjectGroup $objectGroup): Collection; /** @@ -73,32 +57,11 @@ interface ObjectGroupRepositoryInterface */ public function resetOrder(): void; - /** - * @param string $query - * @param int $limit - * - * @return Collection - */ public function search(string $query, int $limit): Collection; - /** - * @param ObjectGroup $objectGroup - * @param int $newOrder - * - * @return ObjectGroup - */ public function setOrder(ObjectGroup $objectGroup, int $newOrder): ObjectGroup; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; - /** - * @param ObjectGroup $objectGroup - * @param array $data - * - * @return ObjectGroup - */ public function update(ObjectGroup $objectGroup, array $data): ObjectGroup; } diff --git a/app/Repositories/ObjectGroup/OrganisesObjectGroups.php b/app/Repositories/ObjectGroup/OrganisesObjectGroups.php index 1b1e53aaa7..4f23af15a5 100644 --- a/app/Repositories/ObjectGroup/OrganisesObjectGroups.php +++ b/app/Repositories/ObjectGroup/OrganisesObjectGroups.php @@ -29,27 +29,18 @@ namespace FireflyIII\Repositories\ObjectGroup; */ trait OrganisesObjectGroups { - /** - * - */ protected function cleanupObjectGroups(): void { $this->deleteEmptyObjectGroups(); $this->sortObjectGroups(); } - /** - * - */ private function deleteEmptyObjectGroups(): void { $repository = app(ObjectGroupRepositoryInterface::class); $repository->deleteEmpty(); } - /** - * - */ private function sortObjectGroups(): void { $repository = app(ObjectGroupRepositoryInterface::class); diff --git a/app/Repositories/PiggyBank/ModifiesPiggyBanks.php b/app/Repositories/PiggyBank/ModifiesPiggyBanks.php index dbcc7d7c36..169a8f29cb 100644 --- a/app/Repositories/PiggyBank/ModifiesPiggyBanks.php +++ b/app/Repositories/PiggyBank/ModifiesPiggyBanks.php @@ -24,9 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\PiggyBank; -use Exception; use FireflyIII\Events\Model\PiggyBank\ChangedAmount; -use FireflyIII\Events\Model\PiggyBank\ChangedPiggyBankAmount; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Note; use FireflyIII\Models\PiggyBank; @@ -34,7 +32,6 @@ use FireflyIII\Models\PiggyBankRepetition; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; use Illuminate\Database\QueryException; -use JsonException; /** * Trait ModifiesPiggyBanks @@ -43,13 +40,6 @@ trait ModifiesPiggyBanks { use CreatesObjectGroups; - /** - * @param PiggyBankRepetition $repetition - * @param string $amount - * @param TransactionJournal $journal - * - * @return void - */ public function addAmountToRepetition(PiggyBankRepetition $repetition, string $amount, TransactionJournal $journal): void { app('log')->debug(sprintf('addAmountToRepetition: %s', $amount)); @@ -63,13 +53,6 @@ trait ModifiesPiggyBanks } } - /** - * @param PiggyBank $piggyBank - * @param string $amount - * @param TransactionJournal|null $journal - * - * @return bool - */ public function removeAmount(PiggyBank $piggyBank, string $amount, ?TransactionJournal $journal = null): bool { $repetition = $this->getRepetition($piggyBank); @@ -85,13 +68,6 @@ trait ModifiesPiggyBanks return true; } - /** - * @param PiggyBank $piggyBank - * @param string $amount - * @param TransactionJournal|null $journal - * - * @return bool - */ public function addAmount(PiggyBank $piggyBank, string $amount, ?TransactionJournal $journal = null): bool { $repetition = $this->getRepetition($piggyBank); @@ -108,13 +84,6 @@ trait ModifiesPiggyBanks return true; } - /** - * @param PiggyBank $piggyBank - * @param string $amount - * - * @return bool - * @throws JsonException - */ public function canAddAmount(PiggyBank $piggyBank, string $amount): bool { $today = today(config('app.timezone')); @@ -139,12 +108,6 @@ trait ModifiesPiggyBanks return $result; } - /** - * @param PiggyBank $piggyBank - * @param string $amount - * - * @return bool - */ public function canRemoveAmount(PiggyBank $piggyBank, string $amount): bool { $repetition = $this->getRepetition($piggyBank); @@ -157,10 +120,7 @@ trait ModifiesPiggyBanks } /** - * @param PiggyBank $piggyBank - * - * @return bool - * @throws Exception + * @throws \Exception */ public function destroy(PiggyBank $piggyBank): bool { @@ -170,9 +130,6 @@ trait ModifiesPiggyBanks return true; } - /** - * @inheritDoc - */ public function removeObjectGroup(PiggyBank $piggyBank): PiggyBank { $piggyBank->objectGroups()->sync([]); @@ -180,12 +137,6 @@ trait ModifiesPiggyBanks return $piggyBank; } - /** - * @param PiggyBank $piggyBank - * @param string $amount - * - * @return PiggyBank - */ public function setCurrentAmount(PiggyBank $piggyBank, string $amount): PiggyBank { $repetition = $this->getRepetition($piggyBank); @@ -212,9 +163,6 @@ trait ModifiesPiggyBanks return $piggyBank; } - /** - * @inheritDoc - */ public function setObjectGroup(PiggyBank $piggyBank, string $objectGroupTitle): PiggyBank { $objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle); @@ -226,9 +174,6 @@ trait ModifiesPiggyBanks } /** - * @param array $data - * - * @return PiggyBank * @throws FireflyException */ public function store(array $data): PiggyBank @@ -252,6 +197,7 @@ trait ModifiesPiggyBanks $piggyBank = PiggyBank::create($piggyData); } catch (QueryException $e) { app('log')->error(sprintf('Could not store piggy bank: %s', $e->getMessage()), $piggyData); + throw new FireflyException('400005: Could not store new piggy bank.', 0, $e); } @@ -302,21 +248,19 @@ trait ModifiesPiggyBanks $piggyBank->order = $current; $piggyBank->save(); } - $current++; + ++$current; } } - /** - * @inheritDoc - */ public function setOrder(PiggyBank $piggyBank, int $newOrder): bool { $oldOrder = $piggyBank->order; - //app('log')->debug(sprintf('Will move piggy bank #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder)); + // app('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'); + ->where('piggy_banks.id', '!=', $piggyBank->id) + ->decrement('piggy_banks.order') + ; $piggyBank->order = $newOrder; app('log')->debug(sprintf('[1] Order of piggy #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder)); $piggyBank->save(); @@ -325,8 +269,9 @@ trait ModifiesPiggyBanks } $this->user->piggyBanks()->where('piggy_banks.order', '>=', $newOrder)->where('piggy_banks.order', '<', $oldOrder) - ->where('piggy_banks.id', '!=', $piggyBank->id) - ->increment('piggy_banks.order'); + ->where('piggy_banks.id', '!=', $piggyBank->id) + ->increment('piggy_banks.order') + ; $piggyBank->order = $newOrder; app('log')->debug(sprintf('[2] Order of piggy #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder)); $piggyBank->save(); @@ -334,37 +279,6 @@ trait ModifiesPiggyBanks return true; } - /** - * @param PiggyBank $piggyBank - * @param string $note - * - * @return bool - */ - private function updateNote(PiggyBank $piggyBank, string $note): bool - { - if ('' === $note) { - $dbNote = $piggyBank->notes()->first(); - $dbNote?->delete(); - - return true; - } - $dbNote = $piggyBank->notes()->first(); - if (null === $dbNote) { - $dbNote = new Note(); - $dbNote->noteable()->associate($piggyBank); - } - $dbNote->text = trim($note); - $dbNote->save(); - - return true; - } - - /** - * @param PiggyBank $piggyBank - * @param array $data - * - * @return PiggyBank - */ public function update(PiggyBank $piggyBank, array $data): PiggyBank { $piggyBank = $this->updateProperties($piggyBank, $data); @@ -425,12 +339,25 @@ trait ModifiesPiggyBanks return $piggyBank; } - /** - * @param PiggyBank $piggyBank - * @param array $data - * - * @return PiggyBank - */ + private function updateNote(PiggyBank $piggyBank, string $note): bool + { + if ('' === $note) { + $dbNote = $piggyBank->notes()->first(); + $dbNote?->delete(); + + return true; + } + $dbNote = $piggyBank->notes()->first(); + if (null === $dbNote) { + $dbNote = new Note(); + $dbNote->noteable()->associate($piggyBank); + } + $dbNote->text = trim($note); + $dbNote->save(); + + return true; + } + private function updateProperties(PiggyBank $piggyBank, array $data): PiggyBank { if (array_key_exists('name', $data) && '' !== $data['name']) { diff --git a/app/Repositories/PiggyBank/PiggyBankRepository.php b/app/Repositories/PiggyBank/PiggyBankRepository.php index 0f647ff5db..e48b69c4b4 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepository.php +++ b/app/Repositories/PiggyBank/PiggyBankRepository.php @@ -36,12 +36,9 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use JsonException; -use Storage; /** * Class PiggyBankRepository. - * */ class PiggyBankRepository implements PiggyBankRepositoryInterface { @@ -49,20 +46,11 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface private User $user; - /** - * @inheritDoc - */ public function destroyAll(): void { $this->user->piggyBanks()->delete(); } - /** - * @param int|null $piggyBankId - * @param string|null $piggyBankName - * - * @return PiggyBank|null - */ public function findPiggyBank(?int $piggyBankId, ?string $piggyBankName): ?PiggyBank { app('log')->debug('Searching for piggy information.'); @@ -88,11 +76,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface return null; } - /** - * @param int $piggyBankId - * - * @return PiggyBank|null - */ public function find(int $piggyBankId): ?PiggyBank { // phpstan doesn't get the Model. @@ -101,25 +84,18 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** * Find by name or return NULL. - * - * @param string $name - * - * @return PiggyBank|null */ public function findByName(string $name): ?PiggyBank { return $this->user->piggyBanks()->where('piggy_banks.name', $name)->first(['piggy_banks.*']); } - /** - * @inheritDoc - */ public function getAttachments(PiggyBank $piggyBank): Collection { $set = $piggyBank->attachments()->get(); - /** @var Storage $disk */ - $disk = Storage::disk('upload'); + /** @var \Storage $disk */ + $disk = \Storage::disk('upload'); return $set->each( static function (Attachment $attachment) use ($disk) { @@ -134,10 +110,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** * Get current amount saved in piggy bank. - * - * @param PiggyBank $piggyBank - * - * @return string */ public function getCurrentAmount(PiggyBank $piggyBank): string { @@ -149,21 +121,11 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface return $rep->currentamount; } - /** - * @param PiggyBank $piggyBank - * - * @return PiggyBankRepetition|null - */ public function getRepetition(PiggyBank $piggyBank): ?PiggyBankRepetition { return $piggyBank->piggyBankRepetitions()->first(); } - /** - * @param PiggyBank $piggyBank - * - * @return Collection - */ public function getEvents(PiggyBank $piggyBank): Collection { return $piggyBank->piggyBankEvents()->orderBy('date', 'DESC')->orderBy('id', 'DESC')->get(); @@ -172,13 +134,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** * Used for connecting to a piggy bank. * - * @param PiggyBank $piggyBank - * @param PiggyBankRepetition $repetition - * @param TransactionJournal $journal - * - * @return string * @throws FireflyException - * @throws JsonException */ public function getExactAmount(PiggyBank $piggyBank, PiggyBankRepetition $repetition, TransactionJournal $journal): string { @@ -186,6 +142,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface $operator = null; $currency = null; + /** @var JournalRepositoryInterface $journalRepost */ $journalRepost = app(JournalRepositoryInterface::class); $journalRepost->setUser($this->user); @@ -201,6 +158,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** @var Transaction $source */ $source = $journal->transactions()->with(['account'])->where('amount', '<', 0)->first(); + /** @var Transaction $destination */ $destination = $journal->transactions()->with(['account'])->where('amount', '>', 0)->first(); @@ -227,11 +185,11 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface $amount = null; if ((int)$source->transaction_currency_id === $currency->id) { app('log')->debug('Use normal amount'); - $amount = app('steam')->$operator($source->amount); // @phpstan-ignore-line + $amount = app('steam')->{$operator}($source->amount); // @phpstan-ignore-line } if ((int)$source->foreign_currency_id === $currency->id) { app('log')->debug('Use foreign amount'); - $amount = app('steam')->$operator($source->foreign_amount); // @phpstan-ignore-line + $amount = app('steam')->{$operator}($source->foreign_amount); // @phpstan-ignore-line } if (null === $amount) { app('log')->debug('No match on currency, so amount remains null, return "0".'); @@ -243,7 +201,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface $room = bcsub($piggyBank->targetamount, $repetition->currentamount); $compare = bcmul($repetition->currentamount, '-1'); - if (bccomp($piggyBank->targetamount, '0') === 0) { + if (0 === bccomp($piggyBank->targetamount, '0')) { // amount is zero? then the "room" is positive amount of we wish to add or remove. $room = app('steam')->positive($amount); app('log')->debug(sprintf('Room is now %s', $room)); @@ -252,7 +210,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface app('log')->debug(sprintf('Will add/remove %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); // if the amount is positive, make sure it fits in piggy bank: - if (1 === bccomp($amount, '0') && bccomp($room, $amount) === -1) { + if (1 === bccomp($amount, '0') && -1 === bccomp($room, $amount)) { // amount is positive and $room is smaller than $amount app('log')->debug(sprintf('Room in piggy bank for extra money is %f', $room)); app('log')->debug(sprintf('There is NO room to add %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); @@ -262,7 +220,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface } // amount is negative and $currentamount is smaller than $amount - if (bccomp($amount, '0') === -1 && 1 === bccomp($compare, $amount)) { + if (-1 === bccomp($amount, '0') && 1 === bccomp($compare, $amount)) { app('log')->debug(sprintf('Max amount to remove is %f', $repetition->currentamount)); app('log')->debug(sprintf('Cannot remove %f from piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); app('log')->debug(sprintf('New amount is %f', $compare)); @@ -273,19 +231,13 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface return (string)$amount; } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * @return int - */ public function getMaxOrder(): int { return (int)$this->user->piggyBanks()->max('piggy_banks.order'); @@ -293,22 +245,17 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** * Return note for piggy bank. - * - * @param PiggyBank $piggyBank - * - * @return string */ public function getNoteText(PiggyBank $piggyBank): string { - /** @var Note|null $note */ + /** @var null|Note $note */ $note = $piggyBank->notes()->first(); + return (string)$note?->text; } /** * Also add amount in name. - * - * @return Collection */ public function getPiggyBanksWithAmount(): Collection { @@ -319,35 +266,28 @@ 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).')'; } return $set; } - /** - * @return Collection - */ public function getPiggyBanks(): Collection { return $this->user // @phpstan-ignore-line (phpstan does not recognize objectGroups) - ->piggyBanks() - ->with( - [ - 'account', - 'objectGroups', - ] - ) - ->orderBy('order', 'ASC')->get(); + ->piggyBanks() + ->with( + [ + 'account', + 'objectGroups', + ] + ) + ->orderBy('order', 'ASC')->get() + ; } /** * Returns the suggested amount the user should save per month, or "". - * - * @param PiggyBank $piggyBank - * - * @return string - * */ public function getSuggestedMonthlyAmount(PiggyBank $piggyBank): string { @@ -378,12 +318,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** * Get for piggy account what is left to put in piggies. - * - * @param PiggyBank $piggyBank - * @param Carbon $date - * - * @return string - * @throws JsonException */ public function leftOnAccount(PiggyBank $piggyBank, Carbon $date): string { @@ -403,9 +337,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface return $balance; } - /** - * @inheritDoc - */ public function searchPiggyBank(string $query, int $limit): Collection { $search = $this->user->piggyBanks(); @@ -413,7 +344,8 @@ 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(); } diff --git a/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php b/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php index 6a321a9587..7c1e12becc 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php +++ b/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php @@ -37,184 +37,81 @@ use Illuminate\Support\Collection; */ interface PiggyBankRepositoryInterface { - /** - * @param PiggyBank $piggyBank - * @param string $amount - * @param TransactionJournal|null $journal - * - * @return bool - */ public function addAmount(PiggyBank $piggyBank, string $amount, ?TransactionJournal $journal = null): bool; - /** - * @param PiggyBankRepetition $repetition - * @param string $amount - * @param TransactionJournal $journal - * - * @return void - */ public function addAmountToRepetition(PiggyBankRepetition $repetition, string $amount, TransactionJournal $journal): void; - /** - * @param PiggyBank $piggyBank - * @param string $amount - * - * @return bool - */ public function canAddAmount(PiggyBank $piggyBank, string $amount): bool; - /** - * @param PiggyBank $piggyBank - * @param string $amount - * - * @return bool - */ public function canRemoveAmount(PiggyBank $piggyBank, string $amount): bool; /** * Destroy piggy bank. - * - * @param PiggyBank $piggyBank - * - * @return bool */ public function destroy(PiggyBank $piggyBank): bool; - /** - * - */ public function destroyAll(): void; - /** - * @param int $piggyBankId - * - * @return PiggyBank|null - */ public function find(int $piggyBankId): ?PiggyBank; /** * Find by name or return NULL. - * - * @param string $name - * - * @return PiggyBank|null */ public function findByName(string $name): ?PiggyBank; - /** - * @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. - * - * @param PiggyBank $piggyBank - * - * @return string */ public function getCurrentAmount(PiggyBank $piggyBank): string; /** * Get all events. - * - * @param PiggyBank $piggyBank - * - * @return Collection */ public function getEvents(PiggyBank $piggyBank): Collection; /** * Used for connecting to a piggy bank. - * - * @param PiggyBank $piggyBank - * @param PiggyBankRepetition $repetition - * @param TransactionJournal $journal - * - * @return string */ public function getExactAmount(PiggyBank $piggyBank, PiggyBankRepetition $repetition, TransactionJournal $journal): string; /** * Highest order of all piggy banks. - * - * @return int */ public function getMaxOrder(): int; /** * Return note for piggy bank. - * - * @param PiggyBank $piggyBank - * - * @return string */ public function getNoteText(PiggyBank $piggyBank): string; /** * Return all piggy banks. - * - * @return Collection */ public function getPiggyBanks(): Collection; /** * Also add amount in name. - * - * @return Collection */ public function getPiggyBanksWithAmount(): Collection; - /** - * @param PiggyBank $piggyBank - * - * @return PiggyBankRepetition|null - */ public function getRepetition(PiggyBank $piggyBank): ?PiggyBankRepetition; /** * Returns the suggested amount the user should save per month, or "". - * - * @param PiggyBank $piggyBank - * - * @return string */ public function getSuggestedMonthlyAmount(PiggyBank $piggyBank): string; /** * Get for piggy account what is left to put in piggies. - * - * @param PiggyBank $piggyBank - * @param Carbon $date - * - * @return string */ public function leftOnAccount(PiggyBank $piggyBank, Carbon $date): string; - /** - * @param PiggyBank $piggyBank - * @param string $amount - * @param TransactionJournal|null $journal - * - * @return bool - */ public function removeAmount(PiggyBank $piggyBank, string $amount, ?TransactionJournal $journal = null): bool; - /** - * @param PiggyBank $piggyBank - * - * @return PiggyBank - */ public function removeObjectGroup(PiggyBank $piggyBank): PiggyBank; /** @@ -224,62 +121,29 @@ interface PiggyBankRepositoryInterface /** * 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 $newOrder - * - * @return bool */ public function setOrder(PiggyBank $piggyBank, int $newOrder): bool; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; /** * Store new piggy bank. * - * @param array $data - * - * @return PiggyBank * @throws FireflyException */ public function store(array $data): PiggyBank; /** * Update existing piggy bank. - * - * @param PiggyBank $piggyBank - * @param array $data - * - * @return PiggyBank */ public function update(PiggyBank $piggyBank, array $data): PiggyBank; } diff --git a/app/Repositories/Recurring/RecurringRepository.php b/app/Repositories/Recurring/RecurringRepository.php index 4a67a0cae5..d5bfce4440 100644 --- a/app/Repositories/Recurring/RecurringRepository.php +++ b/app/Repositories/Recurring/RecurringRepository.php @@ -47,7 +47,6 @@ use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; -use JsonException; /** * Class RecurringRepository @@ -61,55 +60,52 @@ class RecurringRepository implements RecurringRepositoryInterface private User $user; - /** - * @inheritDoc - */ public function createdPreviously(Recurrence $recurrence, Carbon $date): bool { // if not, loop set and try to read the recurrence_date. If it matches start or end, return it as well. $set - = TransactionJournalMeta::where(static function (Builder $q1) use ($recurrence) { - $q1->where('name', 'recurrence_id'); - $q1->where('data', json_encode((string)$recurrence->id)); - })->get(['journal_meta.transaction_journal_id']); + = TransactionJournalMeta::where(static function (Builder $q1) use ($recurrence): void { + $q1->where('name', 'recurrence_id'); + $q1->where('data', json_encode((string)$recurrence->id)); + })->get(['journal_meta.transaction_journal_id']); // there are X journals made for this recurrence. Any of them meant for today? foreach ($set as $journalMeta) { - $count = TransactionJournalMeta::where(static function (Builder $q2) use ($date) { + $count = TransactionJournalMeta::where(static function (Builder $q2) use ($date): void { $string = (string)$date; app('log')->debug(sprintf('Search for date: %s', json_encode($string))); $q2->where('name', 'recurrence_date'); $q2->where('data', json_encode($string)); }) - ->where('transaction_journal_id', $journalMeta->transaction_journal_id) - ->count(); + ->where('transaction_journal_id', $journalMeta->transaction_journal_id) + ->count() + ; if ($count > 0) { app('log')->debug(sprintf('Looks like journal #%d was already created', $journalMeta->transaction_journal_id)); + return true; } } + return false; } /** * Returns all of the user's recurring transactions. - * - * @return Collection */ public function get(): Collection { return $this->user->recurrences() - ->with(['TransactionCurrency', 'TransactionType', 'RecurrenceRepetitions', 'RecurrenceTransactions']) - ->orderBy('active', 'DESC') - ->orderBy('transaction_type_id', 'ASC') - ->orderBy('title', 'ASC') - ->get(); + ->with(['TransactionCurrency', 'TransactionType', 'RecurrenceRepetitions', 'RecurrenceTransactions']) + ->orderBy('active', 'DESC') + ->orderBy('transaction_type_id', 'ASC') + ->orderBy('title', 'ASC') + ->get() + ; } /** * Destroy a recurring transaction. - * - * @param Recurrence $recurrence */ public function destroy(Recurrence $recurrence): void { @@ -118,9 +114,6 @@ class RecurringRepository implements RecurringRepositoryInterface $service->destroy($recurrence); } - /** - * @inheritDoc - */ public function destroyAll(): void { $this->user->recurrences()->delete(); @@ -128,24 +121,21 @@ class RecurringRepository implements RecurringRepositoryInterface /** * Get ALL recurring transactions. - * - * @return Collection */ public function getAll(): Collection { // grab ALL recurring transactions: return Recurrence::with(['TransactionCurrency', 'TransactionType', 'RecurrenceRepetitions', 'RecurrenceTransactions']) - ->orderBy('active', 'DESC') - ->orderBy('title', 'ASC') - ->get(); + ->orderBy('active', 'DESC') + ->orderBy('title', 'ASC') + ->get() + ; } - /** - * @inheritDoc - */ public function getBillId(RecurrenceTransaction $recTransaction): ?int { $return = null; + /** @var RecurrenceTransactionMeta $meta */ foreach ($recTransaction->recurrenceTransactionMeta as $meta) { if ('bill_id' === $meta->name) { @@ -158,14 +148,11 @@ class RecurringRepository implements RecurringRepositoryInterface /** * Get the budget ID from a recurring transaction transaction. - * - * @param RecurrenceTransaction $recTransaction - * - * @return null|int */ public function getBudget(RecurrenceTransaction $recTransaction): ?int { $return = 0; + /** @var RecurrenceTransactionMeta $meta */ foreach ($recTransaction->recurrenceTransactionMeta as $meta) { if ('budget_id' === $meta->name) { @@ -178,14 +165,11 @@ class RecurringRepository implements RecurringRepositoryInterface /** * Get the category from a recurring transaction transaction. - * - * @param RecurrenceTransaction $recTransaction - * - * @return null|int */ public function getCategoryId(RecurrenceTransaction $recTransaction): ?int { $return = ''; + /** @var RecurrenceTransactionMeta $meta */ foreach ($recTransaction->recurrenceTransactionMeta as $meta) { if ('category_id' === $meta->name) { @@ -198,14 +182,11 @@ class RecurringRepository implements RecurringRepositoryInterface /** * Get the category from a recurring transaction transaction. - * - * @param RecurrenceTransaction $recTransaction - * - * @return null|string */ public function getCategoryName(RecurrenceTransaction $recTransaction): ?string { $return = ''; + /** @var RecurrenceTransactionMeta $meta */ foreach ($recTransaction->recurrenceTransactionMeta as $meta) { if ('category_name' === $meta->name) { @@ -218,67 +199,53 @@ class RecurringRepository implements RecurringRepositoryInterface /** * Returns the journals created for this recurrence, possibly limited by time. - * - * @param Recurrence $recurrence - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return int */ public function getJournalCount(Recurrence $recurrence, Carbon $start = null, Carbon $end = null): int { $query = TransactionJournal::leftJoin('journal_meta', 'journal_meta.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transaction_journals.user_id', $recurrence->user_id) - ->whereNull('transaction_journals.deleted_at') - ->where('journal_meta.name', 'recurrence_id') - ->where('journal_meta.data', '"' . $recurrence->id . '"'); + ->where('transaction_journals.user_id', $recurrence->user_id) + ->whereNull('transaction_journals.deleted_at') + ->where('journal_meta.name', 'recurrence_id') + ->where('journal_meta.data', '"'.$recurrence->id.'"') + ; if (null !== $start) { $query->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00')); } if (null !== $end) { $query->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00')); } + return $query->count('transaction_journals.id'); } /** * Get journal ID's for journals created by this recurring transaction. - * - * @param Recurrence $recurrence - * - * @return array */ public function getJournalIds(Recurrence $recurrence): array { return TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id') - ->where('transaction_journals.user_id', $this->user->id) - ->where('journal_meta.name', '=', 'recurrence_id') - ->where('journal_meta.data', '=', json_encode((string)$recurrence->id)) - ->get(['journal_meta.transaction_journal_id'])->pluck('transaction_journal_id')->toArray(); + ->where('transaction_journals.user_id', $this->user->id) + ->where('journal_meta.name', '=', 'recurrence_id') + ->where('journal_meta.data', '=', json_encode((string)$recurrence->id)) + ->get(['journal_meta.transaction_journal_id'])->pluck('transaction_journal_id')->toArray() + ; } /** * Get the notes. - * - * @param Recurrence $recurrence - * - * @return string */ public function getNoteText(Recurrence $recurrence): string { - /** @var Note|null $note */ + /** @var null|Note $note */ $note = $recurrence->notes()->first(); + return (string)$note?->text; } - /** - * @param RecurrenceTransaction $transaction - * - * @return int|null - */ public function getPiggyBank(RecurrenceTransaction $transaction): ?int { $meta = $transaction->recurrenceTransactionMeta; + /** @var RecurrenceTransactionMeta $metaEntry */ foreach ($meta as $metaEntry) { if ('piggy_bank_id' === $metaEntry->name) { @@ -291,15 +258,11 @@ class RecurringRepository implements RecurringRepositoryInterface /** * Get the tags from the recurring transaction. - * - * @param RecurrenceTransaction $transaction - * - * @return array - * @throws JsonException */ public function getTags(RecurrenceTransaction $transaction): array { $tags = []; + /** @var RecurrenceMeta $meta */ foreach ($transaction->recurrenceTransactionMeta as $meta) { if ('tags' === $meta->name && '' !== $meta->value) { @@ -310,59 +273,48 @@ class RecurringRepository implements RecurringRepositoryInterface return $tags; } - /** - * @param Recurrence $recurrence - * @param int $page - * @param int $pageSize - * - * @return LengthAwarePaginator - */ public function getTransactionPaginator(Recurrence $recurrence, int $page, int $pageSize): LengthAwarePaginator { $journalMeta = TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id') - ->whereNull('transaction_journals.deleted_at') - ->where('transaction_journals.user_id', $this->user->id) - ->where('name', 'recurrence_id') - ->where('data', json_encode((string)$recurrence->id)) - ->get()->pluck('transaction_journal_id')->toArray(); + ->whereNull('transaction_journals.deleted_at') + ->where('transaction_journals.user_id', $this->user->id) + ->where('name', 'recurrence_id') + ->where('data', json_encode((string)$recurrence->id)) + ->get()->pluck('transaction_journal_id')->toArray() + ; $search = []; foreach ($journalMeta as $journalId) { $search[] = (int)$journalId; } + /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setUser($recurrence->user); $collector->withCategoryInformation()->withBudgetInformation()->setLimit($pageSize)->setPage($page) - ->withAccountInformation(); + ->withAccountInformation() + ; $collector->setJournalIds($search); return $collector->getPaginatedGroups(); } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * @param Recurrence $recurrence - * - * @return Collection - */ public function getTransactions(Recurrence $recurrence): Collection { $journalMeta = TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id') - ->whereNull('transaction_journals.deleted_at') - ->where('transaction_journals.user_id', $this->user->id) - ->where('name', 'recurrence_id') - ->where('data', json_encode((string)$recurrence->id)) - ->get()->pluck('transaction_journal_id')->toArray(); + ->whereNull('transaction_journals.deleted_at') + ->where('transaction_journals.user_id', $this->user->id) + ->where('name', 'recurrence_id') + ->where('data', json_encode((string)$recurrence->id)) + ->get()->pluck('transaction_journal_id')->toArray() + ; $search = []; foreach ($journalMeta as $journalId) { @@ -385,13 +337,6 @@ class RecurringRepository implements RecurringRepositoryInterface /** * Calculate the next X iterations starting on the date given in $date. - * - * @param RecurrenceRepetition $repetition - * @param Carbon $date - * @param int $count - * - * @return array - * */ public function getXOccurrences(RecurrenceRepetition $repetition, Carbon $date, int $count): array { @@ -422,13 +367,6 @@ class RecurringRepository implements RecurringRepositoryInterface * 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 */ public function getXOccurrencesSince(RecurrenceRepetition $repetition, Carbon $date, Carbon $afterDate, int $count): array { @@ -460,38 +398,15 @@ class RecurringRepository implements RecurringRepositoryInterface return $this->filterMaxDate($repeatUntil, $occurrences); } - /** - * @param Carbon|null $max - * @param array $occurrences - * - * @return array - */ - private function filterMaxDate(?Carbon $max, array $occurrences): array - { - if (null === $max) { - return $occurrences; - } - $filtered = []; - foreach ($occurrences as $date) { - if ($date->lte($max)) { - $filtered[] = $date; - } - } - - return $filtered; - } - /** * Parse the repetition in a string that is user readable. * - * @param RecurrenceRepetition $repetition - * - * @return string * @throws FireflyException */ public function repetitionDescription(RecurrenceRepetition $repetition): string { app('log')->debug('Now in repetitionDescription()'); + /** @var Preference $pref */ $pref = app('preferences')->getForUser($this->user, 'language', config('firefly.default_language', 'en_US')); $language = $pref->data; @@ -533,7 +448,6 @@ class RecurringRepository implements RecurringRepositoryInterface return (string)trans('firefly.recurring_ndom', ['weekday' => $dayOfWeek, 'dayOfMonth' => $parts[0]], $language); } if ('yearly' === $repetition->repetition_type) { - // $today = today(config('app.timezone'))->endOfYear(); $repDate = Carbon::createFromFormat('Y-m-d', $repetition->repetition_moment); if (false === $repDate) { @@ -549,9 +463,6 @@ class RecurringRepository implements RecurringRepositoryInterface return ''; } - /** - * @inheritDoc - */ public function searchRecurrence(string $query, int $limit): Collection { $search = $this->user->recurrences(); @@ -559,29 +470,24 @@ class RecurringRepository implements RecurringRepositoryInterface $search->where('recurrences.title', 'LIKE', sprintf('%%%s%%', $query)); } $search - ->orderBy('recurrences.title', 'ASC'); + ->orderBy('recurrences.title', 'ASC') + ; return $search->take($limit)->get(['id', 'title', 'description']); } /** - * @param array $data - * - * @return Recurrence * @throws FireflyException - * @throws JsonException */ public function store(array $data): Recurrence { /** @var RecurrenceFactory $factory */ $factory = app(RecurrenceFactory::class); $factory->setUser($this->user); + return $factory->create($data); } - /** - * @inheritDoc - */ public function totalTransactions(Recurrence $recurrence, RecurrenceRepetition $repetition): int { // if repeat = null just return 0. @@ -605,13 +511,6 @@ class RecurringRepository implements RecurringRepositoryInterface /** * Generate events in the date range. - * - * @param RecurrenceRepetition $repetition - * @param Carbon $start - * @param Carbon $end - * - * @return array - * */ public function getOccurrencesInRange(RecurrenceRepetition $repetition, Carbon $start, Carbon $end): array { @@ -645,10 +544,6 @@ class RecurringRepository implements RecurringRepositoryInterface /** * Update a recurring transaction. * - * @param Recurrence $recurrence - * @param array $data - * - * @return Recurrence * @throws FireflyException */ public function update(Recurrence $recurrence, array $data): Recurrence @@ -658,4 +553,19 @@ class RecurringRepository implements RecurringRepositoryInterface return $service->update($recurrence, $data); } + + private function filterMaxDate(?Carbon $max, array $occurrences): array + { + if (null === $max) { + return $occurrences; + } + $filtered = []; + foreach ($occurrences as $date) { + if ($date->lte($max)) { + $filtered[] = $date; + } + } + + return $filtered; + } } diff --git a/app/Repositories/Recurring/RecurringRepositoryInterface.php b/app/Repositories/Recurring/RecurringRepositoryInterface.php index 3da9bacfe5..6c8963aaf4 100644 --- a/app/Repositories/Recurring/RecurringRepositoryInterface.php +++ b/app/Repositories/Recurring/RecurringRepositoryInterface.php @@ -35,22 +35,13 @@ use Illuminate\Support\Collection; /** * Interface RecurringRepositoryInterface - * */ interface RecurringRepositoryInterface { - /** - * @param Recurrence $recurrence - * @param Carbon $date - * - * @return bool - */ public function createdPreviously(Recurrence $recurrence, Carbon $date): bool; /** * Destroy a recurring transaction. - * - * @param Recurrence $recurrence */ public function destroy(Recurrence $recurrence): void; @@ -61,135 +52,69 @@ interface RecurringRepositoryInterface /** * Returns all of the user's recurring transactions. - * - * @return Collection */ public function get(): Collection; /** * Get ALL recurring transactions. - * - * @return Collection */ public function getAll(): Collection; /** * Get the category from a recurring transaction transaction. - * - * @param RecurrenceTransaction $recTransaction - * - * @return null|int */ public function getBillId(RecurrenceTransaction $recTransaction): ?int; /** * Get the budget ID from a recurring transaction transaction. - * - * @param RecurrenceTransaction $recTransaction - * - * @return null|int */ public function getBudget(RecurrenceTransaction $recTransaction): ?int; /** * Get the category from a recurring transaction transaction. - * - * @param RecurrenceTransaction $recTransaction - * - * @return null|int */ public function getCategoryId(RecurrenceTransaction $recTransaction): ?int; /** * Get the category from a recurring transaction transaction. - * - * @param RecurrenceTransaction $recTransaction - * - * @return null|string */ public function getCategoryName(RecurrenceTransaction $recTransaction): ?string; /** * Returns the count of journals created for this recurrence, possibly limited by time. - * - * @param Recurrence $recurrence - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return int */ public function getJournalCount(Recurrence $recurrence, Carbon $start = null, Carbon $end = null): int; /** * Get journal ID's for journals created by this recurring transaction. - * - * @param Recurrence $recurrence - * - * @return array */ public function getJournalIds(Recurrence $recurrence): array; /** * Get the notes. - * - * @param Recurrence $recurrence - * - * @return string */ public function getNoteText(Recurrence $recurrence): string; /** * Generate events in the date range. - * - * @param RecurrenceRepetition $repetition - * @param Carbon $start - * @param Carbon $end - * - * @return array */ public function getOccurrencesInRange(RecurrenceRepetition $repetition, Carbon $start, Carbon $end): array; - /** - * @param RecurrenceTransaction $transaction - * - * @return int|null - */ public function getPiggyBank(RecurrenceTransaction $transaction): ?int; /** * Get the tags from the recurring transaction. - * - * @param RecurrenceTransaction $transaction - * - * @return array */ public function getTags(RecurrenceTransaction $transaction): array; - /** - * @param Recurrence $recurrence - * @param int $page - * @param int $pageSize - * - * @return LengthAwarePaginator - */ public function getTransactionPaginator(Recurrence $recurrence, int $page, int $pageSize): LengthAwarePaginator; - /** - * @param Recurrence $recurrence - * - * @return Collection - */ public function getTransactions(Recurrence $recurrence): Collection; /** * Calculate the next X iterations starting on the date given in $date. * Returns an array of Carbon objects. * - * @param RecurrenceRepetition $repetition - * @param Carbon $date - * @param int $count - * - * @return array * @throws FireflyException */ public function getXOccurrences(RecurrenceRepetition $repetition, Carbon $date, int $count): array; @@ -200,65 +125,33 @@ interface RecurringRepositoryInterface * * 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; /** * Parse the repetition in a string that is user readable. - * - * @param RecurrenceRepetition $repetition - * - * @return string */ public function repetitionDescription(RecurrenceRepetition $repetition): string; - /** - * @param string $query - * @param int $limit - * - * @return Collection - */ public function searchRecurrence(string $query, int $limit): Collection; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; /** * Store a new recurring transaction. * - * @param array $data - * - * @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. - * - * @param Recurrence $recurrence - * @param array $data - * - * @return Recurrence */ public function update(Recurrence $recurrence, array $data): Recurrence; } diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index 6e7a96e799..3e21a7fa31 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Rule; -use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; @@ -37,7 +36,6 @@ use Illuminate\Support\Collection; /** * Class RuleRepository. - * */ class RuleRepository implements RuleRepositoryInterface { @@ -45,10 +43,7 @@ class RuleRepository implements RuleRepositoryInterface private $user; /** - * @param Rule $rule - * - * @return bool - * @throws Exception + * @throws \Exception */ public function destroy(Rule $rule): bool { @@ -63,9 +58,6 @@ class RuleRepository implements RuleRepositoryInterface return true; } - /** - * @inheritDoc - */ public function duplicate(Rule $rule): Rule { $newRule = $rule->replicate(); @@ -93,8 +85,6 @@ class RuleRepository implements RuleRepositoryInterface /** * Get all the users rules. - * - * @return Collection */ public function getAll(): Collection { @@ -103,75 +93,49 @@ class RuleRepository implements RuleRepositoryInterface /** * FIxXME can return null. - * - * @return RuleGroup */ public function getFirstRuleGroup(): RuleGroup { return $this->user->ruleGroups()->first(); } - /** - * @param RuleGroup $ruleGroup - * - * @return int - */ public function getHighestOrderInRuleGroup(RuleGroup $ruleGroup): int { return (int)$ruleGroup->rules()->max('order'); } /** - * @param Rule $rule - * - * @return string - * * @throws FireflyException */ public function getPrimaryTrigger(Rule $rule): string { $count = $rule->ruleTriggers()->count(); if (0 === $count) { - throw new FireflyException('Rules should have more than zero triggers, rule #' . $rule->id . ' has none!'); + throw new FireflyException('Rules should have more than zero triggers, rule #'.$rule->id.' has none!'); } return $rule->ruleTriggers()->where('trigger_type', 'user_action')->first()->trigger_value; } - /** - * @return int - */ public function count(): int { return $this->user->rules()->count(); } - /** - * @param Rule $rule - * - * @return Collection - */ public function getRuleActions(Rule $rule): Collection { return $rule->ruleActions()->orderBy('order', 'ASC')->get(); } - /** - * @param Rule $rule - * - * @return Collection - */ public function getRuleTriggers(Rule $rule): Collection { 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) { @@ -189,20 +153,19 @@ class RuleRepository implements RuleRepositoryInterface 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', true) - ->where('rule_groups.active', true) - ->orderBy('rule_groups.order', 'ASC') - ->orderBy('rules.order', 'ASC') - ->orderBy('rules.id', 'ASC') - ->with(['ruleGroup', 'ruleTriggers'])->get(['rules.*']); + ->leftJoin('rule_groups', 'rule_groups.id', '=', 'rules.rule_group_id') + ->where('rules.active', true) + ->where('rule_groups.active', true) + ->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 */ @@ -216,20 +179,19 @@ class RuleRepository implements RuleRepositoryInterface return $filtered; } - /** - * @inheritDoc - */ public function getUpdateRules(): Collection { $collection = $this->user->rules() - ->leftJoin('rule_groups', 'rule_groups.id', '=', 'rules.rule_group_id') - ->where('rules.active', true) - ->where('rule_groups.active', true) - ->orderBy('rule_groups.order', 'ASC') - ->orderBy('rules.order', 'ASC') - ->orderBy('rules.id', 'ASC') - ->with(['ruleGroup', 'ruleTriggers'])->get(); + ->leftJoin('rule_groups', 'rule_groups.id', '=', 'rules.rule_group_id') + ->where('rules.active', true) + ->where('rule_groups.active', true) + ->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 */ @@ -243,9 +205,6 @@ class RuleRepository implements RuleRepositoryInterface return $filtered; } - /** - * @inheritDoc - */ public function searchRule(string $query, int $limit): Collection { $search = $this->user->rules(); @@ -253,15 +212,13 @@ class RuleRepository implements RuleRepositoryInterface $search->where('rules.title', 'LIKE', sprintf('%%%s%%', $query)); } $search->orderBy('rules.order', 'ASC') - ->orderBy('rules.title', 'ASC'); + ->orderBy('rules.title', 'ASC') + ; return $search->take($limit)->get(['id', 'title', 'description']); } /** - * @param array $data - * - * @return Rule * @throws FireflyException */ public function store(array $data): Rule @@ -313,45 +270,11 @@ class RuleRepository implements RuleRepositoryInterface return $rule; } - /** - * @param int $ruleId - * - * @return Rule|null - */ public function find(int $ruleId): ?Rule { return $this->user->rules()->find($ruleId); } - /** - * @param string $moment - * @param Rule $rule - */ - private function setRuleTrigger(string $moment, Rule $rule): void - { - /** @var RuleTrigger|null $trigger */ - $trigger = $rule->ruleTriggers()->where('trigger_type', 'user_action')->first(); - if (null !== $trigger) { - $trigger->trigger_value = $moment; - $trigger->save(); - - return; - } - $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(); - } - - /** - * @param RuleGroup $ruleGroup - * - * @return bool - */ public function resetRuleOrder(RuleGroup $ruleGroup): bool { $groupRepository = app(RuleGroupRepositoryInterface::class); @@ -361,19 +284,13 @@ class RuleRepository implements RuleRepositoryInterface return true; } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * @inheritDoc - */ public function setOrder(Rule $rule, int $newOrder): void { $oldOrder = $rule->order; @@ -384,11 +301,12 @@ class RuleRepository implements RuleRepositoryInterface 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'); + ->where('rules.rule_group_id', $groupId) + ->where('rules.order', '<=', $newOrder) + ->where('rules.order', '>', $oldOrder) + ->where('rules.id', '!=', $rule->id) + ->decrement('rules.order') + ; $rule->order = $newOrder; app('log')->debug(sprintf('Order of rule #%d ("%s") is now %d', $rule->id, $rule->title, $newOrder)); $rule->save(); @@ -397,60 +315,22 @@ class RuleRepository implements RuleRepositoryInterface } $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'); + ->where('rules.rule_group_id', $groupId) + ->where('rules.order', '>=', $newOrder) + ->where('rules.order', '<', $oldOrder) + ->where('rules.id', '!=', $rule->id) + ->increment('rules.order') + ; $rule->order = $newOrder; app('log')->debug(sprintf('Order of rule #%d ("%s") is now %d', $rule->id, $rule->title, $newOrder)); $rule->save(); } - /** - * @inheritDoc - */ public function maxOrder(RuleGroup $ruleGroup): int { return (int)$ruleGroup->rules()->max('order'); } - /** - * @param Rule $rule - * @param array $data - * - * @return void - */ - private function storeTriggers(Rule $rule, array $data): void - { - $order = 1; - foreach ($data['triggers'] as $trigger) { - $value = $trigger['value'] ?? ''; - $stopProcessing = $trigger['stop_processing'] ?? false; - $active = $trigger['active'] ?? true; - $type = $trigger['type']; - if (true === ($trigger['prohibited'] ?? false) && !str_starts_with($type, '-')) { - $type = sprintf('-%s', $type); - } - - $triggerValues = [ - 'action' => $type, - 'value' => $value, - 'stop_processing' => $stopProcessing, - 'order' => $order, - 'active' => $active, - ]; - $this->storeTrigger($rule, $triggerValues); - ++$order; - } - } - - /** - * @param Rule $rule - * @param array $values - * - * @return RuleTrigger - */ public function storeTrigger(Rule $rule, array $values): RuleTrigger { $ruleTrigger = new RuleTrigger(); @@ -465,37 +345,6 @@ class RuleRepository implements RuleRepositoryInterface return $ruleTrigger; } - /** - * @param Rule $rule - * @param array $data - * - * @return void - */ - private function storeActions(Rule $rule, array $data): void - { - $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, - ]; - $this->storeAction($rule, $actionValues); - ++$order; - } - } - - /** - * @param Rule $rule - * @param array $values - * - * @return RuleAction - */ public function storeAction(Rule $rule, array $values): RuleAction { $ruleAction = new RuleAction(); @@ -510,12 +359,6 @@ class RuleRepository implements RuleRepositoryInterface return $ruleAction; } - /** - * @param Rule $rule - * @param array $data - * - * @return Rule - */ public function update(Rule $rule, array $data): Rule { // update rule: @@ -529,7 +372,7 @@ class RuleRepository implements RuleRepositoryInterface ]; foreach ($fields as $field) { if (array_key_exists($field, $data)) { - $rule->$field = $data[$field]; + $rule->{$field} = $data[$field]; } } $rule->save(); @@ -541,7 +384,6 @@ class RuleRepository implements RuleRepositoryInterface $this->moveRule($rule, $group, (int)$data['order']); } - // update the triggers: if (array_key_exists('trigger', $data) && 'update-journal' === $data['trigger']) { $this->setRuleTrigger('update-journal', $rule); @@ -568,9 +410,6 @@ class RuleRepository implements RuleRepositoryInterface return $rule; } - /** - * @inheritDoc - */ public function moveRule(Rule $rule, RuleGroup $ruleGroup, int $order): Rule { if ($rule->rule_group_id !== $ruleGroup->id) { @@ -582,4 +421,67 @@ class RuleRepository implements RuleRepositoryInterface return $rule; } + + private function setRuleTrigger(string $moment, Rule $rule): void + { + /** @var null|RuleTrigger $trigger */ + $trigger = $rule->ruleTriggers()->where('trigger_type', 'user_action')->first(); + if (null !== $trigger) { + $trigger->trigger_value = $moment; + $trigger->save(); + + return; + } + $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(); + } + + private function storeTriggers(Rule $rule, array $data): void + { + $order = 1; + foreach ($data['triggers'] as $trigger) { + $value = $trigger['value'] ?? ''; + $stopProcessing = $trigger['stop_processing'] ?? false; + $active = $trigger['active'] ?? true; + $type = $trigger['type']; + if (true === ($trigger['prohibited'] ?? false) && !str_starts_with($type, '-')) { + $type = sprintf('-%s', $type); + } + + $triggerValues = [ + 'action' => $type, + 'value' => $value, + 'stop_processing' => $stopProcessing, + 'order' => $order, + 'active' => $active, + ]; + $this->storeTrigger($rule, $triggerValues); + ++$order; + } + } + + private function storeActions(Rule $rule, array $data): void + { + $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, + ]; + $this->storeAction($rule, $actionValues); + ++$order; + } + } } diff --git a/app/Repositories/Rule/RuleRepositoryInterface.php b/app/Repositories/Rule/RuleRepositoryInterface.php index a38590c6de..96cb48c06d 100644 --- a/app/Repositories/Rule/RuleRepositoryInterface.php +++ b/app/Repositories/Rule/RuleRepositoryInterface.php @@ -36,165 +36,61 @@ use Illuminate\Support\Collection; */ interface RuleRepositoryInterface { - /** - * @return int - */ public function count(): int; - /** - * @param Rule $rule - * - * @return bool - */ public function destroy(Rule $rule): bool; - /** - * @param Rule $rule - * - * @return Rule - */ public function duplicate(Rule $rule): Rule; - /** - * @param int $ruleId - * - * @return Rule|null - */ public function find(int $ruleId): ?Rule; /** * Get all the users rules. - * - * @return Collection */ public function getAll(): Collection; - /** - * @return RuleGroup - */ public function getFirstRuleGroup(): RuleGroup; - /** - * @param RuleGroup $ruleGroup - * - * @return int - */ public function getHighestOrderInRuleGroup(RuleGroup $ruleGroup): int; - /** - * @param Rule $rule - * - * @return string - */ public function getPrimaryTrigger(Rule $rule): string; - /** - * @param Rule $rule - * - * @return Collection - */ public function getRuleActions(Rule $rule): Collection; - /** - * @param Rule $rule - * - * @return Collection - */ 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 RuleGroup $ruleGroup - * - * @return int - */ public function maxOrder(RuleGroup $ruleGroup): int; - /** - * @param Rule $rule - * @param RuleGroup $ruleGroup - * @param int $order - * - * @return Rule - */ public function moveRule(Rule $rule, RuleGroup $ruleGroup, int $order): Rule; - /** - * @param RuleGroup $ruleGroup - * - * @return bool - */ public function resetRuleOrder(RuleGroup $ruleGroup): bool; - /** - * @param string $query - * @param int $limit - * - * @return Collection - */ public function searchRule(string $query, int $limit): Collection; - /** - * @param Rule $rule - * @param int $newOrder - */ public function setOrder(Rule $rule, int $newOrder): void; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; - /** - * @param array $data - * - * @return Rule - */ public function store(array $data): Rule; - /** - * @param Rule $rule - * @param array $values - * - * @return RuleAction - */ public function storeAction(Rule $rule, array $values): RuleAction; - /** - * @param Rule $rule - * @param array $values - * - * @return RuleTrigger - */ public function storeTrigger(Rule $rule, array $values): RuleTrigger; - /** - * @param Rule $rule - * @param array $data - * - * @return Rule - */ public function update(Rule $rule, array $data): Rule; } diff --git a/app/Repositories/RuleGroup/RuleGroupRepository.php b/app/Repositories/RuleGroup/RuleGroupRepository.php index 5178ac123a..145a2c77cb 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepository.php +++ b/app/Repositories/RuleGroup/RuleGroupRepository.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Repositories\RuleGroup; -use Exception; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleGroup; @@ -40,9 +39,6 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface { private User $user; - /** - * @inheritDoc - */ public function correctRuleGroupOrder(): void { $set = $this->user @@ -50,40 +46,32 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface ->orderBy('order', 'ASC') ->orderBy('active', 'DESC') ->orderBy('title', 'ASC') - ->get(['rule_groups.id']); + ->get(['rule_groups.id']) + ; $index = 1; + /** @var RuleGroup $ruleGroup */ foreach ($set as $ruleGroup) { if ($ruleGroup->order !== $index) { $ruleGroup->order = $index; $ruleGroup->save(); } - $index++; + ++$index; } } - /** - * @return Collection - */ public function get(): Collection { return $this->user->ruleGroups()->orderBy('order', 'ASC')->get(); } - /** - * @return int - */ public function count(): int { return $this->user->ruleGroups()->count(); } /** - * @param RuleGroup $ruleGroup - * @param RuleGroup|null $moveTo - * - * @return bool - * @throws Exception + * @throws \Exception */ public function destroy(RuleGroup $ruleGroup, ?RuleGroup $moveTo): bool { @@ -91,6 +79,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface foreach ($ruleGroup->rules as $rule) { if (null === $moveTo) { $rule->delete(); + continue; } // move @@ -108,9 +97,6 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface return true; } - /** - * @return bool - */ public function resetOrder(): bool { $set = $this->user @@ -118,8 +104,10 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface ->whereNull('deleted_at') ->orderBy('order', 'ASC') ->orderBy('title', 'DESC') - ->get(); + ->get() + ; $count = 1; + /** @var RuleGroup $entry */ foreach ($set as $entry) { if ($entry->order !== $count) { @@ -136,19 +124,16 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface 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.*']); + ->orderBy('order', 'ASC') + ->orderBy('title', 'DESC') + ->orderBy('updated_at', 'DESC') + ->get(['rules.*']) + ; $count = 1; + /** @var Rule $entry */ foreach ($set as $entry) { if ($entry->order !== $count) { @@ -165,57 +150,10 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface 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 ($action->order !== $index) { - $action->order = $index; - $action->save(); - app('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 = $trigger->order; - if ($order !== $index) { - $trigger->order = $index; - $trigger->save(); - app('log')->debug(sprintf('Rule trigger #%d was on spot %d but must be on spot %d', $trigger->id, $order, $index)); - } - $index++; - } - } - - /** - * @inheritDoc - */ public function destroyAll(): void { $groups = $this->get(); + /** @var RuleGroup $group */ foreach ($groups as $group) { $group->rules()->delete(); @@ -223,98 +161,69 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface } } - /** - * @param int $ruleGroupId - * - * @return RuleGroup|null - */ public function find(int $ruleGroupId): ?RuleGroup { return $this->user->ruleGroups()->find($ruleGroupId); } - /** - * @param string $title - * - * @return RuleGroup|null - */ public function findByTitle(string $title): ?RuleGroup { return $this->user->ruleGroups()->where('title', $title)->first(); } - /** - * @return Collection - */ public function getActiveGroups(): Collection { return $this->user->ruleGroups()->with(['rules'])->where('rule_groups.active', true)->orderBy('order', 'ASC')->get(['rule_groups.*']); } - /** - * @param RuleGroup $group - * - * @return Collection - */ public function getActiveRules(RuleGroup $group): Collection { return $group->rules() - ->where('rules.active', true) - ->get(['rules.*']); + ->where('rules.active', true) + ->get(['rules.*']) + ; } - /** - * @param RuleGroup $group - * - * @return Collection - */ public function getActiveStoreRules(RuleGroup $group): Collection { return $group->rules() - ->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id') - ->where('rule_triggers.trigger_type', 'user_action') - ->where('rule_triggers.trigger_value', 'store-journal') - ->where('rules.active', true) - ->get(['rules.*']); + ->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id') + ->where('rule_triggers.trigger_type', 'user_action') + ->where('rule_triggers.trigger_value', 'store-journal') + ->where('rules.active', true) + ->get(['rules.*']) + ; } - /** - * @param RuleGroup $group - * - * @return Collection - */ public function getActiveUpdateRules(RuleGroup $group): Collection { return $group->rules() - ->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id') - ->where('rule_triggers.trigger_type', 'user_action') - ->where('rule_triggers.trigger_value', 'update-journal') - ->where('rules.active', true) - ->get(['rules.*']); + ->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id') + ->where('rule_triggers.trigger_type', 'user_action') + ->where('rule_triggers.trigger_value', 'update-journal') + ->where('rules.active', true) + ->get(['rules.*']) + ; } - /** - * @param string|null $filter - * - * @return Collection - */ public function getAllRuleGroupsWithRules(?string $filter): Collection { $groups = $this->user->ruleGroups() - ->orderBy('order', 'ASC') - ->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(); + ->orderBy('order', 'ASC') + ->with( + [ + 'rules' => static function (HasMany $query): void { + $query->orderBy('order', 'ASC'); + }, + 'rules.ruleTriggers' => static function (HasMany $query): void { + $query->orderBy('order', 'ASC'); + }, + 'rules.ruleActions' => static function (HasMany $query): void { + $query->orderBy('order', 'ASC'); + }, + ] + )->get() + ; if (null === $filter) { return $groups; } @@ -345,9 +254,6 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface ); } - /** - * @return int - */ public function getHighestOrderRuleGroup(): int { $entry = $this->user->ruleGroups()->max('order'); @@ -355,29 +261,25 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface return (int)$entry; } - /** - * @param string|null $filter - * - * @return Collection - */ public function getRuleGroupsWithRules(?string $filter): Collection { $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(); + ->orderBy('order', 'ASC') + ->where('active', true) + ->with( + [ + 'rules' => static function (HasMany $query): void { + $query->orderBy('order', 'ASC'); + }, + 'rules.ruleTriggers' => static function (HasMany $query): void { + $query->orderBy('order', 'ASC'); + }, + 'rules.ruleActions' => static function (HasMany $query): void { + $query->orderBy('order', 'ASC'); + }, + ] + )->get() + ; if (null === $filter) { return $groups; } @@ -408,28 +310,18 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface ); } - /** - * @param RuleGroup $group - * - * @return Collection - */ public function getRules(RuleGroup $group): Collection { return $group->rules() - ->get(['rules.*']); + ->get(['rules.*']) + ; } - /** - * @inheritDoc - */ public function maxOrder(): int { return (int)$this->user->ruleGroups()->where('active', true)->max('order'); } - /** - * @inheritDoc - */ public function searchRuleGroup(string $query, int $limit): Collection { $search = $this->user->ruleGroups(); @@ -437,26 +329,19 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface $search->where('rule_groups.title', 'LIKE', sprintf('%%%s%%', $query)); } $search->orderBy('rule_groups.order', 'ASC') - ->orderBy('rule_groups.title', 'ASC'); + ->orderBy('rule_groups.title', 'ASC') + ; return $search->take($limit)->get(['id', 'title', 'description']); } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * @param array $data - * - * @return RuleGroup - */ public function store(array $data): RuleGroup { $newRuleGroup = new RuleGroup( @@ -478,17 +363,15 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface return $newRuleGroup; } - /** - * @inheritDoc - */ public function setOrder(RuleGroup $ruleGroup, int $newOrder): void { $oldOrder = $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'); + ->where('rule_groups.id', '!=', $ruleGroup->id) + ->decrement('order') + ; $ruleGroup->order = $newOrder; app('log')->debug(sprintf('Order of group #%d ("%s") is now %d', $ruleGroup->id, $ruleGroup->title, $newOrder)); $ruleGroup->save(); @@ -497,19 +380,14 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface } $this->user->ruleGroups()->where('rule_groups.order', '>=', $newOrder)->where('rule_groups.order', '<', $oldOrder) - ->where('rule_groups.id', '!=', $ruleGroup->id) - ->increment('order'); + ->where('rule_groups.id', '!=', $ruleGroup->id) + ->increment('order') + ; $ruleGroup->order = $newOrder; app('log')->debug(sprintf('Order of group #%d ("%s") is now %d', $ruleGroup->id, $ruleGroup->title, $newOrder)); $ruleGroup->save(); } - /** - * @param RuleGroup $ruleGroup - * @param array $data - * - * @return RuleGroup - */ public function update(RuleGroup $ruleGroup, array $data): RuleGroup { // update the account: @@ -532,4 +410,47 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface return $ruleGroup; } + + 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 ($action->order !== $index) { + $action->order = $index; + $action->save(); + app('log')->debug(sprintf('Rule action #%d was on spot %d but must be on spot %d', $action->id, $action->order, $index)); + } + ++$index; + } + } + + 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 = $trigger->order; + if ($order !== $index) { + $trigger->order = $index; + $trigger->save(); + app('log')->debug(sprintf('Rule trigger #%d was on spot %d but must be on spot %d', $trigger->id, $order, $index)); + } + ++$index; + } + } } diff --git a/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php b/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php index 9bb34dfc2e..d334fbb473 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php +++ b/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php @@ -38,17 +38,8 @@ interface RuleGroupRepositoryInterface */ public function correctRuleGroupOrder(): void; - /** - * @return int - */ public function count(): int; - /** - * @param RuleGroup $ruleGroup - * @param RuleGroup|null $moveTo - * - * @return bool - */ public function destroy(RuleGroup $ruleGroup, ?RuleGroup $moveTo): bool; /** @@ -56,131 +47,50 @@ interface RuleGroupRepositoryInterface */ public function destroyAll(): void; - /** - * @param int $ruleGroupId - * - * @return RuleGroup|null - */ public function find(int $ruleGroupId): ?RuleGroup; - /** - * @param string $title - * - * @return RuleGroup|null - */ public function findByTitle(string $title): ?RuleGroup; /** * Get all rule groups. - * - * @return Collection */ public function get(): Collection; - /** - * @return Collection - */ public function getActiveGroups(): Collection; - /** - * @param RuleGroup $group - * - * @return Collection - */ public function getActiveRules(RuleGroup $group): Collection; - /** - * @param RuleGroup $group - * - * @return Collection - */ public function getActiveStoreRules(RuleGroup $group): Collection; - /** - * @param RuleGroup $group - * - * @return Collection - */ public function getActiveUpdateRules(RuleGroup $group): Collection; /** * Also inactive groups. - * - * @param string|null $filter - * - * @return Collection */ public function getAllRuleGroupsWithRules(?string $filter): Collection; - /** - * @return int - */ public function getHighestOrderRuleGroup(): int; - /** - * @param string|null $filter - * - * @return Collection - */ public function getRuleGroupsWithRules(?string $filter): Collection; - /** - * @param RuleGroup $group - * - * @return Collection - */ public function getRules(RuleGroup $group): Collection; /** * Get highest possible order for a rule group. - * - * @return int */ public function maxOrder(): int; - /** - * @return bool - */ public function resetOrder(): bool; - /** - * @param RuleGroup $ruleGroup - * - * @return bool - */ public function resetRuleOrder(RuleGroup $ruleGroup): bool; - /** - * @param string $query - * @param int $limit - * - * @return Collection - */ public function searchRuleGroup(string $query, int $limit): Collection; - /** - * @param RuleGroup $ruleGroup - * @param int $newOrder - */ public function setOrder(RuleGroup $ruleGroup, int $newOrder): void; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; - /** - * @param array $data - * - * @return RuleGroup - */ public function store(array $data): RuleGroup; - /** - * @param RuleGroup $ruleGroup - * @param array $data - * - * @return RuleGroup - */ public function update(RuleGroup $ruleGroup, array $data): RuleGroup; } diff --git a/app/Repositories/Tag/OperationsRepository.php b/app/Repositories/Tag/OperationsRepository.php index f4b66aed92..113c7f07fd 100644 --- a/app/Repositories/Tag/OperationsRepository.php +++ b/app/Repositories/Tag/OperationsRepository.php @@ -30,11 +30,8 @@ use FireflyIII\Models\TransactionType; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** - * * Class OperationsRepository */ class OperationsRepository implements OperationsRepositoryInterface @@ -46,15 +43,6 @@ class OperationsRepository implements OperationsRepositoryInterface * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period * which have the specified tag(s) 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 $tags - * - * @return array - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $tags = null): array { @@ -119,41 +107,17 @@ class OperationsRepository implements OperationsRepositoryInterface return $array; } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * @return Collection - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ - private function getTags(): Collection - { - $repository = app(TagRepositoryInterface::class); - - return $repository->get(); - } - /** * This method returns a list of all the deposit transaction journals (as arrays) set in that period * which have the specified tag(s) set to them. It's grouped per currency, with as few details in the array * as possible. Amounts are always positive. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $tags - * - * @return array - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $tags = null): array { @@ -222,12 +186,6 @@ class OperationsRepository implements OperationsRepositoryInterface /** * Sum of withdrawal journals in period for a set of tags, grouped per currency. Amounts are always negative. * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $tags - * - * @return array * @throws FireflyException */ public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $tags = null): array @@ -238,16 +196,17 @@ class OperationsRepository implements OperationsRepositoryInterface /** * Sum of income journals in period for a set of tags, grouped per currency. Amounts are always positive. * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $tags - * - * @return array * @throws FireflyException */ public function sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $tags = null): array { throw new FireflyException(sprintf('%s is not yet implemented.', __METHOD__)); } + + private function getTags(): Collection + { + $repository = app(TagRepositoryInterface::class); + + return $repository->get(); + } } diff --git a/app/Repositories/Tag/OperationsRepositoryInterface.php b/app/Repositories/Tag/OperationsRepositoryInterface.php index aecd897518..b0143054c7 100644 --- a/app/Repositories/Tag/OperationsRepositoryInterface.php +++ b/app/Repositories/Tag/OperationsRepositoryInterface.php @@ -30,8 +30,6 @@ use Illuminate\Support\Collection; /** * Interface OperationsRepositoryInterface - * - * @package FireflyIII\Repositories\Tag */ interface OperationsRepositoryInterface { @@ -39,13 +37,6 @@ interface OperationsRepositoryInterface * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period * which have the specified tag(s) 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 $tags - * - * @return array */ public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $tags = null): array; @@ -53,42 +44,18 @@ interface OperationsRepositoryInterface * This method returns a list of all the deposit transaction journals (as arrays) set in that period * which have the specified tag(s) set to them. It's grouped per currency, with as few details in the array * as possible. Amounts are always positive. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $tags - * - * @return array */ public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $tags = null): array; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; /** * Sum of withdrawal journals in period for a set of tags, grouped per currency. Amounts are always negative. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $tags - * - * @return array */ public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $tags = null): array; /** * Sum of income journals in period for a set of tags, grouped per currency. Amounts are always positive. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $tags - * - * @return array */ public function sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $tags = null): array; } diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 6b72491531..1e4411559d 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -24,8 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Tag; use Carbon\Carbon; -use DB; -use Exception; use FireflyIII\Factory\TagFactory; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Attachment; @@ -36,33 +34,25 @@ use FireflyIII\Models\TransactionType; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use Storage; /** * Class TagRepository. - * */ class TagRepository implements TagRepositoryInterface { private User $user; - /** - * @return int - */ public function count(): int { return $this->user->tags()->count(); } /** - * @param Tag $tag - * - * @return bool - * @throws Exception + * @throws \Exception */ public function destroy(Tag $tag): bool { - DB::table('tag_transaction_journal')->where('tag_id', $tag->id)->delete(); + \DB::table('tag_transaction_journal')->where('tag_id', $tag->id)->delete(); $tag->transactionJournals()->sync([]); $tag->delete(); @@ -75,28 +65,19 @@ class TagRepository implements TagRepositoryInterface public function destroyAll(): void { $tags = $this->get(); + /** @var Tag $tag */ foreach ($tags as $tag) { - DB::table('tag_transaction_journal')->where('tag_id', $tag->id)->delete(); + \DB::table('tag_transaction_journal')->where('tag_id', $tag->id)->delete(); $tag->delete(); } } - /** - * @return Collection - */ public function get(): Collection { return $this->user->tags()->orderBy('tag', 'ASC')->get(['tags.*']); } - /** - * @param Tag $tag - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ public function expenseInPeriod(Tag $tag, Carbon $start, Carbon $end): array { /** @var GroupCollectorInterface $collector */ @@ -108,60 +89,40 @@ class TagRepository implements TagRepositoryInterface return $collector->getExtractedJournals(); } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * @param int $tagId - * - * @return Tag|null - */ public function find(int $tagId): ?Tag { return $this->user->tags()->find($tagId); } - /** - * @param string $tag - * - * @return Tag|null - */ public function findByTag(string $tag): ?Tag { - /** @var Tag|null */ + // @var Tag|null return $this->user->tags()->where('tag', $tag)->first(); } - /** - * @param Tag $tag - * - * @return Carbon|null - */ public function firstUseDate(Tag $tag): ?Carbon { - /** @var Carbon|null */ + // @var Carbon|null return $tag->transactionJournals()->orderBy('date', 'ASC')->first()?->date; } - /** - * @inheritDoc - */ public function getAttachments(Tag $tag): Collection { $set = $tag->attachments()->get(); - /** @var Storage $disk */ - $disk = Storage::disk('upload'); + + /** @var \Storage $disk */ + $disk = \Storage::disk('upload'); return $set->each( - static function (Attachment $attachment) use ($disk) { - /** @var Note|null $note */ + static function (Attachment $attachment) use ($disk): void { + /** @var null|Note $note */ $note = $attachment->notes()->first(); // only used in v1 view of tags $attachment->file_exists = $disk->exists($attachment->fileName()); @@ -170,11 +131,6 @@ class TagRepository implements TagRepositoryInterface ); } - /** - * @param int|null $year - * - * @return array - */ public function getTagsInYear(?int $year): array { // get all tags in the year (if present): @@ -188,10 +144,11 @@ class TagRepository implements TagRepositoryInterface if (null !== $year) { app('log')->debug(sprintf('Get tags with year %s.', $year)); - $tagQuery->where('tags.date', '>=', $year . '-01-01 00:00:00')->where('tags.date', '<=', $year . '-12-31 23:59:59'); + $tagQuery->where('tags.date', '>=', $year.'-01-01 00:00:00')->where('tags.date', '<=', $year.'-12-31 23:59:59'); } $collection = $tagQuery->get(); $return = []; + /** @var Tag $tag */ foreach ($collection as $tag) { // return value for tag cloud: @@ -207,13 +164,6 @@ class TagRepository implements TagRepositoryInterface return $return; } - /** - * @param Tag $tag - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ public function incomeInPeriod(Tag $tag, Carbon $start, Carbon $end): array { /** @var GroupCollectorInterface $collector */ @@ -225,43 +175,29 @@ class TagRepository implements TagRepositoryInterface return $collector->getExtractedJournals(); } - /** - * @param Tag $tag - * - * @return Carbon|null - */ public function lastUseDate(Tag $tag): ?Carbon { - /** @var Carbon|null */ + // @var Carbon|null return $tag->transactionJournals()->orderBy('date', 'DESC')->first()?->date; } /** * Will return the newest tag (if known) or NULL. - * - * @return Tag|null */ public function newestTag(): ?Tag { - /** @var Tag|null */ + // @var Tag|null return $this->user->tags()->whereNotNull('date')->orderBy('date', 'DESC')->first(); } - /** - * @return Tag|null - */ public function oldestTag(): ?Tag { - /** @var Tag|null */ + // @var Tag|null return $this->user->tags()->whereNotNull('date')->orderBy('date', 'ASC')->first(); } /** * Find one or more tags based on the query. - * - * @param string $query - * - * @return Collection */ public function searchTag(string $query): Collection { @@ -272,11 +208,6 @@ class TagRepository implements TagRepositoryInterface /** * Search the users tags. - * - * @param string $query - * @param int $limit - * - * @return Collection */ public function searchTags(string $query, int $limit): Collection { @@ -290,11 +221,6 @@ class TagRepository implements TagRepositoryInterface return $tags->take($limit)->get('tags.*'); } - /** - * @param array $data - * - * @return Tag - */ public function store(array $data): Tag { /** @var TagFactory $factory */ @@ -304,14 +230,6 @@ class TagRepository implements TagRepositoryInterface return $factory->create($data); } - /** - * @param Tag $tag - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return array - * - */ public function sumsOfTag(Tag $tag, ?Carbon $start, ?Carbon $end): array { /** @var GroupCollectorInterface $collector */ @@ -374,9 +292,6 @@ class TagRepository implements TagRepositoryInterface return $sums; } - /** - * @inheritDoc - */ public function tagEndsWith(string $query): Collection { $search = sprintf('%%%s', $query); @@ -384,9 +299,6 @@ class TagRepository implements TagRepositoryInterface return $this->user->tags()->where('tag', 'LIKE', $search)->get(['tags.*']); } - /** - * @inheritDoc - */ public function tagStartsWith(string $query): Collection { $search = sprintf('%s%%', $query); @@ -394,13 +306,6 @@ class TagRepository implements TagRepositoryInterface return $this->user->tags()->where('tag', 'LIKE', $search)->get(['tags.*']); } - /** - * @param Tag $tag - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ public function transferredInPeriod(Tag $tag, Carbon $start, Carbon $end): array { /** @var GroupCollectorInterface $collector */ @@ -411,12 +316,6 @@ class TagRepository implements TagRepositoryInterface return $collector->getExtractedJournals(); } - /** - * @param Tag $tag - * @param array $data - * - * @return Tag - */ public function update(Tag $tag, array $data): Tag { if (array_key_exists('tag', $data)) { @@ -466,12 +365,9 @@ class TagRepository implements TagRepositoryInterface return $tag; } - /** - * @inheritDoc - */ public function getLocation(Tag $tag): ?Location { - /** @var Location|null */ + // @var Location|null return $tag->locations()->first(); } } diff --git a/app/Repositories/Tag/TagRepositoryInterface.php b/app/Repositories/Tag/TagRepositoryInterface.php index 6e667964d8..d81efa1cf4 100644 --- a/app/Repositories/Tag/TagRepositoryInterface.php +++ b/app/Repositories/Tag/TagRepositoryInterface.php @@ -35,17 +35,10 @@ use Illuminate\Support\Collection; */ interface TagRepositoryInterface { - /** - * @return int - */ public function count(): int; /** * This method destroys a tag. - * - * @param Tag $tag - * - * @return bool */ public function destroy(Tag $tag): bool; @@ -54,174 +47,78 @@ interface TagRepositoryInterface */ public function destroyAll(): void; - /** - * @param Tag $tag - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ public function expenseInPeriod(Tag $tag, Carbon $start, Carbon $end): array; - /** - * @param int $tagId - * - * @return Tag|null - */ public function find(int $tagId): ?Tag; - /** - * @param string $tag - * - * @return Tag|null - */ public function findByTag(string $tag): ?Tag; - /** - * @param Tag $tag - * - * @return Carbon|null - */ public function firstUseDate(Tag $tag): ?Carbon; /** * This method returns all the user's tags. - * - * @return Collection */ public function get(): Collection; - /** - * @param Tag $tag - * - * @return Collection - */ public function getAttachments(Tag $tag): Collection; /** * Return location, or NULL. - * - * @param Tag $tag - * - * @return Location|null */ public function getLocation(Tag $tag): ?Location; - /** - * @param int|null $year - * - * @return array - */ public function getTagsInYear(?int $year): array; - /** - * @param Tag $tag - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ public function incomeInPeriod(Tag $tag, Carbon $start, Carbon $end): array; - /** - * @param Tag $tag - * - * @return Carbon|null - */ public function lastUseDate(Tag $tag): ?Carbon; /** * Will return the newest tag (if known) or NULL. - * - * @return Tag|null */ public function newestTag(): ?Tag; /** * Will return the newest tag (if known) or NULL. - * - * @return Tag|null */ public function oldestTag(): ?Tag; /** * Find one or more tags based on the query. - * - * @param string $query - * - * @return Collection */ public function searchTag(string $query): Collection; /** * Search the users tags. - * - * @param string $query - * @param int $limit - * - * @return Collection */ public function searchTags(string $query, int $limit): Collection; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; /** * This method stores a tag. - * - * @param array $data - * - * @return Tag */ public function store(array $data): Tag; /** * Calculates various amounts in tag. - * - * @param Tag $tag - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return array */ public function sumsOfTag(Tag $tag, ?Carbon $start, ?Carbon $end): array; /** * Find one or more tags that start with the string in the query - * - * @param string $query - * - * @return Collection */ public function tagEndsWith(string $query): Collection; /** * Find one or more tags that start with the string in the query - * - * @param string $query - * - * @return Collection */ public function tagStartsWith(string $query): Collection; - /** - * @param Tag $tag - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ public function transferredInPeriod(Tag $tag, Carbon $start, Carbon $end): array; /** * Update a tag. - * - * @param Tag $tag - * @param array $data - * - * @return Tag */ public function update(Tag $tag, array $data): Tag; } diff --git a/app/Repositories/TransactionGroup/TransactionGroupRepository.php b/app/Repositories/TransactionGroup/TransactionGroupRepository.php index 7bd677d8e7..27613a0940 100644 --- a/app/Repositories/TransactionGroup/TransactionGroupRepository.php +++ b/app/Repositories/TransactionGroup/TransactionGroupRepository.php @@ -24,8 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Repositories\TransactionGroup; use Carbon\Carbon; -use DB; -use Exception; use FireflyIII\Exceptions\DuplicateTransactionException; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\TransactionGroupFactory; @@ -48,7 +46,6 @@ use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Collection; -use JsonException; /** * Class TransactionGroupRepository @@ -57,9 +54,6 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface { private User $user; - /** - * @inheritDoc - */ public function countAttachments(int $journalId): int { /** @var TransactionJournal $journal */ @@ -70,19 +64,12 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface /** * Find a transaction group by its ID. - * - * @param int $groupId - * - * @return TransactionGroup|null */ public function find(int $groupId): ?TransactionGroup { return $this->user->transactionGroups()->find($groupId); } - /** - * @param TransactionGroup $group - */ public function destroy(TransactionGroup $group): void { app('log')->debug(sprintf('Now in %s', __METHOD__)); @@ -90,13 +77,11 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface $service->destroy($group); } - /** - * @inheritDoc - */ public function expandGroup(TransactionGroup $group): array { $result = $group->toArray(); $result['transaction_journals'] = []; + /** @var TransactionJournal $journal */ foreach ($group->transactionJournals as $journal) { $result['transaction_journals'][] = $this->expandJournal($journal); @@ -105,62 +90,8 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface return $result; } - /** - * @param TransactionJournal $journal - * - * @return array - */ - private function expandJournal(TransactionJournal $journal): array - { - $array = $journal->toArray(); - $array['transactions'] = []; - $array['meta'] = $journal->transactionJournalMeta->toArray(); - $array['tags'] = $journal->tags->toArray(); - $array['categories'] = $journal->categories->toArray(); - $array['budgets'] = $journal->budgets->toArray(); - $array['notes'] = $journal->notes->toArray(); - $array['locations'] = []; - $array['attachments'] = $journal->attachments->toArray(); - $array['links'] = []; - $array['piggy_bank_events'] = $journal->piggyBankEvents->toArray(); - - /** @var Transaction $transaction */ - foreach ($journal->transactions as $transaction) { - $array['transactions'][] = $this->expandTransaction($transaction); - } - - return $array; - } - - /** - * @param Transaction $transaction - * - * @return array - */ - private function expandTransaction(Transaction $transaction): array - { - $array = $transaction->toArray(); - $array['account'] = $transaction->account->toArray(); - $array['budgets'] = []; - $array['categories'] = []; - - foreach ($transaction->categories as $category) { - $array['categories'][] = $category->toArray(); - } - - foreach ($transaction->budgets as $budget) { - $array['budgets'][] = $budget->toArray(); - } - - return $array; - } - /** * Return all attachments for all journals in the group. - * - * @param TransactionGroup $group - * - * @return array */ public function getAttachments(TransactionGroup $group): array { @@ -168,11 +99,13 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface $repository->setUser($this->user); $journals = $group->transactionJournals->pluck('id')->toArray(); $set = Attachment::whereIn('attachable_id', $journals) - ->where('attachable_type', TransactionJournal::class) - ->where('uploaded', true) - ->whereNull('deleted_at')->get(); + ->where('attachable_type', TransactionJournal::class) + ->where('uploaded', true) + ->whereNull('deleted_at')->get() + ; $result = []; + /** @var Attachment $attachment */ foreach ($set as $attachment) { $journalId = $attachment->attachable_id; @@ -188,10 +121,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface return $result; } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof user) { $this->user = $user; @@ -200,17 +130,14 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface /** * Get the note text for a journal (by ID). - * - * @param int $journalId - * - * @return string|null */ public function getNoteText(int $journalId): ?string { - /** @var Note|null $note */ + /** @var null|Note $note */ $note = Note::where('noteable_id', $journalId) - ->where('noteable_type', TransactionJournal::class) - ->first(); + ->where('noteable_type', TransactionJournal::class) + ->first() + ; if (null === $note) { return null; } @@ -220,24 +147,22 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface /** * Return all journal links for all journals in the group. - * - * @param TransactionGroup $group - * - * @return array */ public function getLinks(TransactionGroup $group): array { $return = []; $journals = $group->transactionJournals->pluck('id')->toArray(); $set = TransactionJournalLink::where( - static function (Builder $q) use ($journals) { + static function (Builder $q) use ($journals): void { $q->whereIn('source_id', $journals); $q->orWhereIn('destination_id', $journals); } ) - ->with(['source', 'destination', 'source.transactions']) - ->leftJoin('link_types', 'link_types.id', '=', 'journal_links.link_type_id') - ->get(['journal_links.*', 'link_types.inward', 'link_types.outward', 'link_types.editable']); + ->with(['source', 'destination', 'source.transactions']) + ->leftJoin('link_types', 'link_types.id', '=', 'journal_links.link_type_id') + ->get(['journal_links.*', 'link_types.inward', 'link_types.outward', 'link_types.editable']) + ; + /** @var TransactionJournalLink $entry */ foreach ($set as $entry) { $journalId = in_array($entry->source_id, $journals, true) ? $entry->source_id : $entry->destination_id; @@ -276,11 +201,197 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface return $return; } + public function getLocation(int $journalId): ?Location + { + /** @var TransactionJournal $journal */ + $journal = $this->user->transactionJournals()->find($journalId); + + return $journal->locations()->first(); + } + /** - * @param TransactionJournal $journal + * Return object with all found meta field things as Carbon objects. * - * @return string + * @throws \Exception */ + public function getMetaDateFields(int $journalId, array $fields): NullArrayObject + { + $query = \DB::table('journal_meta') + ->where('transaction_journal_id', $journalId) + ->whereIn('name', $fields) + ->whereNull('deleted_at') + ->get(['name', 'data']) + ; + $return = []; + + foreach ($query as $row) { + $return[$row->name] = new Carbon(json_decode($row->data, true, 512, JSON_THROW_ON_ERROR)); + } + + return new NullArrayObject($return); + } + + /** + * Return object with all found meta field things. + */ + public function getMetaFields(int $journalId, array $fields): NullArrayObject + { + $query = \DB::table('journal_meta') + ->where('transaction_journal_id', $journalId) + ->whereIn('name', $fields) + ->whereNull('deleted_at') + ->get(['name', 'data']) + ; + $return = []; + + foreach ($query as $row) { + $return[$row->name] = json_decode($row->data); + } + + return new NullArrayObject($return); + } + + /** + * Return all piggy bank events for all journals in the group. + * + * @throws FireflyException + */ + public function getPiggyEvents(TransactionGroup $group): array + { + $return = []; + $journals = $group->transactionJournals->pluck('id')->toArray(); + $currency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); + $data = PiggyBankEvent::whereIn('transaction_journal_id', $journals) + ->with('piggyBank', 'piggyBank.account') + ->get(['piggy_bank_events.*']) + ; + + /** @var PiggyBankEvent $row */ + foreach ($data as $row) { + if (null === $row->piggyBank) { + continue; + } + // get currency preference. + $currencyPreference = AccountMeta::where('account_id', $row->piggyBank->account_id) + ->where('name', 'currency_id') + ->first() + ; + if (null !== $currencyPreference) { + $currency = TransactionCurrency::where('id', $currencyPreference->data)->first(); + } + $journalId = $row->transaction_journal_id; + $return[$journalId] ??= []; + + $return[$journalId][] = [ + 'piggy' => $row->piggyBank->name, + 'piggy_id' => $row->piggy_bank_id, + 'amount' => app('amount')->formatAnything($currency, $row->amount), + ]; + } + + return $return; + } + + 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). + */ + public function getTags(int $journalId): array + { + $result = \DB::table('tag_transaction_journal') + ->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id') + ->where('tag_transaction_journal.transaction_journal_id', $journalId) + ->orderBy('tags.tag', 'ASC') + ->get(['tags.tag']) + ; + + return $result->pluck('tag')->toArray(); + } + + /** + * @throws DuplicateTransactionException + * @throws FireflyException + */ + public function store(array $data): TransactionGroup + { + /** @var TransactionGroupFactory $factory */ + $factory = app(TransactionGroupFactory::class); + $factory->setUser($this->user); + + try { + return $factory->create($data); + } catch (DuplicateTransactionException $e) { + app('log')->warning('Group repository caught group factory with a duplicate exception!'); + + throw new DuplicateTransactionException($e->getMessage(), 0, $e); + } catch (FireflyException $e) { + app('log')->warning('Group repository caught group factory with an exception!'); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); + + throw new FireflyException($e->getMessage(), 0, $e); + } + } + + /** + * @throws DuplicateTransactionException + * @throws FireflyException + */ + public function update(TransactionGroup $transactionGroup, array $data): TransactionGroup + { + /** @var GroupUpdateService $service */ + $service = app(GroupUpdateService::class); + + return $service->update($transactionGroup, $data); + } + + private function expandJournal(TransactionJournal $journal): array + { + $array = $journal->toArray(); + $array['transactions'] = []; + $array['meta'] = $journal->transactionJournalMeta->toArray(); + $array['tags'] = $journal->tags->toArray(); + $array['categories'] = $journal->categories->toArray(); + $array['budgets'] = $journal->budgets->toArray(); + $array['notes'] = $journal->notes->toArray(); + $array['locations'] = []; + $array['attachments'] = $journal->attachments->toArray(); + $array['links'] = []; + $array['piggy_bank_events'] = $journal->piggyBankEvents->toArray(); + + /** @var Transaction $transaction */ + foreach ($journal->transactions as $transaction) { + $array['transactions'][] = $this->expandTransaction($transaction); + } + + return $array; + } + + private function expandTransaction(Transaction $transaction): array + { + $array = $transaction->toArray(); + $array['account'] = $transaction->account->toArray(); + $array['budgets'] = []; + $array['categories'] = []; + + foreach ($transaction->categories as $category) { + $array['categories'][] = $category->toArray(); + } + + foreach ($transaction->budgets as $budget) { + $array['budgets'][] = $budget->toArray(); + } + + return $array; + } + private function getFormattedAmount(TransactionJournal $journal): string { /** @var Transaction $transaction */ @@ -299,11 +410,6 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface return $return; } - /** - * @param TransactionJournal $journal - * - * @return string - */ private function getFormattedForeignAmount(TransactionJournal $journal): string { /** @var Transaction $transaction */ @@ -327,178 +433,4 @@ 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. - * - * @param int $journalId - * @param array $fields - * - * @return NullArrayObject - * @throws Exception - */ - public function getMetaDateFields(int $journalId, array $fields): NullArrayObject - { - $query = DB::table('journal_meta') - ->where('transaction_journal_id', $journalId) - ->whereIn('name', $fields) - ->whereNull('deleted_at') - ->get(['name', 'data']); - $return = []; - - foreach ($query as $row) { - $return[$row->name] = new Carbon(json_decode($row->data, true, 512, JSON_THROW_ON_ERROR)); - } - - return new NullArrayObject($return); - } - - /** - * Return object with all found meta field things. - * - * @param int $journalId - * @param array $fields - * - * @return NullArrayObject - */ - public function getMetaFields(int $journalId, array $fields): NullArrayObject - { - $query = DB::table('journal_meta') - ->where('transaction_journal_id', $journalId) - ->whereIn('name', $fields) - ->whereNull('deleted_at') - ->get(['name', 'data']); - $return = []; - - foreach ($query as $row) { - $return[$row->name] = json_decode($row->data); - } - - return new NullArrayObject($return); - } - - /** - * Return all piggy bank events for all journals in the group. - * - * @param TransactionGroup $group - * - * @return array - * @throws FireflyException - * @throws JsonException - */ - public function getPiggyEvents(TransactionGroup $group): array - { - $return = []; - $journals = $group->transactionJournals->pluck('id')->toArray(); - $currency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); - $data = PiggyBankEvent::whereIn('transaction_journal_id', $journals) - ->with('piggyBank', 'piggyBank.account') - ->get(['piggy_bank_events.*']); - /** @var PiggyBankEvent $row */ - foreach ($data as $row) { - if (null === $row->piggyBank) { - continue; - } - // get currency preference. - $currencyPreference = AccountMeta::where('account_id', $row->piggyBank->account_id) - ->where('name', 'currency_id') - ->first(); - if (null !== $currencyPreference) { - $currency = TransactionCurrency::where('id', $currencyPreference->data)->first(); - } - $journalId = $row->transaction_journal_id; - $return[$journalId] ??= []; - - $return[$journalId][] = [ - 'piggy' => $row->piggyBank->name, - 'piggy_id' => $row->piggy_bank_id, - 'amount' => app('amount')->formatAnything($currency, $row->amount), - ]; - } - - 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). - * - * @param int $journalId - * - * @return array - */ - public function getTags(int $journalId): array - { - $result = DB::table('tag_transaction_journal') - ->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id') - ->where('tag_transaction_journal.transaction_journal_id', $journalId) - ->orderBy('tags.tag', 'ASC') - ->get(['tags.tag']); - - return $result->pluck('tag')->toArray(); - } - - /** - * @param array $data - * - * @return TransactionGroup - * @throws DuplicateTransactionException - * @throws FireflyException - * @throws JsonException - */ - public function store(array $data): TransactionGroup - { - /** @var TransactionGroupFactory $factory */ - $factory = app(TransactionGroupFactory::class); - $factory->setUser($this->user); - try { - return $factory->create($data); - } catch (DuplicateTransactionException $e) { - app('log')->warning('Group repository caught group factory with a duplicate exception!'); - throw new DuplicateTransactionException($e->getMessage(), 0, $e); - } catch (FireflyException $e) { - app('log')->warning('Group repository caught group factory with an exception!'); - app('log')->error($e->getMessage()); - app('log')->error($e->getTraceAsString()); - throw new FireflyException($e->getMessage(), 0, $e); - } - } - - /** - * @param TransactionGroup $transactionGroup - * @param array $data - * - * @return TransactionGroup - * - * @throws DuplicateTransactionException - * @throws FireflyException - */ - public function update(TransactionGroup $transactionGroup, array $data): TransactionGroup - { - /** @var GroupUpdateService $service */ - $service = app(GroupUpdateService::class); - - return $service->update($transactionGroup, $data); - } } diff --git a/app/Repositories/TransactionGroup/TransactionGroupRepositoryInterface.php b/app/Repositories/TransactionGroup/TransactionGroupRepositoryInterface.php index 8056677ee9..dfe1405ab7 100644 --- a/app/Repositories/TransactionGroup/TransactionGroupRepositoryInterface.php +++ b/app/Repositories/TransactionGroup/TransactionGroupRepositoryInterface.php @@ -37,130 +37,70 @@ use Illuminate\Support\Collection; */ interface TransactionGroupRepositoryInterface { - /** - * @param int $journalId - * - * @return int - */ public function countAttachments(int $journalId): int; - /** - * @param TransactionGroup $group - */ public function destroy(TransactionGroup $group): void; /** * Return a group and expand all meta data etc. - * - * @param TransactionGroup $group - * - * @return array */ public function expandGroup(TransactionGroup $group): array; /** * Find a transaction group by its ID. - * - * @param int $groupId - * - * @return TransactionGroup|null */ public function find(int $groupId): ?TransactionGroup; /** * Return all attachments for all journals in the group. - * - * @param TransactionGroup $group - * - * @return array */ public function getAttachments(TransactionGroup $group): array; /** * Return all journal links for all journals in the group. - * - * @param TransactionGroup $group - * - * @return array */ 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. - * - * @param int $journalId - * @param array $fields - * - * @return NullArrayObject */ public function getMetaDateFields(int $journalId, array $fields): NullArrayObject; /** * Return object with all found meta field things. - * - * @param int $journalId - * @param array $fields - * - * @return NullArrayObject */ public function getMetaFields(int $journalId, array $fields): NullArrayObject; /** * Get the note text for a journal (by ID). - * - * @param int $journalId - * - * @return string|null */ public function getNoteText(int $journalId): ?string; /** * Return all piggy bank events for all journals in the group. - * - * @param TransactionGroup $group - * - * @return array */ public function getPiggyEvents(TransactionGroup $group): array; /** * Get the tags for a journal (by ID) as Tag objects. - * - * @param int $journalId - * - * @return Collection */ 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; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; /** * Create a new transaction group. * - * @param array $data - * - * @return TransactionGroup * @throws DuplicateTransactionException * @throws FireflyException */ @@ -168,11 +108,6 @@ interface TransactionGroupRepositoryInterface /** * Update an existing transaction group. - * - * @param TransactionGroup $transactionGroup - * @param array $data - * - * @return TransactionGroup */ public function update(TransactionGroup $transactionGroup, array $data): TransactionGroup; } diff --git a/app/Repositories/TransactionType/TransactionTypeRepository.php b/app/Repositories/TransactionType/TransactionTypeRepository.php index 529c7f7bb0..3219b15a97 100644 --- a/app/Repositories/TransactionType/TransactionTypeRepository.php +++ b/app/Repositories/TransactionType/TransactionTypeRepository.php @@ -31,12 +31,6 @@ use Illuminate\Support\Collection; */ class TransactionTypeRepository implements TransactionTypeRepositoryInterface { - /** - * @param TransactionType|null $type - * @param string|null $typeString - * - * @return TransactionType - */ public function findTransactionType(?TransactionType $type, ?string $typeString): TransactionType { app('log')->debug('Now looking for a transaction type.'); @@ -55,11 +49,6 @@ class TransactionTypeRepository implements TransactionTypeRepositoryInterface return $search; } - /** - * @param string $type - * - * @return TransactionType|null - */ public function findByType(string $type): ?TransactionType { $search = ucfirst($type); @@ -67,12 +56,6 @@ class TransactionTypeRepository implements TransactionTypeRepositoryInterface return TransactionType::whereType($search)->first(); } - /** - * @param string $query - * @param int $limit - * - * @return Collection - */ public function searchTypes(string $query, int $limit): Collection { if ('' === $query) { diff --git a/app/Repositories/TransactionType/TransactionTypeRepositoryInterface.php b/app/Repositories/TransactionType/TransactionTypeRepositoryInterface.php index ff4c4b296f..862111f101 100644 --- a/app/Repositories/TransactionType/TransactionTypeRepositoryInterface.php +++ b/app/Repositories/TransactionType/TransactionTypeRepositoryInterface.php @@ -31,26 +31,9 @@ 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 - * - * @return TransactionType - */ public function findTransactionType(?TransactionType $type, ?string $typeString): TransactionType; - /** - * @param string $query - * @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 b2430d05ed..b645719c88 100644 --- a/app/Repositories/User/UserRepository.php +++ b/app/Repositories/User/UserRepository.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Repositories\User; -use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\GroupMembership; @@ -34,11 +33,9 @@ use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\QueryException; use Illuminate\Support\Collection; -use Str; /** * Class UserRepository. - * */ class UserRepository implements UserRepositoryInterface { @@ -46,13 +43,9 @@ class UserRepository implements UserRepositoryInterface * This updates the users email address and records some things so it can be confirmed or undone later. * The user is blocked until the change is confirmed. * - * @param User $user - * @param string $newEmail + * @throws \Exception * - * @return bool - * @throws Exception * @see updateEmail - * */ public function changeEmail(User $user, string $newEmail): bool { @@ -60,7 +53,7 @@ class UserRepository implements UserRepositoryInterface // save old email as pref app('preferences')->setForUser($user, 'previous_email_latest', $oldEmail); - app('preferences')->setForUser($user, 'previous_email_' . date('Y-m-d-H-i-s'), $oldEmail); + app('preferences')->setForUser($user, 'previous_email_'.date('Y-m-d-H-i-s'), $oldEmail); // set undo and confirm token: app('preferences')->setForUser($user, 'email_change_undo_token', bin2hex(random_bytes(16))); @@ -75,12 +68,6 @@ class UserRepository implements UserRepositoryInterface return true; } - /** - * @param User $user - * @param string $password - * - * @return bool - */ public function changePassword(User $user, string $password): bool { $user->password = bcrypt($password); @@ -89,13 +76,6 @@ class UserRepository implements UserRepositoryInterface return true; } - /** - * @param User $user - * @param bool $isBlocked - * @param string $code - * - * @return bool - */ public function changeStatus(User $user, bool $isBlocked, string $code): bool { // change blocked status and code: @@ -106,21 +86,11 @@ class UserRepository implements UserRepositoryInterface return true; } - /** - * @param string $name - * @param string $displayName - * @param string $description - * - * @return Role - */ public function createRole(string $name, string $displayName, string $description): Role { return Role::create(['name' => $name, 'display_name' => $displayName, 'description' => $description]); } - /** - * @inheritDoc - */ public function deleteInvite(InvitedUser $invite): void { app('log')->debug(sprintf('Deleting invite #%d', $invite->id)); @@ -128,10 +98,7 @@ class UserRepository implements UserRepositoryInterface } /** - * @param User $user - * - * @return bool - * @throws Exception + * @throws \Exception */ public function destroy(User $user): bool { @@ -144,12 +111,10 @@ class UserRepository implements UserRepositoryInterface return true; } - /** - * @inheritDoc - */ public function deleteEmptyGroups(): void { $groups = UserGroup::get(); + /** @var UserGroup $group */ foreach ($groups as $group) { $count = $group->groupMemberships()->count(); @@ -160,27 +125,16 @@ class UserRepository implements UserRepositoryInterface } } - /** - * @return int - */ public function count(): int { return $this->all()->count(); } - /** - * @return Collection - */ public function all(): Collection { return User::orderBy('id', 'DESC')->get(['users.*']); } - /** - * @param string $email - * - * @return User|null - */ public function findByEmail(string $email): ?User { return User::where('email', $email)->first(); @@ -188,30 +142,20 @@ class UserRepository implements UserRepositoryInterface /** * Returns the first user in the DB. Generally only works when there is just one. - * - * @return null|User */ public function first(): ?User { return User::orderBy('id', 'ASC')->first(); } - /** - * @inheritDoc - */ public function getInvitedUsers(): Collection { return InvitedUser::with('user')->get(); } - /** - * @param User $user - * - * @return string|null - */ public function getRoleByUser(User $user): ?string { - /** @var Role|null $role */ + /** @var null|Role $role */ $role = $user->roles()->first(); if (null !== $role) { return $role->name; @@ -221,31 +165,27 @@ class UserRepository implements UserRepositoryInterface } /** - * @inheritDoc * @throws FireflyException */ public function getRolesInGroup(User $user, int $groupId): array { - /** @var UserGroup|null $group */ + /** @var null|UserGroup $group */ $group = UserGroup::find($groupId); if (null === $group) { throw new FireflyException(sprintf('Could not find group #%d', $groupId)); } $memberships = $group->groupMemberships()->where('user_id', $user->id)->get(); $roles = []; + /** @var GroupMembership $membership */ foreach ($memberships as $membership) { $role = $membership->userRole; $roles[] = $role->title; } + return $roles; } - /** - * @param int $userId - * - * @return User|null - */ public function find(int $userId): ?User { return User::find($userId); @@ -253,17 +193,13 @@ class UserRepository implements UserRepositoryInterface /** * Return basic user information. - * - * @param User $user - * - * @return array */ public function getUserData(User $user): array { $return = []; // two factor: - $return['has_2fa'] = $user->mfa_secret !== null; + $return['has_2fa'] = null !== $user->mfa_secret; $return['is_admin'] = $this->hasRole($user, 'owner'); $return['blocked'] = 1 === (int)$user->blocked; $return['blocked_code'] = $user->blocked_code; @@ -276,11 +212,12 @@ class UserRepository implements UserRepositoryInterface $return['categories'] = $user->categories()->count(); $return['budgets'] = $user->budgets()->count(); $return['budgets_with_limits'] = BudgetLimit::distinct() - ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') - ->where('amount', '>', 0) - ->whereNull('budgets.deleted_at') - ->where('budgets.user_id', $user->id) - ->count('budget_limits.budget_id'); + ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') + ->where('amount', '>', 0) + ->whereNull('budgets.deleted_at') + ->where('budgets.user_id', $user->id) + ->count('budget_limits.budget_id') + ; $return['rule_groups'] = $user->ruleGroups()->count(); $return['rules'] = $user->rules()->count(); $return['tags'] = $user->tags()->count(); @@ -288,13 +225,7 @@ class UserRepository implements UserRepositoryInterface return $return; } - /** - * @param User|Authenticatable|null $user - * @param string $role - * - * @return bool - */ - public function hasRole(User | Authenticatable | null $user, string $role): bool + public function hasRole(null|Authenticatable|User $user, string $role): bool { if (null === $user) { return false; @@ -311,16 +242,13 @@ class UserRepository implements UserRepositoryInterface return false; } - /** - * @inheritDoc - */ - public function inviteUser(User | Authenticatable | null $user, string $email): InvitedUser + public function inviteUser(null|Authenticatable|User $user, string $email): InvitedUser { $now = today(config('app.timezone')); $now->addDays(2); $invitee = new InvitedUser(); $invitee->user()->associate($user); - $invitee->invite_code = Str::random(64); + $invitee->invite_code = \Str::random(64); $invitee->email = $email; $invitee->redeemed = false; $invitee->expires = $now; @@ -329,9 +257,6 @@ class UserRepository implements UserRepositoryInterface return $invitee; } - /** - * @inheritDoc - */ public function redeemCode(string $code): void { $obj = InvitedUser::where('invite_code', $code)->where('redeemed', 0)->first(); @@ -343,9 +268,6 @@ class UserRepository implements UserRepositoryInterface /** * Set MFA code. - * - * @param User $user - * @param string|null $code */ public function setMFACode(User $user, ?string $code): void { @@ -353,11 +275,6 @@ class UserRepository implements UserRepositoryInterface $user->save(); } - /** - * @param array $data - * - * @return User - */ public function store(array $data): User { $user = User::create( @@ -365,7 +282,7 @@ class UserRepository implements UserRepositoryInterface 'blocked' => $data['blocked'] ?? false, 'blocked_code' => $data['blocked_code'] ?? null, 'email' => $data['email'], - 'password' => Str::random(24), + 'password' => \Str::random(24), ] ); $role = $data['role'] ?? ''; @@ -376,12 +293,6 @@ class UserRepository implements UserRepositoryInterface return $user; } - /** - * @param User $user - * @param string $role - * - * @return bool - */ public function attachRole(User $user, string $role): bool { $roleObject = Role::where('name', $role)->first(); @@ -401,9 +312,6 @@ class UserRepository implements UserRepositoryInterface return true; } - /** - * @param User $user - */ public function unblockUser(User $user): void { $user->blocked = false; @@ -414,10 +322,6 @@ class UserRepository implements UserRepositoryInterface /** * Update user info. * - * @param User $user - * @param array $data - * - * @return User * @throws FireflyException */ public function update(User $user, array $data): User @@ -443,11 +347,8 @@ class UserRepository implements UserRepositoryInterface * This updates the users email address. Same as changeEmail just without most logging. This makes sure that the * undo/confirm routine can't catch this one. The user is NOT blocked. * - * @param User $user - * @param string $newEmail - * - * @return bool * @throws FireflyException + * * @see changeEmail */ public function updateEmail(User $user, string $newEmail): bool @@ -459,7 +360,7 @@ class UserRepository implements UserRepositoryInterface // save old email as pref app('preferences')->setForUser($user, 'admin_previous_email_latest', $oldEmail); - app('preferences')->setForUser($user, 'admin_previous_email_' . date('Y-m-d-H-i-s'), $oldEmail); + app('preferences')->setForUser($user, 'admin_previous_email_'.date('Y-m-d-H-i-s'), $oldEmail); $user->email = $newEmail; $user->save(); @@ -469,9 +370,6 @@ class UserRepository implements UserRepositoryInterface /** * Remove any role the user has. - * - * @param User $user - * @param string $role */ public function removeRole(User $user, string $role): void { @@ -482,23 +380,16 @@ class UserRepository implements UserRepositoryInterface $user->roles()->detach($roleObj->id); } - /** - * @param string $role - * - * @return Role|null - */ public function getRole(string $role): ?Role { return Role::where('name', $role)->first(); } - /** - * @inheritDoc - */ public function validateInviteCode(string $code): bool { $now = today(config('app.timezone')); $invitee = InvitedUser::where('invite_code', $code)->where('expires', '>', $now->format('Y-m-d H:i:s'))->where('redeemed', 0)->first(); + return null !== $invitee; } } diff --git a/app/Repositories/User/UserRepositoryInterface.php b/app/Repositories/User/UserRepositoryInterface.php index 3803d385ce..a76e8d770e 100644 --- a/app/Repositories/User/UserRepositoryInterface.php +++ b/app/Repositories/User/UserRepositoryInterface.php @@ -36,18 +36,11 @@ interface UserRepositoryInterface { /** * Returns a collection of all users. - * - * @return Collection */ public function all(): Collection; /** * Gives a user a role. - * - * @param User $user - * @param string $role - * - * @return bool */ public function attachRole(User $user, string $role): bool; @@ -55,182 +48,74 @@ interface UserRepositoryInterface * This updates the users email address and records some things so it can be confirmed or undone later. * The user is blocked until the change is confirmed. * - * @param User $user - * @param string $newEmail - * - * @return bool * @see updateEmail - * */ public function changeEmail(User $user, string $newEmail): bool; /** - * @param User $user - * @param string $password - * * @return mixed */ public function changePassword(User $user, string $password); - /** - * @param User $user - * @param bool $isBlocked - * @param string $code - * - * @return bool - */ public function changeStatus(User $user, bool $isBlocked, string $code): bool; /** * Returns a count of all users. - * - * @return int */ public function count(): int; - /** - * @param string $name - * @param string $displayName - * @param string $description - * - * @return Role - */ public function createRole(string $name, string $displayName, string $description): Role; - /** - * - */ public function deleteEmptyGroups(): void; - /** - * @param InvitedUser $invite - * - * @return void - */ public function deleteInvite(InvitedUser $invite): void; - /** - * @param User $user - * - * @return bool - */ public function destroy(User $user): bool; - /** - * @param int $userId - * - * @return User|null - */ public function find(int $userId): ?User; - /** - * @param string $email - * - * @return User|null - */ public function findByEmail(string $email): ?User; /** * Returns the first user in the DB. Generally only works when there is just one. - * - * @return null|User */ public function first(): ?User; - /** - * @return Collection - */ public function getInvitedUsers(): Collection; - /** - * @param string $role - * - * @return Role|null - */ public function getRole(string $role): ?Role; - /** - * @param User $user - * - * @return string|null - */ public function getRoleByUser(User $user): ?string; - /** - * @param User $user - * @param int $groupId - * - * @return array - */ public function getRolesInGroup(User $user, int $groupId): array; /** * Return basic user information. - * - * @param User $user - * - * @return array */ public function getUserData(User $user): array; - /** - * @param User|Authenticatable|null $user - * @param string $role - * - * @return bool - */ - public function hasRole(User | Authenticatable | null $user, string $role): bool; + public function hasRole(null|Authenticatable|User $user, string $role): bool; - /** - * @param User|Authenticatable|null $user - * @param string $email - * - * @return InvitedUser - */ - public function inviteUser(User | Authenticatable | null $user, string $email): InvitedUser; + public function inviteUser(null|Authenticatable|User $user, string $email): InvitedUser; - /** - * @param string $code - * - * @return void - */ public function redeemCode(string $code): void; /** * Remove any role the user has. - * - * @param User $user - * @param string $role */ public function removeRole(User $user, string $role): void; /** * Set MFA code. - * - * @param User $user - * @param string|null $code */ public function setMFACode(User $user, ?string $code): void; - /** - * @param array $data - * - * @return User - */ public function store(array $data): User; - /** - * @param User $user - */ public function unblockUser(User $user): void; /** * Update user info. - * - * @param User $user - * @param array $data - * - * @return User */ public function update(User $user, array $data): User; @@ -238,19 +123,9 @@ interface UserRepositoryInterface * This updates the users email address. Same as changeEmail just without most logging. This makes sure that the * undo/confirm routine can't catch this one. The user is NOT blocked. * - * @param User $user - * @param string $newEmail - * - * @return bool * @see changeEmail - * */ public function updateEmail(User $user, string $newEmail): bool; - /** - * @param string $code - * - * @return bool - */ public function validateInviteCode(string $code): bool; } diff --git a/app/Repositories/UserGroup/UserGroupRepository.php b/app/Repositories/UserGroup/UserGroupRepository.php index a12b5da7d1..b92a50aa3d 100644 --- a/app/Repositories/UserGroup/UserGroupRepository.php +++ b/app/Repositories/UserGroup/UserGroupRepository.php @@ -1,6 +1,5 @@ debug(sprintf('Going to destroy user group #%d ("%s").', $userGroup->id, $userGroup->title)); $memberships = $userGroup->groupMemberships()->get(); + /** @var GroupMembership $membership */ foreach ($memberships as $membership) { - /** @var User|null $user */ + /** @var null|User $user */ $user = $membership->user()->first(); if (null === $user) { continue; @@ -70,6 +66,7 @@ class UserGroupRepository implements UserGroupRepositoryInterface // user has other memberships, select one at random and assign it to the user. if ($count > 0) { app('log')->debug('User has other memberships and will be assigned a new administration.'); + /** @var GroupMembership $first */ $first = $user->groupMemberships()->where('user_group_id', '!=', $userGroup->id)->inRandomOrder()->first(); $user->user_group_id = $first->id; @@ -82,10 +79,10 @@ class UserGroupRepository implements UserGroupRepositoryInterface // time to DESTROY all objects. // we have to do this one by one to trigger the necessary observers :( $objects = ['availableBudgets', 'bills', 'budgets', 'categories', 'currencyExchangeRates', 'objectGroups', - 'recurrences', 'rules', 'ruleGroups', 'tags', 'transactionGroups', 'transactionJournals', 'piggyBanks', 'accounts', 'webhooks', + 'recurrences', 'rules', 'ruleGroups', 'tags', 'transactionGroups', 'transactionJournals', 'piggyBanks', 'accounts', 'webhooks', ]; foreach ($objects as $object) { - foreach ($userGroup->$object()->get() as $item) { // @phpstan-ignore-line + foreach ($userGroup->{$object}()->get() as $item) { // @phpstan-ignore-line $item->delete(); } } @@ -96,91 +93,54 @@ class UserGroupRepository implements UserGroupRepositoryInterface /** * Returns all groups the user is member in. * - * @inheritDoc + * {@inheritDoc} */ public function get(): Collection { $collection = new Collection(); $memberships = $this->user->groupMemberships()->get(); + /** @var GroupMembership $membership */ foreach ($memberships as $membership) { - /** @var UserGroup|null $group */ + /** @var null|UserGroup $group */ $group = $membership->userGroup()->first(); if (null !== $group) { $collection->push($group); } } + return $collection; } - /** - * Because there is the chance that a group with this name already exists, - * Firefly III runs a little loop of combinations to make sure the group name is unique. - * - * @param User $user - * - * @return UserGroup - */ - private function createNewUserGroup(User $user): UserGroup - { - $loop = 0; - $groupName = $user->email; - $exists = true; - $existingGroup = null; - while ($exists && $loop < 10) { - $existingGroup = $this->findByName($groupName); - if (null === $existingGroup) { - $exists = false; - /** @var UserGroup|null $existingGroup */ - $existingGroup = $this->store(['user' => $user, 'title' => $groupName]); - } - if (null !== $existingGroup) { - // group already exists - $groupName = sprintf('%s-%s', $user->email, substr(sha1((rand(1000, 9999) . microtime())), 0, 4)); - } - $loop++; - } - return $existingGroup; - } - - /** - * @param string $title - * - * @return UserGroup|null - */ public function findByName(string $title): ?UserGroup { return UserGroup::whereTitle($title)->first(); } /** - * @param array $data - * - * @return UserGroup * @throws FireflyException */ public function store(array $data): UserGroup { $data['user'] = $this->user; + /** @var UserGroupFactory $factory */ $factory = app(UserGroupFactory::class); + return $factory->create($data); } /** * Returns all groups. * - * @inheritDoc + * {@inheritDoc} */ public function getAll(): Collection { return UserGroup::all(); } - /** - * @inheritDoc - */ - public function setUser(Authenticatable | User | null $user): void + public function setUser(null|Authenticatable|User $user): void { app('log')->debug(sprintf('Now in %s', __METHOD__)); if ($user instanceof User) { @@ -188,39 +148,40 @@ class UserGroupRepository implements UserGroupRepositoryInterface } } - /** - * @inheritDoc - */ public function update(UserGroup $userGroup, array $data): UserGroup { $userGroup->title = $data['title']; $userGroup->save(); + return $userGroup; } /** - * @inheritDoc + * @SuppressWarnings(PHPMD.NPathComplexity) + * * @throws FireflyException */ public function updateMembership(UserGroup $userGroup, array $data): UserGroup { $owner = UserRole::whereTitle(UserRoleEnum::OWNER)->first(); app('log')->debug('in update membership'); - /** @var User|null $user */ + + /** @var null|User $user */ $user = null; if (array_key_exists('id', $data)) { - /** @var User|null $user */ + /** @var null|User $user */ $user = User::find($data['id']); app('log')->debug('Found user by ID'); } if (array_key_exists('email', $data) && '' !== (string)$data['email']) { - /** @var User|null $user */ + /** @var null|User $user */ $user = User::whereEmail($data['email'])->first(); app('log')->debug('Found user by email'); } if (null === $user) { // should throw error, but validator already catches this. app('log')->debug('No user found'); + return $userGroup; } // count the number of members in the group right now: @@ -232,26 +193,30 @@ class UserGroupRepository implements UserGroupRepositoryInterface // if this is also the user we're editing right now, and we remove all of their roles: if ($lastUserId === (int)$user->id && 0 === count($data['roles'])) { app('log')->debug('User is last in this group, refuse to act'); + throw new FireflyException('You cannot remove the last member from this user group. Delete the user group instead.'); } // if this is also the user we're editing right now, and do not grant them the owner role: if ($lastUserId === (int)$user->id && count($data['roles']) > 0 && !in_array(UserRoleEnum::OWNER->value, $data['roles'], true)) { app('log')->debug('User needs to have owner role in this group, refuse to act'); + throw new FireflyException('The last member in this user group must get or keep the "owner" role.'); } } if ($membershipCount > 1) { // group has multiple members. How many are owner, except the user we're editing now? $ownerCount = $userGroup->groupMemberships() - ->where('user_role_id', $owner->id) - ->where('user_id', '!=', $user->id)->count(); + ->where('user_role_id', $owner->id) + ->where('user_id', '!=', $user->id)->count() + ; // if there are no other owners and the current users does not get or keep the owner role, refuse. if ( - 0 === $ownerCount && - (0 === count($data['roles']) || - (count($data['roles']) > 0 && // @phpstan-ignore-line - !in_array(UserRoleEnum::OWNER->value, $data['roles'], true)))) { + 0 === $ownerCount + && (0 === count($data['roles']) + || (count($data['roles']) > 0 // @phpstan-ignore-line + && !in_array(UserRoleEnum::OWNER->value, $data['roles'], true)))) { app('log')->debug('User needs to keep owner role in this group, refuse to act'); + throw new FireflyException('The last owner in this user group must keep the "owner" role.'); } } @@ -263,31 +228,58 @@ class UserGroupRepository implements UserGroupRepositoryInterface foreach ($rolesSimplified as $role) { try { $enum = UserRoleEnum::from($role); - } catch (ValueError $e) { + } catch (\ValueError $e) { // TODO error message continue; } $userRole = UserRole::whereTitle($enum->value)->first(); $user->groupMemberships()->create(['user_group_id' => $userGroup->id, 'user_role_id' => $userRole->id]); } + return $userGroup; } /** - * @param array $roles - * - * @return array + * Because there is the chance that a group with this name already exists, + * Firefly III runs a little loop of combinations to make sure the group name is unique. */ + private function createNewUserGroup(User $user): UserGroup + { + $loop = 0; + $groupName = $user->email; + $exists = true; + $existingGroup = null; + while ($exists && $loop < 10) { + $existingGroup = $this->findByName($groupName); + if (null === $existingGroup) { + $exists = false; + + /** @var null|UserGroup $existingGroup */ + $existingGroup = $this->store(['user' => $user, 'title' => $groupName]); + } + if (null !== $existingGroup) { + // group already exists + $groupName = sprintf('%s-%s', $user->email, substr(sha1(rand(1000, 9999).microtime()), 0, 4)); + } + ++$loop; + } + + return $existingGroup; + } + private function simplifyListByName(array $roles): array { if (in_array(UserRoleEnum::OWNER->value, $roles, true)) { app('log')->debug(sprintf('List of roles is [%1$s] but this includes "%2$s", so return [%2$s]', implode(',', $roles), UserRoleEnum::OWNER->value)); + return [UserRoleEnum::OWNER->value]; } if (in_array(UserRoleEnum::FULL->value, $roles, true)) { app('log')->debug(sprintf('List of roles is [%1$s] but this includes "%2$s", so return [%2$s]', implode(',', $roles), UserRoleEnum::FULL->value)); + return [UserRoleEnum::FULL->value]; } + return $roles; } } diff --git a/app/Repositories/UserGroup/UserGroupRepositoryInterface.php b/app/Repositories/UserGroup/UserGroupRepositoryInterface.php index 0cc3bf1cf6..2d2789804e 100644 --- a/app/Repositories/UserGroup/UserGroupRepositoryInterface.php +++ b/app/Repositories/UserGroup/UserGroupRepositoryInterface.php @@ -1,6 +1,5 @@ userGroup @@ -50,27 +46,23 @@ class AccountRepository implements AccountRepositoryInterface ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') ->where('accounts.active', true) ->where( - static function (EloquentBuilder $q1) use ($number) { // @phpstan-ignore-line + static function (EloquentBuilder $q1) use ($number): void { // @phpstan-ignore-line $json = json_encode($number); $q1->where('account_meta.name', '=', 'account_number'); $q1->where('account_meta.data', '=', $json); } - ); + ) + ; if (0 !== count($types)) { $dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); $dbQuery->whereIn('account_types.type', $types); } - /** @var Account|null */ + + // @var Account|null return $dbQuery->first(['accounts.*']); } - /** - * @param string $iban - * @param array $types - * - * @return Account|null - */ public function findByIbanNull(string $iban, array $types): ?Account { $query = $this->userGroup->accounts()->where('iban', '!=', '')->whereNotNull('iban'); @@ -80,13 +72,10 @@ class AccountRepository implements AccountRepositoryInterface $query->whereIn('account_types.type', $types); } - /** @var Account|null */ + // @var Account|null return $query->where('iban', $iban)->first(['accounts.*']); } - /** - * @inheritDoc - */ public function findByName(string $name, array $types): ?Account { $query = $this->userGroup->accounts(); @@ -98,7 +87,8 @@ class AccountRepository implements AccountRepositoryInterface app('log')->debug(sprintf('Searching for account named "%s" (of user #%d) of the following type(s)', $name, $this->user->id), ['types' => $types]); $query->where('accounts.name', $name); - /** @var Account|null $account */ + + /** @var null|Account $account */ $account = $query->first(['accounts.*']); if (null === $account) { app('log')->debug(sprintf('There is no account with name "%s" of types', $name), $types); @@ -110,11 +100,6 @@ class AccountRepository implements AccountRepositoryInterface return $account; } - /** - * @param Account $account - * - * @return TransactionCurrency|null - */ public function getAccountCurrency(Account $account): ?TransactionCurrency { $type = $account->accountType->type; @@ -134,11 +119,6 @@ class AccountRepository implements AccountRepositoryInterface /** * Return meta value for account. Null if not found. - * - * @param Account $account - * @param string $field - * - * @return null|string */ public function getMetaValue(Account $account, string $field): ?string { @@ -157,25 +137,16 @@ class AccountRepository implements AccountRepositoryInterface return null; } - /** - * @param int $accountId - * - * @return Account|null - */ public function find(int $accountId): ?Account { $account = $this->user->accounts()->find($accountId); if (null === $account) { $account = $this->userGroup->accounts()->find($accountId); } + return $account; } - /** - * @param array $accountIds - * - * @return Collection - */ public function getAccountsById(array $accountIds): Collection { $query = $this->userGroup->accounts(); @@ -190,9 +161,6 @@ class AccountRepository implements AccountRepositoryInterface return $query->get(['accounts.*']); } - /** - * @inheritDoc - */ public function getAccountsByType(array $types, ?array $sort = []): Collection { $res = array_intersect([AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT], $types); @@ -215,14 +183,10 @@ class AccountRepository implements AccountRepositoryInterface $query->orderBy('accounts.active', 'DESC'); $query->orderBy('accounts.name', 'ASC'); } + return $query->get(['accounts.*']); } - /** - * @param array $types - * - * @return Collection - */ public function getActiveAccountsByType(array $types): Collection { $query = $this->userGroup->accounts(); @@ -237,18 +201,16 @@ class AccountRepository implements AccountRepositoryInterface return $query->get(['accounts.*']); } - /** - * @inheritDoc - */ public function searchAccount(string $query, array $types, int $limit): Collection { // search by group, not by user $dbQuery = $this->userGroup->accounts() - ->where('active', true) - ->orderBy('accounts.order', 'ASC') - ->orderBy('accounts.account_type_id', 'ASC') - ->orderBy('accounts.name', 'ASC') - ->with(['accountType']); + ->where('active', true) + ->orderBy('accounts.order', 'ASC') + ->orderBy('accounts.account_type_id', 'ASC') + ->orderBy('accounts.name', 'ASC') + ->with(['accountType']) + ; if ('' !== $query) { // split query on spaces just in case: $parts = explode(' ', $query); diff --git a/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php b/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php index 60d17c1ff3..bd0b8116fb 100644 --- a/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php +++ b/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php @@ -1,6 +1,5 @@ order = $current; $bill->save(); } - $current++; + ++$current; } } - /** - * @return Collection - */ public function getBills(): Collection { return $this->userGroup->bills() - ->orderBy('bills.name', 'ASC') - ->get(['bills.*']); + ->orderBy('bills.name', 'ASC') + ->get(['bills.*']) + ; } - /** - * @inheritDoc - */ public function sumPaidInRange(Carbon $start, Carbon $end): array { $bills = $this->getActiveBills(); $default = app('amount')->getDefaultCurrency(); $return = []; $converter = new ExchangeRateConverter(); + /** @var Bill $bill */ foreach ($bills as $bill) { /** @var Collection $set */ @@ -100,7 +95,7 @@ class BillRepository implements BillRepositoryInterface /** @var TransactionJournal $transactionJournal */ foreach ($set as $transactionJournal) { - /** @var Transaction|null $sourceTransaction */ + /** @var null|Transaction $sourceTransaction */ $sourceTransaction = $transactionJournal->transactions()->where('amount', '<', 0)->first(); if (null !== $sourceTransaction) { $amount = $sourceTransaction->amount; @@ -123,29 +118,27 @@ class BillRepository implements BillRepositoryInterface } } } + $converter->summarize(); + return $return; } - /** - * @return Collection - */ public function getActiveBills(): Collection { return $this->userGroup->bills() - ->where('active', true) - ->orderBy('bills.name', 'ASC') - ->get(['bills.*']); + ->where('active', true) + ->orderBy('bills.name', 'ASC') + ->get(['bills.*']) + ; } - /** - * @inheritDoc - */ public function sumUnpaidInRange(Carbon $start, Carbon $end): array { $bills = $this->getActiveBills(); $return = []; $default = app('amount')->getDefaultCurrency(); $converter = new ExchangeRateConverter(); + /** @var Bill $bill */ foreach ($bills as $bill) { $dates = $this->getPayDatesInRange($bill, $start, $end); @@ -175,6 +168,7 @@ class BillRepository implements BillRepositoryInterface $return[$currencyId]['native_sum'] = bcadd($return[$currencyId]['native_sum'], bcmul($nativeAverage, (string)$total)); } } + $converter->summarize(); return $return; } @@ -182,32 +176,26 @@ class BillRepository implements BillRepositoryInterface /** * Between start and end, tells you on which date(s) the bill is expected to hit. * TODO duplicate of function in other billrepositoryinterface - * - * @param Bill $bill - * @param Carbon $start - * @param Carbon $end - * - * @return Collection */ public function getPayDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection { $set = new Collection(); $currentStart = clone $start; - //app('log')->debug(sprintf('Now at bill "%s" (%s)', $bill->name, $bill->repeat_freq)); - //app('log')->debug(sprintf('First currentstart is %s', $currentStart->format('Y-m-d'))); + // app('log')->debug(sprintf('Now at bill "%s" (%s)', $bill->name, $bill->repeat_freq)); + // app('log')->debug(sprintf('First currentstart is %s', $currentStart->format('Y-m-d'))); while ($currentStart <= $end) { - //app('log')->debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d'))); + // app('log')->debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d'))); $nextExpectedMatch = $this->nextDateMatch($bill, $currentStart); - //app('log')->debug(sprintf('Next Date match after %s is %s', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); + // app('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 break; } $set->push(clone $nextExpectedMatch); - //app('log')->debug(sprintf('Now %d dates in set.', $set->count())); + // app('log')->debug(sprintf('Now %d dates in set.', $set->count())); $nextExpectedMatch->addDay(); - //app('log')->debug(sprintf('Currentstart (%s) has become %s.', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); + // app('log')->debug(sprintf('Currentstart (%s) has become %s.', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); $currentStart = clone $nextExpectedMatch; } @@ -220,11 +208,6 @@ class BillRepository implements BillRepositoryInterface * transaction. Whether it is there already, is not relevant. * * TODO duplicate of other repos - * - * @param Bill $bill - * @param Carbon $date - * - * @return Carbon */ public function nextDateMatch(Bill $bill, Carbon $date): Carbon { diff --git a/app/Repositories/UserGroups/Bill/BillRepositoryInterface.php b/app/Repositories/UserGroups/Bill/BillRepositoryInterface.php index 6a5882bdf2..6bf9d979a5 100644 --- a/app/Repositories/UserGroups/Bill/BillRepositoryInterface.php +++ b/app/Repositories/UserGroups/Bill/BillRepositoryInterface.php @@ -1,6 +1,5 @@ getDefaultCurrency(); $availableBudgets = $this->userGroup->availableBudgets() - ->where('start_date', $start->format('Y-m-d')) - ->where('end_date', $end->format('Y-m-d'))->get(); + ->where('start_date', $start->format('Y-m-d')) + ->where('end_date', $end->format('Y-m-d'))->get() + ; + /** @var AvailableBudget $availableBudget */ foreach ($availableBudgets as $availableBudget) { $currencyId = $availableBudget->transaction_currency_id; @@ -72,6 +67,8 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface $return[$currencyId]['amount'] = bcadd($return[$currencyId]['amount'], $availableBudget->amount); $return[$currencyId]['native_amount'] = bcadd($return[$currencyId]['native_amount'], $nativeAmount); } + $converter->summarize(); + return $return; } } diff --git a/app/Repositories/UserGroups/Budget/AvailableBudgetRepositoryInterface.php b/app/Repositories/UserGroups/Budget/AvailableBudgetRepositoryInterface.php index 8008460342..6d1015f348 100644 --- a/app/Repositories/UserGroups/Budget/AvailableBudgetRepositoryInterface.php +++ b/app/Repositories/UserGroups/Budget/AvailableBudgetRepositoryInterface.php @@ -1,6 +1,5 @@ userGroup->budgets()->where('active', true) - ->orderBy('order', 'ASC') - ->orderBy('name', 'ASC') - ->get(); + ->orderBy('order', 'ASC') + ->orderBy('name', 'ASC') + ->get() + ; } } diff --git a/app/Repositories/UserGroups/Budget/BudgetRepositoryInterface.php b/app/Repositories/UserGroups/Budget/BudgetRepositoryInterface.php index 1230ea6a36..6b52c939be 100644 --- a/app/Repositories/UserGroups/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/UserGroups/Budget/BudgetRepositoryInterface.php @@ -1,6 +1,5 @@ whereNull('accounts.deleted_at') - ->where('account_meta.name', 'currency_id')->where('account_meta.data', json_encode($currency->id))->count(); + ->whereNull('accounts.deleted_at') + ->where('account_meta.name', 'currency_id')->where('account_meta.data', json_encode($currency->id))->count() + ; if ($meta > 0) { app('log')->info(sprintf('Used in %d accounts as currency_id, return true. ', $meta)); @@ -157,28 +150,14 @@ class CurrencyRepository implements CurrencyRepositoryInterface return null; } - /** - * @param TransactionCurrency $currency - * - * @return int - */ - private 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(); - } - /** * Returns ALL currencies, regardless of whether they are enabled or not. - * - * @return Collection */ public function getAll(): Collection { $all = TransactionCurrency::orderBy('code', 'ASC')->get(); $local = $this->get(); + return $all->map(static function (TransactionCurrency $current) use ($local) { $hasId = $local->contains(static function (TransactionCurrency $entry) use ($current) { return $entry->id === $current->id; @@ -188,29 +167,24 @@ class CurrencyRepository implements CurrencyRepositoryInterface }); $current->userGroupEnabled = $hasId; $current->userGroupDefault = $isDefault; + return $current; }); } - /** - * @inheritDoc - */ public function get(): Collection { $all = $this->userGroup->currencies()->orderBy('code', 'ASC')->withPivot(['group_default'])->get(); $all->map(static function (TransactionCurrency $current) { $current->userGroupEnabled = true; $current->userGroupDefault = 1 === (int)$current->pivot->group_default; + return $current; }); + return $all; } - /** - * @param TransactionCurrency $currency - * - * @return bool - */ public function destroy(TransactionCurrency $currency): bool { /** @var UserRepositoryInterface $repository */ @@ -226,8 +200,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface /** * Disables a currency - * - * @param TransactionCurrency $currency */ public function disable(TransactionCurrency $currency): void { @@ -236,9 +208,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface $currency->save(); } - /** - * @inheritDoc - */ public function findByName(string $name): ?TransactionCurrency { return TransactionCurrency::where('name', $name)->first(); @@ -247,12 +216,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface /** * Find by object, ID or code. Returns user default or system default. * - * @param int|null $currencyId - * @param string|null $currencyCode - * - * @return TransactionCurrency * @throws FireflyException - * @throws JsonException */ public function findCurrency(?int $currencyId, ?string $currencyCode): TransactionCurrency { @@ -260,7 +224,8 @@ class CurrencyRepository implements CurrencyRepositoryInterface if (null === $result) { app('log')->debug('Grabbing default currency for this user...'); - /** @var TransactionCurrency|null $result */ + + /** @var null|TransactionCurrency $result */ $result = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); } @@ -275,11 +240,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface /** * Find by object, ID or code. Returns NULL if nothing found. - * - * @param int|null $currencyId - * @param string|null $currencyCode - * - * @return TransactionCurrency|null */ public function findCurrencyNull(?int $currencyId, ?string $currencyCode): ?TransactionCurrency { @@ -299,10 +259,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface /** * Find by ID, return NULL if not found. - * - * @param int $currencyId - * - * @return TransactionCurrency|null */ public function find(int $currencyId): ?TransactionCurrency { @@ -311,10 +267,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface /** * Find by currency code, return NULL if unfound. - * - * @param string $currencyCode - * - * @return TransactionCurrency|null */ public function findByCode(string $currencyCode): ?TransactionCurrency { @@ -323,7 +275,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface /** * @param TransactionCurrency $currency - * Enables a currency + * Enables a currency */ public function enable(TransactionCurrency $currency): void { @@ -332,27 +284,16 @@ class CurrencyRepository implements CurrencyRepositoryInterface $currency->save(); } - /** - * @param array $ids - * - * @return Collection - */ public function getByIds(array $ids): Collection { return TransactionCurrency::orderBy('code', 'ASC')->whereIn('id', $ids)->get(); } - /** - * @inheritDoc - */ public function isFallbackCurrency(TransactionCurrency $currency): bool { return $currency->code === config('firefly.default_currency', 'EUR'); } - /** - * @inheritDoc - */ public function makeDefault(TransactionCurrency $currency): void { app('log')->debug(sprintf('Enabled + made default currency %s for user #%d', $currency->code, $this->userGroup->id)); @@ -363,12 +304,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface $this->userGroup->currencies()->syncWithoutDetaching([$currency->id => ['group_default' => true]]); } - /** - * @param string $search - * @param int $limit - * - * @return Collection - */ public function searchCurrency(string $search, int $limit): Collection { $query = TransactionCurrency::where('enabled', true); @@ -380,9 +315,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface } /** - * @param array $data - * - * @return TransactionCurrency * @throws FireflyException */ public function store(array $data): TransactionCurrency @@ -398,12 +330,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface return $result; } - /** - * @param TransactionCurrency $currency - * @param array $data - * - * @return TransactionCurrency - */ public function update(TransactionCurrency $currency, array $data): TransactionCurrency { app('log')->debug('Now in update()'); @@ -450,4 +376,12 @@ class CurrencyRepository implements CurrencyRepositoryInterface return $service->update($currency, $data); } + + private 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(); + } } diff --git a/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php b/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php index a1ca8278c9..b989d1aa8a 100644 --- a/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php +++ b/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php @@ -1,6 +1,5 @@ userGroup->transactionJournals() - ->orderBy('date', 'DESC'); + ->orderBy('date', 'DESC') + ; if ('' !== $search) { $query->where('description', 'LIKE', sprintf('%%%s%%', $search)); } diff --git a/app/Repositories/UserGroups/Journal/JournalRepositoryInterface.php b/app/Repositories/UserGroups/Journal/JournalRepositoryInterface.php index 5ecc648d1b..7e2c8c0c72 100644 --- a/app/Repositories/UserGroups/Journal/JournalRepositoryInterface.php +++ b/app/Repositories/UserGroups/Journal/JournalRepositoryInterface.php @@ -33,18 +33,8 @@ interface JournalRepositoryInterface { /** * Search in journal descriptions. - * - * @param string $search - * @param int $limit - * - * @return Collection */ public function searchJournalDescriptions(string $search, int $limit): Collection; - /** - * @param User $user - * - * @return void - */ public function setUser(User $user): void; } diff --git a/app/Repositories/UserGroups/PiggyBank/PiggyBankRepository.php b/app/Repositories/UserGroups/PiggyBank/PiggyBankRepository.php index e6b80198b7..7652e55c0b 100644 --- a/app/Repositories/UserGroups/PiggyBank/PiggyBankRepository.php +++ b/app/Repositories/UserGroups/PiggyBank/PiggyBankRepository.php @@ -1,6 +1,5 @@ userGroup->piggyBanks() - ->with( - [ - 'account', - 'objectGroups', - ] - ) - ->orderBy('order', 'ASC')->get(); + ->with( + [ + 'account', + 'objectGroups', + ] + ) + ->orderBy('order', 'ASC')->get() + ; } } diff --git a/app/Repositories/UserGroups/PiggyBank/PiggyBankRepositoryInterface.php b/app/Repositories/UserGroups/PiggyBank/PiggyBankRepositoryInterface.php index 8a02a8e84d..32a98d5423 100644 --- a/app/Repositories/UserGroups/PiggyBank/PiggyBankRepositoryInterface.php +++ b/app/Repositories/UserGroups/PiggyBank/PiggyBankRepositoryInterface.php @@ -1,6 +1,5 @@ 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.*']); + ->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( - static function (WebhookMessage $message) { - return $message->webhookAttempts()->count() <= 2; - } - )->splice(0, 3); + ->where('webhook_messages.sent', 0) + ->where('webhook_messages.errored', 0) + ->get(['webhook_messages.*']) + ->filter( + static function (WebhookMessage $message) { + return $message->webhookAttempts()->count() <= 2; + } + )->splice(0, 3) + ; } - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; } } - /** - * @inheritDoc - */ public function store(array $data): Webhook { - $secret = Str::random(24); + $secret = \Str::random(24); $fullData = [ 'user_id' => $this->user->id, 'user_group_id' => $this->user->user_group_id, @@ -135,9 +109,6 @@ class WebhookRepository implements WebhookRepositoryInterface return Webhook::create($fullData); } - /** - * @inheritDoc - */ public function update(Webhook $webhook, array $data): Webhook { $webhook->active = $data['active'] ?? $webhook->active; @@ -148,7 +119,7 @@ class WebhookRepository implements WebhookRepositoryInterface $webhook->url = $data['url'] ?? $webhook->url; if (true === $data['secret']) { - $secret = Str::random(24); + $secret = \Str::random(24); $webhook->secret = $secret; } diff --git a/app/Repositories/Webhook/WebhookRepositoryInterface.php b/app/Repositories/Webhook/WebhookRepositoryInterface.php index c0dd28c6ae..a313f0b1f9 100644 --- a/app/Repositories/Webhook/WebhookRepositoryInterface.php +++ b/app/Repositories/Webhook/WebhookRepositoryInterface.php @@ -37,64 +37,24 @@ interface WebhookRepositoryInterface { /** * Return all webhooks. - * - * @return Collection */ 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; - /** - * @param User|Authenticatable|null $user - */ - public function setUser(User | Authenticatable | null $user): void; + public function setUser(null|Authenticatable|User $user): void; - /** - * @param array $data - * - * @return Webhook - */ public function store(array $data): Webhook; - /** - * @param Webhook $webhook - * @param array $data - * - * @return Webhook - */ public function update(Webhook $webhook, array $data): Webhook; } diff --git a/app/Rules/BelongsUser.php b/app/Rules/BelongsUser.php index 40eb1c625b..a0c0ceff3d 100644 --- a/app/Rules/BelongsUser.php +++ b/app/Rules/BelongsUser.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Closure; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; @@ -39,14 +38,12 @@ use Illuminate\Contracts\Validation\ValidationRule; */ class BelongsUser implements ValidationRule { - /** - * @inheritDoc - */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validate(string $attribute, mixed $value, \Closure $fail): void { $attribute = $this->parseAttribute($attribute); if (!auth()->check()) { $fail('validation.belongs_user')->translate(); + return; } app('log')->debug(sprintf('Going to validate %s', $attribute)); @@ -68,11 +65,32 @@ class BelongsUser implements ValidationRule } } - /** - * @param string $attribute - * - * @return string - */ + protected function countField(string $class, string $field, string $value): int + { + $value = trim($value); + $objects = []; + // get all objects belonging to user: + if (PiggyBank::class === $class) { + $objects = PiggyBank::leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') + ->where('accounts.user_id', '=', auth()->user()->id)->get(['piggy_banks.*']) + ; + } + if (PiggyBank::class !== $class) { + $objects = $class::where('user_id', '=', auth()->user()->id)->get(); + } + $count = 0; + foreach ($objects as $object) { + $objectValue = trim((string)$object->{$field}); // @phpstan-ignore-line + app('log')->debug(sprintf('Comparing object "%s" with value "%s"', $objectValue, $value)); + if ($objectValue === $value) { + ++$count; + app('log')->debug(sprintf('Hit! Count is now %d', $count)); + } + } + + return $count; + } + private function parseAttribute(string $attribute): string { $parts = explode('.', $attribute); @@ -86,25 +104,16 @@ class BelongsUser implements ValidationRule return $attribute; } - /** - * @param int $value - * - * @return bool - */ private function validatePiggyBankId(int $value): bool { $count = PiggyBank::leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') - ->where('piggy_banks.id', '=', $value) - ->where('accounts.user_id', '=', auth()->user()->id)->count(); + ->where('piggy_banks.id', '=', $value) + ->where('accounts.user_id', '=', auth()->user()->id)->count() + ; return 1 === $count; } - /** - * @param string $value - * - * @return bool - */ private function validatePiggyBankName(string $value): bool { $count = $this->countField(PiggyBank::class, 'name', $value); @@ -112,44 +121,6 @@ class BelongsUser implements ValidationRule return 1 === $count; } - /** - * @param string $class - * @param string $field - * @param string $value - * - * @return int - * - */ - protected function countField(string $class, string $field, string $value): int - { - $value = trim($value); - $objects = []; - // get all objects belonging to user: - if (PiggyBank::class === $class) { - $objects = PiggyBank::leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') - ->where('accounts.user_id', '=', auth()->user()->id)->get(['piggy_banks.*']); - } - if (PiggyBank::class !== $class) { - $objects = $class::where('user_id', '=', auth()->user()->id)->get(); - } - $count = 0; - foreach ($objects as $object) { - $objectValue = trim((string)$object->$field); // @phpstan-ignore-line - app('log')->debug(sprintf('Comparing object "%s" with value "%s"', $objectValue, $value)); - if ($objectValue === $value) { - $count++; - app('log')->debug(sprintf('Hit! Count is now %d', $count)); - } - } - - return $count; - } - - /** - * @param int $value - * - * @return bool - */ private function validateBillId(int $value): bool { if (0 === $value) { @@ -160,11 +131,6 @@ class BelongsUser implements ValidationRule return 1 === $count; } - /** - * @param int $value - * - * @return bool - */ private function validateJournalId(int $value): bool { if (0 === $value) { @@ -175,11 +141,6 @@ class BelongsUser implements ValidationRule return 1 === $count; } - /** - * @param string $value - * - * @return bool - */ private function validateBillName(string $value): bool { $count = $this->countField(Bill::class, 'name', $value); @@ -188,11 +149,6 @@ class BelongsUser implements ValidationRule return 1 === $count; } - /** - * @param int $value - * - * @return bool - */ private function validateBudgetId(int $value): bool { if (0 === $value) { @@ -203,11 +159,6 @@ class BelongsUser implements ValidationRule return 1 === $count; } - /** - * @param int $value - * - * @return bool - */ private function validateCategoryId(int $value): bool { $count = Category::where('id', '=', $value)->where('user_id', '=', auth()->user()->id)->count(); @@ -215,11 +166,6 @@ class BelongsUser implements ValidationRule return 1 === $count; } - /** - * @param string $value - * - * @return bool - */ private function validateBudgetName(string $value): bool { $count = $this->countField(Budget::class, 'name', $value); @@ -227,11 +173,6 @@ class BelongsUser implements ValidationRule return 1 === $count; } - /** - * @param int $value - * - * @return bool - */ private function validateAccountId(int $value): bool { if (0 === $value) { diff --git a/app/Rules/BelongsUserGroup.php b/app/Rules/BelongsUserGroup.php index e74194bf6a..e31cde0aa5 100644 --- a/app/Rules/BelongsUserGroup.php +++ b/app/Rules/BelongsUserGroup.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Closure; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; @@ -46,22 +45,18 @@ class BelongsUserGroup implements ValidationRule /** * Create a new rule instance. - * - * @return void */ public function __construct(UserGroup $userGroup) { $this->userGroup = $userGroup; } - /** - * @inheritDoc - */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validate(string $attribute, mixed $value, \Closure $fail): void { $attribute = $this->parseAttribute($attribute); if (!auth()->check()) { $fail('validation.belongs_user_or_user_group')->translate(); + return; } app('log')->debug(sprintf('Group: Going to validate "%s"', $attribute)); @@ -83,11 +78,32 @@ class BelongsUserGroup implements ValidationRule } } - /** - * @param string $attribute - * - * @return string - */ + protected function countField(string $class, string $field, string $value): int + { + $value = trim($value); + $objects = []; + // get all objects belonging to user: + if (PiggyBank::class === $class) { + $objects = PiggyBank::leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') + ->where('accounts.user_group_id', '=', $this->userGroup->id)->get(['piggy_banks.*']) + ; + } + if (PiggyBank::class !== $class) { + $objects = $class::where('user_group_id', '=', $this->userGroup->id)->get(); + } + $count = 0; + foreach ($objects as $object) { + $objectValue = trim((string)$object->{$field}); // @phpstan-ignore-line + app('log')->debug(sprintf('Comparing object "%s" with value "%s"', $objectValue, $value)); + if ($objectValue === $value) { + ++$count; + app('log')->debug(sprintf('Hit! Count is now %d', $count)); + } + } + + return $count; + } + private function parseAttribute(string $attribute): string { $parts = explode('.', $attribute); @@ -101,25 +117,16 @@ class BelongsUserGroup implements ValidationRule return $attribute; } - /** - * @param int $value - * - * @return bool - */ private function validatePiggyBankId(int $value): bool { $count = PiggyBank::leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') - ->where('piggy_banks.id', '=', $value) - ->where('accounts.user_group_id', '=', $this->userGroup->id)->count(); + ->where('piggy_banks.id', '=', $value) + ->where('accounts.user_group_id', '=', $this->userGroup->id)->count() + ; return 1 === $count; } - /** - * @param string $value - * - * @return bool - */ private function validatePiggyBankName(string $value): bool { $count = $this->countField(PiggyBank::class, 'name', $value); @@ -127,44 +134,6 @@ class BelongsUserGroup implements ValidationRule return 1 === $count; } - /** - * @param string $class - * @param string $field - * @param string $value - * - * @return int - * - */ - protected function countField(string $class, string $field, string $value): int - { - $value = trim($value); - $objects = []; - // get all objects belonging to user: - if (PiggyBank::class === $class) { - $objects = PiggyBank::leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') - ->where('accounts.user_group_id', '=', $this->userGroup->id)->get(['piggy_banks.*']); - } - if (PiggyBank::class !== $class) { - $objects = $class::where('user_group_id', '=', $this->userGroup->id)->get(); - } - $count = 0; - foreach ($objects as $object) { - $objectValue = trim((string)$object->$field); // @phpstan-ignore-line - app('log')->debug(sprintf('Comparing object "%s" with value "%s"', $objectValue, $value)); - if ($objectValue === $value) { - $count++; - app('log')->debug(sprintf('Hit! Count is now %d', $count)); - } - } - - return $count; - } - - /** - * @param int $value - * - * @return bool - */ private function validateBillId(int $value): bool { if (0 === $value) { @@ -175,11 +144,6 @@ class BelongsUserGroup implements ValidationRule return 1 === $count; } - /** - * @param int $value - * - * @return bool - */ private function validateJournalId(int $value): bool { if (0 === $value) { @@ -190,11 +154,6 @@ class BelongsUserGroup implements ValidationRule return 1 === $count; } - /** - * @param string $value - * - * @return bool - */ private function validateBillName(string $value): bool { $count = $this->countField(Bill::class, 'name', $value); @@ -203,11 +162,6 @@ class BelongsUserGroup implements ValidationRule return 1 === $count; } - /** - * @param int $value - * - * @return bool - */ private function validateBudgetId(int $value): bool { if (0 === $value) { @@ -218,11 +172,6 @@ class BelongsUserGroup implements ValidationRule return 1 === $count; } - /** - * @param int $value - * - * @return bool - */ private function validateCategoryId(int $value): bool { $count = Category::where('id', '=', $value)->where('user_group_id', '=', $this->userGroup->id)->count(); @@ -230,11 +179,6 @@ class BelongsUserGroup implements ValidationRule return 1 === $count; } - /** - * @param string $value - * - * @return bool - */ private function validateBudgetName(string $value): bool { $count = $this->countField(Budget::class, 'name', $value); @@ -242,11 +186,6 @@ class BelongsUserGroup implements ValidationRule return 1 === $count; } - /** - * @param int $value - * - * @return bool - */ private function validateAccountId(int $value): bool { if (0 === $value) { diff --git a/app/Rules/IsAssetAccountId.php b/app/Rules/IsAssetAccountId.php index e77e5c2fae..9a57ae4cee 100644 --- a/app/Rules/IsAssetAccountId.php +++ b/app/Rules/IsAssetAccountId.php @@ -23,36 +23,30 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Closure; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use Illuminate\Contracts\Validation\ValidationRule; /** - * * Class IsAssetAccountId */ class IsAssetAccountId implements ValidationRule { /** - * @param string $attribute - * @param mixed $value - * @param Closure $fail - * - * @return void - * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validate(string $attribute, mixed $value, \Closure $fail): void { $accountId = (int)$value; - /** @var Account|null $account */ + + /** @var null|Account $account */ $account = Account::with('accountType')->find($accountId); if (null === $account) { $fail('validation.no_asset_account')->translate(); + return; } - if ($account->accountType->type !== AccountType::ASSET && $account->accountType->type !== AccountType::DEFAULT) { + if (AccountType::ASSET !== $account->accountType->type && AccountType::DEFAULT !== $account->accountType->type) { $fail('validation.no_asset_account')->translate(); } } diff --git a/app/Rules/IsBoolean.php b/app/Rules/IsBoolean.php index bd45f1f7b3..4ca0ea2c63 100644 --- a/app/Rules/IsBoolean.php +++ b/app/Rules/IsBoolean.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Closure; use Illuminate\Contracts\Validation\ValidationRule; /** @@ -33,15 +32,9 @@ use Illuminate\Contracts\Validation\ValidationRule; class IsBoolean implements ValidationRule { /** - * @param string $attribute - * @param mixed $value - * @param Closure $fail - * - * @return void - * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validate(string $attribute, mixed $value, \Closure $fail): void { if (is_bool($value)) { return; diff --git a/app/Rules/IsDateOrTime.php b/app/Rules/IsDateOrTime.php index f3e3284efe..60ccb3a963 100644 --- a/app/Rules/IsDateOrTime.php +++ b/app/Rules/IsDateOrTime.php @@ -27,7 +27,6 @@ namespace FireflyIII\Rules; use Carbon\Carbon; use Carbon\Exceptions\InvalidDateException; use Carbon\Exceptions\InvalidFormatException; -use Closure; use Illuminate\Contracts\Validation\ValidationRule; /** @@ -36,19 +35,14 @@ use Illuminate\Contracts\Validation\ValidationRule; class IsDateOrTime implements ValidationRule { /** - * @param string $attribute - * @param mixed $value - * @param Closure $fail - * - * @return void - * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validate(string $attribute, mixed $value, \Closure $fail): void { $value = (string)$value; if ('' === $value) { $fail('validation.date_or_time')->translate(); + return; } if (10 === strlen($value)) { @@ -59,16 +53,19 @@ class IsDateOrTime implements ValidationRule app('log')->error(sprintf('"%s" is not a valid date: %s', $value, $e->getMessage())); $fail('validation.date_or_time')->translate(); + return; } catch (InvalidFormatException $e) { // @phpstan-ignore-line app('log')->error(sprintf('"%s" is of an invalid format: %s', $value, $e->getMessage())); $fail('validation.date_or_time')->translate(); + return; } return; } + // is an atom string, I hope? try { Carbon::parse($value); @@ -76,11 +73,13 @@ class IsDateOrTime implements ValidationRule app('log')->error(sprintf('"%s" is not a valid date or time: %s', $value, $e->getMessage())); $fail('validation.date_or_time')->translate(); + return; } catch (InvalidFormatException $e) { app('log')->error(sprintf('"%s" is of an invalid format: %s', $value, $e->getMessage())); $fail('validation.date_or_time')->translate(); + return; } } diff --git a/app/Rules/IsDuplicateTransaction.php b/app/Rules/IsDuplicateTransaction.php index 4f61474828..b729ff46c3 100644 --- a/app/Rules/IsDuplicateTransaction.php +++ b/app/Rules/IsDuplicateTransaction.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Closure; use Illuminate\Contracts\Validation\ValidationRule; /** @@ -36,9 +35,9 @@ class IsDuplicateTransaction implements ValidationRule private string $value; /** - * @inheritDoc + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validate(string $attribute, mixed $value, \Closure $fail): void { $this->value = $value; diff --git a/app/Rules/IsTransferAccount.php b/app/Rules/IsTransferAccount.php index 373be6b236..5b021fcefb 100644 --- a/app/Rules/IsTransferAccount.php +++ b/app/Rules/IsTransferAccount.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Closure; use FireflyIII\Models\TransactionType; use FireflyIII\Validation\AccountValidator; use Illuminate\Contracts\Validation\ValidationRule; @@ -35,30 +34,25 @@ use Illuminate\Contracts\Validation\ValidationRule; class IsTransferAccount implements ValidationRule { /** - * @param string $attribute - * @param mixed $value - * @param Closure $fail - * - * @return void - * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validate(string $attribute, mixed $value, \Closure $fail): void { app('log')->debug(sprintf('Now in %s(%s)', __METHOD__, $value)); + /** @var AccountValidator $validator */ $validator = app(AccountValidator::class); $validator->setTransactionType(TransactionType::TRANSFER); $validator->setUser(auth()->user()); - $validAccount = $validator->validateSource(['name' => (string)$value,]); + $validAccount = $validator->validateSource(['name' => (string)$value]); if (true === $validAccount) { app('log')->debug('Found account based on name. Return true.'); // found by name, use repos to return. return; } - $validAccount = $validator->validateSource(['id' => (int)$value,]); + $validAccount = $validator->validateSource(['id' => (int)$value]); app('log')->debug(sprintf('Search by id (%d), result is %s.', (int)$value, var_export($validAccount, true))); if (false === $validAccount) { diff --git a/app/Rules/IsValidAttachmentModel.php b/app/Rules/IsValidAttachmentModel.php index f58ec2f95c..1456c386e9 100644 --- a/app/Rules/IsValidAttachmentModel.php +++ b/app/Rules/IsValidAttachmentModel.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Closure; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; use FireflyIII\Models\Budget; @@ -47,14 +46,10 @@ use Illuminate\Contracts\Validation\ValidationRule; */ class IsValidAttachmentModel implements ValidationRule { - /** @var string */ private string $model; /** * IsValidAttachmentModel constructor. - * - * - * @param string $model */ public function __construct(string $model) { @@ -63,32 +58,13 @@ class IsValidAttachmentModel implements ValidationRule } /** - * @param string $model - * - * @return string - */ - private function normalizeModel(string $model): string - { - $search = ['FireflyIII\Models\\']; - $replace = ''; - $model = str_replace($search, $replace, $model); - - return sprintf('FireflyIII\Models\%s', $model); - } - - /** - * @param string $attribute - * @param mixed $value - * @param Closure $fail - * - * @return void - * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validate(string $attribute, mixed $value, \Closure $fail): void { if (!auth()->check()) { $fail('validation.model_id_invalid')->translate(); + return; } $result = match ($this->model) { @@ -108,11 +84,15 @@ class IsValidAttachmentModel implements ValidationRule } } - /** - * @param int $value - * - * @return bool - */ + private function normalizeModel(string $model): string + { + $search = ['FireflyIII\Models\\']; + $replace = ''; + $model = str_replace($search, $replace, $model); + + return sprintf('FireflyIII\Models\%s', $model); + } + private function validateAccount(int $value): bool { /** @var AccountRepositoryInterface $repository */ @@ -122,11 +102,6 @@ class IsValidAttachmentModel implements ValidationRule return null !== $repository->find($value); } - /** - * @param int $value - * - * @return bool - */ private function validateBill(int $value): bool { /** @var BillRepositoryInterface $repository */ @@ -136,11 +111,6 @@ class IsValidAttachmentModel implements ValidationRule return null !== $repository->find($value); } - /** - * @param int $value - * - * @return bool - */ private function validateBudget(int $value): bool { /** @var BudgetRepositoryInterface $repository */ @@ -150,11 +120,6 @@ class IsValidAttachmentModel implements ValidationRule return null !== $repository->find($value); } - /** - * @param int $value - * - * @return bool - */ private function validateCategory(int $value): bool { /** @var CategoryRepositoryInterface $repository */ @@ -164,11 +129,6 @@ class IsValidAttachmentModel implements ValidationRule return null !== $repository->find($value); } - /** - * @param int $value - * - * @return bool - */ private function validatePiggyBank(int $value): bool { /** @var PiggyBankRepositoryInterface $repository */ @@ -178,11 +138,6 @@ class IsValidAttachmentModel implements ValidationRule return null !== $repository->find($value); } - /** - * @param int $value - * - * @return bool - */ private function validateTag(int $value): bool { /** @var TagRepositoryInterface $repository */ @@ -192,11 +147,6 @@ class IsValidAttachmentModel implements ValidationRule return null !== $repository->find($value); } - /** - * @param int $value - * - * @return bool - */ private function validateTransaction(int $value): bool { /** @var JournalAPIRepositoryInterface $repository */ @@ -206,11 +156,6 @@ class IsValidAttachmentModel implements ValidationRule return null !== $repository->findTransaction($value); } - /** - * @param int $value - * - * @return bool - */ private function validateJournal(int $value): bool { $repository = app(JournalRepositoryInterface::class); diff --git a/app/Rules/IsValidBulkClause.php b/app/Rules/IsValidBulkClause.php index e5aaf17e8a..1a79add025 100644 --- a/app/Rules/IsValidBulkClause.php +++ b/app/Rules/IsValidBulkClause.php @@ -24,10 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Closure; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Facades\Validator; -use JsonException; /** * Class IsValidBulkClause @@ -37,33 +35,21 @@ class IsValidBulkClause implements ValidationRule private string $error; private array $rules; - /** - * @param string $type - */ public function __construct(string $type) { $this->rules = config(sprintf('bulk.%s', $type)); $this->error = (string)trans('firefly.belongs_user'); } - /** - * @return string - */ public function message(): string { return $this->error; } /** - * @param string $attribute - * @param mixed $value - * @param Closure $fail - * - * @return void - * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validate(string $attribute, mixed $value, \Closure $fail): void { $result = $this->basicValidation((string)$value); if (false === $result) { @@ -73,16 +59,12 @@ class IsValidBulkClause implements ValidationRule /** * Does basic rule based validation. - * - * @param string $value - * - * @return bool */ private function basicValidation(string $value): bool { try { $array = json_decode($value, true, 8, JSON_THROW_ON_ERROR); - } catch (JsonException $e) { + } catch (\JsonException $e) { $this->error = (string)trans('validation.json'); return false; @@ -94,6 +76,7 @@ class IsValidBulkClause implements ValidationRule return false; } + /** * @var string $arrayKey * @var mixed $arrayValue @@ -109,7 +92,7 @@ class IsValidBulkClause implements ValidationRule 'value' => $this->rules[$clause][$arrayKey], ]); if ($validator->fails()) { - $this->error = sprintf('%s: %s: %s', $clause, $arrayKey, implode(', ', ($validator->errors()->get('value')))); + $this->error = sprintf('%s: %s: %s', $clause, $arrayKey, implode(', ', $validator->errors()->get('value'))); return false; } diff --git a/app/Rules/LessThanPiggyTarget.php b/app/Rules/LessThanPiggyTarget.php index 7b1dd17d7a..433d02dd2e 100644 --- a/app/Rules/LessThanPiggyTarget.php +++ b/app/Rules/LessThanPiggyTarget.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Closure; use Illuminate\Contracts\Validation\ValidationRule; /** @@ -34,8 +33,6 @@ class LessThanPiggyTarget implements ValidationRule { /** * Get the validation error message. - * - * @return string */ public function message(): string { @@ -43,15 +40,9 @@ class LessThanPiggyTarget implements ValidationRule } /** - * @param string $attribute - * @param mixed $value - * @param Closure $fail - * - * @return void - * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validate(string $attribute, mixed $value, \Closure $fail): void { // TODO not sure if this is still used. } diff --git a/app/Rules/UniqueAccountNumber.php b/app/Rules/UniqueAccountNumber.php index 5fae561d1d..cc23bd74bf 100644 --- a/app/Rules/UniqueAccountNumber.php +++ b/app/Rules/UniqueAccountNumber.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Closure; use FireflyIII\Models\Account; use FireflyIII\Models\AccountMeta; use FireflyIII\Models\AccountType; @@ -39,10 +38,6 @@ class UniqueAccountNumber implements ValidationRule /** * Create a new rule instance. - * - * - * @param Account|null $account - * @param string|null $expectedType */ public function __construct(?Account $account, ?string $expectedType) { @@ -64,9 +59,6 @@ class UniqueAccountNumber implements ValidationRule /** * Get the validation error message. - * - * - * @return string */ public function message(): string { @@ -74,15 +66,9 @@ class UniqueAccountNumber implements ValidationRule } /** - * @param string $attribute - * @param mixed $value - * @param Closure $fail - * - * @return void - * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validate(string $attribute, mixed $value, \Closure $fail): void { if (!auth()->check()) { return; @@ -107,16 +93,13 @@ class UniqueAccountNumber implements ValidationRule ); $fail('validation.unique_account_number_for_user')->translate(); + return; } } app('log')->debug('Account number is valid.'); } - /** - * @return array - * - */ private function getMaxOccurrences(): array { $maxCounts = [ @@ -139,20 +122,15 @@ class UniqueAccountNumber implements ValidationRule return $maxCounts; } - /** - * @param string $type - * @param string $accountNumber - * - * @return int - */ private function countHits(string $type, string $accountNumber): int { $query = AccountMeta::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)); + ->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)) + ; if (null !== $this->account) { $query->where('accounts.id', '!=', $this->account->id); diff --git a/app/Rules/UniqueIban.php b/app/Rules/UniqueIban.php index 8d1bea0cfd..29c10e6704 100644 --- a/app/Rules/UniqueIban.php +++ b/app/Rules/UniqueIban.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Closure; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use Illuminate\Contracts\Validation\ValidationRule; @@ -38,10 +37,6 @@ class UniqueIban implements ValidationRule /** * Create a new rule instance. - * - * - * @param Account|null $account - * @param string|null $expectedType */ public function __construct(?Account $account, ?string $expectedType) { @@ -68,19 +63,13 @@ class UniqueIban implements ValidationRule /** * Get the validation error message. - * - * - * @return string */ public function message(): string { return (string)trans('validation.unique_iban_for_user'); } - /** - * @inheritDoc - */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validate(string $attribute, mixed $value, \Closure $fail): void { if (!$this->passes($attribute, $value)) { $fail((string)trans('validation.unique_iban_for_user')); @@ -93,10 +82,7 @@ class UniqueIban implements ValidationRule * @param string $attribute * @param mixed $value * - * @return bool - * * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * */ public function passes($attribute, $value): bool { @@ -129,10 +115,6 @@ class UniqueIban implements ValidationRule return true; } - /** - * @return array - * - */ private function getMaxOccurrences(): array { $maxCounts = [ @@ -156,12 +138,6 @@ class UniqueIban implements ValidationRule return $maxCounts; } - /** - * @param string $type - * @param string $iban - * - * @return int - */ private function countHits(string $type, string $iban): int { $typesArray = [$type]; @@ -170,10 +146,11 @@ class UniqueIban implements ValidationRule } $query = auth()->user() - ->accounts() - ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') - ->where('accounts.iban', $iban) - ->whereIn('account_types.type', $typesArray); + ->accounts() + ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') + ->where('accounts.iban', $iban) + ->whereIn('account_types.type', $typesArray) + ; if (null !== $this->account) { $query->where('accounts.id', '!=', $this->account->id); diff --git a/app/Rules/ValidJournals.php b/app/Rules/ValidJournals.php index 4a286f36f7..a73ba98f2b 100644 --- a/app/Rules/ValidJournals.php +++ b/app/Rules/ValidJournals.php @@ -24,27 +24,18 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Closure; use FireflyIII\Models\TransactionJournal; use Illuminate\Contracts\Validation\ValidationRule; /** * Class ValidJournals - * - */ class ValidJournals implements ValidationRule { /** - * @param string $attribute - * @param mixed $value - * @param Closure $fail - * - * @return void - * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validate(string $attribute, mixed $value, \Closure $fail): void { app('log')->debug('In ValidJournals::passes'); if (!is_array($value)) { @@ -57,6 +48,7 @@ class ValidJournals implements ValidationRule app('log')->debug(sprintf('Count for transaction #%d and user #%d is zero! Return FALSE', $journalId, $userId)); $fail('validation.invalid_selection')->translate(); + return; } } diff --git a/app/Rules/ValidRecurrenceRepetitionType.php b/app/Rules/ValidRecurrenceRepetitionType.php index 4f680b108d..d4015d1d7c 100644 --- a/app/Rules/ValidRecurrenceRepetitionType.php +++ b/app/Rules/ValidRecurrenceRepetitionType.php @@ -23,33 +23,26 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Closure; use Illuminate\Contracts\Validation\ValidationRule; /** * Class ValidRecurrenceRepetitionType - * - */ class ValidRecurrenceRepetitionType implements ValidationRule { /** * Determine if the validation rule passes. * - * @param string $attribute - * @param mixed $value - * @param Closure $fail - * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validate(string $attribute, mixed $value, \Closure $fail): void { $value = (string)$value; if ('daily' === $value) { return; } - //monthly,17 - //ndom,3,7 + // monthly,17 + // ndom,3,7 if (in_array(substr($value, 0, 6), ['yearly', 'weekly'], true)) { return; } diff --git a/app/Rules/ValidRecurrenceRepetitionValue.php b/app/Rules/ValidRecurrenceRepetitionValue.php index f665eaabd3..318420c949 100644 --- a/app/Rules/ValidRecurrenceRepetitionValue.php +++ b/app/Rules/ValidRecurrenceRepetitionValue.php @@ -24,27 +24,17 @@ declare(strict_types=1); namespace FireflyIII\Rules; use Carbon\Carbon; -use Closure; use Illuminate\Contracts\Validation\ValidationRule; -use InvalidArgumentException; /** * Class ValidRecurrenceRepetitionValue - * - */ class ValidRecurrenceRepetitionValue implements ValidationRule { /** - * @param string $attribute - * @param mixed $value - * @param Closure $fail - * - * @return void - * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validate(string $attribute, mixed $value, \Closure $fail): void { $value = (string)$value; @@ -75,11 +65,6 @@ class ValidRecurrenceRepetitionValue implements ValidationRule $fail('validation.valid_recurrence_rep_type')->translate(); } - /** - * @param string $value - * - * @return bool - */ private function validateMonthly(string $value): bool { $dayOfMonth = (int)substr($value, 8); @@ -87,12 +72,6 @@ class ValidRecurrenceRepetitionValue implements ValidationRule return $dayOfMonth > 0 && $dayOfMonth < 32; } - /** - * @param string $value - * - * @return bool - * - */ private function validateNdom(string $value): bool { $parameters = explode(',', substr($value, 5)); @@ -108,11 +87,6 @@ class ValidRecurrenceRepetitionValue implements ValidationRule return $dayOfWeek > 0 && $dayOfWeek < 8; } - /** - * @param string $value - * - * @return bool - */ private function validateWeekly(string $value): bool { $dayOfWeek = (int)substr($value, 7); @@ -120,18 +94,14 @@ class ValidRecurrenceRepetitionValue implements ValidationRule return $dayOfWeek > 0 && $dayOfWeek < 8; } - /** - * @param string $value - * - * @return bool - */ private function validateYearly(string $value): bool { // rest of the string must be valid date: $dateString = substr($value, 7); + try { Carbon::createFromFormat('Y-m-d', $dateString); - } catch (InvalidArgumentException $e) { // @phpstan-ignore-line + } catch (\InvalidArgumentException $e) { // @phpstan-ignore-line app('log')->debug(sprintf('Could not parse date %s: %s', $dateString, $e->getMessage())); return false; diff --git a/app/Services/FireflyIIIOrg/Update/UpdateRequest.php b/app/Services/FireflyIIIOrg/Update/UpdateRequest.php index fa683b1e34..b5d01c780f 100644 --- a/app/Services/FireflyIIIOrg/Update/UpdateRequest.php +++ b/app/Services/FireflyIIIOrg/Update/UpdateRequest.php @@ -28,18 +28,12 @@ use Carbon\Carbon; use FireflyIII\Events\NewVersionAvailable; use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; -use JsonException; /** * Class UpdateRequest */ class UpdateRequest implements UpdateRequestInterface { - /** - * @param string $channel - * - * @return array - */ public function getUpdateInformation(string $channel): array { app('log')->debug(sprintf('Now in getUpdateInformation(%s)', $channel)); @@ -62,11 +56,6 @@ class UpdateRequest implements UpdateRequestInterface return $this->parseResult($updateInfo); } - /** - * @param string $channel - * - * @return array - */ private function contactServer(string $channel): array { app('log')->debug(sprintf('Now in contactServer(%s)', $channel)); @@ -80,6 +69,7 @@ class UpdateRequest implements UpdateRequestInterface $url = config('firefly.update_endpoint'); app('log')->debug(sprintf('Going to call %s', $url)); + try { $client = new Client(); $options = [ @@ -106,9 +96,10 @@ class UpdateRequest implements UpdateRequestInterface return $return; } $body = (string)$res->getBody(); + try { $json = json_decode($body, true, 512, JSON_THROW_ON_ERROR); - } catch (JsonException $e) { + } catch (\JsonException $e) { app('log')->error('Body is not valid JSON'); app('log')->error($body); $return['message'] = 'Invalid JSON :('; @@ -137,11 +128,6 @@ class UpdateRequest implements UpdateRequestInterface return $return; } - /** - * @param array $information - * - * @return array - */ private function parseResult(array $information): array { app('log')->debug('Now in parseResult()', $information); diff --git a/app/Services/FireflyIIIOrg/Update/UpdateRequestInterface.php b/app/Services/FireflyIIIOrg/Update/UpdateRequestInterface.php index de0e8f294b..f0e51d43be 100644 --- a/app/Services/FireflyIIIOrg/Update/UpdateRequestInterface.php +++ b/app/Services/FireflyIIIOrg/Update/UpdateRequestInterface.php @@ -29,10 +29,5 @@ namespace FireflyIII\Services\FireflyIIIOrg\Update; */ interface UpdateRequestInterface { - /** - * @param string $channel - * - * @return array - */ public function getUpdateInformation(string $channel): array; } diff --git a/app/Services/Internal/Destroy/AccountDestroyService.php b/app/Services/Internal/Destroy/AccountDestroyService.php index dc8be9c16e..a388fe25cb 100644 --- a/app/Services/Internal/Destroy/AccountDestroyService.php +++ b/app/Services/Internal/Destroy/AccountDestroyService.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Services\Internal\Destroy; -use DB; use FireflyIII\Models\Account; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\RecurrenceTransaction; @@ -31,19 +30,12 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use Illuminate\Database\Eloquent\Builder; -use stdClass; /** * Class AccountDestroyService */ class AccountDestroyService { - /** - * @param Account $account - * @param Account|null $moveTo - * - * @return void - */ public function destroy(Account $account, ?Account $moveTo): void { // find and delete opening balance journal + opposing account @@ -69,25 +61,55 @@ class AccountDestroyService $account->delete(); } - /** - * @param Account $account - */ + public function moveTransactions(Account $account, Account $moveTo): void + { + app('log')->debug(sprintf('Move from account #%d to #%d', $account->id, $moveTo->id)); + \DB::table('transactions')->where('account_id', $account->id)->update(['account_id' => $moveTo->id]); + + $collection = Transaction::groupBy('transaction_journal_id', 'account_id') + ->where('account_id', $moveTo->id) + ->get(['transaction_journal_id', 'account_id', \DB::raw('count(*) as the_count')]) // @phpstan-ignore-line + ; + if (0 === $collection->count()) { + return; + } + + /** @var JournalDestroyService $service */ + $service = app(JournalDestroyService::class); + $user = $account->user; + + /** @var \stdClass $row */ + foreach ($collection as $row) { + if ((int)$row->the_count > 1) { + $journalId = $row->transaction_journal_id; + $journal = $user->transactionJournals()->find($journalId); + if (null !== $journal) { + app('log')->debug(sprintf('Deleted journal #%d because it has the same source as destination.', $journal->id)); + $service->destroy($journal); + } + } + } + } + private function destroyOpeningBalance(Account $account): void { app('log')->debug(sprintf('Searching for opening balance for account #%d "%s"', $account->id, $account->name)); $set = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') - ->where('transaction_types.type', TransactionType::OPENING_BALANCE) - ->get(['transactions.transaction_journal_id']); + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->where('transaction_types.type', TransactionType::OPENING_BALANCE) + ->get(['transactions.transaction_journal_id']) + ; if ($set->count() > 0) { $journalId = $set->first()->transaction_journal_id; app('log')->debug(sprintf('Found opening balance journal with ID #%d', $journalId)); // get transactions with this journal (should be just one): $transactions = Transaction::where('transaction_journal_id', $journalId) - ->where('account_id', '!=', $account->id) - ->get(); + ->where('account_id', '!=', $account->id) + ->get() + ; + /** @var Transaction $transaction */ foreach ($transactions as $transaction) { app('log')->debug(sprintf('Found transaction with ID #%d', $transaction->id)); @@ -107,76 +129,36 @@ class AccountDestroyService } } - /** - * @param Account $account - * @param Account $moveTo - */ - public function moveTransactions(Account $account, Account $moveTo): void - { - app('log')->debug(sprintf('Move from account #%d to #%d', $account->id, $moveTo->id)); - DB::table('transactions')->where('account_id', $account->id)->update(['account_id' => $moveTo->id]); - - $collection = Transaction::groupBy('transaction_journal_id', 'account_id') - ->where('account_id', $moveTo->id) - ->get(['transaction_journal_id', 'account_id', DB::raw('count(*) as the_count')]); // @phpstan-ignore-line - if (0 === $collection->count()) { - return; - } - - /** @var JournalDestroyService $service */ - $service = app(JournalDestroyService::class); - $user = $account->user; - /** @var stdClass $row */ - foreach ($collection as $row) { - if ((int)$row->the_count > 1) { - $journalId = $row->transaction_journal_id; - $journal = $user->transactionJournals()->find($journalId); - if (null !== $journal) { - app('log')->debug(sprintf('Deleted journal #%d because it has the same source as destination.', $journal->id)); - $service->destroy($journal); - } - } - } - } - - /** - * @param Account $account - * @param Account $moveTo - */ private function updateRecurrences(Account $account, Account $moveTo): void { - DB::table('recurrences_transactions')->where('source_id', $account->id)->update(['source_id' => $moveTo->id]); - DB::table('recurrences_transactions')->where('destination_id', $account->id)->update(['destination_id' => $moveTo->id]); + \DB::table('recurrences_transactions')->where('source_id', $account->id)->update(['source_id' => $moveTo->id]); + \DB::table('recurrences_transactions')->where('destination_id', $account->id)->update(['destination_id' => $moveTo->id]); } - /** - * @param Account $account - */ private function destroyJournals(Account $account): void { /** @var JournalDestroyService $service */ $service = app(JournalDestroyService::class); - app('log')->debug('Now trigger account delete response #' . $account->id); + app('log')->debug('Now trigger account delete response #'.$account->id); + /** @var Transaction $transaction */ foreach ($account->transactions()->get() as $transaction) { - app('log')->debug('Now at transaction #' . $transaction->id); - /** @var TransactionJournal|null $journal */ + app('log')->debug('Now at transaction #'.$transaction->id); + + /** @var null|TransactionJournal $journal */ $journal = $transaction->transactionJournal()->first(); if (null !== $journal) { - app('log')->debug('Call for deletion of journal #' . $journal->id); + app('log')->debug('Call for deletion of journal #'.$journal->id); $service->destroy($journal); } } } - /** - * @param Account $account - */ private function destroyRecurrences(Account $account): void { $recurrences = RecurrenceTransaction::where( - static function (Builder $q) use ($account) { + static function (Builder $q) use ($account): void { $q->where('source_id', $account->id); $q->orWhere('destination_id', $account->id); } diff --git a/app/Services/Internal/Destroy/BillDestroyService.php b/app/Services/Internal/Destroy/BillDestroyService.php index 6b0f48b007..a1cd2ce5f5 100644 --- a/app/Services/Internal/Destroy/BillDestroyService.php +++ b/app/Services/Internal/Destroy/BillDestroyService.php @@ -30,9 +30,6 @@ use FireflyIII\Models\Bill; */ class BillDestroyService { - /** - * @param Bill $bill - */ public function destroy(Bill $bill): void { $bill->delete(); diff --git a/app/Services/Internal/Destroy/BudgetDestroyService.php b/app/Services/Internal/Destroy/BudgetDestroyService.php index 29856c8773..51058e4491 100644 --- a/app/Services/Internal/Destroy/BudgetDestroyService.php +++ b/app/Services/Internal/Destroy/BudgetDestroyService.php @@ -23,19 +23,13 @@ declare(strict_types=1); namespace FireflyIII\Services\Internal\Destroy; -use DB; use FireflyIII\Models\Budget; /** * Class BudgetDestroyService - * - */ class BudgetDestroyService { - /** - * @param Budget $budget - */ public function destroy(Budget $budget): void { $budget->delete(); @@ -46,10 +40,10 @@ class BudgetDestroyService } // also delete all relations between categories and transaction journals: - DB::table('budget_transaction_journal')->where('budget_id', $budget->id)->delete(); + \DB::table('budget_transaction_journal')->where('budget_id', $budget->id)->delete(); // also delete all relations between categories and transactions: - DB::table('budget_transaction')->where('budget_id', $budget->id)->delete(); + \DB::table('budget_transaction')->where('budget_id', $budget->id)->delete(); // also delete all budget limits foreach ($budget->budgetlimits()->get() as $limit) { diff --git a/app/Services/Internal/Destroy/CategoryDestroyService.php b/app/Services/Internal/Destroy/CategoryDestroyService.php index 15daf3d6e2..5a3211acb5 100644 --- a/app/Services/Internal/Destroy/CategoryDestroyService.php +++ b/app/Services/Internal/Destroy/CategoryDestroyService.php @@ -23,30 +23,24 @@ declare(strict_types=1); namespace FireflyIII\Services\Internal\Destroy; -use DB; use FireflyIII\Models\Category; /** * Class CategoryDestroyService - * - */ class CategoryDestroyService { - /** - * @param Category $category - */ public function destroy(Category $category): void { $category->delete(); // also delete all relations between categories and transaction journals: - DB::table('category_transaction_journal')->where('category_id', $category->id)->delete(); + \DB::table('category_transaction_journal')->where('category_id', $category->id)->delete(); // also delete all relations between categories and transactions: - DB::table('category_transaction')->where('category_id', $category->id)->delete(); + \DB::table('category_transaction')->where('category_id', $category->id)->delete(); // delete references to category from recurring transactions. - DB::table('rt_meta')->where('name', 'category_id')->where('value', $category->id)->delete(); + \DB::table('rt_meta')->where('name', 'category_id')->where('value', $category->id)->delete(); } } diff --git a/app/Services/Internal/Destroy/CurrencyDestroyService.php b/app/Services/Internal/Destroy/CurrencyDestroyService.php index 4a2295d32a..7482740c7d 100644 --- a/app/Services/Internal/Destroy/CurrencyDestroyService.php +++ b/app/Services/Internal/Destroy/CurrencyDestroyService.php @@ -27,14 +27,9 @@ use FireflyIII\Models\TransactionCurrency; /** * Class CurrencyDestroyService - * - */ class CurrencyDestroyService { - /** - * @param TransactionCurrency $currency - */ public function destroy(TransactionCurrency $currency): void { $currency->delete(); diff --git a/app/Services/Internal/Destroy/JournalDestroyService.php b/app/Services/Internal/Destroy/JournalDestroyService.php index a49771a149..026d99e12d 100644 --- a/app/Services/Internal/Destroy/JournalDestroyService.php +++ b/app/Services/Internal/Destroy/JournalDestroyService.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Services\Internal\Destroy; -use DB; use FireflyIII\Models\Attachment; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; @@ -35,12 +34,10 @@ use FireflyIII\Models\TransactionJournalMeta; */ class JournalDestroyService { - /** - * @param TransactionJournal $journal - */ public function destroy(TransactionJournal $journal): void { app('log')->debug(sprintf('Now in %s', __METHOD__)); + /** @var Transaction $transaction */ foreach ($journal->transactions()->get() as $transaction) { app('log')->debug(sprintf('Will now delete transaction #%d', $transaction->id)); @@ -61,16 +58,19 @@ class JournalDestroyService } // delete all from 'budget_transaction_journal' - DB::table('budget_transaction_journal') - ->where('transaction_journal_id', $journal->id)->delete(); + \DB::table('budget_transaction_journal') + ->where('transaction_journal_id', $journal->id)->delete() + ; // delete all from 'category_transaction_journal' - DB::table('category_transaction_journal') - ->where('transaction_journal_id', $journal->id)->delete(); + \DB::table('category_transaction_journal') + ->where('transaction_journal_id', $journal->id)->delete() + ; // delete all from 'tag_transaction_journal' - DB::table('tag_transaction_journal') - ->where('transaction_journal_id', $journal->id)->delete(); + \DB::table('tag_transaction_journal') + ->where('transaction_journal_id', $journal->id)->delete() + ; // delete all links: TransactionJournalLink::where('source_id', $journal->id)->delete(); diff --git a/app/Services/Internal/Destroy/RecurrenceDestroyService.php b/app/Services/Internal/Destroy/RecurrenceDestroyService.php index 8937b13815..73949f34cb 100644 --- a/app/Services/Internal/Destroy/RecurrenceDestroyService.php +++ b/app/Services/Internal/Destroy/RecurrenceDestroyService.php @@ -33,8 +33,6 @@ class RecurrenceDestroyService { /** * Delete recurrence by ID - * - * @param int $recurrenceId */ public function destroyById(int $recurrenceId): void { @@ -47,14 +45,12 @@ class RecurrenceDestroyService /** * Delete recurrence. - * - * @param Recurrence $recurrence - * */ public function destroy(Recurrence $recurrence): void { // delete all meta data $recurrence->recurrenceMeta()->delete(); + // delete all transactions. /** @var RecurrenceTransaction $transaction */ foreach ($recurrence->recurrenceTransactions as $transaction) { diff --git a/app/Services/Internal/Destroy/TransactionGroupDestroyService.php b/app/Services/Internal/Destroy/TransactionGroupDestroyService.php index 94152f0cb9..2b5621cf4a 100644 --- a/app/Services/Internal/Destroy/TransactionGroupDestroyService.php +++ b/app/Services/Internal/Destroy/TransactionGroupDestroyService.php @@ -28,17 +28,13 @@ use FireflyIII\Models\TransactionGroup; /** * Class TransactionGroupDestroyService - * - */ class TransactionGroupDestroyService { - /** - * @param TransactionGroup $transactionGroup - */ public function destroy(TransactionGroup $transactionGroup): void { app('log')->debug(sprintf('Now in %s', __METHOD__)); + /** @var JournalDestroyService $service */ $service = app(JournalDestroyService::class); foreach ($transactionGroup->transactionJournals as $journal) { diff --git a/app/Services/Internal/Support/AccountServiceTrait.php b/app/Services/Internal/Support/AccountServiceTrait.php index c456ea5ca3..b35b7518ec 100644 --- a/app/Services/Internal/Support/AccountServiceTrait.php +++ b/app/Services/Internal/Support/AccountServiceTrait.php @@ -38,22 +38,14 @@ use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Services\Internal\Destroy\TransactionGroupDestroyService; -use JsonException; -use Validator; /** * Trait AccountServiceTrait - * */ trait AccountServiceTrait { protected AccountRepositoryInterface $accountRepository; - /** - * @param null|string $iban - * - * @return null|string - */ public function filterIban(?string $iban): ?string { if (null === $iban) { @@ -61,23 +53,18 @@ trait AccountServiceTrait } $data = ['iban' => $iban]; $rules = ['iban' => 'required|iban']; - $validator = Validator::make($data, $rules); + $validator = \Validator::make($data, $rules); if ($validator->fails()) { app('log')->info(sprintf('Detected invalid IBAN ("%s"). Return NULL instead.', $iban)); return null; } - return app('steam')->filterSpaces($iban); } /** * Returns true if the data in the array is submitted but empty. - * - * @param array $data - * - * @return bool */ public function isEmptyOBData(array $data): bool { @@ -101,16 +88,14 @@ trait AccountServiceTrait /** * Update metadata for account. Depends on type which fields are valid. * + * @SuppressWarnings(PHPMD.NPathComplexity) + * * TODO this method treats expense accounts and liabilities the same way (tries to save interest) - * - * @param Account $account - * @param array $data - * */ public function updateMetaData(Account $account, array $data): void { $fields = $this->validFields; - if ($account->accountType->type === AccountType::ASSET) { + if (AccountType::ASSET === $account->accountType->type) { $fields = $this->validAssetFields; } @@ -119,7 +104,7 @@ trait AccountServiceTrait $list = config('firefly.valid_currency_account_types'); if (!in_array($type, $list, true)) { $pos = array_search('currency_id', $fields, true); - if ($pos !== false) { + if (false !== $pos) { unset($fields[$pos]); } } @@ -133,13 +118,14 @@ trait AccountServiceTrait } // only asset account may have a role: - if ($account->accountType->type !== AccountType::ASSET) { + if (AccountType::ASSET !== $account->accountType->type) { $data['account_role'] = ''; } - if ($account->accountType->type === AccountType::ASSET && 'ccAsset' === $data['account_role']) { + if (AccountType::ASSET === $account->accountType->type && 'ccAsset' === $data['account_role']) { $fields = $this->validCCFields; } + /** @var AccountMetaFactory $factory */ $factory = app(AccountMetaFactory::class); foreach ($fields as $field) { @@ -162,12 +148,6 @@ trait AccountServiceTrait } } - /** - * @param Account $account - * @param string $note - * - * @return bool - */ public function updateNote(Account $account, string $note): bool { $dbNote = $account->notes()->first(); @@ -190,10 +170,6 @@ trait AccountServiceTrait /** * Verify if array contains valid data to possibly store or update the opening balance. - * - * @param array $data - * - * @return bool */ public function validOBData(array $data): bool { @@ -213,12 +189,8 @@ trait AccountServiceTrait } /** - * @param Account $account - * @param array $data - * - * @return TransactionGroup * @throws FireflyException - * @throws JsonException + * * * @deprecated */ protected function createOBGroup(Account $account, array $data): TransactionGroup @@ -250,6 +222,7 @@ trait AccountServiceTrait // amount is 0 if (0 === bccomp($amount, '0')) { app('log')->debug('Amount is zero, so will not make an OB group.'); + throw new FireflyException('Amount for new opening balance was unexpectedly 0.'); } @@ -303,6 +276,7 @@ trait AccountServiceTrait } catch (DuplicateTransactionException $e) { app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); + throw new FireflyException($e->getMessage(), 0, $e); } @@ -311,8 +285,6 @@ trait AccountServiceTrait /** * Delete TransactionGroup with liability credit in it. - * - * @param Account $account */ protected function deleteCreditTransaction(Account $account): void { @@ -321,6 +293,7 @@ trait AccountServiceTrait if (null !== $creditGroup) { app('log')->debug('Credit journal found, delete journal.'); + /** @var TransactionGroupDestroyService $service */ $service = app(TransactionGroupDestroyService::class); $service->destroy($creditGroup); @@ -329,10 +302,6 @@ trait AccountServiceTrait /** * Returns the credit transaction group, or NULL if it does not exist. - * - * @param Account $account - * - * @return TransactionGroup|null */ protected function getCreditTransaction(Account $account): ?TransactionGroup { @@ -343,8 +312,6 @@ trait AccountServiceTrait /** * Delete TransactionGroup with opening balance in it. - * - * @param Account $account */ protected function deleteOBGroup(Account $account): void { @@ -354,6 +321,7 @@ trait AccountServiceTrait // opening balance data? update it! if (null !== $openingBalanceGroup) { app('log')->debug('Opening balance journal found, delete journal.'); + /** @var TransactionGroupDestroyService $service */ $service = app(TransactionGroupDestroyService::class); $service->destroy($openingBalanceGroup); @@ -362,10 +330,6 @@ trait AccountServiceTrait /** * Returns the opening balance group, or NULL if it does not exist. - * - * @param Account $account - * - * @return TransactionGroup|null */ protected function getOBGroup(Account $account): ?TransactionGroup { @@ -373,19 +337,15 @@ trait AccountServiceTrait } /** - * @param int $currencyId - * @param string $currencyCode - * - * @return TransactionCurrency * @throws FireflyException - * @throws JsonException */ protected function getCurrency(int $currencyId, string $currencyCode): TransactionCurrency { // find currency, or use default currency instead. /** @var TransactionCurrencyFactory $factory */ $factory = app(TransactionCurrencyFactory::class); - /** @var TransactionCurrency|null $currency */ + + /** @var null|TransactionCurrency $currency */ $currency = $factory->find($currencyId, $currencyCode); if (null === $currency) { @@ -401,15 +361,7 @@ trait AccountServiceTrait /** * Create the opposing "credit liability" transaction for credit liabilities. * - * - * @param Account $account - * @param string $direction - * @param string $openingBalance - * @param Carbon $openingBalanceDate - * - * @return TransactionGroup * @throws FireflyException - * @throws JsonException */ protected function updateCreditTransaction(Account $account, string $direction, string $openingBalance, Carbon $openingBalanceDate): TransactionGroup { @@ -417,6 +369,7 @@ trait AccountServiceTrait if (0 === bccomp($openingBalance, '0')) { app('log')->debug('Amount is zero, so will not update liability credit/debit group.'); + throw new FireflyException('Amount for update liability credit/debit was unexpectedly 0.'); } // if direction is "debit" (i owe this debt), amount is negative. @@ -464,13 +417,7 @@ trait AccountServiceTrait } /** - * @param Account $account - * @param string $openingBalance - * @param Carbon $openingBalanceDate - * - * @return TransactionGroup * @throws FireflyException - * @throws JsonException */ protected function createCreditTransaction(Account $account, string $openingBalance, Carbon $openingBalanceDate): TransactionGroup { @@ -478,6 +425,7 @@ trait AccountServiceTrait if (0 === bccomp($openingBalance, '0')) { app('log')->debug('Amount is zero, so will not make an liability credit group.'); + throw new FireflyException('Amount for new liability credit was unexpectedly 0.'); } @@ -552,80 +500,18 @@ trait AccountServiceTrait } catch (DuplicateTransactionException $e) { app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); + throw new FireflyException($e->getMessage(), 0, $e); } return $group; } - /** - * TODO refactor to "getfirstjournal" - * - * @param TransactionGroup $group - * - * @return TransactionJournal - * @throws FireflyException - */ - private function getObJournal(TransactionGroup $group): TransactionJournal - { - /** @var TransactionJournal|null $journal */ - $journal = $group->transactionJournals()->first(); - if (null === $journal) { - throw new FireflyException(sprintf('Group #%d has no OB journal', $group->id)); - } - - return $journal; - } - - /** - * TODO Rename to getOpposingTransaction - * - * @param TransactionJournal $journal - * @param Account $account - * - * @return Transaction - * @throws FireflyException - */ - private function getOBTransaction(TransactionJournal $journal, Account $account): Transaction - { - /** @var Transaction|null $transaction */ - $transaction = $journal->transactions()->where('account_id', '!=', $account->id)->first(); - if (null === $transaction) { - throw new FireflyException(sprintf('Could not get OB transaction for journal #%d', $journal->id)); - } - - return $transaction; - } - - /** - * @param TransactionJournal $journal - * @param Account $account - * - * @return Transaction - * @throws FireflyException - */ - private function getNotOBTransaction(TransactionJournal $journal, Account $account): Transaction - { - /** @var Transaction|null $transaction */ - $transaction = $journal->transactions()->where('account_id', $account->id)->first(); - if (null === $transaction) { - throw new FireflyException(sprintf('Could not get non-OB transaction for journal #%d', $journal->id)); - } - - return $transaction; - } - /** * Update or create the opening balance group. * Since opening balance and date can still be empty strings, it may fail. * - * @param Account $account - * @param string $openingBalance - * @param Carbon $openingBalanceDate - * - * @return TransactionGroup * @throws FireflyException - * @throws JsonException */ protected function updateOBGroupV2(Account $account, string $openingBalance, Carbon $openingBalanceDate): TransactionGroup { @@ -650,7 +536,6 @@ trait AccountServiceTrait $journal->date = $openingBalanceDate; $journal->transactionCurrency()->associate($currency); - // if amount is negative: if (1 === bccomp('0', $openingBalance)) { app('log')->debug('Amount is negative.'); @@ -682,13 +567,7 @@ trait AccountServiceTrait } /** - * @param Account $account - * @param string $openingBalance - * @param Carbon $openingBalanceDate - * - * @return TransactionGroup * @throws FireflyException - * @throws JsonException */ protected function createOBGroupV2(Account $account, string $openingBalance, Carbon $openingBalanceDate): TransactionGroup { @@ -718,6 +597,7 @@ trait AccountServiceTrait // amount is 0 if (0 === bccomp($openingBalance, '0')) { app('log')->debug('Amount is zero, so will not make an OB group.'); + throw new FireflyException('Amount for new opening balance was unexpectedly 0.'); } @@ -771,9 +651,56 @@ trait AccountServiceTrait } catch (DuplicateTransactionException $e) { app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); + throw new FireflyException($e->getMessage(), 0, $e); } return $group; } + + /** + * TODO refactor to "getfirstjournal" + * + * @throws FireflyException + */ + private function getObJournal(TransactionGroup $group): TransactionJournal + { + /** @var null|TransactionJournal $journal */ + $journal = $group->transactionJournals()->first(); + if (null === $journal) { + throw new FireflyException(sprintf('Group #%d has no OB journal', $group->id)); + } + + return $journal; + } + + /** + * TODO Rename to getOpposingTransaction + * + * @throws FireflyException + */ + private function getOBTransaction(TransactionJournal $journal, Account $account): Transaction + { + /** @var null|Transaction $transaction */ + $transaction = $journal->transactions()->where('account_id', '!=', $account->id)->first(); + if (null === $transaction) { + throw new FireflyException(sprintf('Could not get OB transaction for journal #%d', $journal->id)); + } + + return $transaction; + } + + /** + * @throws FireflyException + */ + private function getNotOBTransaction(TransactionJournal $journal, Account $account): Transaction + { + /** @var null|Transaction $transaction */ + $transaction = $journal->transactions()->where('account_id', $account->id)->first(); + if (null === $transaction) { + throw new FireflyException(sprintf('Could not get non-OB transaction for journal #%d', $journal->id)); + } + + return $transaction; + } } diff --git a/app/Services/Internal/Support/BillServiceTrait.php b/app/Services/Internal/Support/BillServiceTrait.php index 06ab5e443a..15bc6514ff 100644 --- a/app/Services/Internal/Support/BillServiceTrait.php +++ b/app/Services/Internal/Support/BillServiceTrait.php @@ -29,16 +29,9 @@ use FireflyIII\Models\RuleAction; /** * Trait BillServiceTrait - * - */ trait BillServiceTrait { - /** - * @param Bill $bill - * @param string $oldName - * @param string $newName - */ public function updateBillActions(Bill $bill, string $oldName, string $newName): void { if ($oldName === $newName) { @@ -46,8 +39,9 @@ trait BillServiceTrait } $ruleIds = $bill->user->rules()->get(['id'])->pluck('id')->toArray(); $set = RuleAction::whereIn('rule_id', $ruleIds) - ->where('action_type', 'link_to_bill') - ->where('action_value', $oldName)->get(); + ->where('action_type', 'link_to_bill') + ->where('action_value', $oldName)->get() + ; /** @var RuleAction $ruleAction */ foreach ($set as $ruleAction) { @@ -57,12 +51,6 @@ trait BillServiceTrait } } - /** - * @param Bill $bill - * @param string $note - * - * @return bool - */ public function updateNote(Bill $bill, string $note): bool { if ('' === $note) { diff --git a/app/Services/Internal/Support/CreditRecalculateService.php b/app/Services/Internal/Support/CreditRecalculateService.php index ae726fd6e0..ecfe0057b3 100644 --- a/app/Services/Internal/Support/CreditRecalculateService.php +++ b/app/Services/Internal/Support/CreditRecalculateService.php @@ -28,6 +28,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\AccountMetaFactory; use FireflyIII\Models\Account; use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; @@ -53,9 +54,6 @@ class CreditRecalculateService $this->work = []; } - /** - * - */ public function recalculate(): void { if (true !== config('firefly.feature_flags.handle_debts')) { @@ -74,9 +72,16 @@ class CreditRecalculateService $this->processWork(); } - /** - * - */ + public function setAccount(?Account $account): void + { + $this->account = $account; + } + + public function setGroup(TransactionGroup $group): void + { + $this->group = $group; + } + private function processGroup(): void { /** @var TransactionJournal $journal */ @@ -91,8 +96,6 @@ class CreditRecalculateService } /** - * @param TransactionJournal $journal - * * @throws FireflyException */ private function findByJournal(TransactionJournal $journal): void @@ -111,9 +114,6 @@ class CreditRecalculateService } /** - * @param TransactionJournal $journal - * - * @return Account * @throws FireflyException */ private function getSourceAccount(TransactionJournal $journal): Account @@ -122,20 +122,17 @@ class CreditRecalculateService } /** - * @param TransactionJournal $journal - * @param string $direction - * - * @return Account * @throws FireflyException */ private function getAccountByDirection(TransactionJournal $journal, string $direction): Account { - /** @var Transaction|null $transaction */ + /** @var null|Transaction $transaction */ $transaction = $journal->transactions()->where('amount', $direction, '0')->first(); if (null === $transaction) { throw new FireflyException(sprintf('Cannot find "%s"-transaction of journal #%d', $direction, $journal->id)); } - /** @var Account|null $foundAccount */ + + /** @var null|Account $foundAccount */ $foundAccount = $transaction->account; if (null === $foundAccount) { throw new FireflyException(sprintf('Cannot find "%s"-account of transaction #%d of journal #%d', $direction, $transaction->id, $journal->id)); @@ -145,9 +142,6 @@ class CreditRecalculateService } /** - * @param TransactionJournal $journal - * - * @return Account * @throws FireflyException */ private function getDestinationAccount(TransactionJournal $journal): Account @@ -155,9 +149,6 @@ class CreditRecalculateService return $this->getAccountByDirection($journal, '>'); } - /** - * - */ private function processAccount(): void { $valid = config('firefly.valid_liabilities'); @@ -166,9 +157,6 @@ class CreditRecalculateService } } - /** - * - */ private function processWork(): void { $this->repository = app(AccountRepositoryInterface::class); @@ -177,15 +165,12 @@ class CreditRecalculateService } } - /** - * @param Account $account - */ private function processWorkAccount(Account $account): void { - app('log')->debug(sprintf('Now processing account #%d ("%s")', $account->id, $account->name)); + app('log')->debug(sprintf('Now processing account #%d ("%s"). All amounts with 2 decimals!', $account->id, $account->name)); // get opening balance (if present) $this->repository->setUser($account->user); - $direction = (string)$this->repository->getMetaValue($account, 'liability_direction'); + $direction = (string) $this->repository->getMetaValue($account, 'liability_direction'); $openingBalance = $this->repository->getOpeningBalance($account); if (null !== $openingBalance) { app('log')->debug(sprintf('Found opening balance transaction journal #%d', $openingBalance->id)); @@ -196,9 +181,7 @@ class CreditRecalculateService } $startOfDebt = $this->repository->getOpeningBalanceAmount($account) ?? '0'; $leftOfDebt = app('steam')->positive($startOfDebt); - $currency = $this->repository->getAccountCurrency($account); - $decimals = $currency?->decimal_places ?? 2; - app('log')->debug(sprintf('Start of debt is "%s", so initial left of debt is "%s"', app('steam')->bcround($startOfDebt, $decimals), app('steam')->bcround($leftOfDebt, $decimals))); + app('log')->debug(sprintf('Start of debt is "%s", so initial left of debt is "%s"', app('steam')->bcround($startOfDebt, 2), app('steam')->bcround($leftOfDebt, 2))); /** @var AccountMetaFactory $factory */ $factory = app(AccountMetaFactory::class); @@ -210,11 +193,13 @@ class CreditRecalculateService // now loop all transactions (except opening balance and credit thing) $transactions = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->orderBy('transaction_journals.date', 'ASC') - ->get(['transactions.*']); + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->orderBy('transaction_journals.date', 'ASC') + ->get(['transactions.*']) + ; $total = $transactions->count(); app('log')->debug(sprintf('Found %d transaction(s) to process.', $total)); + /** @var Transaction $transaction */ foreach ($transactions as $index => $transaction) { app('log')->debug(sprintf('[%d/%d] Processing transaction.', $index + 1, $total)); @@ -226,16 +211,12 @@ class CreditRecalculateService /** * If account direction is "debit" ("I owe this amount") the opening balance must always be AWAY from the account: - * - * @param Account $account - * @param TransactionJournal $openingBalance - * - * @return void */ - private function validateOpeningBalance(Account $account, TransactionJournal $openingBalance) + private function validateOpeningBalance(Account $account, TransactionJournal $openingBalance): void { /** @var Transaction $source */ $source = $openingBalance->transactions()->where('amount', '<', 0)->first(); + /** @var Transaction $dest */ $dest = $openingBalance->transactions()->where('amount', '>', 0)->first(); if ($source->account_id !== $account->id) { @@ -255,201 +236,196 @@ class CreditRecalculateService } $source->save(); $dest->save(); + return; } app('log')->debug('Opening balance is valid'); } /** - * @param Account $account - * @param string $direction - * @param Transaction $transaction - * @param string $leftOfDebt + * A complex and long method, but rarely used luckily. * - * @return string + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ private function processTransaction(Account $account, string $direction, Transaction $transaction, string $leftOfDebt): string { $journal = $transaction->transactionJournal; $foreignCurrency = $transaction->foreignCurrency; $accountCurrency = $this->repository->getAccountCurrency($account); - $decimals = $accountCurrency->decimal_places; $type = $journal->transactionType->type; - /** @var Transaction $destTransaction */ - $destTransaction = $journal->transactions()->where('amount', '>', '0')->first(); - /** @var Transaction $sourceTransaction */ - $sourceTransaction = $journal->transactions()->where('amount', '<', '0')->first(); - - - app('log')->debug(sprintf('Left of debt is: %s', app('steam')->bcround($leftOfDebt, $decimals))); + app('log')->debug(sprintf('Left of debt is: %s', app('steam')->bcround($leftOfDebt, 2))); if ('' === $direction) { app('log')->warning('Direction is empty, so do nothing.'); + return $leftOfDebt; } if (TransactionType::LIABILITY_CREDIT === $type || TransactionType::OPENING_BALANCE === $type) { app('log')->warning(sprintf('Transaction type is "%s", so do nothing.', $type)); + return $leftOfDebt; } // amount to use depends on the currency: - $usedAmount = $transaction->amount; - app('log')->debug(sprintf('Amount of transaction is %s', app('steam')->bcround($usedAmount, $decimals))); - if (null !== $foreignCurrency && $foreignCurrency->id === $accountCurrency->id) { - $usedAmount = $transaction->foreign_amount; - app('log')->debug(sprintf('Overruled by foreign amount. Amount of transaction is now %s', app('steam')->bcround($usedAmount, $decimals))); - } + $usedAmount = $this->getAmountToUse($transaction, $accountCurrency, $foreignCurrency); + $isSameAccount = $account->id === $transaction->account_id; + $isDebit = 'debit' === $direction; + $isCredit = 'credit' === $direction; - // Case 1 - // it's a withdrawal into this liability (from asset). - // if it's a credit ("I am owed"), this increases the amount due, - // because we're lending person X more money - if ( - $type === TransactionType::WITHDRAWAL - && $account->id === $transaction->account_id - && 1 === bccomp($usedAmount, '0') - && 'credit' === $direction - ) { + if ($isSameAccount && $isCredit && $this->isWithdrawalIn($usedAmount, $type)) { // case 1 $usedAmount = app('steam')->positive($usedAmount); $result = bcadd($leftOfDebt, $usedAmount); - app('log')->debug(sprintf('Case 1 (withdrawal into credit liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, $decimals), app('steam')->bcround($usedAmount, $decimals), app('steam')->bcround($result, $decimals))); + app('log')->debug(sprintf('Case 1 (withdrawal into credit liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); + return $result; } - // Case 2 - // it's a withdrawal away from this liability (into expense account). - // if it's a credit ("I am owed"), this decreases the amount due, - // because we're sending money away from the loan (like loan forgiveness) - if ( - $type === TransactionType::WITHDRAWAL - && $account->id === $sourceTransaction->account_id - && -1 === bccomp($usedAmount, '0') - && 'credit' === $direction - ) { + if ($isSameAccount && $isCredit && $this->isWithdrawalOut($usedAmount, $type)) { // case 2 $usedAmount = app('steam')->positive($usedAmount); $result = bcsub($leftOfDebt, $usedAmount); - app('log')->debug(sprintf('Case 2 (withdrawal away from liability): %s - %s = %s', app('steam')->bcround($leftOfDebt, $decimals), app('steam')->bcround($usedAmount, $decimals), app('steam')->bcround($result, $decimals))); + app('log')->debug(sprintf('Case 2 (withdrawal away from liability): %s - %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); + return $result; } - // case 3 - // it's a deposit out of this liability (to asset). - // if it's a credit ("I am owed") this decreases the amount due. - // because the person is paying us back. - if ( - $type === TransactionType::DEPOSIT - && $account->id === $transaction->account_id - && -1 === bccomp($usedAmount, '0') - && 'credit' === $direction - ) { + if ($isSameAccount && $isCredit && $this->isDepositOut($usedAmount, $type)) { // case 3 $usedAmount = app('steam')->positive($usedAmount); $result = bcsub($leftOfDebt, $usedAmount); - app('log')->debug(sprintf('Case 3 (deposit away from liability): %s - %s = %s', app('steam')->bcround($leftOfDebt, $decimals), app('steam')->bcround($usedAmount, $decimals), app('steam')->bcround($result, $decimals))); + app('log')->debug(sprintf('Case 3 (deposit away from liability): %s - %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); + return $result; } - // case 4 - // it's a deposit into this liability (from revenue account). - // if it's a credit ("I am owed") this increases the amount due. - // because the person is having to pay more money. - if ( - $type === TransactionType::DEPOSIT - && $account->id === $destTransaction->account_id - && 1 === bccomp($usedAmount, '0') - && 'credit' === $direction - ) { + if ($isSameAccount && $isCredit && $this->isDepositIn($usedAmount, $type)) { // case 4 $usedAmount = app('steam')->positive($usedAmount); $result = bcadd($leftOfDebt, $usedAmount); - app('log')->debug(sprintf('Case 4 (deposit into credit liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, $decimals), app('steam')->bcround($usedAmount, $decimals), app('steam')->bcround($result, $decimals))); + app('log')->debug(sprintf('Case 4 (deposit into credit liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); + return $result; } - // case 5: transfer into loan (from other loan). - // if it's a credit ("I am owed") this increases the amount due, - // because the person has to pay more back. - if ( - $type === TransactionType::TRANSFER - && $account->id === $destTransaction->account_id - && 1 === bccomp($usedAmount, '0') - && 'credit' === $direction - ) { + if ($isSameAccount && $isCredit && $this->isTransferIn($usedAmount, $type)) { // case 5 $usedAmount = app('steam')->positive($usedAmount); $result = bcadd($leftOfDebt, $usedAmount); - app('log')->debug(sprintf('Case 5 (transfer into credit liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, $decimals), app('steam')->bcround($usedAmount, $decimals), app('steam')->bcround($result, $decimals))); + app('log')->debug(sprintf('Case 5 (transfer into credit liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); + return $result; } - // Case 6 - // it's a withdrawal into this liability (from asset). - // if it's a debit ("I owe this amount"), this decreases the amount due, - // because we're paying off the debt - if ( - $type === TransactionType::WITHDRAWAL - && $account->id === $transaction->account_id - && 1 === bccomp($usedAmount, '0') - && 'debit' === $direction - ) { + if ($isSameAccount && $isDebit && $this->isWithdrawalIn($usedAmount, $type)) { // case 6 $usedAmount = app('steam')->positive($usedAmount); $result = bcsub($leftOfDebt, $usedAmount); - app('log')->debug(sprintf('Case 6 (withdrawal into debit liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, $decimals), app('steam')->bcround($usedAmount, $decimals), app('steam')->bcround($result, $decimals))); - return $result; - } - // case 7 - // it's a deposit out of this liability (to asset). - // if it's a credit ("I am owed") this increases the amount due. - // because we are borrowing more money. - if ( - $type === TransactionType::DEPOSIT - && $account->id === $transaction->account_id - && -1 === bccomp($usedAmount, '0') - && 'debit' === $direction - ) { - $usedAmount = app('steam')->positive($usedAmount); - $result = bcadd($leftOfDebt, $usedAmount); - app('log')->debug(sprintf('Case 7 (deposit away from liability): %s - %s = %s', app('steam')->bcround($leftOfDebt, $decimals), app('steam')->bcround($usedAmount, $decimals), app('steam')->bcround($result, $decimals))); - return $result; - } - // case 8 - // it's a withdrawal from this liability (to expense account). - // if it's a debit ("I owe this amount") this increase the amount due. - // because we are paying interest. - if ( - $type === TransactionType::WITHDRAWAL - && $account->id === $transaction->account_id - && -1 === bccomp($usedAmount, '0') - && 'debit' === $direction - ) { - $usedAmount = app('steam')->positive($usedAmount); - $result = bcadd($leftOfDebt, $usedAmount); - app('log')->debug(sprintf('Case 8 (withdrawal away from liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, $decimals), app('steam')->bcround($usedAmount, $decimals), app('steam')->bcround($result, $decimals))); - return $result; - } + app('log')->debug(sprintf('Case 6 (withdrawal into debit liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); + return $result; + } + if ($isSameAccount && $isDebit && $this->isDepositOut($usedAmount, $type)) { // case 7 + $usedAmount = app('steam')->positive($usedAmount); + $result = bcadd($leftOfDebt, $usedAmount); + app('log')->debug(sprintf('Case 7 (deposit away from liability): %s - %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); + + return $result; + } + if ($isSameAccount && $isDebit && $this->isWithdrawalOut($usedAmount, $type)) { // case 8 + $usedAmount = app('steam')->positive($usedAmount); + $result = bcadd($leftOfDebt, $usedAmount); + app('log')->debug(sprintf('Case 8 (withdrawal away from liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); + + return $result; + } // in any other case, remove amount from left of debt. if (in_array($type, [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER], true)) { $usedAmount = app('steam')->negative($usedAmount); $result = bcadd($leftOfDebt, $usedAmount); - app('log')->debug(sprintf('Case X (all other cases): %s + %s = %s', app('steam')->bcround($leftOfDebt, $decimals), app('steam')->bcround($usedAmount, $decimals), app('steam')->bcround($result, $decimals))); + app('log')->debug(sprintf('Case X (all other cases): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); + return $result; } - app('log')->warning(sprintf('[-1] Catch-all, should not happen. Left of debt = %s', app('steam')->bcround($leftOfDebt, $decimals))); + app('log')->warning(sprintf('[-1] Catch-all, should not happen. Left of debt = %s', app('steam')->bcround($leftOfDebt, 2))); return $leftOfDebt; } - /** - * @param Account|null $account - */ - public function setAccount(?Account $account): void + private function getAmountToUse(Transaction $transaction, TransactionCurrency $accountCurrency, ?TransactionCurrency $foreignCurrency): string { - $this->account = $account; + $usedAmount = $transaction->amount; + app('log')->debug(sprintf('Amount of transaction is %s', app('steam')->bcround($usedAmount, 2))); + if (null !== $foreignCurrency && $foreignCurrency->id === $accountCurrency->id) { + $usedAmount = $transaction->foreign_amount; + app('log')->debug(sprintf('Overruled by foreign amount. Amount of transaction is now %s', app('steam')->bcround($usedAmount, 2))); + } + + return $usedAmount; } /** - * @param TransactionGroup $group + * It's a withdrawal into this liability (from asset). + * + * Case 1 = credit + * if it's a credit ("I am owed"), this increases the amount due, + * because we're lending person X more money + * + * Case 6 = debit + * if it's a debit ("I owe this amount"), this decreases the amount due, + * because we're paying off the debt */ - public function setGroup(TransactionGroup $group): void + private function isWithdrawalIn(string $amount, string $transactionType): bool { - $this->group = $group; + return TransactionType::WITHDRAWAL === $transactionType && 1 === bccomp($amount, '0'); + } + + /** + * it's a withdrawal away from this liability (into expense account). + * + * Case 2 + * if it's a credit ("I am owed"), this decreases the amount due, + * because we're sending money away from the loan (like loan forgiveness) + * + * Case 8 + * if it's a debit ("I owe this amount") this increase the amount due. + * because we are paying interest. + */ + private function isWithdrawalOut(string $amount, string $transactionType): bool + { + return TransactionType::WITHDRAWAL === $transactionType && -1 === bccomp($amount, '0'); + } + + /** + * it's a deposit out of this liability (to asset). + * + * case 3 + * if it's a credit ("I am owed") this decreases the amount due. + * because the person is paying us back. + * + * case 7 + * if it's a credit ("I am owed") this increases the amount due. + * because we are borrowing more money. + */ + private function isDepositOut(string $amount, string $transactionType): bool + { + return TransactionType::DEPOSIT === $transactionType && -1 === bccomp($amount, '0'); + } + + /** + * case 4 + * it's a deposit into this liability (from revenue account). + * if it's a credit ("I am owed") this increases the amount due. + * because the person is having to pay more money. + */ + private function isDepositIn(string $amount, string $transactionType): bool + { + return TransactionType::DEPOSIT === $transactionType && 1 === bccomp($amount, '0'); + } + + /** + * case 5: transfer into loan (from other loan). + * if it's a credit ("I am owed") this increases the amount due, + * because the person has to pay more back. + */ + private function isTransferIn(string $amount, string $transactionType): bool + { + return TransactionType::TRANSFER === $transactionType && 1 === bccomp($amount, '0'); } } diff --git a/app/Services/Internal/Support/JournalServiceTrait.php b/app/Services/Internal/Support/JournalServiceTrait.php index 8dd6a93a96..f7dfabc158 100644 --- a/app/Services/Internal/Support/JournalServiceTrait.php +++ b/app/Services/Internal/Support/JournalServiceTrait.php @@ -38,7 +38,6 @@ use FireflyIII\Support\NullArrayObject; /** * Trait JournalServiceTrait - * */ trait JournalServiceTrait { @@ -48,11 +47,6 @@ trait JournalServiceTrait private TagFactory $tagFactory; /** - * @param string $transactionType - * @param string $direction - * @param array $data - * - * @return Account|null * @throws FireflyException */ protected function getAccount(string $transactionType, string $direction, array $data): ?Account @@ -89,7 +83,6 @@ trait JournalServiceTrait // the account that Firefly III creates must be "creatable", aka select the one we can create from the list just in case $creatableType = $this->getCreatableType($expectedTypes[$transactionType]); - // if the result is NULL but the ID is set, an account could exist of the wrong type. // that data can be used to create a new account of the right type. if (null === $result && null !== $data['id'] && null !== $creatableType) { @@ -113,15 +106,128 @@ trait JournalServiceTrait app('log')->debug('If cant be created, return cash account.'); $result = $this->getCashAccount($result, $data, $expectedTypes[$transactionType]); } + return $result; } /** - * @param array $data - * @param array $types - * - * @return Account|null + * @throws FireflyException */ + protected function getAmount(string $amount): string + { + if ('' === $amount) { + throw new FireflyException(sprintf('The amount cannot be an empty string: "%s"', $amount)); + } + app('log')->debug(sprintf('Now in getAmount("%s")', $amount)); + if (0 === bccomp('0', $amount)) { + throw new FireflyException(sprintf('The amount seems to be zero: "%s"', $amount)); + } + + return $amount; + } + + protected function getForeignAmount(?string $amount): ?string + { + if (null === $amount) { + app('log')->debug('No foreign amount info in array. Return NULL'); + + return null; + } + if ('' === $amount) { + app('log')->debug('Foreign amount is empty string, return NULL.'); + + return null; + } + if (0 === bccomp('0', $amount)) { + app('log')->debug('Foreign amount is 0.0, return NULL.'); + + return null; + } + app('log')->debug(sprintf('Foreign amount is %s', $amount)); + + return $amount; + } + + protected function storeBudget(TransactionJournal $journal, NullArrayObject $data): void + { + if (TransactionType::WITHDRAWAL !== $journal->transactionType->type) { + $journal->budgets()->sync([]); + + return; + } + $budget = $this->budgetRepository->findBudget($data['budget_id'], $data['budget_name']); + if (null !== $budget) { + app('log')->debug(sprintf('Link budget #%d to journal #%d', $budget->id, $journal->id)); + $journal->budgets()->sync([$budget->id]); + + return; + } + // if the budget is NULL, sync empty. + $journal->budgets()->sync([]); + } + + protected function storeCategory(TransactionJournal $journal, NullArrayObject $data): void + { + $category = $this->categoryRepository->findCategory($data['category_id'], $data['category_name']); + if (null !== $category) { + app('log')->debug(sprintf('Link category #%d to journal #%d', $category->id, $journal->id)); + $journal->categories()->sync([$category->id]); + + return; + } + // if the category is NULL, sync empty. + $journal->categories()->sync([]); + } + + protected function storeNotes(TransactionJournal $journal, ?string $notes): void + { + $notes = (string)$notes; + $note = $journal->notes()->first(); + if ('' !== $notes) { + if (null === $note) { + $note = new Note(); + $note->noteable()->associate($journal); + } + $note->text = $notes; + $note->save(); + app('log')->debug(sprintf('Stored notes for journal #%d', $journal->id)); + + return; + } + // try to delete existing notes. + $note?->delete(); + } + + /** + * Link tags to journal. + */ + protected function storeTags(TransactionJournal $journal, ?array $tags): void + { + app('log')->debug('Now in storeTags()', $tags ?? []); + $this->tagFactory->setUser($journal->user); + $set = []; + if (!is_array($tags)) { + app('log')->debug('Tags is not an array, break.'); + + return; + } + app('log')->debug('Start of loop.'); + foreach ($tags as $string) { + $string = (string)$string; + app('log')->debug(sprintf('Now at tag "%s"', $string)); + if ('' !== $string) { + $tag = $this->tagFactory->findOrCreate($string); + if (null !== $tag) { + $set[] = $tag->id; + } + } + } + $set = array_unique($set); + app('log')->debug('End of loop.'); + app('log')->debug(sprintf('Total nr. of tags: %d', count($tags)), $tags); + $journal->tags()->sync($set); + } + private function findAccountById(array $data, array $types): ?Account { // first attempt, find by ID. @@ -131,34 +237,32 @@ trait JournalServiceTrait app('log')->debug( sprintf('Found "account_id" object: #%d, "%s" of type %s (1)', $search->id, $search->name, $search->accountType->type) ); + return $search; } if (null !== $search && 0 === count($types)) { app('log')->debug( sprintf('Found "account_id" object: #%d, "%s" of type %s (2)', $search->id, $search->name, $search->accountType->type) ); + return $search; } } app('log')->debug(sprintf('Found no account by ID #%d of types', $data['id']), $types); + return null; } - /** - * @param Account|null $account - * @param array $data - * @param array $types - * - * @return Account|null - */ private function findAccountByIban(?Account $account, array $data, array $types): ?Account { if (null !== $account) { app('log')->debug(sprintf('Already have account #%d ("%s"), return that.', $account->id, $account->name)); + return $account; } if (null === $data['iban'] || '' === $data['iban']) { app('log')->debug('IBAN is empty, will not search for IBAN.'); + return null; } // find by preferred type. @@ -172,24 +276,20 @@ trait JournalServiceTrait return $source; } app('log')->debug(sprintf('Found no account with IBAN "%s" of expected types', $data['iban']), $types); + return null; } - /** - * @param Account|null $account - * @param array $data - * @param array $types - * - * @return Account|null - */ private function findAccountByNumber(?Account $account, array $data, array $types): ?Account { if (null !== $account) { app('log')->debug(sprintf('Already have account #%d ("%s"), return that.', $account->id, $account->name)); + return $account; } if (null === $data['number'] || '' === $data['number']) { app('log')->debug('Account number is empty, will not search for account number.'); + return null; } // find by preferred type. @@ -205,24 +305,20 @@ trait JournalServiceTrait } app('log')->debug(sprintf('Found no account with account number "%s" of expected types', $data['number']), $types); + return null; } - /** - * @param Account|null $account - * @param array $data - * @param array $types - * - * @return Account|null - */ private function findAccountByName(?Account $account, array $data, array $types): ?Account { if (null !== $account) { app('log')->debug(sprintf('Already have account #%d ("%s"), return that.', $account->id, $account->name)); + return $account; } if (null === $data['name'] || '' === $data['name']) { app('log')->debug('Account name is empty, will not search for account name.'); + return null; } @@ -238,34 +334,28 @@ trait JournalServiceTrait return $source; } app('log')->debug(sprintf('Found no account with account name "%s" of expected types', $data['name']), $types); + return null; } - /** - * @param array $types - * - * @return null|string - */ private function getCreatableType(array $types): ?string { $result = null; $list = config('firefly.dynamic_creation_allowed'); + /** @var string $type */ foreach ($types as $type) { if (true === in_array($type, $list, true)) { $result = $type; + break; } } + return $result; } /** - * @param Account|null $account - * @param array $data - * @param string $preferredType - * - * @return Account|null * @throws FireflyException */ private function createAccount(?Account $account, array $data, string $preferredType): ?Account @@ -300,9 +390,10 @@ trait JournalServiceTrait // if name is still NULL, return NULL. if ('' === (string)$data['name']) { app('log')->debug('Account name is still NULL, return NULL.'); + return null; } - //$data['name'] = $data['name'] ?? '(no name)'; + // $data['name'] = $data['name'] ?? '(no name)'; $account = $this->accountRepository->store( [ @@ -333,13 +424,6 @@ trait JournalServiceTrait return $account; } - /** - * @param Account|null $account - * @param array $data - * @param array $types - * - * @return Account|null - */ private function getCashAccount(?Account $account, array $data, array $types): ?Account { // return cash account. @@ -348,155 +432,7 @@ trait JournalServiceTrait $account = $this->accountRepository->getCashAccount(); } app('log')->debug('Cannot return cash account, return input instead.'); + return $account; } - - /** - * @param string $amount - * - * @return string - * @throws FireflyException - */ - protected function getAmount(string $amount): string - { - if ('' === $amount) { - throw new FireflyException(sprintf('The amount cannot be an empty string: "%s"', $amount)); - } - app('log')->debug(sprintf('Now in getAmount("%s")', $amount)); - if (0 === bccomp('0', $amount)) { - throw new FireflyException(sprintf('The amount seems to be zero: "%s"', $amount)); - } - - return $amount; - } - - /** - * @param string|null $amount - * - * @return string|null - */ - protected function getForeignAmount(?string $amount): ?string - { - if (null === $amount) { - app('log')->debug('No foreign amount info in array. Return NULL'); - - return null; - } - if ('' === $amount) { - app('log')->debug('Foreign amount is empty string, return NULL.'); - - return null; - } - if (0 === bccomp('0', $amount)) { - app('log')->debug('Foreign amount is 0.0, return NULL.'); - - return null; - } - app('log')->debug(sprintf('Foreign amount is %s', $amount)); - - return $amount; - } - - /** - * @param TransactionJournal $journal - * @param NullArrayObject $data - * - - */ - protected function storeBudget(TransactionJournal $journal, NullArrayObject $data): void - { - if (TransactionType::WITHDRAWAL !== $journal->transactionType->type) { - $journal->budgets()->sync([]); - - return; - } - $budget = $this->budgetRepository->findBudget($data['budget_id'], $data['budget_name']); - if (null !== $budget) { - app('log')->debug(sprintf('Link budget #%d to journal #%d', $budget->id, $journal->id)); - $journal->budgets()->sync([$budget->id]); - - return; - } - // if the budget is NULL, sync empty. - $journal->budgets()->sync([]); - } - - /** - * @param TransactionJournal $journal - * @param NullArrayObject $data - * - - */ - protected function storeCategory(TransactionJournal $journal, NullArrayObject $data): void - { - $category = $this->categoryRepository->findCategory($data['category_id'], $data['category_name']); - if (null !== $category) { - app('log')->debug(sprintf('Link category #%d to journal #%d', $category->id, $journal->id)); - $journal->categories()->sync([$category->id]); - - return; - } - // if the category is NULL, sync empty. - $journal->categories()->sync([]); - } - - /** - * @param TransactionJournal $journal - * @param string|null $notes - * - - */ - protected function storeNotes(TransactionJournal $journal, ?string $notes): void - { - $notes = (string)$notes; - $note = $journal->notes()->first(); - if ('' !== $notes) { - if (null === $note) { - $note = new Note(); - $note->noteable()->associate($journal); - } - $note->text = $notes; - $note->save(); - app('log')->debug(sprintf('Stored notes for journal #%d', $journal->id)); - - return; - } - // try to delete existing notes. - $note?->delete(); - } - - /** - * Link tags to journal. - * - * @param TransactionJournal $journal - * @param array|null $tags - * - - */ - protected function storeTags(TransactionJournal $journal, ?array $tags): void - { - app('log')->debug('Now in storeTags()', $tags ?? []); - $this->tagFactory->setUser($journal->user); - $set = []; - if (!is_array($tags)) { - app('log')->debug('Tags is not an array, break.'); - - return; - } - app('log')->debug('Start of loop.'); - foreach ($tags as $string) { - $string = (string)$string; - app('log')->debug(sprintf('Now at tag "%s"', $string)); - if ('' !== $string) { - $tag = $this->tagFactory->findOrCreate($string); - if (null !== $tag) { - $set[] = $tag->id; - } - } - } - $set = array_unique($set); - app('log')->debug('End of loop.'); - app('log')->debug(sprintf('Total nr. of tags: %d', count($tags)), $tags); - $journal->tags()->sync($set); - } } diff --git a/app/Services/Internal/Support/LocationServiceTrait.php b/app/Services/Internal/Support/LocationServiceTrait.php index 26281af7a2..38b63f963d 100644 --- a/app/Services/Internal/Support/LocationServiceTrait.php +++ b/app/Services/Internal/Support/LocationServiceTrait.php @@ -32,12 +32,6 @@ use Illuminate\Database\Eloquent\Model; */ trait LocationServiceTrait { - /** - * @param Model $model - * @param array $data - * - * @return Location|null - */ protected function storeNewLocation(Model $model, array $data): ?Location { $data['store_location'] ??= false; diff --git a/app/Services/Internal/Support/RecurringTransactionTrait.php b/app/Services/Internal/Support/RecurringTransactionTrait.php index 6c66716c01..cb778faedd 100644 --- a/app/Services/Internal/Support/RecurringTransactionTrait.php +++ b/app/Services/Internal/Support/RecurringTransactionTrait.php @@ -40,20 +40,12 @@ use FireflyIII\Models\RecurrenceTransaction; use FireflyIII\Models\RecurrenceTransactionMeta; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Validation\AccountValidator; -use JsonException; /** * Trait RecurringTransactionTrait - * */ trait RecurringTransactionTrait { - /** - * @param Recurrence $recurrence - * @param string $note - * - * @return bool - */ public function updateNote(Recurrence $recurrence, string $note): bool { if ('' === $note) { @@ -75,10 +67,6 @@ trait RecurringTransactionTrait return true; } - /** - * @param Recurrence $recurrence - * @param array $repetitions - */ protected function createRepetitions(Recurrence $recurrence, array $repetitions): void { /** @var array $array */ @@ -98,11 +86,9 @@ trait RecurringTransactionTrait /** * Store transactions of a recurring transactions. It's complex but readable. * - * @param Recurrence $recurrence - * @param array $transactions - * * @throws FireflyException - * @throws JsonException + * + * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function createTransactions(Recurrence $recurrence, array $transactions): void { @@ -139,7 +125,7 @@ trait RecurringTransactionTrait if (!$validator->validateDestination(['id' => $destination->id])) { throw new FireflyException(sprintf('Destination invalid: %s', $validator->destError)); } - if (array_key_exists('foreign_amount', $array) && '' === (string)$array['foreign_amount']) { + if (array_key_exists('foreign_amount', $array) && '' === (string) $array['foreign_amount']) { unset($array['foreign_amount']); } // TODO typeOverrule. The account validator may have a different opinion on the type of the transaction. @@ -151,25 +137,25 @@ trait RecurringTransactionTrait 'source_id' => $source->id, 'destination_id' => $destination->id, 'amount' => $array['amount'], - 'foreign_amount' => array_key_exists('foreign_amount', $array) ? (string)$array['foreign_amount'] : null, + 'foreign_amount' => array_key_exists('foreign_amount', $array) ? (string) $array['foreign_amount'] : null, 'description' => $array['description'], ] ); $transaction->save(); if (array_key_exists('budget_id', $array)) { - $this->setBudget($transaction, (int)$array['budget_id']); + $this->setBudget($transaction, (int) $array['budget_id']); } if (array_key_exists('bill_id', $array)) { - $this->setBill($transaction, (int)$array['bill_id']); + $this->setBill($transaction, (int) $array['bill_id']); } if (array_key_exists('category_id', $array)) { - $this->setCategory($transaction, (int)$array['category_id']); + $this->setCategory($transaction, (int) $array['category_id']); } // same for piggy bank if (array_key_exists('piggy_bank_id', $array)) { - $this->updatePiggyBank($transaction, (int)$array['piggy_bank_id']); + $this->updatePiggyBank($transaction, (int) $array['piggy_bank_id']); } if (array_key_exists('tags', $array) && is_array($array['tags'])) { @@ -178,19 +164,12 @@ trait RecurringTransactionTrait } } - /** - * @param array $expectedTypes - * @param int|null $accountId - * @param string|null $accountName - * - * @return Account - * @throws JsonException - */ protected function findAccount(array $expectedTypes, ?int $accountId, ?string $accountName): Account { $result = null; - $accountId = (int)$accountId; - $accountName = (string)$accountName; + $accountId = (int) $accountId; + $accountName = (string) $accountName; + /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); $repository->setUser($this->user); @@ -209,9 +188,11 @@ trait RecurringTransactionTrait // maybe we can create it? Try to avoid LOAN and other asset types. $cannotCreate = [AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD]; + /** @var AccountFactory $factory */ $factory = app(AccountFactory::class); $factory->setUser($this->user); + /** @var string $expectedType */ foreach ($expectedTypes as $expectedType) { if (in_array($expectedType, $cannotCreate, true)) { @@ -229,10 +210,61 @@ trait RecurringTransactionTrait return $result ?? $repository->getCashAccount(); } - /** - * @param RecurrenceTransaction $transaction - * @param int $budgetId - */ + protected function updatePiggyBank(RecurrenceTransaction $transaction, int $piggyId): void + { + /** @var PiggyBankFactory $factory */ + $factory = app(PiggyBankFactory::class); + $factory->setUser($transaction->recurrence->user); + $piggyBank = $factory->find($piggyId, null); + if (null !== $piggyBank) { + /** @var null|RecurrenceMeta $entry */ + $entry = $transaction->recurrenceTransactionMeta()->where('name', 'piggy_bank_id')->first(); + if (null === $entry) { + $entry = RecurrenceTransactionMeta::create(['rt_id' => $transaction->id, 'name' => 'piggy_bank_id', 'value' => $piggyBank->id]); + } + $entry->value = $piggyBank->id; + $entry->save(); + } + if (null === $piggyBank) { + // delete if present + $transaction->recurrenceTransactionMeta()->where('name', 'piggy_bank_id')->delete(); + } + } + + protected function updateTags(RecurrenceTransaction $transaction, array $tags): void + { + if (0 !== count($tags)) { + /** @var null|RecurrenceMeta $entry */ + $entry = $transaction->recurrenceTransactionMeta()->where('name', 'tags')->first(); + if (null === $entry) { + $entry = RecurrenceTransactionMeta::create(['rt_id' => $transaction->id, 'name' => 'tags', 'value' => json_encode($tags)]); + } + $entry->value = json_encode($tags); + $entry->save(); + } + if (0 === count($tags)) { + // delete if present + $transaction->recurrenceTransactionMeta()->where('name', 'tags')->delete(); + } + } + + protected function deleteRepetitions(Recurrence $recurrence): void + { + $recurrence->recurrenceRepetitions()->delete(); + } + + protected function deleteTransactions(Recurrence $recurrence): void + { + app('log')->debug('deleteTransactions()'); + + /** @var RecurrenceTransaction $transaction */ + foreach ($recurrence->recurrenceTransactions as $transaction) { + $transaction->recurrenceTransactionMeta()->delete(); + + $transaction->delete(); + } + } + private function setBudget(RecurrenceTransaction $transaction, int $budgetId): void { $budgetFactory = app(BudgetFactory::class); @@ -252,10 +284,6 @@ trait RecurringTransactionTrait $meta->save(); } - /** - * @param RecurrenceTransaction $transaction - * @param int $billId - */ private function setBill(RecurrenceTransaction $transaction, int $billId): void { $billFactory = app(BillFactory::class); @@ -275,10 +303,6 @@ trait RecurringTransactionTrait $meta->save(); } - /** - * @param RecurrenceTransaction $transaction - * @param int $categoryId - */ private function setCategory(RecurrenceTransaction $transaction, int $categoryId): void { $categoryFactory = app(CategoryFactory::class); @@ -301,76 +325,4 @@ trait RecurringTransactionTrait $meta->value = $category->id; $meta->save(); } - - /** - * @param RecurrenceTransaction $transaction - * @param int $piggyId - */ - protected function updatePiggyBank(RecurrenceTransaction $transaction, int $piggyId): void - { - /** @var PiggyBankFactory $factory */ - $factory = app(PiggyBankFactory::class); - $factory->setUser($transaction->recurrence->user); - $piggyBank = $factory->find($piggyId, null); - if (null !== $piggyBank) { - /** @var RecurrenceMeta|null $entry */ - $entry = $transaction->recurrenceTransactionMeta()->where('name', 'piggy_bank_id')->first(); - if (null === $entry) { - $entry = RecurrenceTransactionMeta::create(['rt_id' => $transaction->id, 'name' => 'piggy_bank_id', 'value' => $piggyBank->id]); - } - $entry->value = $piggyBank->id; - $entry->save(); - } - if (null === $piggyBank) { - // delete if present - $transaction->recurrenceTransactionMeta()->where('name', 'piggy_bank_id')->delete(); - } - } - - /** - * @param RecurrenceTransaction $transaction - * @param array $tags - */ - protected function updateTags(RecurrenceTransaction $transaction, array $tags): void - { - if (0 !== count($tags)) { - /** @var RecurrenceMeta|null $entry */ - $entry = $transaction->recurrenceTransactionMeta()->where('name', 'tags')->first(); - if (null === $entry) { - $entry = RecurrenceTransactionMeta::create(['rt_id' => $transaction->id, 'name' => 'tags', 'value' => json_encode($tags)]); - } - $entry->value = json_encode($tags); - $entry->save(); - } - if (0 === count($tags)) { - // delete if present - $transaction->recurrenceTransactionMeta()->where('name', 'tags')->delete(); - } - } - - /** - * @param Recurrence $recurrence - * - - */ - protected function deleteRepetitions(Recurrence $recurrence): void - { - $recurrence->recurrenceRepetitions()->delete(); - } - - /** - * @param Recurrence $recurrence - * - - */ - protected function deleteTransactions(Recurrence $recurrence): void - { - app('log')->debug('deleteTransactions()'); - /** @var RecurrenceTransaction $transaction */ - foreach ($recurrence->recurrenceTransactions as $transaction) { - $transaction->recurrenceTransactionMeta()->delete(); - - $transaction->delete(); - } - } } diff --git a/app/Services/Internal/Support/TransactionTypeTrait.php b/app/Services/Internal/Support/TransactionTypeTrait.php index 47f9fac10d..5814b95cb9 100644 --- a/app/Services/Internal/Support/TransactionTypeTrait.php +++ b/app/Services/Internal/Support/TransactionTypeTrait.php @@ -29,7 +29,6 @@ use FireflyIII\Models\TransactionType; /** * Trait TransactionTypeTrait - * */ trait TransactionTypeTrait { @@ -37,9 +36,6 @@ trait TransactionTypeTrait * Get the transaction type. Since this is mandatory, will throw an exception when nothing comes up. Will always * use TransactionType repository. * - * @param string $type - * - * @return TransactionType * @throws FireflyException */ protected function findTransactionType(string $type): TransactionType @@ -48,6 +44,7 @@ trait TransactionTypeTrait $transactionType = $factory->find($type); if (null === $transactionType) { app('log')->error(sprintf('Could not find transaction type for "%s"', $type)); + throw new FireflyException(sprintf('Could not find transaction type for "%s"', $type)); } diff --git a/app/Services/Internal/Update/AccountUpdateService.php b/app/Services/Internal/Update/AccountUpdateService.php index 49ee1a0e57..7831bb8bdc 100644 --- a/app/Services/Internal/Update/AccountUpdateService.php +++ b/app/Services/Internal/Update/AccountUpdateService.php @@ -31,7 +31,6 @@ use FireflyIII\Models\Location; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Services\Internal\Support\AccountServiceTrait; use FireflyIII\User; -use JsonException; /** * Class AccountUpdateService @@ -63,12 +62,7 @@ class AccountUpdateService /** * Update account data. * - * @param Account $account - * @param array $data - * - * @return Account * @throws FireflyException - * @throws JsonException */ public function update(Account $account, array $data): Account { @@ -110,20 +104,60 @@ class AccountUpdateService return $account; } - /** - * @param User $user - */ public function setUser(User $user): void { $this->user = $user; } - /** - * @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) { + app('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)) { + app('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 = $account->order; + $newOrder = $data['order']; + app('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 (AccountType::ASSET === $type) { + $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') + ; + $account->order = $newOrder; + app('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') + ; + $account->order = $newOrder; + app('log')->debug(sprintf('Order of account #%d ("%s") is now %d', $account->id, $account->name, $newOrder)); + $account->save(); + + return $account; + } + private function updateAccount(Account $account, array $data): Account { // update the account itself: @@ -138,7 +172,7 @@ class AccountUpdateService } // set liability, but account must already be a liability. - //$liabilityType = $data['liability_type'] ?? ''; + // $liabilityType = $data['liability_type'] ?? ''; if ($this->isLiability($account) && array_key_exists('liability_type', $data)) { $type = $this->getAccountType($data['liability_type']); $account->account_type_id = $type->id; @@ -163,11 +197,6 @@ class AccountUpdateService return $account; } - /** - * @param Account $account - * - * @return bool - */ private function isLiability(Account $account): bool { $type = $account->accountType->type; @@ -175,77 +204,15 @@ class AccountUpdateService return in_array($type, [AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE], true); } - /** - * @param string $type - * - * @return AccountType - */ 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) { - app('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)) { - app('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 = $account->order; - $newOrder = $data['order']; - app('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 ($type === AccountType::ASSET) { - $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'); - $account->order = $newOrder; - app('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'); - $account->order = $newOrder; - app('log')->debug(sprintf('Order of account #%d ("%s") is now %d', $account->id, $account->name, $newOrder)); - $account->save(); - - return $account; - } - - /** - * @param array $array - * - * @return array - */ private function getTypeIds(array $array): array { $return = []; + /** @var string $type */ foreach ($array as $type) { /** @var AccountType $type */ @@ -256,10 +223,6 @@ class AccountUpdateService return $return; } - /** - * @param Account $account - * @param array $data - */ private function updateLocation(Account $account, array $data): void { $updateLocation = $data['update_location'] ?? false; @@ -287,9 +250,6 @@ class AccountUpdateService } /** - * @param Account $account - * @param array $data - * * @throws FireflyException */ private function updateOpeningBalance(Account $account, array $data): void @@ -323,8 +283,6 @@ class AccountUpdateService } /** - * @param Account $account - * * @throws FireflyException */ private function updatePreferences(Account $account): void diff --git a/app/Services/Internal/Update/BillUpdateService.php b/app/Services/Internal/Update/BillUpdateService.php index 82429d5886..855c1651ed 100644 --- a/app/Services/Internal/Update/BillUpdateService.php +++ b/app/Services/Internal/Update/BillUpdateService.php @@ -33,7 +33,6 @@ use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; use FireflyIII\Services\Internal\Support\BillServiceTrait; use FireflyIII\User; use Illuminate\Support\Collection; -use JsonException; /** * Class BillUpdateService @@ -46,12 +45,7 @@ class BillUpdateService protected User $user; /** - * @param Bill $bill - * @param array $data - * - * @return Bill * @throws FireflyException - * @throws JsonException */ public function update(Bill $bill, array $data): Bill { @@ -59,7 +53,7 @@ class BillUpdateService 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) ?? + $currency = $factory->find((int) ($data['currency_id'] ?? null), $data['currency_code'] ?? null) ?? app('amount')->getDefaultCurrencyByUserGroup($bill->user->userGroup); // enable the currency if it isn't. @@ -81,14 +75,14 @@ class BillUpdateService ]; // update note: if (array_key_exists('notes', $data)) { - $this->updateNote($bill, (string)$data['notes']); + $this->updateNote($bill, (string) $data['notes']); } // update order. if (array_key_exists('order', $data)) { // update the order of the piggy bank: $oldOrder = $bill->order; - $newOrder = (int)($data['order'] ?? $oldOrder); + $newOrder = (int) ($data['order'] ?? $oldOrder); if ($oldOrder !== $newOrder) { $this->updateOrder($bill, $oldOrder, $newOrder); } @@ -118,7 +112,7 @@ class BillUpdateService } if (array_key_exists('object_group_id', $data)) { // try also with ID: - $objectGroupId = (int)($data['object_group_id'] ?? 0); + $objectGroupId = (int) ($data['object_group_id'] ?? 0); if (0 !== $objectGroupId) { $objectGroup = $this->findObjectGroupById($objectGroupId); if (null !== $objectGroup) { @@ -136,27 +130,24 @@ class BillUpdateService } /** - * @param Bill $bill - * @param array $data - * - * @return Bill + * @SuppressWarnings(PHPMD.NPathComplexity) */ private function updateBillProperties(Bill $bill, array $data): Bill { - if (array_key_exists('name', $data) && '' !== (string)$data['name']) { + if (array_key_exists('name', $data) && '' !== (string) $data['name']) { $bill->name = $data['name']; } - if (array_key_exists('amount_min', $data) && '' !== (string)$data['amount_min']) { + if (array_key_exists('amount_min', $data) && '' !== (string) $data['amount_min']) { $bill->amount_min = $data['amount_min']; } - if (array_key_exists('amount_max', $data) && '' !== (string)$data['amount_max']) { + if (array_key_exists('amount_max', $data) && '' !== (string) $data['amount_max']) { $bill->amount_max = $data['amount_max']; } - if (array_key_exists('date', $data) && '' !== (string)$data['date']) { + if (array_key_exists('date', $data) && '' !== (string) $data['date']) { $bill->date = $data['date']; } - if (array_key_exists('repeat_freq', $data) && '' !== (string)$data['repeat_freq']) { + if (array_key_exists('repeat_freq', $data) && '' !== (string) $data['repeat_freq']) { $bill->repeat_freq = $data['repeat_freq']; } if (array_key_exists('skip', $data)) { @@ -179,37 +170,30 @@ class BillUpdateService 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'); + ->where('bills.id', '!=', $bill->id) + ->decrement('bills.order') + ; $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'); + ->where('bills.id', '!=', $bill->id) + ->increment('bills.order') + ; $bill->order = $newOrder; $bill->save(); } } - /** - * @param Bill $bill - * @param array $oldData - * @param array $newData - */ private function updateBillTriggers(Bill $bill, array $oldData, array $newData): void { app('log')->debug(sprintf('Now in updateBillTriggers(%d, "%s")', $bill->id, $bill->name)); + /** @var BillRepositoryInterface $repository */ $repository = app(BillRepositoryInterface::class); $repository->setUser($bill->user); @@ -232,18 +216,13 @@ class BillUpdateService } if ($oldData[$field] === $newData[$field]) { app('log')->debug(sprintf('Field %s is unchanged ("%s"), continue.', $field, $oldData[$field])); + continue; } $this->updateRules($rules, $ruleTriggerKey, $oldData[$field], $newData[$field]); } } - /** - * @param Collection $rules - * @param string $key - * @param string $oldValue - * @param string $newValue - */ private function updateRules(Collection $rules, string $key, string $oldValue, string $newValue): void { /** @var Rule $rule */ @@ -253,6 +232,7 @@ class BillUpdateService app('log')->debug(sprintf('Updated rule trigger #%d from value "%s" to value "%s"', $trigger->id, $oldValue, $newValue)); $trigger->trigger_value = $newValue; $trigger->save(); + continue; } if (null !== $trigger && $trigger->trigger_value !== $oldValue && in_array($key, ['amount_more', 'amount_less'], true) @@ -264,12 +244,6 @@ class BillUpdateService } } - /** - * @param Rule $rule - * @param string $key - * - * @return RuleTrigger|null - */ private function getRuleTrigger(Rule $rule, string $key): ?RuleTrigger { return $rule->ruleTriggers()->where('trigger_type', $key)->first(); diff --git a/app/Services/Internal/Update/CategoryUpdateService.php b/app/Services/Internal/Update/CategoryUpdateService.php index 10ae3ca6f3..0863bd7119 100644 --- a/app/Services/Internal/Update/CategoryUpdateService.php +++ b/app/Services/Internal/Update/CategoryUpdateService.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Services\Internal\Update; -use Exception; use FireflyIII\Models\Category; use FireflyIII\Models\Note; use FireflyIII\Models\RecurrenceTransactionMeta; @@ -33,8 +32,6 @@ use FireflyIII\User; /** * Class CategoryUpdateService - * - */ class CategoryUpdateService { @@ -61,11 +58,7 @@ class CategoryUpdateService } /** - * @param Category $category - * @param array $data - * - * @return Category - * @throws Exception + * @throws \Exception */ public function update(Category $category, array $data): Category { @@ -84,19 +77,17 @@ class CategoryUpdateService return $category; } - /** - * @param string $oldName - * @param string $newName - */ private function updateRuleTriggers(string $oldName, string $newName): void { - $types = ['category_is',]; + $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.*']); + ->where('rules.user_id', $this->user->id) + ->whereIn('rule_triggers.trigger_type', $types) + ->where('rule_triggers.trigger_value', $oldName) + ->get(['rule_triggers.*']) + ; app('log')->debug(sprintf('Found %d triggers to update.', $triggers->count())); + /** @var RuleTrigger $trigger */ foreach ($triggers as $trigger) { $trigger->trigger_value = $newName; @@ -105,19 +96,17 @@ class CategoryUpdateService } } - /** - * @param string $oldName - * @param string $newName - */ private function updateRuleActions(string $oldName, string $newName): void { - $types = ['set_category',]; + $types = ['set_category']; $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.*']); + ->where('rules.user_id', $this->user->id) + ->whereIn('rule_actions.action_type', $types) + ->where('rule_actions.action_value', $oldName) + ->get(['rule_actions.*']) + ; app('log')->debug(sprintf('Found %d actions to update.', $actions->count())); + /** @var RuleAction $action */ foreach ($actions as $action) { $action->action_value = $newName; @@ -126,25 +115,19 @@ class CategoryUpdateService } } - /** - * @param string $oldName - * @param string $newName - */ private function updateRecurrences(string $oldName, string $newName): void { RecurrenceTransactionMeta::leftJoin('recurrences_transactions', 'rt_meta.rt_id', '=', 'recurrences_transactions.id') - ->leftJoin('recurrences', 'recurrences.id', '=', 'recurrences_transactions.recurrence_id') - ->where('recurrences.user_id', $this->user->id) - ->where('rt_meta.name', 'category_name') - ->where('rt_meta.value', $oldName) - ->update(['rt_meta.value' => $newName]); + ->leftJoin('recurrences', 'recurrences.id', '=', 'recurrences_transactions.recurrence_id') + ->where('recurrences.user_id', $this->user->id) + ->where('rt_meta.name', 'category_name') + ->where('rt_meta.value', $oldName) + ->update(['rt_meta.value' => $newName]) + ; } /** - * @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 d68d9d4e5d..8fd3c2b6dc 100644 --- a/app/Services/Internal/Update/CurrencyUpdateService.php +++ b/app/Services/Internal/Update/CurrencyUpdateService.php @@ -27,17 +27,9 @@ use FireflyIII\Models\TransactionCurrency; /** * Class CurrencyUpdateService - * - */ class CurrencyUpdateService { - /** - * @param TransactionCurrency $currency - * @param array $data - * - * @return TransactionCurrency - */ public function update(TransactionCurrency $currency, array $data): TransactionCurrency { if (array_key_exists('code', $data) && '' !== (string)$data['code']) { diff --git a/app/Services/Internal/Update/GroupCloneService.php b/app/Services/Internal/Update/GroupCloneService.php index 5cf3734f88..2d1240b4d5 100644 --- a/app/Services/Internal/Update/GroupCloneService.php +++ b/app/Services/Internal/Update/GroupCloneService.php @@ -39,11 +39,6 @@ use FireflyIII\Models\TransactionJournalMeta; */ class GroupCloneService { - /** - * @param TransactionGroup $group - * - * @return TransactionGroup - */ public function cloneGroup(TransactionGroup $group): TransactionGroup { $newGroup = $group->replicate(); @@ -55,11 +50,6 @@ class GroupCloneService return $newGroup; } - /** - * @param TransactionJournal $journal - * @param TransactionGroup $newGroup - * @param int $originalGroup - */ private function cloneJournal(TransactionJournal $journal, TransactionGroup $newGroup, int $originalGroup): void { $newJournal = $journal->replicate(); @@ -83,6 +73,7 @@ class GroupCloneService foreach ($journal->transactionJournalMeta as $meta) { $this->cloneMeta($meta, $newJournal); } + // clone category /** @var Category $category */ foreach ($journal->categories as $category) { @@ -105,7 +96,7 @@ class GroupCloneService // add relation. // TODO clone ALL linked piggy banks - /** @var PiggyBankEvent|null $event */ + /** @var null|PiggyBankEvent $event */ $event = $journal->piggyBankEvents()->first(); if (null !== $event) { $piggyBank = $event->piggyBank; @@ -114,10 +105,6 @@ class GroupCloneService } } - /** - * @param Transaction $transaction - * @param TransactionJournal $newJournal - */ private function cloneTransaction(Transaction $transaction, TransactionJournal $newJournal): void { $newTransaction = $transaction->replicate(); @@ -126,11 +113,6 @@ class GroupCloneService $newTransaction->save(); } - /** - * @param Note $note - * @param TransactionJournal $newJournal - * @param int $oldGroupId - */ private function cloneNote(Note $note, TransactionJournal $newJournal, int $oldGroupId): void { $newNote = $note->replicate(); @@ -142,10 +124,6 @@ class GroupCloneService $newNote->save(); } - /** - * @param TransactionJournalMeta $meta - * @param TransactionJournal $newJournal - */ private function cloneMeta(TransactionJournalMeta $meta, TransactionJournal $newJournal): void { $newMeta = $meta->replicate(); diff --git a/app/Services/Internal/Update/GroupUpdateService.php b/app/Services/Internal/Update/GroupUpdateService.php index 7ced30befc..9a7b310ca5 100644 --- a/app/Services/Internal/Update/GroupUpdateService.php +++ b/app/Services/Internal/Update/GroupUpdateService.php @@ -30,7 +30,6 @@ use FireflyIII\Factory\TransactionJournalFactory; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Services\Internal\Destroy\JournalDestroyService; -use JsonException; /** * Class GroupUpdateService @@ -40,18 +39,14 @@ class GroupUpdateService /** * Update a transaction group. * - * @param TransactionGroup $transactionGroup - * @param array $data - * - * @return TransactionGroup * @throws DuplicateTransactionException * @throws FireflyException - * @throws JsonException */ public function update(TransactionGroup $transactionGroup, array $data): TransactionGroup { app('log')->debug(sprintf('Now in %s', __METHOD__)); app('log')->debug('Now in group update service', $data); + /** @var array $transactions */ $transactions = $data['transactions'] ?? []; // update group name. @@ -71,7 +66,6 @@ class GroupUpdateService ); } - if (0 === count($transactions)) { app('log')->debug('No transactions submitted, do nothing.'); @@ -85,6 +79,7 @@ class GroupUpdateService sprintf('Will now update journal #%d (only journal in group #%d)', $first->id, $transactionGroup->id) ); $this->updateTransactionJournal($transactionGroup, $first, reset($transactions)); + $transactionGroup->touch(); $transactionGroup->refresh(); app('preferences')->mark(); @@ -99,6 +94,7 @@ class GroupUpdateService if (0 === count($updated)) { app('log')->error('There were no transactions updated or created. Will not delete anything.'); + $transactionGroup->touch(); $transactionGroup->refresh(); app('preferences')->mark(); @@ -112,6 +108,7 @@ class GroupUpdateService foreach ($result as $deletedId) { /** @var TransactionJournal $journal */ $journal = $transactionGroup->transactionJournals()->find((int)$deletedId); + /** @var JournalDestroyService $service */ $service = app(JournalDestroyService::class); $service->destroy($journal); @@ -119,6 +116,7 @@ class GroupUpdateService } app('preferences')->mark(); + $transactionGroup->touch(); $transactionGroup->refresh(); return $transactionGroup; @@ -126,17 +124,12 @@ class GroupUpdateService /** * Update single journal. - * - * @param TransactionGroup $transactionGroup - * @param TransactionJournal $journal - * @param array $data */ private function updateTransactionJournal( TransactionGroup $transactionGroup, TransactionJournal $journal, array $data - ): void - { + ): void { app('log')->debug(sprintf('Now in %s', __METHOD__)); if (0 === count($data)) { return; @@ -144,6 +137,7 @@ class GroupUpdateService if (1 === count($data) && array_key_exists('transaction_journal_id', $data)) { return; } + /** @var JournalUpdateService $updateService */ $updateService = app(JournalUpdateService::class); $updateService->setTransactionGroup($transactionGroup); @@ -153,27 +147,24 @@ class GroupUpdateService } /** - * @param TransactionGroup $transactionGroup - * @param array $transactions - * - * @return array * @throws DuplicateTransactionException * @throws FireflyException - * @throws JsonException */ private function updateTransactions(TransactionGroup $transactionGroup, array $transactions): array { app('log')->debug(sprintf('Now in %s', __METHOD__)); // updated or created transaction journals: $updated = []; + /** * @var int $index * @var array $transaction */ foreach ($transactions as $index => $transaction) { - app('log')->debug(sprintf('Now at #%d of %d', ($index + 1), count($transactions)), $transaction); + app('log')->debug(sprintf('Now at #%d of %d', $index + 1, count($transactions)), $transaction); $journalId = (int)($transaction['transaction_journal_id'] ?? 0); - /** @var TransactionJournal|null $journal */ + + /** @var null|TransactionJournal $journal */ $journal = $transactionGroup->transactionJournals()->find($journalId); if (null === $journal) { app('log')->debug('This entry has no existing journal: make a new split.'); @@ -181,7 +172,8 @@ class GroupUpdateService // by plucking it from another journal in the group: if (!array_key_exists('type', $transaction)) { app('log')->debug('No transaction type is indicated.'); - /** @var TransactionJournal|null $randomJournal */ + + /** @var null|TransactionJournal $randomJournal */ $randomJournal = $transactionGroup->transactionJournals()->inRandomOrder()->with( ['transactionType'] )->first(); @@ -212,14 +204,8 @@ class GroupUpdateService } /** - * @param TransactionGroup $transactionGroup - * @param array $data - * - * @return TransactionJournal|null - * * @throws DuplicateTransactionException * @throws FireflyException - * @throws JsonException */ private function createTransactionJournal(TransactionGroup $transactionGroup, array $data): ?TransactionJournal { @@ -228,14 +214,17 @@ class GroupUpdateService $data, ], ]; + /** @var TransactionJournalFactory $factory */ $factory = app(TransactionJournalFactory::class); $factory->setUser($transactionGroup->user); + try { $collection = $factory->create($submission); } catch (FireflyException $e) { app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); + throw new FireflyException( sprintf('Could not create new transaction journal: %s', $e->getMessage()), 0, @@ -243,7 +232,7 @@ class GroupUpdateService ); } $collection->each( - static function (TransactionJournal $journal) use ($transactionGroup) { + static function (TransactionJournal $journal) use ($transactionGroup): void { $transactionGroup->transactionJournals()->save($journal); } ); diff --git a/app/Services/Internal/Update/JournalUpdateService.php b/app/Services/Internal/Update/JournalUpdateService.php index b02401cf79..b34159532a 100644 --- a/app/Services/Internal/Update/JournalUpdateService.php +++ b/app/Services/Internal/Update/JournalUpdateService.php @@ -25,6 +25,7 @@ namespace FireflyIII\Services\Internal\Update; use Carbon\Carbon; use Carbon\Exceptions\InvalidDateException; +use Carbon\Exceptions\InvalidFormatException; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\TagFactory; @@ -98,20 +99,14 @@ class JournalUpdateService 'external_url', ]; $this->metaDate = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', - 'invoice_date',]; + 'invoice_date', ]; } - /** - * @param array $data - */ public function setData(array $data): void { $this->data = $data; } - /** - * @param TransactionGroup $transactionGroup - */ public function setTransactionGroup(TransactionGroup $transactionGroup): void { $this->transactionGroup = $transactionGroup; @@ -126,23 +121,16 @@ class JournalUpdateService $this->sourceTransaction = null; } - /** - * @param TransactionJournal $transactionJournal - */ public function setTransactionJournal(TransactionJournal $transactionJournal): void { $this->transactionJournal = $transactionJournal; } - /** - * - */ public function update(): void { app('log')->debug(sprintf('Now in %s', __METHOD__)); app('log')->debug(sprintf('Now in JournalUpdateService for journal #%d.', $this->transactionJournal->id)); - $this->data['reconciled'] = array_key_exists('reconciled', $this->data) ? $this->data['reconciled'] : false; // can we update account data using the new type? @@ -180,17 +168,11 @@ class JournalUpdateService $this->transactionJournal->refresh(); } - /** - * @return bool - */ private function hasValidAccounts(): bool { return $this->hasValidSourceAccount() && $this->hasValidDestinationAccount(); } - /** - * @return bool - */ private function hasValidSourceAccount(): bool { app('log')->debug('Now in hasValidSourceAccount().'); @@ -224,11 +206,6 @@ class JournalUpdateService return $result; } - /** - * @param array $fields - * - * @return bool - */ private function hasFields(array $fields): bool { foreach ($fields as $field) { @@ -240,9 +217,6 @@ class JournalUpdateService return false; } - /** - * @return Account - */ private function getOriginalSourceAccount(): Account { if (null === $this->sourceAccount) { @@ -253,9 +227,6 @@ class JournalUpdateService return $this->sourceAccount; } - /** - * @return Transaction - */ private function getSourceTransaction(): Transaction { if (null === $this->sourceTransaction) { @@ -276,8 +247,6 @@ class JournalUpdateService * * If the array contains key 'type' and the value is correct, this is returned. Otherwise, the original type is * returned. - * - * @return string */ private function getExpectedType(): string { @@ -289,9 +258,6 @@ class JournalUpdateService return $this->transactionJournal->transactionType->type; } - /** - * @return bool - */ private function hasValidDestinationAccount(): bool { app('log')->debug('Now in hasValidDestinationAccount().'); @@ -331,9 +297,6 @@ class JournalUpdateService return $result; } - /** - * @return Account - */ private function getOriginalDestinationAccount(): Account { if (null === $this->destinationAccount) { @@ -346,8 +309,6 @@ class JournalUpdateService /** * Get destination transaction. - * - * @return Transaction */ private function getDestinationTransaction(): Transaction { @@ -360,8 +321,6 @@ class JournalUpdateService /** * Does a validation and returns the source account. This method will break if the source isn't really valid. - * - * @return Account */ private function getValidSourceAccount(): Account { @@ -380,6 +339,7 @@ class JournalUpdateService ]; $expectedType = $this->getExpectedType(); + try { $result = $this->getAccount($expectedType, 'source', $sourceInfo); } catch (FireflyException $e) { @@ -425,8 +385,6 @@ 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 getValidDestinationAccount(): Account { @@ -447,6 +405,7 @@ class JournalUpdateService // make new account validator. $expectedType = $this->getExpectedType(); app('log')->debug(sprintf('Expected type (new or unchanged) is %s', $expectedType)); + try { $result = $this->getAccount($expectedType, 'destination', $destInfo); } catch (FireflyException $e) { @@ -512,8 +471,6 @@ class JournalUpdateService /** * Update journal generic field. Cannot be set to NULL. - * - * @param string $fieldName */ private function updateField(string $fieldName): void { @@ -525,7 +482,7 @@ class JournalUpdateService // update timezone. $value->setTimezone(config('app.timezone')); } - if (!($value instanceof Carbon)) { + if (!$value instanceof Carbon) { $value = new Carbon($value); } // do some parsing. @@ -536,19 +493,16 @@ class JournalUpdateService $this->transactionJournal->user, $this->transactionJournal, sprintf('update_%s', $fieldName), - $this->transactionJournal->$fieldName, // @phpstan-ignore-line + $this->transactionJournal->{$fieldName}, // @phpstan-ignore-line $value ) ); - $this->transactionJournal->$fieldName = $value;// @phpstan-ignore-line + $this->transactionJournal->{$fieldName} = $value; // @phpstan-ignore-line app('log')->debug(sprintf('Updated %s', $fieldName)); } } - /** - * - */ private function updateCategory(): void { // update category @@ -559,9 +513,6 @@ class JournalUpdateService } } - /** - * - */ private function updateBudget(): void { // update budget @@ -575,9 +526,6 @@ class JournalUpdateService } } - /** - * - */ private function updateTags(): void { if ($this->hasFields(['tags'])) { @@ -587,9 +535,6 @@ class JournalUpdateService } } - /** - * - */ private function updateReconciled(): void { if (array_key_exists('reconciled', $this->data) && is_bool($this->data['reconciled'])) { @@ -597,9 +542,6 @@ class JournalUpdateService } } - /** - * - */ private function updateNotes(): void { // update notes. @@ -609,9 +551,6 @@ class JournalUpdateService } } - /** - * - */ private function updateMeta(): void { // update meta fields. @@ -628,9 +567,6 @@ class JournalUpdateService } } - /** - * - */ private function updateMetaFields(): void { /** @var TransactionJournalMetaFactory $factory */ @@ -650,9 +586,6 @@ class JournalUpdateService } } - /** - * - */ private function updateMetaDateFields(): void { /** @var TransactionJournalMetaFactory $factory */ @@ -662,7 +595,7 @@ class JournalUpdateService if ($this->hasFields([$field])) { try { $value = '' === (string)$this->data[$field] ? null : new Carbon($this->data[$field]); - } catch (InvalidDateException $e) { // @phpstan-ignore-line + } catch (InvalidDateException|InvalidFormatException $e) { // @phpstan-ignore-line app('log')->debug(sprintf('%s is not a valid date value: %s', $this->data[$field], $e->getMessage())); return; @@ -678,9 +611,6 @@ class JournalUpdateService } } - /** - * - */ private function updateCurrency(): void { // update transactions. @@ -708,9 +638,6 @@ class JournalUpdateService app('log')->debug(sprintf('Updated currency to #%d (%s)', $currency->id, $currency->code)); } - /** - * - */ private function updateAmount(): void { app('log')->debug(sprintf('Now in %s', __METHOD__)); @@ -720,6 +647,7 @@ class JournalUpdateService $value = $this->data['amount'] ?? ''; app('log')->debug(sprintf('Amount is now "%s"', $value)); + try { $amount = $this->getAmount($value); } catch (FireflyException $e) { @@ -739,9 +667,6 @@ class JournalUpdateService app('log')->debug(sprintf('Updated amount to "%s"', $amount)); } - /** - * - */ private function updateForeignAmount(): void { // amount, foreign currency. diff --git a/app/Services/Internal/Update/RecurrenceUpdateService.php b/app/Services/Internal/Update/RecurrenceUpdateService.php index 0253b7a33e..6f16f05283 100644 --- a/app/Services/Internal/Update/RecurrenceUpdateService.php +++ b/app/Services/Internal/Update/RecurrenceUpdateService.php @@ -32,12 +32,9 @@ use FireflyIII\Models\RecurrenceTransaction; use FireflyIII\Services\Internal\Support\RecurringTransactionTrait; use FireflyIII\Services\Internal\Support\TransactionTypeTrait; use FireflyIII\User; -use JsonException; /** * Class RecurrenceUpdateService - * - */ class RecurrenceUpdateService { @@ -51,10 +48,6 @@ class RecurrenceUpdateService * * TODO if the user updates the type, the accounts must be validated again. * - * @param Recurrence $recurrence - * @param array $data - * - * @return Recurrence * @throws FireflyException */ public function update(Recurrence $recurrence, array $data): Recurrence @@ -78,7 +71,7 @@ class RecurrenceUpdateService $recurrence->repetitions = 0; } if (array_key_exists('nr_of_repetitions', $info)) { - if (0 !== (int)$info['nr_of_repetitions']) { + if (0 !== (int) $info['nr_of_repetitions']) { $recurrence->repeat_until = null; } $recurrence->repetitions = $info['nr_of_repetitions']; @@ -111,10 +104,6 @@ class RecurrenceUpdateService return $recurrence; } - /** - * @param Recurrence $recurrence - * @param string $text - */ private function setNoteText(Recurrence $recurrence, string $text): void { $dbNote = $recurrence->notes()->first(); @@ -132,10 +121,6 @@ class RecurrenceUpdateService } /** - * - * @param Recurrence $recurrence - * @param array $repetitions - * * @throws FireflyException */ private function updateRepetitions(Recurrence $recurrence, array $repetitions): void @@ -168,25 +153,20 @@ class RecurrenceUpdateService ]; foreach ($fields as $field => $column) { if (array_key_exists($field, $current)) { - $match->$column = $current[$field]; + $match->{$column} = $current[$field]; $match->save(); } } } } - /** - * @param Recurrence $recurrence - * @param array $data - * - * @return RecurrenceRepetition|null - */ private function matchRepetition(Recurrence $recurrence, array $data): ?RecurrenceRepetition { $originalCount = $recurrence->recurrenceRepetitions()->count(); if (1 === $originalCount) { app('log')->debug('Return the first one'); - /** @var RecurrenceRepetition|null */ + + // @var RecurrenceRepetition|null return $recurrence->recurrenceRepetitions()->first(); } // find it: @@ -203,18 +183,15 @@ class RecurrenceUpdateService $query->where($column, $data[$field]); } } - /** @var RecurrenceRepetition|null */ + + // @var RecurrenceRepetition|null return $query->first(); } /** * TODO this method is very complex. * - * @param Recurrence $recurrence - * @param array $transactions - * * @throws FireflyException - * @throws JsonException */ private function updateTransactions(Recurrence $recurrence, array $transactions): void { @@ -224,29 +201,25 @@ class RecurrenceUpdateService if (0 === count($transactions)) { // won't drop transactions, rather avoid. app('log')->warning('No transactions to update, too scared to continue!'); + return; } $combinations = []; $originalTransactions = $recurrence->recurrenceTransactions()->get()->toArray(); - /** - * First, make sure to loop all existing transactions and match them to a counterpart in the submitted transactions array. - */ + // First, make sure to loop all existing transactions and match them to a counterpart in the submitted transactions array. foreach ($originalTransactions as $i => $originalTransaction) { foreach ($transactions as $ii => $submittedTransaction) { - if (array_key_exists('id', $submittedTransaction) && (int)$originalTransaction['id'] === (int)$submittedTransaction['id']) { + if (array_key_exists('id', $submittedTransaction) && (int) $originalTransaction['id'] === (int) $submittedTransaction['id']) { app('log')->debug(sprintf('Match original transaction #%d with an entry in the submitted array.', $originalTransaction['id'])); $combinations[] = [ 'original' => $originalTransaction, 'submitted' => $submittedTransaction, ]; - unset($originalTransactions[$i]); - unset($transactions[$ii]); + unset($originalTransactions[$i], $transactions[$ii]); } } } - /** - * If one left of both we can match those as well and presto. - */ + // If one left of both we can match those as well and presto. if (1 === count($originalTransactions) && 1 === count($transactions)) { $first = array_shift($originalTransactions); app('log')->debug(sprintf('One left of each, link them (ID is #%d)', $first['id'])); @@ -265,34 +238,34 @@ class RecurrenceUpdateService // anything left in the original transactions array can be deleted. foreach ($originalTransactions as $original) { app('log')->debug(sprintf('Original transaction #%d is unmatched, delete it!', $original['id'])); - $this->deleteTransaction($recurrence, (int)$original['id']); + $this->deleteTransaction($recurrence, (int) $original['id']); } // anything left is new. $this->createTransactions($recurrence, $transactions); } /** - * @param Recurrence $recurrence - * @param array $combination + * It's a complex method but nothing surprising. * - * @return void + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ private function updateCombination(Recurrence $recurrence, array $combination): void { - $original = $combination['original']; - $submitted = $combination['submitted']; + $original = $combination['original']; + $submitted = $combination['submitted']; + $currencyFactory = app(TransactionCurrencyFactory::class); + /** @var RecurrenceTransaction $transaction */ $transaction = $recurrence->recurrenceTransactions()->find($original['id']); app('log')->debug(sprintf('Now in updateCombination(#%d)', $original['id'])); - $currencyFactory = app(TransactionCurrencyFactory::class); - // loop all and try to match them: $currency = null; $foreignCurrency = null; if (array_key_exists('currency_id', $submitted) || array_key_exists('currency_code', $submitted)) { $currency = $currencyFactory->find( - array_key_exists('currency_id', $submitted) ? (int)$submitted['currency_id'] : null, + array_key_exists('currency_id', $submitted) ? (int) $submitted['currency_id'] : null, array_key_exists('currency_code', $submitted) ? $submitted['currency_code'] : null ); } @@ -304,7 +277,7 @@ class RecurrenceUpdateService } if (array_key_exists('foreign_currency_id', $submitted) || array_key_exists('foreign_currency_code', $submitted)) { $foreignCurrency = $currencyFactory->find( - array_key_exists('foreign_currency_id', $submitted) ? (int)$submitted['foreign_currency_id'] : null, + array_key_exists('foreign_currency_id', $submitted) ? (int) $submitted['foreign_currency_id'] : null, array_key_exists('foreign_currency_code', $submitted) ? $submitted['foreign_currency_code'] : null ); } @@ -327,44 +300,38 @@ class RecurrenceUpdateService ]; foreach ($fields as $field => $column) { if (array_key_exists($field, $submitted)) { - $transaction->$column = $submitted[$field]; + $transaction->{$column} = $submitted[$field]; $transaction->save(); } } // update meta data if (array_key_exists('budget_id', $submitted)) { - $this->setBudget($transaction, (int)$submitted['budget_id']); + $this->setBudget($transaction, (int) $submitted['budget_id']); } if (array_key_exists('bill_id', $submitted)) { - $this->setBill($transaction, (int)$submitted['bill_id']); + $this->setBill($transaction, (int) $submitted['bill_id']); } // reset category if name is set but empty: // can be removed when v1 is retired. - if (array_key_exists('category_name', $submitted) && '' === (string)$submitted['category_name']) { + if (array_key_exists('category_name', $submitted) && '' === (string) $submitted['category_name']) { app('log')->debug('Category name is submitted but is empty. Set category to be empty.'); $submitted['category_name'] = null; $submitted['category_id'] = 0; } if (array_key_exists('category_id', $submitted)) { - app('log')->debug(sprintf('Category ID is submitted, set category to be %d.', (int)$submitted['category_id'])); - $this->setCategory($transaction, (int)$submitted['category_id']); + app('log')->debug(sprintf('Category ID is submitted, set category to be %d.', (int) $submitted['category_id'])); + $this->setCategory($transaction, (int) $submitted['category_id']); } if (array_key_exists('tags', $submitted) && is_array($submitted['tags'])) { $this->updateTags($transaction, $submitted['tags']); } if (array_key_exists('piggy_bank_id', $submitted)) { - $this->updatePiggyBank($transaction, (int)$submitted['piggy_bank_id']); + $this->updatePiggyBank($transaction, (int) $submitted['piggy_bank_id']); } } - /** - * @param Recurrence $recurrence - * @param int $transactionId - * - * @return void - */ private function deleteTransaction(Recurrence $recurrence, int $transactionId): void { app('log')->debug(sprintf('Will delete transaction #%d in recurrence #%d.', $transactionId, $recurrence->id)); diff --git a/app/Services/Password/PwndVerifierV2.php b/app/Services/Password/PwndVerifierV2.php index 6db7ac8e91..79067b27f3 100644 --- a/app/Services/Password/PwndVerifierV2.php +++ b/app/Services/Password/PwndVerifierV2.php @@ -29,17 +29,11 @@ use GuzzleHttp\Exception\RequestException; /** * Class PwndVerifierV2. - * - */ class PwndVerifierV2 implements Verifier { /** * Verify the given password against (some) service. - * - * @param string $password - * - * @return bool */ public function validPassword(string $password): bool { @@ -62,7 +56,7 @@ class PwndVerifierV2 implements Verifier try { $client = new Client(); $res = $client->request('GET', $url, $opt); - } catch (GuzzleException | RequestException $e) { + } catch (GuzzleException|RequestException $e) { app('log')->error(sprintf('Could not verify password security: %s', $e->getMessage())); return true; diff --git a/app/Services/Password/Verifier.php b/app/Services/Password/Verifier.php index 372771e484..705053bbfb 100644 --- a/app/Services/Password/Verifier.php +++ b/app/Services/Password/Verifier.php @@ -30,10 +30,6 @@ interface Verifier { /** * Verify the given password against (some) service. - * - * @param string $password - * - * @return bool */ public function validPassword(string $password): bool; } diff --git a/app/Services/Webhook/StandardWebhookSender.php b/app/Services/Webhook/StandardWebhookSender.php index da8ee71d71..596bc455b4 100644 --- a/app/Services/Webhook/StandardWebhookSender.php +++ b/app/Services/Webhook/StandardWebhookSender.php @@ -30,7 +30,6 @@ use FireflyIII\Models\WebhookMessage; use GuzzleHttp\Client; use GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Exception\RequestException; -use JsonException; /** * Class StandardWebhookSender @@ -40,16 +39,15 @@ class StandardWebhookSender implements WebhookSenderInterface private WebhookMessage $message; private int $version = 1; - /** - * @inheritDoc - */ public function getVersion(): int { return $this->version; } /** - * @inheritDoc + * @throws \GuzzleHttp\Exception\GuzzleException + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function send(): void { @@ -58,6 +56,7 @@ class StandardWebhookSender implements WebhookSenderInterface $signatureGenerator = app(SignatureGeneratorInterface::class); $this->message->sent = true; $this->message->save(); + try { $signature = $signatureGenerator->generate($this->message); } catch (FireflyException $e) { @@ -80,7 +79,7 @@ class StandardWebhookSender implements WebhookSenderInterface try { $json = json_encode($this->message->message, JSON_THROW_ON_ERROR); - } catch (JsonException $e) { + } catch (\JsonException $e) { app('log')->error('Did not send message because of a JSON error.'); app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); @@ -107,14 +106,14 @@ class StandardWebhookSender implements WebhookSenderInterface ], ]; $client = new Client(); + try { $res = $client->request('POST', $this->message->webhook->url, $options); - } catch (RequestException | ConnectException $e) { + } catch (ConnectException|RequestException $e) { app('log')->error('The webhook could NOT be submitted but Firefly III caught the error below.'); app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); - $logs = sprintf("%s\n%s", $e->getMessage(), $e->getTraceAsString()); $this->message->errored = true; @@ -143,9 +142,6 @@ class StandardWebhookSender implements WebhookSenderInterface app('log')->debug(sprintf('Response body: %s', $res->getBody())); } - /** - * @inheritDoc - */ public function setMessage(WebhookMessage $message): void { $this->message = $message; diff --git a/app/Services/Webhook/WebhookSenderInterface.php b/app/Services/Webhook/WebhookSenderInterface.php index 6b827fa9d0..67c7d569be 100644 --- a/app/Services/Webhook/WebhookSenderInterface.php +++ b/app/Services/Webhook/WebhookSenderInterface.php @@ -30,18 +30,9 @@ use FireflyIII\Models\WebhookMessage; */ interface WebhookSenderInterface { - /** - * @return int - */ public function getVersion(): int; - /** - * - */ public function send(): void; - /** - * @param WebhookMessage $message - */ public function setMessage(WebhookMessage $message): void; } diff --git a/app/Support/Amount.php b/app/Support/Amount.php index 568387c61f..68eb5c4a20 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -23,18 +23,14 @@ declare(strict_types=1); namespace FireflyIII\Support; -use Crypt; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\UserGroup; use FireflyIII\User; use Illuminate\Support\Collection; -use NumberFormatter; /** * Class Amount. - * - */ class Amount { @@ -42,11 +38,6 @@ class Amount * This method will properly format the given number, in color or "black and white", * as a currency, given two things: the currency required and the current locale. * - * @param TransactionCurrency $format - * @param string $amount - * @param bool $coloured - * - * @return string * @throws FireflyException */ public function formatAnything(TransactionCurrency $format, string $amount, bool $coloured = null): string @@ -58,14 +49,9 @@ class Amount * This method will properly format the given number, in color or "black and white", * as a currency, given two things: the currency required and the current locale. * - * @param string $symbol - * @param int $decimalPlaces - * @param string $amount - * @param bool $coloured - * - * @return string - * * @throws FireflyException + * + * @SuppressWarnings(PHPMD.MissingImport) */ public function formatFlat(string $symbol, int $decimalPlaces, string $amount, bool $coloured = null): string { @@ -73,11 +59,11 @@ class Amount $rounded = app('steam')->bcround($amount, $decimalPlaces); $coloured ??= true; - $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); - $fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, $symbol); - $fmt->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces); - $fmt->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $decimalPlaces); - $result = (string)$fmt->format((float)$rounded); // intentional float + $fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY); + $fmt->setSymbol(\NumberFormatter::CURRENCY_SYMBOL, $symbol); + $fmt->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces); + $fmt->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, $decimalPlaces); + $result = (string) $fmt->format((float) $rounded); // intentional float if (true === $coloured) { if (1 === bccomp($rounded, '0')) { @@ -93,28 +79,19 @@ class Amount return $result; } - /** - * @return Collection - */ public function getAllCurrencies(): Collection { return TransactionCurrency::orderBy('code', 'ASC')->get(); } - /** - * @return Collection - */ public function getCurrencies(): Collection { /** @var User $user */ $user = auth()->user(); + return $user->currencies()->orderBy('code', 'ASC')->get(); } - /** - * @return TransactionCurrency - * @throws FireflyException - */ public function getDefaultCurrency(): TransactionCurrency { /** @var User $user */ @@ -123,11 +100,6 @@ class Amount return $this->getDefaultCurrencyByUserGroup($user->userGroup); } - /** - * @param UserGroup $userGroup - * - * @return TransactionCurrency - */ public function getDefaultCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency { $cache = new CacheProperties(); @@ -147,19 +119,13 @@ class Amount return $default; } - /** - * @return TransactionCurrency - */ public function getSystemCurrency(): TransactionCurrency { return TransactionCurrency::where('code', 'EUR')->first(); } /** - * @param User $user - * - * @return TransactionCurrency - * @deprecated use getDefaultCurrencyByUserGroup instead. + * @deprecated use getDefaultCurrencyByUserGroup instead */ public function getDefaultCurrencyByUser(User $user): TransactionCurrency { @@ -172,7 +138,6 @@ class Amount * * Used only in one place. * - * @return array * @throws FireflyException */ public function getJsConfig(): array @@ -192,59 +157,11 @@ class Amount ]; } - /** - * @return array - * @throws FireflyException - */ - private function getLocaleInfo(): array - { - // get config from preference, not from translation: - $locale = app('steam')->getLocale(); - $array = app('steam')->getLocaleArray($locale); - - setlocale(LC_MONETARY, $array); - $info = localeconv(); - - // correct variables - $info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes'); - $info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes'); - - $info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space'); - $info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space'); - - $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); - - $info['mon_decimal_point'] = $fmt->getSymbol(NumberFormatter::MONETARY_SEPARATOR_SYMBOL); - $info['mon_thousands_sep'] = $fmt->getSymbol(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL); - - return $info; - } - - /** - * @param array $info - * @param string $field - * - * @return bool - */ - private function getLocaleField(array $info, string $field): bool - { - return (is_bool($info[$field]) && true === $info[$field]) - || (is_int($info[$field]) && 1 === $info[$field]); - } - /** * bool $sepBySpace is $localeconv['n_sep_by_space'] * int $signPosn = $localeconv['n_sign_posn'] * string $sign = $localeconv['negative_sign'] * bool $csPrecedes = $localeconv['n_cs_precedes']. - * - * @param bool $sepBySpace - * @param int $signPosn - * @param string $sign - * @param bool $csPrecedes - * - * @return string - * */ public static function getAmountJsConfig(bool $sepBySpace, int $signPosn, string $sign, bool $csPrecedes): string { @@ -277,32 +194,75 @@ class Amount // ( and ) around the whole thing $posA = '('; $posE = ')'; + break; + case 1: // The sign string precedes the quantity and currency_symbol $posA = $sign; + break; + case 2: // The sign string succeeds the quantity and currency_symbol $posE = $sign; + break; + case 3: // The sign string immediately precedes the currency_symbol $posB = $sign; + break; + case 4: // The sign string immediately succeeds the currency_symbol $posC = $sign; } // default is amount before currency - $format = $posA . $posD . '%v' . $space . $posB . '%s' . $posC . $posE; + $format = $posA.$posD.'%v'.$space.$posB.'%s'.$posC.$posE; if ($csPrecedes) { // alternative is currency before amount - $format = $posA . $posB . '%s' . $posC . $space . $posD . '%v' . $posE; + $format = $posA.$posB.'%s'.$posC.$space.$posD.'%v'.$posE; } return $format; } + + /** + * @throws FireflyException + * + * @SuppressWarnings(PHPMD.MissingImport) + */ + private function getLocaleInfo(): array + { + // get config from preference, not from translation: + $locale = app('steam')->getLocale(); + $array = app('steam')->getLocaleArray($locale); + + setlocale(LC_MONETARY, $array); + $info = localeconv(); + + // correct variables + $info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes'); + $info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes'); + + $info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space'); + $info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space'); + + $fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY); + + $info['mon_decimal_point'] = $fmt->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL); + $info['mon_thousands_sep'] = $fmt->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL); + + return $info; + } + + private function getLocaleField(array $info, string $field): bool + { + return (is_bool($info[$field]) && true === $info[$field]) + || (is_int($info[$field]) && 1 === $info[$field]); + } } diff --git a/app/Support/Authentication/RemoteUserGuard.php b/app/Support/Authentication/RemoteUserGuard.php index a03662a869..fda3d0e223 100644 --- a/app/Support/Authentication/RemoteUserGuard.php +++ b/app/Support/Authentication/RemoteUserGuard.php @@ -31,8 +31,6 @@ use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Auth\UserProvider; use Illuminate\Contracts\Foundation\Application; use Illuminate\Http\Request; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class RemoteUserGuard @@ -41,20 +39,14 @@ class RemoteUserGuard implements Guard { protected Application $application; protected UserProvider $provider; - protected User | null $user; + protected null|User $user; /** * Create a new authentication guard. - * - * @param UserProvider $provider - * @param Application $app - * - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function __construct(UserProvider $provider, Application $app) { - /** @var Request|null $request */ + /** @var null|Request $request */ $request = $app->get('request'); app('log')->debug(sprintf('Created RemoteUserGuard for %s "%s"', $request?->getMethod(), $request?->getRequestUri())); $this->application = $app; @@ -62,9 +54,6 @@ class RemoteUserGuard implements Guard $this->user = null; } - /** - * - */ public function authenticate(): void { app('log')->debug(sprintf('Now at %s', __METHOD__)); @@ -84,6 +73,7 @@ class RemoteUserGuard implements Guard if (null === $userID || '' === $userID) { app('log')->error(sprintf('No user in header "%s".', $header)); + throw new FireflyException('The guard header was unexpectedly empty. See the logs.'); } @@ -112,86 +102,77 @@ class RemoteUserGuard implements Guard $this->user = $retrievedUser; } - /** - * @inheritDoc - */ public function guest(): bool { app('log')->debug(sprintf('Now at %s', __METHOD__)); + return !$this->check(); } - /** - * @inheritDoc - */ public function check(): bool { app('log')->debug(sprintf('Now at %s', __METHOD__)); + return null !== $this->user(); } - /** - * @inheritDoc - */ public function user(): ?User { app('log')->debug(sprintf('Now at %s', __METHOD__)); $user = $this->user; if (null === $user) { app('log')->debug('User is NULL'); + return null; } return $user; } - /** - * @inheritDoc - */ public function hasUser(): bool { app('log')->debug(sprintf('Now at %s', __METHOD__)); + throw new FireflyException('Did not implement RemoteUserGuard::hasUser()'); } /** - * @inheritDoc * @SuppressWarnings(PHPMD.ShortMethodName) */ - public function id(): int | string | null + public function id(): null|int|string { app('log')->debug(sprintf('Now at %s', __METHOD__)); + return $this->user?->id; } - /** - * @inheritDoc - */ - public function setUser(Authenticatable | User | null $user) + public function setUser(null|Authenticatable|User $user): void { app('log')->debug(sprintf('Now at %s', __METHOD__)); if ($user instanceof User) { $this->user = $user; + return; } app('log')->error(sprintf('Did not set user at %s', __METHOD__)); } /** - * @inheritDoc + * @throws FireflyException + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function validate(array $credentials = []) + public function validate(array $credentials = []): bool { app('log')->debug(sprintf('Now at %s', __METHOD__)); + throw new FireflyException('Did not implement RemoteUserGuard::validate()'); } - /** - * @return bool - */ public function viaRemember(): bool { app('log')->debug(sprintf('Now at %s', __METHOD__)); + return false; } } diff --git a/app/Support/Authentication/RemoteUserProvider.php b/app/Support/Authentication/RemoteUserProvider.php index a7ef0938b1..55dbbb1c2b 100644 --- a/app/Support/Authentication/RemoteUserProvider.php +++ b/app/Support/Authentication/RemoteUserProvider.php @@ -30,7 +30,6 @@ use FireflyIII\Models\Role; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\UserProvider; -use Str; /** * Class RemoteUserProvider @@ -38,16 +37,21 @@ use Str; class RemoteUserProvider implements UserProvider { /** - * @inheritDoc + * @throws FireflyException + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function retrieveByCredentials(array $credentials) + public function retrieveByCredentials(array $credentials): null|Authenticatable { app('log')->debug(sprintf('Now at %s', __METHOD__)); + throw new FireflyException(sprintf('Did not implement %s', __METHOD__)); } /** - * @inheritDoc + * @param mixed $identifier + * + * @throws FireflyException */ public function retrieveById($identifier): User { @@ -60,7 +64,7 @@ class RemoteUserProvider implements UserProvider 'blocked' => false, 'blocked_code' => null, 'email' => $identifier, - 'password' => bcrypt(Str::random(64)), + 'password' => bcrypt(\Str::random(64)), ] ); // if this is the first user, give them admin as well. @@ -77,29 +81,43 @@ class RemoteUserProvider implements UserProvider } /** - * @inheritDoc + * @param mixed $identifier + * @param mixed $token + * + * @throws FireflyException + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function retrieveByToken($identifier, $token) + public function retrieveByToken($identifier, $token): null|Authenticatable { app('log')->debug(sprintf('Now at %s', __METHOD__)); + throw new FireflyException(sprintf('A) Did not implement %s', __METHOD__)); } /** - * @inheritDoc + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * + * @param mixed $token + * + * @throws FireflyException */ - public function updateRememberToken(Authenticatable $user, $token) + public function updateRememberToken(Authenticatable $user, $token): void { app('log')->debug(sprintf('Now at %s', __METHOD__)); + throw new FireflyException(sprintf('B) Did not implement %s', __METHOD__)); } /** - * @inheritDoc + * @throws FireflyException + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function validateCredentials(Authenticatable $user, array $credentials) + public function validateCredentials(Authenticatable $user, array $credentials): bool { app('log')->debug(sprintf('Now at %s', __METHOD__)); + throw new FireflyException(sprintf('C) Did not implement %s', __METHOD__)); } } diff --git a/app/Support/Binder/AccountList.php b/app/Support/Binder/AccountList.php index 3859b5be1a..e7c8d2b7c8 100644 --- a/app/Support/Binder/AccountList.php +++ b/app/Support/Binder/AccountList.php @@ -34,12 +34,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class AccountList implements BinderInterface { /** - * @param string $value - * @param Route $route - * - * @return Collection * @throws NotFoundHttpException - * */ public static function routeBinder(string $value, Route $route): Collection { @@ -48,20 +43,23 @@ class AccountList implements BinderInterface if ('allAssetAccounts' === $value) { /** @var Collection $collection */ $collection = auth()->user()->accounts() - ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') - ->whereIn('account_types.type', [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]) - ->orderBy('accounts.name', 'ASC') - ->get(['accounts.*']); + ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') + ->whereIn('account_types.type', [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]) + ->orderBy('accounts.name', 'ASC') + ->get(['accounts.*']) + ; } if ('allAssetAccounts' !== $value) { $incoming = array_map('\intval', explode(',', $value)); $list = array_merge(array_unique($incoming), [0]); + /** @var Collection $collection */ $collection = auth()->user()->accounts() - ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') - ->whereIn('accounts.id', $list) - ->orderBy('accounts.name', 'ASC') - ->get(['accounts.*']); + ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') + ->whereIn('accounts.id', $list) + ->orderBy('accounts.name', 'ASC') + ->get(['accounts.*']) + ; } if ($collection->count() > 0) { @@ -69,6 +67,7 @@ class AccountList implements BinderInterface } } app('log')->error(sprintf('Trying to show account list (%s), but user is not logged in or list is empty.', $route->uri)); + throw new NotFoundHttpException(); } } diff --git a/app/Support/Binder/BinderInterface.php b/app/Support/Binder/BinderInterface.php index 7a9c489268..345c357f7c 100644 --- a/app/Support/Binder/BinderInterface.php +++ b/app/Support/Binder/BinderInterface.php @@ -31,9 +31,6 @@ use Illuminate\Routing\Route; interface BinderInterface { /** - * @param string $value - * @param Route $route - * * @return mixed */ public static function routeBinder(string $value, Route $route); diff --git a/app/Support/Binder/BudgetList.php b/app/Support/Binder/BudgetList.php index df1130b80d..80042162c3 100644 --- a/app/Support/Binder/BudgetList.php +++ b/app/Support/Binder/BudgetList.php @@ -34,37 +34,33 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class BudgetList implements BinderInterface { /** - * @param string $value - * @param Route $route - * - * @return Collection * @throws NotFoundHttpException - * */ public static function routeBinder(string $value, Route $route): Collection { if (auth()->check()) { if ('allBudgets' === $value) { return auth()->user()->budgets()->where('active', true) - ->orderBy('order', 'ASC') - ->orderBy('name', 'ASC') - ->get(); + ->orderBy('order', 'ASC') + ->orderBy('name', 'ASC') + ->get() + ; } $list = array_unique(array_map('\intval', explode(',', $value))); - if (0 === count($list)) { // @phpstan-ignore-line app('log')->warning('Budget list count is zero, return 404.'); + throw new NotFoundHttpException(); } - /** @var Collection $collection */ $collection = auth()->user()->budgets() - ->where('active', true) - ->whereIn('id', $list) - ->get(); + ->where('active', true) + ->whereIn('id', $list) + ->get() + ; // add empty budget if applicable. if (in_array(0, $list, true)) { @@ -76,6 +72,7 @@ class BudgetList implements BinderInterface } } app('log')->warning('BudgetList fallback to 404.'); + throw new NotFoundHttpException(); } } diff --git a/app/Support/Binder/CLIToken.php b/app/Support/Binder/CLIToken.php index fcb1889880..f965e505c8 100644 --- a/app/Support/Binder/CLIToken.php +++ b/app/Support/Binder/CLIToken.php @@ -34,10 +34,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class CLIToken implements BinderInterface { /** - * @param string $value - * @param Route $route - * * @return mixed + * * @throws FireflyException */ public static function routeBinder(string $value, Route $route) @@ -60,6 +58,7 @@ class CLIToken implements BinderInterface } } app('log')->error(sprintf('Recognized no users by access token "%s"', $value)); + throw new NotFoundHttpException(); } } diff --git a/app/Support/Binder/CategoryList.php b/app/Support/Binder/CategoryList.php index 8722688763..7b676fefeb 100644 --- a/app/Support/Binder/CategoryList.php +++ b/app/Support/Binder/CategoryList.php @@ -34,20 +34,16 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class CategoryList implements BinderInterface { /** - * @param string $value - * @param Route $route - * - * @return Collection * @throws NotFoundHttpException - * */ public static function routeBinder(string $value, Route $route): Collection { if (auth()->check()) { if ('allCategories' === $value) { return auth()->user()->categories() - ->orderBy('name', 'ASC') - ->get(); + ->orderBy('name', 'ASC') + ->get() + ; } $list = array_unique(array_map('\intval', explode(',', $value))); @@ -57,8 +53,9 @@ class CategoryList implements BinderInterface /** @var Collection $collection */ $collection = auth()->user()->categories() - ->whereIn('id', $list) - ->get(); + ->whereIn('id', $list) + ->get() + ; // add empty category if applicable. if (in_array(0, $list, true)) { @@ -69,6 +66,7 @@ class CategoryList implements BinderInterface return $collection; } } + throw new NotFoundHttpException(); } } diff --git a/app/Support/Binder/CurrencyCode.php b/app/Support/Binder/CurrencyCode.php index 2f8c141fa9..cc5000ede6 100644 --- a/app/Support/Binder/CurrencyCode.php +++ b/app/Support/Binder/CurrencyCode.php @@ -33,10 +33,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class CurrencyCode implements BinderInterface { /** - * @param string $value - * @param Route $route - * - * @return TransactionCurrency * @throws NotFoundHttpException */ public static function routeBinder(string $value, Route $route): TransactionCurrency @@ -47,6 +43,7 @@ class CurrencyCode implements BinderInterface return $currency; } } + throw new NotFoundHttpException(); } } diff --git a/app/Support/Binder/Date.php b/app/Support/Binder/Date.php index 11f65ee027..1c9bc1f006 100644 --- a/app/Support/Binder/Date.php +++ b/app/Support/Binder/Date.php @@ -25,6 +25,7 @@ namespace FireflyIII\Support\Binder; use Carbon\Carbon; use Carbon\Exceptions\InvalidDateException; +use Carbon\Exceptions\InvalidFormatException; use FireflyIII\Helpers\Fiscal\FiscalHelperInterface; use Illuminate\Routing\Route; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -35,10 +36,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class Date implements BinderInterface { /** - * @param string $value - * @param Route $route - * - * @return Carbon * @throws NotFoundHttpException */ public static function routeBinder(string $value, Route $route): Carbon @@ -71,10 +68,11 @@ class Date implements BinderInterface try { $result = new Carbon($value); - } catch (InvalidDateException $e) { // @phpstan-ignore-line + } catch (InvalidDateException|InvalidFormatException $e) { // @phpstan-ignore-line $message = sprintf('Could not parse date "%s" for user #%d: %s', $value, auth()->user()->id, $e->getMessage()); app('log')->error($message); - throw new NotFoundHttpException($message, $e); + + throw new NotFoundHttpException('Could not parse value', $e); } return $result; diff --git a/app/Support/Binder/DynamicConfigKey.php b/app/Support/Binder/DynamicConfigKey.php index 90a1b7bf65..d277fc52f1 100644 --- a/app/Support/Binder/DynamicConfigKey.php +++ b/app/Support/Binder/DynamicConfigKey.php @@ -40,11 +40,8 @@ class DynamicConfigKey ]; /** - * @param string $value - * @param Route $route - * - * @return string * @throws NotFoundHttpException + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public static function routeBinder(string $value, Route $route): string @@ -52,6 +49,7 @@ class DynamicConfigKey if (in_array($value, self::$accepted, true)) { return $value; } + throw new NotFoundHttpException(); } } diff --git a/app/Support/Binder/EitherConfigKey.php b/app/Support/Binder/EitherConfigKey.php index cc62184c68..719f98d481 100644 --- a/app/Support/Binder/EitherConfigKey.php +++ b/app/Support/Binder/EitherConfigKey.php @@ -57,10 +57,6 @@ class EitherConfigKey ]; /** - * @param string $value - * @param Route $route - * - * @return string * @throws NotFoundHttpException * * @SuppressWarnings(PHPMD.UnusedFormalParameter) @@ -70,6 +66,7 @@ class EitherConfigKey if (in_array($value, self::$static, true) || in_array($value, DynamicConfigKey::$accepted, true)) { return $value; } + throw new NotFoundHttpException(); } } diff --git a/app/Support/Binder/JournalList.php b/app/Support/Binder/JournalList.php index b2594dbb87..9cb8017e13 100644 --- a/app/Support/Binder/JournalList.php +++ b/app/Support/Binder/JournalList.php @@ -34,11 +34,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class JournalList implements BinderInterface { /** - * @param string $value - * @param Route $route - * - * @return array - * * @throws NotFoundHttpException */ public static function routeBinder(string $value, Route $route): array @@ -59,14 +54,10 @@ class JournalList implements BinderInterface return $result; } + throw new NotFoundHttpException(); } - /** - * @param string $value - * - * @return array - */ protected static function parseList(string $value): array { $list = array_unique(array_map('\intval', explode(',', $value))); diff --git a/app/Support/Binder/TagList.php b/app/Support/Binder/TagList.php index d2c2988883..c8f9cc475d 100644 --- a/app/Support/Binder/TagList.php +++ b/app/Support/Binder/TagList.php @@ -35,10 +35,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class TagList implements BinderInterface { /** - * @param string $value - * @param Route $route - * - * @return Collection * @throws NotFoundHttpException */ public static function routeBinder(string $value, Route $route): Collection @@ -46,18 +42,19 @@ class TagList implements BinderInterface if (auth()->check()) { if ('allTags' === $value) { return auth()->user()->tags() - ->orderBy('tag', 'ASC') - ->get(); + ->orderBy('tag', 'ASC') + ->get() + ; } $list = array_unique(array_map('\strtolower', explode(',', $value))); app('log')->debug('List of tags is', $list); if (0 === count($list)) { // @phpstan-ignore-line app('log')->error('Tag list is empty.'); + throw new NotFoundHttpException(); } - /** @var TagRepositoryInterface $repository */ $repository = app(TagRepositoryInterface::class); $repository->setUser(auth()->user()); @@ -81,6 +78,7 @@ class TagList implements BinderInterface } } app('log')->error('TagList: user is not logged in.'); + throw new NotFoundHttpException(); } } diff --git a/app/Support/Binder/TagOrId.php b/app/Support/Binder/TagOrId.php index d02fb49812..ad3a866e1a 100644 --- a/app/Support/Binder/TagOrId.php +++ b/app/Support/Binder/TagOrId.php @@ -33,12 +33,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class TagOrId implements BinderInterface { - /** - * @param string $value - * @param Route $route - * - * @return Tag - */ public static function routeBinder(string $value, Route $route): Tag { if (auth()->check()) { @@ -54,9 +48,11 @@ class TagOrId implements BinderInterface return $result; } app('log')->error('TagOrId: tag not found.'); + throw new NotFoundHttpException(); } app('log')->error('TagOrId: user is not logged in.'); + throw new NotFoundHttpException(); } } diff --git a/app/Support/Binder/UserGroupAccount.php b/app/Support/Binder/UserGroupAccount.php index f8b7824912..db7a3fbf5a 100644 --- a/app/Support/Binder/UserGroupAccount.php +++ b/app/Support/Binder/UserGroupAccount.php @@ -34,12 +34,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class UserGroupAccount implements BinderInterface { /** - * @param string $value - * @param Route $route - * - * @return Account * @throws NotFoundHttpException - * */ public static function routeBinder(string $value, Route $route): Account { @@ -47,12 +42,14 @@ class UserGroupAccount implements BinderInterface /** @var User $user */ $user = auth()->user(); $currency = Account::where('id', (int)$value) - ->where('user_group_id', $user->user_group_id) - ->first(); + ->where('user_group_id', $user->user_group_id) + ->first() + ; if (null !== $currency) { return $currency; } } + throw new NotFoundHttpException(); } } diff --git a/app/Support/Binder/UserGroupBill.php b/app/Support/Binder/UserGroupBill.php index 09530c1230..bd2489965e 100644 --- a/app/Support/Binder/UserGroupBill.php +++ b/app/Support/Binder/UserGroupBill.php @@ -34,12 +34,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class UserGroupBill implements BinderInterface { /** - * @param string $value - * @param Route $route - * - * @return Bill * @throws NotFoundHttpException - * */ public static function routeBinder(string $value, Route $route): Bill { @@ -47,12 +42,14 @@ class UserGroupBill implements BinderInterface /** @var User $user */ $user = auth()->user(); $currency = Bill::where('id', (int)$value) - ->where('user_group_id', $user->user_group_id) - ->first(); + ->where('user_group_id', $user->user_group_id) + ->first() + ; if (null !== $currency) { return $currency; } } + throw new NotFoundHttpException(); } } diff --git a/app/Support/CacheProperties.php b/app/Support/CacheProperties.php index 8156dc691a..0a0bb96c17 100644 --- a/app/Support/CacheProperties.php +++ b/app/Support/CacheProperties.php @@ -23,23 +23,16 @@ declare(strict_types=1); namespace FireflyIII\Support; -use Cache; use Illuminate\Support\Collection; -use JsonException; /** * Class CacheProperties. - * - */ class CacheProperties { protected string $hash = ''; protected Collection $properties; - /** - * - */ public function __construct() { $this->properties = new Collection(); @@ -62,20 +55,14 @@ class CacheProperties */ public function get() { - return Cache::get($this->hash); + return \Cache::get($this->hash); } - /** - * @return string - */ public function getHash(): string { return $this->hash; } - /** - * @return bool - */ public function has(): bool { if ('testing' === config('app.env')) { @@ -83,23 +70,7 @@ class CacheProperties } $this->hash(); - return Cache::has($this->hash); - } - - /** - */ - private function hash(): void - { - $content = ''; - foreach ($this->properties as $property) { - try { - $content .= json_encode($property, JSON_THROW_ON_ERROR); - } catch (JsonException $e) { - // @ignoreException - $content .= hash('sha256', (string)time()); - } - } - $this->hash = substr(hash('sha256', $content), 0, 16); + return \Cache::has($this->hash); } /** @@ -107,6 +78,20 @@ class CacheProperties */ public function store($data): void { - Cache::forever($this->hash, $data); + \Cache::forever($this->hash, $data); + } + + private function hash(): void + { + $content = ''; + foreach ($this->properties as $property) { + try { + $content .= json_encode($property, JSON_THROW_ON_ERROR); + } catch (\JsonException $e) { + // @ignoreException + $content .= hash('sha256', (string)time()); + } + } + $this->hash = substr(hash('sha256', $content), 0, 16); } } diff --git a/app/Support/Calendar/Calculator.php b/app/Support/Calendar/Calculator.php index 7981470b19..e6e9255bc5 100644 --- a/app/Support/Calendar/Calculator.php +++ b/app/Support/Calendar/Calculator.php @@ -1,6 +1,5 @@ offsetGet($periodicity); $interval = $this->skipInterval($skipInterval); + return $periodicity->nextDate($epoch->clone(), $interval); } - /** - * @param Periodicity $periodicity - * - * @return bool - */ public function isAvailablePeriodicity(Periodicity $periodicity): bool { return self::containsInterval($periodicity); } - /** - * @param Periodicity $periodicity - * - * @return bool - */ private static function containsInterval(Periodicity $periodicity): bool { return self::loadIntervalMap()->contains($periodicity); } /** - * @return SplObjectStorage + * @SuppressWarnings(PHPMD.MissingImport) */ - private static function loadIntervalMap(): SplObjectStorage + private static function loadIntervalMap(): \SplObjectStorage { - if (self::$intervalMap !== null) { + if (null !== self::$intervalMap) { return self::$intervalMap; } - self::$intervalMap = new SplObjectStorage(); + self::$intervalMap = new \SplObjectStorage(); foreach (Periodicity::cases() as $interval) { - $periodicityClass = __NAMESPACE__ . "\\Periodicity\\{$interval->name}"; + $periodicityClass = __NAMESPACE__."\\Periodicity\\{$interval->name}"; self::$intervals[] = $interval->name; self::$intervalMap->attach($interval, new $periodicityClass()); } + return self::$intervalMap; } - /** - * @param int $skip - * - * @return int - */ private function skipInterval(int $skip): int { return self::DEFAULT_INTERVAL + $skip; } - } diff --git a/app/Support/Calendar/Periodicity.php b/app/Support/Calendar/Periodicity.php index 83803c2ce4..e7e37b0ca5 100644 --- a/app/Support/Calendar/Periodicity.php +++ b/app/Support/Calendar/Periodicity.php @@ -1,6 +1,5 @@ diff --git a/app/Support/Calendar/Periodicity/Bimonthly.php b/app/Support/Calendar/Periodicity/Bimonthly.php index 75e2de620a..ce89863100 100644 --- a/app/Support/Calendar/Periodicity/Bimonthly.php +++ b/app/Support/Calendar/Periodicity/Bimonthly.php @@ -1,6 +1,5 @@ diff --git a/app/Support/Calendar/Periodicity/Daily.php b/app/Support/Calendar/Periodicity/Daily.php index 5b9a661322..1b5811644b 100644 --- a/app/Support/Calendar/Periodicity/Daily.php +++ b/app/Support/Calendar/Periodicity/Daily.php @@ -1,6 +1,5 @@ @@ -32,14 +31,8 @@ use Carbon\Carbon; */ final class Daily extends Interval { - /** - * @param Carbon $date - * @param int $interval - * - * @return Carbon - */ public function nextDate(Carbon $date, int $interval = 1): Carbon { - return ($date->clone())->addDays($this->skip($interval)); + return $date->clone()->addDays($this->skip($interval)); } } diff --git a/app/Support/Calendar/Periodicity/Fortnightly.php b/app/Support/Calendar/Periodicity/Fortnightly.php index 1647092a59..99327e0276 100644 --- a/app/Support/Calendar/Periodicity/Fortnightly.php +++ b/app/Support/Calendar/Periodicity/Fortnightly.php @@ -1,6 +1,5 @@ diff --git a/app/Support/Calendar/Periodicity/HalfYearly.php b/app/Support/Calendar/Periodicity/HalfYearly.php index b19a803b02..4471a4f030 100644 --- a/app/Support/Calendar/Periodicity/HalfYearly.php +++ b/app/Support/Calendar/Periodicity/HalfYearly.php @@ -1,6 +1,5 @@ diff --git a/app/Support/Calendar/Periodicity/Interspacable.php b/app/Support/Calendar/Periodicity/Interspacable.php index 0e3281a4ba..b1d2ab5b42 100644 --- a/app/Support/Calendar/Periodicity/Interspacable.php +++ b/app/Support/Calendar/Periodicity/Interspacable.php @@ -1,6 +1,5 @@ @@ -32,11 +31,5 @@ use Carbon\Carbon; */ interface Interspacable { - /** - * @param Carbon $date - * @param int $interval - * - * @return Carbon - */ public function nextDate(Carbon $date, int $interval = 1): Carbon; } diff --git a/app/Support/Calendar/Periodicity/Interval.php b/app/Support/Calendar/Periodicity/Interval.php index 62269fb7b5..ec34c1d43d 100644 --- a/app/Support/Calendar/Periodicity/Interval.php +++ b/app/Support/Calendar/Periodicity/Interval.php @@ -1,6 +1,5 @@ @@ -32,11 +31,6 @@ abstract class Interval implements Interspacable { public const int INTERVAL = 1; - /** - * @param int $skip - * - * @return int - */ final public function skip(int $skip): int { return static::INTERVAL * $skip; diff --git a/app/Support/Calendar/Periodicity/Monthly.php b/app/Support/Calendar/Periodicity/Monthly.php index 983383ab90..a2b1112d93 100644 --- a/app/Support/Calendar/Periodicity/Monthly.php +++ b/app/Support/Calendar/Periodicity/Monthly.php @@ -1,6 +1,5 @@ @@ -32,14 +31,8 @@ use Carbon\Carbon; */ class Monthly extends Interval { - /** - * @param Carbon $date - * @param int $interval - * - * @return Carbon - */ public function nextDate(Carbon $date, int $interval = 1): Carbon { - return ($date->clone())->addMonthsNoOverflow($this->skip($interval)); + return $date->clone()->addMonthsNoOverflow($this->skip($interval)); } } diff --git a/app/Support/Calendar/Periodicity/Quarterly.php b/app/Support/Calendar/Periodicity/Quarterly.php index 8c958f33a6..a9cd21b9a1 100644 --- a/app/Support/Calendar/Periodicity/Quarterly.php +++ b/app/Support/Calendar/Periodicity/Quarterly.php @@ -1,6 +1,5 @@ diff --git a/app/Support/Calendar/Periodicity/Weekly.php b/app/Support/Calendar/Periodicity/Weekly.php index 54e33408a6..c4252eab93 100644 --- a/app/Support/Calendar/Periodicity/Weekly.php +++ b/app/Support/Calendar/Periodicity/Weekly.php @@ -1,6 +1,5 @@ @@ -32,14 +31,8 @@ use Carbon\Carbon; */ class Weekly extends Interval { - /** - * @param Carbon $date - * @param int $interval - * - * @return Carbon - */ public function nextDate(Carbon $date, int $interval = 1): Carbon { - return ($date->clone())->addWeeks($this->skip($interval)); + return $date->clone()->addWeeks($this->skip($interval)); } } diff --git a/app/Support/Calendar/Periodicity/Yearly.php b/app/Support/Calendar/Periodicity/Yearly.php index 794bc111dd..1cf997c3fe 100644 --- a/app/Support/Calendar/Periodicity/Yearly.php +++ b/app/Support/Calendar/Periodicity/Yearly.php @@ -1,6 +1,5 @@ @@ -32,14 +31,8 @@ use Carbon\Carbon; */ final class Yearly extends Interval { - /** - * @param Carbon $date - * @param int $interval - * - * @return Carbon - */ public function nextDate(Carbon $date, int $interval = 1): Carbon { - return ($date->clone())->addYearsNoOverflow($this->skip($interval)); + return $date->clone()->addYearsNoOverflow($this->skip($interval)); } } diff --git a/app/Support/Chart/Budget/FrontpageChartGenerator.php b/app/Support/Chart/Budget/FrontpageChartGenerator.php index 3d681750fc..266274499b 100644 --- a/app/Support/Chart/Budget/FrontpageChartGenerator.php +++ b/app/Support/Chart/Budget/FrontpageChartGenerator.php @@ -78,15 +78,33 @@ class FrontpageChartGenerator return $data; } + public function setEnd(Carbon $end): void + { + $this->end = $end; + } + + public function setStart(Carbon $start): void + { + $this->start = $start; + } + + /** + * A basic setter for the user. Also updates the repositories with the right user. + */ + public function setUser(User $user): void + { + $this->budgetRepository->setUser($user); + $this->blRepository->setUser($user); + $this->opsRepository->setUser($user); + + $locale = app('steam')->getLocale(); + $this->monthAndDayFormat = (string)trans('config.month_and_day_js', [], $locale); + } + /** * For each budget, gets all budget limits for the current time range. * When no limits are present, the time range is used to collect information on money spent. * If limits are present, each limit is processed individually. - * - * @param array $data - * @param Budget $budget - * - * @return array */ private function processBudget(array $data, Budget $budget): array { @@ -104,15 +122,11 @@ class FrontpageChartGenerator /** * When no limits are present, the expenses of the whole period are collected and grouped. * This is grouped per currency. Because there is no limit set, "left to spend" and "overspent" are empty. - * - * @param array $data - * @param Budget $budget - * - * @return array */ private function noBudgetLimits(array $data, Budget $budget): array { $spent = $this->opsRepository->sumExpenses($this->start, $this->end, null, new Collection([$budget])); + /** @var array $entry */ foreach ($spent as $entry) { $title = sprintf('%s (%s)', $budget->name, $entry['currency_name']); @@ -126,12 +140,6 @@ class FrontpageChartGenerator /** * If a budget has budget limit, each limit is processed individually. - * - * @param array $data - * @param Budget $budget - * @param Collection $limits - * - * @return array */ private function budgetLimits(array $data, Budget $budget, Collection $limits): array { @@ -146,16 +154,11 @@ class FrontpageChartGenerator /** * For each limit, the expenses from the time range of the limit are collected. Each row from the result is * processed individually. - * - * @param array $data - * @param Budget $budget - * @param BudgetLimit $limit - * - * @return array */ private function processLimit(array $data, Budget $budget, BudgetLimit $limit): array { $spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection([$budget]), $limit->transactionCurrency); + /** @var array $entry */ foreach ($spent as $entry) { // only spent the entry where the entry's currency matches the budget limit's currency @@ -172,13 +175,6 @@ class FrontpageChartGenerator * * Each one is added to the $data array. If the limit's date range is different from the global $start and $end * dates, for example when a limit only partially falls into this month, the title is expanded to clarify. - * - * @param array $data - * @param Budget $budget - * @param BudgetLimit $limit - * @param array $entry - * - * @return array */ private function processRow(array $data, Budget $budget, BudgetLimit $limit, array $entry): array { @@ -200,35 +196,4 @@ class FrontpageChartGenerator return $data; } - - /** - * @param Carbon $end - */ - public function setEnd(Carbon $end): void - { - $this->end = $end; - } - - /** - * @param Carbon $start - */ - public function setStart(Carbon $start): void - { - $this->start = $start; - } - - /** - * A basic setter for the user. Also updates the repositories with the right user. - * - * @param User $user - */ - public function setUser(User $user): void - { - $this->budgetRepository->setUser($user); - $this->blRepository->setUser($user); - $this->opsRepository->setUser($user); - - $locale = app('steam')->getLocale(); - $this->monthAndDayFormat = (string)trans('config.month_and_day_js', [], $locale); - } } diff --git a/app/Support/Chart/Category/FrontpageChartGenerator.php b/app/Support/Chart/Category/FrontpageChartGenerator.php index 1bdae34f96..59d33e4a4e 100644 --- a/app/Support/Chart/Category/FrontpageChartGenerator.php +++ b/app/Support/Chart/Category/FrontpageChartGenerator.php @@ -51,9 +51,6 @@ class FrontpageChartGenerator /** * FrontpageChartGenerator constructor. - * - * @param Carbon $start - * @param Carbon $end */ public function __construct(Carbon $start, Carbon $end) { @@ -66,9 +63,6 @@ class FrontpageChartGenerator $this->noCatRepos = app(NoCategoryRepositoryInterface::class); } - /** - * @return array - */ public function generate(): array { $categories = $this->repository->getCategories(); @@ -78,6 +72,7 @@ class FrontpageChartGenerator // get expenses + income per category: $collection = []; + /** @var Category $category */ foreach ($categories as $category) { // get expenses @@ -98,12 +93,6 @@ class FrontpageChartGenerator return $this->insertValues($currencyData, $tempData); } - /** - * @param Category $category - * @param Collection $accounts - * - * @return array - */ private function collectExpenses(Category $category, Collection $accounts): array { $spent = $this->opsRepos->sumExpenses($this->start, $this->end, $accounts, new Collection([$category])); @@ -121,9 +110,6 @@ class FrontpageChartGenerator return $tempData; } - /** - * @param array $currency - */ private function addCurrency(array $currency): void { $currencyId = (int)$currency['currency_id']; @@ -137,11 +123,6 @@ class FrontpageChartGenerator ]; } - /** - * @param Collection $accounts - * - * @return array - */ private function collectNoCatExpenses(Collection $accounts): array { $noCatExp = $this->noCatRepos->sumExpenses($this->start, $this->end, $accounts); @@ -159,15 +140,11 @@ class FrontpageChartGenerator return $tempData; } - /** - * @param array $data - * - * @return array - */ private function createCurrencyGroups(array $data): array { $return = []; $names = $this->expandNames($data); + /** * @var array $currency */ @@ -184,12 +161,6 @@ class FrontpageChartGenerator return $return; } - /** - * @param array $currencyData - * @param array $monetaryData - * - * @return array - */ private function insertValues(array $currencyData, array $monetaryData): array { /** @var array $array */ diff --git a/app/Support/Chart/Category/WholePeriodChartGenerator.php b/app/Support/Chart/Category/WholePeriodChartGenerator.php index 9c4b8ef915..cff791226b 100644 --- a/app/Support/Chart/Category/WholePeriodChartGenerator.php +++ b/app/Support/Chart/Category/WholePeriodChartGenerator.php @@ -35,13 +35,6 @@ use Illuminate\Support\Collection; */ class WholePeriodChartGenerator { - /** - * @param Category $category - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ public function generate(Category $category, Carbon $start, Carbon $end): array { $collection = new Collection([$category]); @@ -116,11 +109,6 @@ class WholePeriodChartGenerator /** * TODO this method is duplicated - * - * @param Carbon $start - * @param Carbon $end - * - * @return string */ protected function calculateStep(Carbon $start, Carbon $end): string { @@ -142,10 +130,6 @@ class WholePeriodChartGenerator /** * Loop array of spent/earned info, and extract which currencies are present. * Key is the currency ID. - * - * @param array $array - * - * @return array */ private function extractCurrencies(array $array): array { diff --git a/app/Support/ChartColour.php b/app/Support/ChartColour.php index 54d64c32ea..f08de5c258 100644 --- a/app/Support/ChartColour.php +++ b/app/Support/ChartColour.php @@ -25,8 +25,6 @@ namespace FireflyIII\Support; /** * Class ChartColour. - * - */ class ChartColour { @@ -54,11 +52,6 @@ class ChartColour [194, 24, 91], ]; - /** - * @param int $index - * - * @return string - */ public static function getColour(int $index): string { $index %= count(self::$colours); diff --git a/app/Support/Cronjobs/AbstractCronjob.php b/app/Support/Cronjobs/AbstractCronjob.php index 581051e3db..35ccedfa44 100644 --- a/app/Support/Cronjobs/AbstractCronjob.php +++ b/app/Support/Cronjobs/AbstractCronjob.php @@ -27,8 +27,6 @@ use Carbon\Carbon; /** * Class AbstractCronjob - * - */ abstract class AbstractCronjob { @@ -53,23 +51,14 @@ abstract class AbstractCronjob $this->message = null; } - /** - * - */ abstract public function fire(): void; - /** - * @param Carbon $date - */ final public function setDate(Carbon $date): void { $newDate = clone $date; $this->date = $newDate; } - /** - * @param bool $force - */ final public function setForce(bool $force): void { $this->force = $force; diff --git a/app/Support/Cronjobs/AutoBudgetCronjob.php b/app/Support/Cronjobs/AutoBudgetCronjob.php index 6008e834c8..5947f8ebcf 100644 --- a/app/Support/Cronjobs/AutoBudgetCronjob.php +++ b/app/Support/Cronjobs/AutoBudgetCronjob.php @@ -33,9 +33,6 @@ use FireflyIII\Models\Configuration; */ class AutoBudgetCronjob extends AbstractCronjob { - /** - * @inheritDoc - */ public function fire(): void { /** @var Configuration $config */ @@ -66,12 +63,10 @@ class AutoBudgetCronjob extends AbstractCronjob app('preferences')->mark(); } - /** - * - */ private function fireAutoBudget(): void { app('log')->info(sprintf('Will now fire auto budget cron job task for date "%s".', $this->date->format('Y-m-d'))); + /** @var CreateAutoBudgetLimits $job */ $job = app(CreateAutoBudgetLimits::class, [$this->date]); $job->setDate($this->date); diff --git a/app/Support/Cronjobs/BillWarningCronjob.php b/app/Support/Cronjobs/BillWarningCronjob.php index 2e290c18f7..af0a3c5aef 100644 --- a/app/Support/Cronjobs/BillWarningCronjob.php +++ b/app/Support/Cronjobs/BillWarningCronjob.php @@ -27,8 +27,6 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Jobs\WarnAboutBills; use FireflyIII\Models\Configuration; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class BillWarningCronjob @@ -37,12 +35,11 @@ class BillWarningCronjob extends AbstractCronjob { /** * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function fire(): void { app('log')->debug(sprintf('Now in %s', __METHOD__)); + /** @var Configuration $config */ $config = app('fireflyconfig')->get('last_bw_job', 0); $lastTime = (int)$config->data; @@ -77,12 +74,10 @@ class BillWarningCronjob extends AbstractCronjob app('preferences')->mark(); } - /** - * - */ private function fireWarnings(): void { app('log')->info(sprintf('Will now fire bill warning job task for date "%s".', $this->date->format('Y-m-d H:i:s'))); + /** @var WarnAboutBills $job */ $job = app(WarnAboutBills::class); $job->setDate($this->date); diff --git a/app/Support/Cronjobs/ExchangeRatesCronjob.php b/app/Support/Cronjobs/ExchangeRatesCronjob.php index 9793078677..356f71a2e4 100644 --- a/app/Support/Cronjobs/ExchangeRatesCronjob.php +++ b/app/Support/Cronjobs/ExchangeRatesCronjob.php @@ -33,9 +33,6 @@ use FireflyIII\Models\Configuration; */ class ExchangeRatesCronjob extends AbstractCronjob { - /** - * @inheritDoc - */ public function fire(): void { /** @var Configuration $config */ @@ -67,12 +64,10 @@ class ExchangeRatesCronjob extends AbstractCronjob app('preferences')->mark(); } - /** - * - */ private function fireExchangeRateJob(): void { app('log')->info(sprintf('Will now fire exchange rates cron job task for date "%s".', $this->date->format('Y-m-d'))); + /** @var DownloadExchangeRates $job */ $job = app(DownloadExchangeRates::class); $job->setDate($this->date); diff --git a/app/Support/Cronjobs/RecurringCronjob.php b/app/Support/Cronjobs/RecurringCronjob.php index 3388398140..04f449dc23 100644 --- a/app/Support/Cronjobs/RecurringCronjob.php +++ b/app/Support/Cronjobs/RecurringCronjob.php @@ -27,8 +27,6 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Jobs\CreateRecurringTransactions; use FireflyIII\Models\Configuration; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class RecurringCronjob @@ -37,12 +35,11 @@ class RecurringCronjob extends AbstractCronjob { /** * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function fire(): void { app('log')->debug(sprintf('Now in %s', __METHOD__)); + /** @var Configuration $config */ $config = app('fireflyconfig')->get('last_rt_job', 0); $lastTime = (int)$config->data; @@ -76,12 +73,10 @@ class RecurringCronjob extends AbstractCronjob app('preferences')->mark(); } - /** - * - */ private function fireRecurring(): void { app('log')->info(sprintf('Will now fire recurring cron job task for date "%s".', $this->date->format('Y-m-d H:i:s'))); + /** @var CreateRecurringTransactions $job */ $job = app(CreateRecurringTransactions::class); $job->setDate($this->date); diff --git a/app/Support/Domain.php b/app/Support/Domain.php index 42def30123..c37a70e431 100644 --- a/app/Support/Domain.php +++ b/app/Support/Domain.php @@ -25,30 +25,19 @@ namespace FireflyIII\Support; /** * Class Domain. - * - */ class Domain { - /** - * @return array - */ public static function getBindables(): array { return config('firefly.bindables'); } - /** - * @return array - */ public static function getRuleActions(): array { return config('firefly.rule-actions'); } - /** - * @return array - */ public static function getRuleTriggers(): array { return array_keys(config('search.operators')); diff --git a/app/Support/ExpandedForm.php b/app/Support/ExpandedForm.php index 222d4de291..84f56e21b7 100644 --- a/app/Support/ExpandedForm.php +++ b/app/Support/ExpandedForm.php @@ -27,24 +27,17 @@ use Eloquent; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Support\Form\FormSupport; use Illuminate\Support\Collection; -use Throwable; /** * Class ExpandedForm. - * - * - */ class ExpandedForm { use FormSupport; /** - * @param string $name - * @param mixed $value - * @param array|null $options + * @param mixed $value * - * @return string * @throws FireflyException */ public function amountNoCurrency(string $name, $value = null, array $options = null): string @@ -58,14 +51,15 @@ class ExpandedForm unset($options['currency'], $options['placeholder']); // make sure value is formatted nicely: - //if (null !== $value && '' !== $value) { - //$value = round((float)$value, 8); - //} + // if (null !== $value && '' !== $value) { + // $value = round((float)$value, 8); + // } try { $html = view('form.amount-no-currency', compact('classes', 'name', 'label', 'value', 'options'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render amountNoCurrency(): %s', $e->getMessage())); $html = 'Could not render amountNoCurrency.'; + throw new FireflyException($html, 0, $e); } @@ -73,12 +67,8 @@ class ExpandedForm } /** - * @param string $name - * @param int|null $value - * @param mixed $checked - * @param array|null $options + * @param mixed $checked * - * @return string * @throws FireflyException */ public function checkbox(string $name, int $value = null, $checked = null, array $options = null): string @@ -98,11 +88,13 @@ class ExpandedForm $value = $this->fillFieldValue($name, $value); unset($options['placeholder'], $options['autocomplete'], $options['class']); + try { $html = view('form.checkbox', compact('classes', 'name', 'label', 'value', 'options'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render checkbox(): %s', $e->getMessage())); $html = 'Could not render checkbox.'; + throw new FireflyException($html, 0, $e); } @@ -110,11 +102,8 @@ class ExpandedForm } /** - * @param string $name - * @param mixed $value - * @param array|null $options + * @param mixed $value * - * @return string * @throws FireflyException */ public function date(string $name, $value = null, array $options = null): string @@ -124,11 +113,13 @@ class ExpandedForm $classes = $this->getHolderClasses($name); $value = $this->fillFieldValue($name, $value); unset($options['placeholder']); + try { $html = view('form.date', compact('classes', 'name', 'label', 'value', 'options'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render date(): %s', $e->getMessage())); $html = 'Could not render date.'; + throw new FireflyException($html, 0, $e); } @@ -136,10 +127,6 @@ class ExpandedForm } /** - * @param string $name - * @param array|null $options - * - * @return string * @throws FireflyException */ public function file(string $name, array $options = null): string @@ -148,11 +135,13 @@ class ExpandedForm $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); + try { $html = view('form.file', compact('classes', 'name', 'label', 'options'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render file(): %s', $e->getMessage())); $html = 'Could not render file.'; + throw new FireflyException($html, 0, $e); } @@ -160,11 +149,8 @@ class ExpandedForm } /** - * @param string $name - * @param mixed $value - * @param array|null $options + * @param mixed $value * - * @return string * @throws FireflyException */ public function integer(string $name, $value = null, array $options = null): string @@ -175,11 +161,13 @@ class ExpandedForm $classes = $this->getHolderClasses($name); $value = $this->fillFieldValue($name, $value); $options['step'] ??= '1'; + try { $html = view('form.integer', compact('classes', 'name', 'label', 'value', 'options'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render integer(): %s', $e->getMessage())); $html = 'Could not render integer.'; + throw new FireflyException($html, 0, $e); } @@ -187,11 +175,8 @@ class ExpandedForm } /** - * @param string $name - * @param mixed $value - * @param array|null $options + * @param mixed $value * - * @return string * @throws FireflyException */ public function location(string $name, $value = null, array $options = null): string @@ -201,29 +186,26 @@ class ExpandedForm $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); $value = $this->fillFieldValue($name, $value); + try { $html = view('form.location', compact('classes', 'name', 'label', 'value', 'options'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render location(): %s', $e->getMessage())); $html = 'Could not render location.'; + throw new FireflyException($html, 0, $e); } return $html; } - /** - * @param Collection $set - * - * @return array - * - */ public function makeSelectListWithEmpty(Collection $set): array { $selectList = []; $selectList[0] = '(none)'; $fields = ['title', 'name', 'description']; - /** @var Eloquent $entry */ + + /** @var \Eloquent $entry */ foreach ($set as $entry) { // All Eloquent models have an ID $entryId = $entry->id; // @phpstan-ignore-line @@ -240,12 +222,9 @@ class ExpandedForm return $selectList; } - /** - * @param null $value - * @param array|null $options + * @param null $value * - * @return string * @throws FireflyException */ public function objectGroup($value = null, array $options = null): string @@ -263,9 +242,10 @@ class ExpandedForm try { $html = view('form.object_group', compact('classes', 'name', 'label', 'value', 'options'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render objectGroup(): %s', $e->getMessage())); $html = 'Could not render objectGroup.'; + throw new FireflyException($html, 0, $e); } @@ -273,19 +253,16 @@ class ExpandedForm } /** - * @param string $type - * @param string $name - * - * @return string * @throws FireflyException */ public function optionsList(string $type, string $name): string { try { $html = view('form.options', compact('type', 'name'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render select(): %s', $e->getMessage())); $html = 'Could not render optionsList.'; + throw new FireflyException($html, 0, $e); } @@ -293,10 +270,6 @@ class ExpandedForm } /** - * @param string $name - * @param array|null $options - * - * @return string * @throws FireflyException */ public function password(string $name, array $options = null): string @@ -304,11 +277,13 @@ class ExpandedForm $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); + try { $html = view('form.password', compact('classes', 'name', 'label', 'options'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render password(): %s', $e->getMessage())); $html = 'Could not render password.'; + throw new FireflyException($html, 0, $e); } @@ -318,11 +293,8 @@ class ExpandedForm /** * Function to render a percentage. * - * @param string $name - * @param mixed $value - * @param array|null $options + * @param mixed $value * - * @return string * @throws FireflyException */ public function percentage(string $name, $value = null, array $options = null): string @@ -333,11 +305,13 @@ class ExpandedForm $value = $this->fillFieldValue($name, $value); $options['step'] = 'any'; unset($options['placeholder']); + try { $html = view('form.percentage', compact('classes', 'name', 'label', 'value', 'options'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render percentage(): %s', $e->getMessage())); $html = 'Could not render percentage.'; + throw new FireflyException($html, 0, $e); } @@ -345,11 +319,8 @@ class ExpandedForm } /** - * @param string $name - * @param mixed $value - * @param array|null $options + * @param mixed $value * - * @return string * @throws FireflyException */ public function staticText(string $name, $value, array $options = null): string @@ -357,11 +328,13 @@ class ExpandedForm $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); + try { $html = view('form.static', compact('classes', 'name', 'label', 'value', 'options'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render staticText(): %s', $e->getMessage())); $html = 'Could not render staticText.'; + throw new FireflyException($html, 0, $e); } @@ -369,11 +342,8 @@ class ExpandedForm } /** - * @param string $name - * @param mixed $value - * @param array|null $options + * @param mixed $value * - * @return string * @throws FireflyException */ public function text(string $name, $value = null, array $options = null): string @@ -382,11 +352,13 @@ class ExpandedForm $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); $value = $this->fillFieldValue($name, $value); + try { $html = view('form.text', compact('classes', 'name', 'label', 'value', 'options'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render text(): %s', $e->getMessage())); $html = 'Could not render text.'; + throw new FireflyException($html, 0, $e); } @@ -394,11 +366,8 @@ class ExpandedForm } /** - * @param string $name - * @param mixed $value - * @param array|null $options + * @param mixed $value * - * @return string * @throws FireflyException */ public function textarea(string $name, $value = null, array $options = null): string @@ -415,9 +384,10 @@ class ExpandedForm try { $html = view('form.textarea', compact('classes', 'name', 'label', 'value', 'options'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render textarea(): %s', $e->getMessage())); $html = 'Could not render textarea.'; + throw new FireflyException($html, 0, $e); } diff --git a/app/Support/Export/ExportDataGenerator.php b/app/Support/Export/ExportDataGenerator.php index 7e740e07f7..8cd5caa992 100644 --- a/app/Support/Export/ExportDataGenerator.php +++ b/app/Support/Export/ExportDataGenerator.php @@ -56,8 +56,6 @@ use Illuminate\Support\Collection; use League\Csv\CannotInsertRecord; use League\Csv\Exception; use League\Csv\Writer; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Class ExportDataGenerator @@ -100,10 +98,7 @@ class ExportDataGenerator } /** - * @return array - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface */ public function export(): array { @@ -139,8 +134,88 @@ class ExportDataGenerator return $return; } + public function setUser(User $user): void + { + $this->user = $user; + } + + /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function get(string $key, mixed $default = null): mixed + { + return null; + } + + public function setAccounts(Collection $accounts): void + { + $this->accounts = $accounts; + } + + /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function has(mixed $key): mixed + { + return null; + } + + public function setEnd(Carbon $end): void + { + $this->end = $end; + } + + public function setExportAccounts(bool $exportAccounts): void + { + $this->exportAccounts = $exportAccounts; + } + + public function setExportBills(bool $exportBills): void + { + $this->exportBills = $exportBills; + } + + public function setExportBudgets(bool $exportBudgets): void + { + $this->exportBudgets = $exportBudgets; + } + + public function setExportCategories(bool $exportCategories): void + { + $this->exportCategories = $exportCategories; + } + + public function setExportPiggies(bool $exportPiggies): void + { + $this->exportPiggies = $exportPiggies; + } + + public function setExportRecurring(bool $exportRecurring): void + { + $this->exportRecurring = $exportRecurring; + } + + public function setExportRules(bool $exportRules): void + { + $this->exportRules = $exportRules; + } + + public function setExportTags(bool $exportTags): void + { + $this->exportTags = $exportTags; + } + + public function setExportTransactions(bool $exportTransactions): void + { + $this->exportTransactions = $exportTransactions; + } + + public function setStart(Carbon $start): void + { + $this->start = $start; + } + /** - * @return string * @throws CannotInsertRecord * @throws Exception * @throws FireflyException @@ -166,11 +241,13 @@ class ExportDataGenerator 'interest', 'interest_period', ]; + /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); $repository->setUser($this->user); $allAccounts = $repository->getAccountsByType([]); $records = []; + /** @var Account $account */ foreach ($allAccounts as $account) { $currency = $repository->getAccountCurrency($account); @@ -195,23 +272,24 @@ class ExportDataGenerator ]; } - //load the CSV document from a string + // load the CSV document from a string $csv = Writer::createFromString(); - //insert the header + // insert the header try { $csv->insertOne($header); } catch (CannotInsertRecord $e) { throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); } - //insert all the records + // insert all the records $csv->insertAll($records); try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception app('log')->error($e->getMessage()); + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } @@ -219,15 +297,6 @@ class ExportDataGenerator } /** - * @param User $user - */ - public function setUser(User $user): void - { - $this->user = $user; - } - - /** - * @return string * @throws CannotInsertRecord * @throws Exception * @throws FireflyException @@ -272,23 +341,24 @@ class ExportDataGenerator ]; } - //load the CSV document from a string + // load the CSV document from a string $csv = Writer::createFromString(); - //insert the header + // insert the header try { $csv->insertOne($header); } catch (CannotInsertRecord $e) { throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); } - //insert all the records + // insert all the records $csv->insertAll($records); try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception app('log')->error($e->getMessage()); + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } @@ -296,7 +366,6 @@ class ExportDataGenerator } /** - * @return string * @throws CannotInsertRecord * @throws Exception * @throws FireflyException @@ -320,9 +389,11 @@ class ExportDataGenerator $limitRepos = app(BudgetLimitRepositoryInterface::class); $budgets = $budgetRepos->getBudgets(); $records = []; + /** @var Budget $budget */ foreach ($budgets as $budget) { $limits = $limitRepos->getBudgetLimits($budget); + /** @var BudgetLimit $limit */ foreach ($limits as $limit) { $records[] = [ @@ -339,23 +410,24 @@ class ExportDataGenerator } } - //load the CSV document from a string + // load the CSV document from a string $csv = Writer::createFromString(); - //insert the header + // insert the header try { $csv->insertOne($header); } catch (CannotInsertRecord $e) { throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); } - //insert all the records + // insert all the records $csv->insertAll($records); try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception app('log')->error($e->getMessage()); + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } @@ -363,7 +435,6 @@ class ExportDataGenerator } /** - * @return string * @throws CannotInsertRecord * @throws Exception * @throws FireflyException @@ -390,23 +461,24 @@ class ExportDataGenerator ]; } - //load the CSV document from a string + // load the CSV document from a string $csv = Writer::createFromString(); - //insert the header + // insert the header try { $csv->insertOne($header); } catch (CannotInsertRecord $e) { throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); } - //insert all the records + // insert all the records $csv->insertAll($records); try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception app('log')->error($e->getMessage()); + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } @@ -414,7 +486,6 @@ class ExportDataGenerator } /** - * @return string * @throws CannotInsertRecord * @throws Exception * @throws FireflyException @@ -470,23 +541,24 @@ class ExportDataGenerator ]; } - //load the CSV document from a string + // load the CSV document from a string $csv = Writer::createFromString(); - //insert the header + // insert the header try { $csv->insertOne($header); } catch (CannotInsertRecord $e) { throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); } - //insert all the records + // insert all the records $csv->insertAll($records); try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception app('log')->error($e->getMessage()); + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } @@ -494,7 +566,6 @@ class ExportDataGenerator } /** - * @return string * @throws CannotInsertRecord * @throws Exception * @throws FireflyException @@ -506,61 +577,25 @@ class ExportDataGenerator $recurringRepos->setUser($this->user); $header = [ // recurrence: - 'user_id', - 'recurrence_id', - 'row_contains', - 'created_at', - 'updated_at', - 'type', - 'title', - 'description', - 'first_date', - 'repeat_until', - 'latest_date', - 'repetitions', - 'apply_rules', - 'active', + 'user_id', 'recurrence_id', 'row_contains', 'created_at', 'updated_at', 'type', 'title', 'description', 'first_date', 'repeat_until', 'latest_date', 'repetitions', 'apply_rules', 'active', // repetition info: - 'type', - 'moment', - 'skip', - 'weekend', + 'type', 'moment', 'skip', 'weekend', // transactions + meta: - 'currency_code', - 'foreign_currency_code', - 'source_name', - 'source_type', - 'destination_name', - 'destination_type', - 'amount', - 'foreign_amount', - 'category', - 'budget', - 'piggy_bank', - 'tags', + 'currency_code', 'foreign_currency_code', 'source_name', 'source_type', 'destination_name', 'destination_type', 'amount', 'foreign_amount', 'category', 'budget', 'piggy_bank', 'tags', ]; $records = []; $recurrences = $recurringRepos->getAll(); + /** @var Recurrence $recurrence */ foreach ($recurrences as $recurrence) { // add recurrence: $records[] = [ - $this->user->id, - $recurrence->id, + $this->user->id, $recurrence->id, 'recurrence', - $recurrence->created_at->toAtomString(), - $recurrence->updated_at->toAtomString(), - $recurrence->transactionType->type, - $recurrence->title, - $recurrence->description, - $recurrence->first_date?->format('Y-m-d'), - $recurrence->repeat_until?->format('Y-m-d'), - $recurrence->latest_date?->format('Y-m-d'), - $recurrence->repetitions, - $recurrence->apply_rules, - $recurrence->active, + $recurrence->created_at->toAtomString(), $recurrence->updated_at->toAtomString(), $recurrence->transactionType->type, $recurrence->title, $recurrence->description, $recurrence->first_date?->format('Y-m-d'), $recurrence->repeat_until?->format('Y-m-d'), $recurrence->latest_date?->format('Y-m-d'), $recurrence->repetitions, $recurrence->apply_rules, $recurrence->active, ]; + // add new row for each repetition /** @var RecurrenceRepetition $repetition */ foreach ($recurrence->recurrenceRepetitions as $repetition) { @@ -569,25 +604,13 @@ class ExportDataGenerator $this->user->id, $recurrence->id, 'repetition', - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, + null, null, null, null, null, null, null, null, null, null, null, // repetition: - $repetition->repetition_type, - $repetition->repetition_moment, - $repetition->repetition_skip, - $repetition->weekend, + $repetition->repetition_type, $repetition->repetition_moment, $repetition->repetition_skip, $repetition->weekend, ]; } + /** @var RecurrenceTransaction $transaction */ foreach ($recurrence->recurrenceTransactions as $transaction) { $categoryName = $recurringRepos->getCategoryName($transaction); @@ -600,57 +623,34 @@ class ExportDataGenerator $this->user->id, $recurrence->id, 'transaction', - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, + null, null, null, null, null, null, null, null, null, null, null, // repetition: - null, - null, - null, - null, + null, null, null, null, // transaction: - $transaction->transactionCurrency->code, - $transaction->foreignCurrency?->code, - $transaction->sourceAccount->name, - $transaction->sourceAccount->accountType->type, - $transaction->destinationAccount->name, - $transaction->destinationAccount->accountType->type, - $transaction->amount, - $transaction->foreign_amount, - $categoryName, - $budgetId, - $piggyBankId, - implode(',', $tags), + $transaction->transactionCurrency->code, $transaction->foreignCurrency?->code, $transaction->sourceAccount->name, $transaction->sourceAccount->accountType->type, $transaction->destinationAccount->name, $transaction->destinationAccount->accountType->type, $transaction->amount, $transaction->foreign_amount, $categoryName, $budgetId, $piggyBankId, implode(',', $tags), ]; } } - //load the CSV document from a string + // load the CSV document from a string $csv = Writer::createFromString(); - //insert the header + // insert the header try { $csv->insertOne($header); } catch (CannotInsertRecord $e) { throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); } - //insert all the records + // insert all the records $csv->insertAll($records); try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception app('log')->error($e->getMessage()); + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } @@ -658,80 +658,34 @@ class ExportDataGenerator } /** - * @return string * @throws CannotInsertRecord * @throws Exception * @throws FireflyException */ private function exportRules(): string { - $header = [ - 'user_id', - 'rule_id', - 'row_contains', - 'created_at', - 'updated_at', - 'group_id', - 'group_name', - 'title', - 'description', - 'order', - 'active', - 'stop_processing', - 'strict', - 'trigger_type', - 'trigger_value', - 'trigger_order', - 'trigger_active', - 'trigger_stop_processing', - 'action_type', - 'action_value', - 'action_order', - 'action_active', - 'action_stop_processing', - ]; + $header = ['user_id', 'rule_id', 'row_contains', 'created_at', 'updated_at', 'group_id', 'title', 'description', 'order', 'active', 'stop_processing', 'strict', 'trigger_type', 'trigger_value', 'trigger_order', 'trigger_active', 'trigger_stop_processing', 'action_type', 'action_value', 'action_order', 'action_active', 'action_stop_processing']; $ruleRepos = app(RuleRepositoryInterface::class); $ruleRepos->setUser($this->user); $rules = $ruleRepos->getAll(); $records = []; + /** @var Rule $rule */ foreach ($rules as $rule) { $records[] = [ - $this->user->id, - $rule->id, + $this->user->id, $rule->id, 'rule', - $rule->created_at->toAtomString(), - $rule->updated_at->toAtomString(), - $rule->ruleGroup->id, - $rule->ruleGroup->title, - $rule->title, - $rule->description, - $rule->order, - $rule->active, - $rule->stop_processing, - $rule->strict, + $rule->created_at->toAtomString(), $rule->updated_at->toAtomString(), $rule->ruleGroup->id, $rule->ruleGroup->title, $rule->title, $rule->description, $rule->order, $rule->active, $rule->stop_processing, $rule->strict, ]; + /** @var RuleTrigger $trigger */ foreach ($rule->ruleTriggers as $trigger) { $records[] = [ $this->user->id, $rule->id, 'trigger', - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - $trigger->trigger_type, - $trigger->trigger_value, - $trigger->order, - $trigger->active, - $trigger->stop_processing, + null, null, null, null, null, null, null, null, null, null, + $trigger->trigger_type, $trigger->trigger_value, $trigger->order, $trigger->active, $trigger->stop_processing, ]; } @@ -741,47 +695,30 @@ class ExportDataGenerator $this->user->id, $rule->id, 'action', - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - $action->action_type, - $action->action_value, - $action->order, - $action->active, - $action->stop_processing, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + $action->action_type, $action->action_value, $action->order, $action->active, $action->stop_processing, ]; } } - //load the CSV document from a string + // load the CSV document from a string $csv = Writer::createFromString(); - //insert the header + // insert the header try { $csv->insertOne($header); } catch (CannotInsertRecord $e) { throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); } - //insert all the records + // insert all the records $csv->insertAll($records); try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception app('log')->error($e->getMessage()); + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } @@ -789,12 +726,9 @@ class ExportDataGenerator } /** - * @return string * @throws CannotInsertRecord - * @throws ContainerExceptionInterface * @throws Exception * @throws FireflyException - * @throws NotFoundExceptionInterface */ private function exportTags(): string { @@ -804,6 +738,7 @@ class ExportDataGenerator $tagRepos->setUser($this->user); $tags = $tagRepos->get(); $records = []; + /** @var Tag $tag */ foreach ($tags as $tag) { $records[] = [ @@ -820,23 +755,24 @@ class ExportDataGenerator ]; } - //load the CSV document from a string + // load the CSV document from a string $csv = Writer::createFromString(); - //insert the header + // insert the header try { $csv->insertOne($header); } catch (CannotInsertRecord $e) { throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); } - //insert all the records + // insert all the records $csv->insertAll($records); try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception app('log')->error($e->getMessage()); + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } @@ -844,15 +780,6 @@ class ExportDataGenerator } /** - * @inheritDoc - */ - public function get(string $key, mixed $default = null): mixed - { - return null; - } - - /** - * @return string * @throws CannotInsertRecord * @throws Exception * @throws FireflyException @@ -860,34 +787,7 @@ class ExportDataGenerator private function exportTransactions(): string { // TODO better place for keys? - $header = [ - 'user_id', - 'group_id', - 'journal_id', - 'created_at', - 'updated_at', - 'group_title', - 'type', - 'amount', - 'foreign_amount', - 'currency_code', - 'foreign_currency_code', - 'description', - 'date', - 'source_name', - 'source_iban', - 'source_type', - 'destination_name', - 'destination_iban', - 'destination_type', - 'reconciled', - 'category', - 'budget', - 'bill', - 'tags', - 'notes', - // all optional meta fields: - ]; + $header = ['user_id', 'group_id', 'journal_id', 'created_at', 'updated_at', 'group_title', 'type', 'amount', 'foreign_amount', 'currency_code', 'foreign_currency_code', 'description', 'date', 'source_name', 'source_iban', 'source_type', 'destination_name', 'destination_iban', 'destination_type', 'reconciled', 'category', 'budget', 'bill', 'tags', 'notes']; $metaFields = config('firefly.journal_meta_fields'); $header = array_merge($header, $metaFields); @@ -895,7 +795,8 @@ class ExportDataGenerator $collector = app(GroupCollectorInterface::class); $collector->setUser($this->user); $collector->setRange($this->start, $this->end)->withAccountInformation()->withCategoryInformation()->withBillInformation() - ->withBudgetInformation()->withTagInformation()->withNotes(); + ->withBudgetInformation()->withTagInformation()->withNotes() + ; if (0 !== $this->accounts->count()) { $collector->setAccounts($this->accounts); } @@ -907,108 +808,53 @@ class ExportDataGenerator $repository->setUser($this->user); $records = []; + /** @var array $journal */ foreach ($journals as $journal) { $metaData = $repository->getMetaFields($journal['transaction_journal_id'], $metaFields); $records[] = [ - $journal['user_id'], - $journal['transaction_group_id'], - $journal['transaction_journal_id'], - $journal['created_at']->toAtomString(), - $journal['updated_at']->toAtomString(), - $journal['transaction_group_title'], - $journal['transaction_type_type'], - $journal['amount'], - $journal['foreign_amount'], - $journal['currency_code'], - $journal['foreign_currency_code'], - $journal['description'], - $journal['date']->toAtomString(), - $journal['source_account_name'], - $journal['source_account_iban'], - $journal['source_account_type'], - $journal['destination_account_name'], - $journal['destination_account_iban'], - $journal['destination_account_type'], - $journal['reconciled'], - $journal['category_name'], - $journal['budget_name'], - $journal['bill_name'], + $journal['user_id'], $journal['transaction_group_id'], $journal['transaction_journal_id'], $journal['created_at']->toAtomString(), $journal['updated_at']->toAtomString(), $journal['transaction_group_title'], $journal['transaction_type_type'], $journal['amount'], $journal['foreign_amount'], $journal['currency_code'], $journal['foreign_currency_code'], $journal['description'], $journal['date']->toAtomString(), $journal['source_account_name'], $journal['source_account_iban'], $journal['source_account_type'], $journal['destination_account_name'], $journal['destination_account_iban'], $journal['destination_account_type'], $journal['reconciled'], $journal['category_name'], $journal['budget_name'], $journal['bill_name'], $this->mergeTags($journal['tags']), $this->clearStringKeepNewlines($journal['notes']), - // export also the optional fields (ALL) - // sepa - $metaData['sepa_cc'], - $metaData['sepa_ct_op'], - $metaData['sepa_ct_id'], - $metaData['sepa_db'], - $metaData['sepa_country'], - $metaData['sepa_ep'], - $metaData['sepa_ci'], - $metaData['sepa_batch_id'], - $metaData['external_url'], + $metaData['sepa_cc'], $metaData['sepa_ct_op'], $metaData['sepa_ct_id'], $metaData['sepa_db'], $metaData['sepa_country'], $metaData['sepa_ep'], $metaData['sepa_ci'], $metaData['sepa_batch_id'], $metaData['external_url'], // dates - $metaData['interest_date'], - $metaData['book_date'], - $metaData['process_date'], - $metaData['due_date'], - $metaData['payment_date'], - $metaData['invoice_date'], + $metaData['interest_date'], $metaData['book_date'], $metaData['process_date'], $metaData['due_date'], $metaData['payment_date'], $metaData['invoice_date'], // others - $metaData['recurrence_id'], - $metaData['internal_reference'], - $metaData['bunq_payment_id'], - $metaData['import_hash'], - $metaData['import_hash_v2'], - $metaData['external_id'], - $metaData['original_source'], + $metaData['recurrence_id'], $metaData['internal_reference'], $metaData['bunq_payment_id'], $metaData['import_hash'], $metaData['import_hash_v2'], $metaData['external_id'], $metaData['original_source'], // recurring transactions - $metaData['recurrence_total'], - $metaData['recurrence_count'], + $metaData['recurrence_total'], $metaData['recurrence_count'], ]; } - //load the CSV document from a string + // load the CSV document from a string $csv = Writer::createFromString(); - //insert the header + // insert the header try { $csv->insertOne($header); } catch (CannotInsertRecord $e) { throw new FireflyException(sprintf(self::ADD_RECORD_ERR, $e->getMessage()), 0, $e); } - //insert all the records + // insert all the records $csv->insertAll($records); try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception app('log')->error($e->getMessage()); + throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } return $string; } - /** - * @param Collection $accounts - */ - public function setAccounts(Collection $accounts): void - { - $this->accounts = $accounts; - } - - /** - * @param array $tags - * - * @return string - */ private function mergeTags(array $tags): string { if (0 === count($tags)) { @@ -1021,100 +867,4 @@ class ExportDataGenerator return implode(',', $smol); } - - /** - * @inheritDoc - */ - public function has(mixed $key): mixed - { - return null; - } - - /** - * @param Carbon $end - */ - public function setEnd(Carbon $end): void - { - $this->end = $end; - } - - /** - * @param bool $exportAccounts - */ - public function setExportAccounts(bool $exportAccounts): void - { - $this->exportAccounts = $exportAccounts; - } - - /** - * @param bool $exportBills - */ - public function setExportBills(bool $exportBills): void - { - $this->exportBills = $exportBills; - } - - /** - * @param bool $exportBudgets - */ - public function setExportBudgets(bool $exportBudgets): void - { - $this->exportBudgets = $exportBudgets; - } - - /** - * @param bool $exportCategories - */ - public function setExportCategories(bool $exportCategories): void - { - $this->exportCategories = $exportCategories; - } - - /** - * @param bool $exportPiggies - */ - public function setExportPiggies(bool $exportPiggies): void - { - $this->exportPiggies = $exportPiggies; - } - - /** - * @param bool $exportRecurring - */ - public function setExportRecurring(bool $exportRecurring): void - { - $this->exportRecurring = $exportRecurring; - } - - /** - * @param bool $exportRules - */ - public function setExportRules(bool $exportRules): void - { - $this->exportRules = $exportRules; - } - - /** - * @param bool $exportTags - */ - public function setExportTags(bool $exportTags): void - { - $this->exportTags = $exportTags; - } - - /** - * @param bool $exportTransactions - */ - public function setExportTransactions(bool $exportTransactions): void - { - $this->exportTransactions = $exportTransactions; - } - - /** - * @param Carbon $start - */ - public function setStart(Carbon $start): void - { - $this->start = $start; - } } diff --git a/app/Support/Facades/AccountForm.php b/app/Support/Facades/AccountForm.php index 6681ad427c..2a44acd2bd 100644 --- a/app/Support/Facades/AccountForm.php +++ b/app/Support/Facades/AccountForm.php @@ -27,14 +27,11 @@ use Illuminate\Support\Facades\Facade; /** * Class AccountForm. - * */ class AccountForm extends Facade { /** * Get the registered name of the component. - * - * @return string */ protected static function getFacadeAccessor(): string { diff --git a/app/Support/Facades/Amount.php b/app/Support/Facades/Amount.php index c11965dc7d..81abb85066 100644 --- a/app/Support/Facades/Amount.php +++ b/app/Support/Facades/Amount.php @@ -31,11 +31,11 @@ use Illuminate\Support\Facades\Facade; /** * Class Amount. * - * @method string formatAnything(TransactionCurrency $format, string $amount, bool $coloured = true) - * @method Collection getAllCurrencies() - * @method Collection getCurrencies() - * @method string getCurrencyCode() - * @method string getCurrencySymbol() + * @method string formatAnything(TransactionCurrency $format, string $amount, bool $coloured = true) + * @method Collection getAllCurrencies() + * @method Collection getCurrencies() + * @method string getCurrencyCode() + * @method string getCurrencySymbol() * @method TransactionCurrency getDefaultCurrency() * @method TransactionCurrency getDefaultCurrencyByUser(User $user) */ @@ -43,8 +43,6 @@ class Amount extends Facade { /** * Get the registered name of the component. - * - * @return string */ protected static function getFacadeAccessor(): string { diff --git a/app/Support/Facades/CurrencyForm.php b/app/Support/Facades/CurrencyForm.php index 08fdf6b78d..528ece371b 100644 --- a/app/Support/Facades/CurrencyForm.php +++ b/app/Support/Facades/CurrencyForm.php @@ -27,14 +27,11 @@ use Illuminate\Support\Facades\Facade; /** * Class CurrencyForm. - * */ class CurrencyForm extends Facade { /** * Get the registered name of the component. - * - * @return string */ protected static function getFacadeAccessor(): string { diff --git a/app/Support/Facades/ExpandedForm.php b/app/Support/Facades/ExpandedForm.php index bd9c781816..9763269708 100644 --- a/app/Support/Facades/ExpandedForm.php +++ b/app/Support/Facades/ExpandedForm.php @@ -32,8 +32,6 @@ class ExpandedForm extends Facade { /** * Get the registered name of the component. - * - * @return string */ protected static function getFacadeAccessor(): string { diff --git a/app/Support/Facades/FireflyConfig.php b/app/Support/Facades/FireflyConfig.php index 3c5fc74e53..c5c3b505e2 100644 --- a/app/Support/Facades/FireflyConfig.php +++ b/app/Support/Facades/FireflyConfig.php @@ -28,18 +28,17 @@ use Illuminate\Support\Facades\Facade; /** * Class FireflyConfig. + * * @method null|Configuration get($name, $default = null) - * @method Configuration set(string $name, $value) + * @method Configuration set(string $name, $value) * @method delete(string $name) - * @method Configuration|null getFresh(string $name, $default = null) - * @method Configuration put(string $name, $value) + * @method null|Configuration getFresh(string $name, $default = null) + * @method Configuration put(string $name, $value) */ class FireflyConfig extends Facade { /** * Get the registered name of the component. - * - * @return string */ protected static function getFacadeAccessor(): string { diff --git a/app/Support/Facades/Navigation.php b/app/Support/Facades/Navigation.php index 3e18b1568c..bb171321a6 100644 --- a/app/Support/Facades/Navigation.php +++ b/app/Support/Facades/Navigation.php @@ -30,10 +30,10 @@ use Illuminate\Support\Facades\Facade; * Class Navigation. * * @method Carbon addPeriod(Carbon $theDate, string $repeatFreq, int $skip) - * @method array blockPeriods(Carbon $start, Carbon $end, string $range) + * @method array blockPeriods(Carbon $start, Carbon $end, string $range) * @method Carbon endOfPeriod(Carbon $end, string $repeatFreq) * @method Carbon endOfX(Carbon $theCurrentEnd, string $repeatFreq, Carbon $maxDate = null) - * @method array listOfPeriods(Carbon $start, Carbon $end) + * @method array listOfPeriods(Carbon $start, Carbon $end) * @method string periodShow(Carbon $theDate, string $repeatFrequency) * @method string preferredCarbonFormat(Carbon $start, Carbon $end) * @method string preferredCarbonLocalizedFormat(Carbon $start, Carbon $end) @@ -49,8 +49,6 @@ class Navigation extends Facade { /** * Get the registered name of the component. - * - * @return string */ protected static function getFacadeAccessor(): string { diff --git a/app/Support/Facades/PiggyBankForm.php b/app/Support/Facades/PiggyBankForm.php index bfb9bfe307..3dd4c84bda 100644 --- a/app/Support/Facades/PiggyBankForm.php +++ b/app/Support/Facades/PiggyBankForm.php @@ -27,14 +27,11 @@ use Illuminate\Support\Facades\Facade; /** * Class PiggyBankForm. - * */ class PiggyBankForm extends Facade { /** * Get the registered name of the component. - * - * @return string */ protected static function getFacadeAccessor(): string { diff --git a/app/Support/Facades/Preferences.php b/app/Support/Facades/Preferences.php index 4fa1bb978e..207f5bf5a0 100644 --- a/app/Support/Facades/Preferences.php +++ b/app/Support/Facades/Preferences.php @@ -31,16 +31,16 @@ use Illuminate\Support\Facades\Facade; /** * Class Preferences. * - * @method Collection beginsWith(User $user, string $search) - * @method bool delete(string $name) - * @method Collection findByName(string $name) - * @method Preference get(string $name, $value = null) - * @method array getArrayForUser(User $user, array $list) - * @method Preference|null getForUser(User $user, string $name, $default = null) - * @method string lastActivity() - * @method void mark() - * @method Preference set(string $name, $value) - * @method Preference setForUser(User $user, string $name, $value) + * @method Collection beginsWith(User $user, string $search) + * @method bool delete(string $name) + * @method Collection findByName(string $name) + * @method Preference get(string $name, $value = null) + * @method array getArrayForUser(User $user, array $list) + * @method null|Preference getForUser(User $user, string $name, $default = null) + * @method string lastActivity() + * @method void mark() + * @method Preference set(string $name, $value) + * @method Preference setForUser(User $user, string $name, $value) */ class Preferences extends Facade { @@ -51,8 +51,6 @@ class Preferences extends Facade /** * Get the registered name of the component. - * - * @return string */ protected static function getFacadeAccessor(): string { diff --git a/app/Support/Facades/RuleForm.php b/app/Support/Facades/RuleForm.php index a13a626cac..b2aacf783f 100644 --- a/app/Support/Facades/RuleForm.php +++ b/app/Support/Facades/RuleForm.php @@ -27,14 +27,11 @@ use Illuminate\Support\Facades\Facade; /** * Class RuleForm. - * */ class RuleForm extends Facade { /** * Get the registered name of the component. - * - * @return string */ protected static function getFacadeAccessor(): string { diff --git a/app/Support/Facades/Steam.php b/app/Support/Facades/Steam.php index 89ac24b108..747f990bf0 100644 --- a/app/Support/Facades/Steam.php +++ b/app/Support/Facades/Steam.php @@ -33,24 +33,20 @@ use Illuminate\Support\Facades\Facade; * * @method string balance(Account $account, Carbon $date) * @method string balanceIgnoreVirtual(Account $account, Carbon $date) - * @method array balanceInRange(Account $account, Carbon $start, Carbon $end) - * @method array balancesByAccounts(Collection $accounts, Carbon $date) + * @method array balanceInRange(Account $account, Carbon $start, Carbon $end) + * @method array balancesByAccounts(Collection $accounts, Carbon $date) * @method decrypt(int $isEncrypted, string $value) - * @method array getLastActivities(array $accounts) - * @method string negative(string $amount) - * @method string|null opposite(string $amount = null) - * @method int phpBytes(string $string) - * @method string positive(string $amount) - * @method array balancesPerCurrencyByAccounts(Collection $accounts, Carbon $date) - * - + * @method array getLastActivities(array $accounts) + * @method string negative(string $amount) + * @method null|string opposite(string $amount = null) + * @method int phpBytes(string $string) + * @method string positive(string $amount) + * @method array balancesPerCurrencyByAccounts(Collection $accounts, Carbon $date) */ class Steam extends Facade { /** * Get the registered name of the component. - * - * @return string */ protected static function getFacadeAccessor(): string { diff --git a/app/Support/FireflyConfig.php b/app/Support/FireflyConfig.php index 5f5e7026a7..c4078dbfd0 100644 --- a/app/Support/FireflyConfig.php +++ b/app/Support/FireflyConfig.php @@ -23,64 +23,50 @@ declare(strict_types=1); namespace FireflyIII\Support; -use Cache; -use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Configuration; use Illuminate\Database\QueryException; /** * Class FireflyConfig. - * - */ class FireflyConfig { - /** - * @param string $name - */ public function delete(string $name): void { - $fullName = 'ff-config-' . $name; - if (Cache::has($fullName)) { - Cache::forget($fullName); + $fullName = 'ff-config-'.$name; + if (\Cache::has($fullName)) { + \Cache::forget($fullName); } Configuration::where('name', $name)->forceDelete(); } - /** - * @param string $name - * - * @return bool - */ public function has(string $name): bool { - return Configuration::where('name', $name)->count() === 1; + return 1 === Configuration::where('name', $name)->count(); } /** - * @param string $name - * @param bool|string|int|null $default + * @param null|bool|int|string $default * - * @return Configuration|null * @throws FireflyException */ public function get(string $name, $default = null): ?Configuration { - $fullName = 'ff-config-' . $name; - if (Cache::has($fullName)) { - return Cache::get($fullName); + $fullName = 'ff-config-'.$name; + if (\Cache::has($fullName)) { + return \Cache::get($fullName); } try { - /** @var Configuration|null $config */ + /** @var null|Configuration $config */ $config = Configuration::where('name', $name)->first(['id', 'name', 'data']); - } catch (QueryException | Exception $e) { + } catch (\Exception|QueryException $e) { throw new FireflyException(sprintf('Could not poll the database: %s', $e->getMessage()), 0, $e); } if (null !== $config) { - Cache::forever($fullName, $config); + \Cache::forever($fullName, $config); return $config; } @@ -93,10 +79,7 @@ class FireflyConfig } /** - * @param string $name - * @param mixed $value - * - * @return Configuration + * @param mixed $value */ public function set(string $name, $value): Configuration { @@ -116,22 +99,19 @@ class FireflyConfig $item->name = $name; $item->data = $value; $item->save(); - Cache::forget('ff-config-' . $name); + \Cache::forget('ff-config-'.$name); return $item; } $config->data = $value; $config->save(); - Cache::forget('ff-config-' . $name); + \Cache::forget('ff-config-'.$name); return $config; } /** - * @param string $name - * @param mixed $default - * - * @return Configuration|null + * @param mixed $default */ public function getFresh(string $name, $default = null): ?Configuration { @@ -148,10 +128,7 @@ class FireflyConfig } /** - * @param string $name - * @param mixed $value - * - * @return Configuration + * @param mixed $value */ public function put(string $name, $value): Configuration { diff --git a/app/Support/Form/AccountForm.php b/app/Support/Form/AccountForm.php index 52c169f273..f215357155 100644 --- a/app/Support/Form/AccountForm.php +++ b/app/Support/Form/AccountForm.php @@ -27,7 +27,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use Throwable; /** * Class AccountForm @@ -43,16 +42,10 @@ class AccountForm /** * Grouped dropdown list of all accounts that are valid as the destination of a withdrawal. - * - * @param string $name - * @param mixed $value - * @param array|null $options - * - * @return string */ public function activeDepositDestinations(string $name, mixed $value = null, array $options = null): string { - $types = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN, AccountType::REVENUE,]; + $types = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN, AccountType::REVENUE]; $repository = $this->getAccountRepository(); $grouped = $this->getAccountsGrouped($types, $repository); $cash = $repository->getCashAccount(); @@ -63,18 +56,85 @@ class AccountForm } /** - * @param array $types - * @param AccountRepositoryInterface|null $repository - * - * @return array + * Grouped dropdown list of all accounts that are valid as the destination of a withdrawal. */ + public function activeWithdrawalDestinations(string $name, mixed $value = null, array $options = null): string + { + $types = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN, AccountType::EXPENSE]; + $repository = $this->getAccountRepository(); + $grouped = $this->getAccountsGrouped($types, $repository); + + $cash = $repository->getCashAccount(); + $key = (string)trans('firefly.cash_account_type'); + $grouped[$key][$cash->id] = sprintf('(%s)', (string)trans('firefly.cash')); + + return $this->select($name, $grouped, $value, $options); + } + + /** + * Check list of asset accounts. + * + * @throws FireflyException + */ + public function assetAccountCheckList(string $name, array $options = null): string + { + $options ??= []; + $label = $this->label($name, $options); + $options = $this->expandOptionArray($name, $label, $options); + $classes = $this->getHolderClasses($name); + $selected = request()->old($name) ?? []; + + // get all asset accounts: + $types = [AccountType::ASSET, AccountType::DEFAULT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::DEBT]; + $grouped = $this->getAccountsGrouped($types); + + unset($options['class']); + + try { + $html = view('form.assetAccountCheckList', compact('classes', 'selected', 'name', 'label', 'options', 'grouped'))->render(); + } catch (\Throwable $e) { + app('log')->debug(sprintf('Could not render assetAccountCheckList(): %s', $e->getMessage())); + $html = 'Could not render assetAccountCheckList.'; + + throw new FireflyException($html, 0, $e); + } + + return $html; + } + + /** + * Basic list of asset accounts. + * + * @param mixed $value + */ + public function assetAccountList(string $name, $value = null, array $options = null): string + { + $types = [AccountType::ASSET, AccountType::DEFAULT]; + $grouped = $this->getAccountsGrouped($types); + + return $this->select($name, $grouped, $value, $options); + } + + /** + * Same list but all liabilities as well. + * + * @param mixed $value + */ + public function longAccountList(string $name, $value = null, array $options = null): string + { + $types = [AccountType::ASSET, AccountType::DEFAULT, AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN]; + $grouped = $this->getAccountsGrouped($types); + + return $this->select($name, $grouped, $value, $options); + } + private function getAccountsGrouped(array $types, AccountRepositoryInterface $repository = null): array { if (null === $repository) { $repository = $this->getAccountRepository(); } $accountList = $repository->getActiveAccountsByType($types); - $liabilityTypes = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN,]; + $liabilityTypes = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN]; $grouped = []; /** @var Account $account */ @@ -98,93 +158,4 @@ class AccountForm return $grouped; } - - /** - * Grouped dropdown list of all accounts that are valid as the destination of a withdrawal. - * - * @param string $name - * @param mixed $value - * @param array|null $options - * - * @return string - */ - public function activeWithdrawalDestinations(string $name, mixed $value = null, array $options = null): string - { - $types = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN, AccountType::EXPENSE,]; - $repository = $this->getAccountRepository(); - $grouped = $this->getAccountsGrouped($types, $repository); - - $cash = $repository->getCashAccount(); - $key = (string)trans('firefly.cash_account_type'); - $grouped[$key][$cash->id] = sprintf('(%s)', (string)trans('firefly.cash')); - - return $this->select($name, $grouped, $value, $options); - } - - /** - * Check list of asset accounts. - * - * @param string $name - * @param array|null $options - * - * @return string - * @throws FireflyException - */ - public function assetAccountCheckList(string $name, array $options = null): string - { - $options ??= []; - $label = $this->label($name, $options); - $options = $this->expandOptionArray($name, $label, $options); - $classes = $this->getHolderClasses($name); - $selected = request()->old($name) ?? []; - - // get all asset accounts: - $types = [AccountType::ASSET, AccountType::DEFAULT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::DEBT]; - $grouped = $this->getAccountsGrouped($types); - - unset($options['class']); - try { - $html = view('form.assetAccountCheckList', compact('classes', 'selected', 'name', 'label', 'options', 'grouped'))->render(); - } catch (Throwable $e) { - app('log')->debug(sprintf('Could not render assetAccountCheckList(): %s', $e->getMessage())); - $html = 'Could not render assetAccountCheckList.'; - throw new FireflyException($html, 0, $e); - } - - return $html; - } - - /** - * Basic list of asset accounts. - * - * @param string $name - * @param mixed $value - * @param array|null $options - * - * @return string - */ - public function assetAccountList(string $name, $value = null, array $options = null): string - { - $types = [AccountType::ASSET, AccountType::DEFAULT]; - $grouped = $this->getAccountsGrouped($types); - - return $this->select($name, $grouped, $value, $options); - } - - /** - * Same list but all liabilities as well. - * - * @param string $name - * @param mixed $value - * @param array|null $options - * - * @return string - */ - public function longAccountList(string $name, $value = null, array $options = null): string - { - $types = [AccountType::ASSET, AccountType::DEFAULT, AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN,]; - $grouped = $this->getAccountsGrouped($types); - - return $this->select($name, $grouped, $value, $options); - } } diff --git a/app/Support/Form/CurrencyForm.php b/app/Support/Form/CurrencyForm.php index 376dbf1bfa..5058bfcdfc 100644 --- a/app/Support/Form/CurrencyForm.php +++ b/app/Support/Form/CurrencyForm.php @@ -27,7 +27,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use Illuminate\Support\Collection; -use Throwable; /** * Class CurrencyForm @@ -39,11 +38,8 @@ class CurrencyForm use FormSupport; /** - * @param string $name - * @param mixed $value - * @param array|null $options + * @param mixed $value * - * @return string * @throws FireflyException */ public function amount(string $name, $value = null, array $options = null): string @@ -51,68 +47,11 @@ class CurrencyForm return $this->currencyField($name, 'amount', $value, $options); } - /** - * @param string $name - * @param string $view - * @param mixed $value - * @param array|null $options - * - * @return string - * @throws FireflyException - */ - protected function currencyField(string $name, string $view, mixed $value = null, array $options = null): string - { - $label = $this->label($name, $options); - $options = $this->expandOptionArray($name, $label, $options); - $classes = $this->getHolderClasses($name); - $value = $this->fillFieldValue($name, $value); - $options['step'] = 'any'; - $defaultCurrency = $options['currency'] ?? app('amount')->getDefaultCurrency(); - /** @var Collection $currencies */ - $currencies = app('amount')->getCurrencies(); - unset($options['currency'], $options['placeholder']); - // perhaps the currency has been sent to us in the field $amount_currency_id_$name (amount_currency_id_amount) - $preFilled = session('preFilled'); - if (!is_array($preFilled)) { - $preFilled = []; - } - $key = 'amount_currency_id_' . $name; - $sentCurrencyId = array_key_exists($key, $preFilled) ? (int)$preFilled[$key] : $defaultCurrency->id; - - app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); - - // find this currency in set of currencies: - foreach ($currencies as $currency) { - if ($currency->id === $sentCurrencyId) { - $defaultCurrency = $currency; - app('log')->debug(sprintf('default currency is now %s', $defaultCurrency->code)); - break; - } - } - - // make sure value is formatted nicely: - if (null !== $value && '' !== $value) { - $value = app('steam')->bcround($value, $defaultCurrency->decimal_places); - } - try { - $html = view('form.' . $view, compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); - } catch (Throwable $e) { - app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); - $html = 'Could not render currencyField.'; - throw new FireflyException($html, 0, $e); - } - - return $html; - } - /** * TODO describe and cleanup. * - * @param string $name - * @param mixed $value - * @param array|null $options + * @param mixed $value * - * @return string * @throws FireflyException */ public function balanceAll(string $name, $value = null, array $options = null): string @@ -120,71 +59,10 @@ class CurrencyForm return $this->allCurrencyField($name, 'balance', $value, $options); } - /** - * TODO describe and cleanup - * - * @param string $name - * @param string $view - * @param mixed $value - * @param array|null $options - * - * @return string - * @throws FireflyException - */ - protected function allCurrencyField(string $name, string $view, $value = null, array $options = null): string - { - $label = $this->label($name, $options); - $options = $this->expandOptionArray($name, $label, $options); - $classes = $this->getHolderClasses($name); - $value = $this->fillFieldValue($name, $value); - $options['step'] = 'any'; - $defaultCurrency = $options['currency'] ?? app('amount')->getDefaultCurrency(); - /** @var Collection $currencies */ - $currencies = app('amount')->getAllCurrencies(); - unset($options['currency'], $options['placeholder']); - - // perhaps the currency has been sent to us in the field $amount_currency_id_$name (amount_currency_id_amount) - $preFilled = session('preFilled'); - if (!is_array($preFilled)) { - $preFilled = []; - } - $key = 'amount_currency_id_' . $name; - $sentCurrencyId = array_key_exists($key, $preFilled) ? (int)$preFilled[$key] : $defaultCurrency->id; - - app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); - - // find this currency in set of currencies: - foreach ($currencies as $currency) { - if ($currency->id === $sentCurrencyId) { - $defaultCurrency = $currency; - app('log')->debug(sprintf('default currency is now %s', $defaultCurrency->code)); - break; - } - } - - // make sure value is formatted nicely: - if (null !== $value && '' !== $value) { - $value = app('steam')->bcround($value, $defaultCurrency->decimal_places); - } - try { - $html = view('form.' . $view, compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); - } catch (Throwable $e) { - app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); - $html = 'Could not render currencyField.'; - throw new FireflyException($html, 0, $e); - } - - return $html; - } - /** * TODO cleanup and describe * - * @param string $name - * @param mixed $value - * @param array|null $options - * - * @return string + * @param mixed $value */ public function currencyList(string $name, $value = null, array $options = null): string { @@ -194,9 +72,10 @@ class CurrencyForm // get all currencies: $list = $currencyRepos->get(); $array = []; + /** @var TransactionCurrency $currency */ foreach ($list as $currency) { - $array[$currency->id] = $currency->name . ' (' . $currency->symbol . ')'; + $array[$currency->id] = $currency->name.' ('.$currency->symbol.')'; } return $this->select($name, $array, $value, $options); @@ -205,11 +84,7 @@ class CurrencyForm /** * TODO cleanup and describe * - * @param string $name - * @param mixed $value - * @param array|null $options - * - * @return string + * @param mixed $value */ public function currencyListEmpty(string $name, $value = null, array $options = null): string { @@ -221,11 +96,121 @@ class CurrencyForm $array = [ 0 => (string)trans('firefly.no_currency'), ]; + /** @var TransactionCurrency $currency */ foreach ($list as $currency) { - $array[$currency->id] = $currency->name . ' (' . $currency->symbol . ')'; + $array[$currency->id] = $currency->name.' ('.$currency->symbol.')'; } return $this->select($name, $array, $value, $options); } + + /** + * @throws FireflyException + */ + protected function currencyField(string $name, string $view, mixed $value = null, array $options = null): string + { + $label = $this->label($name, $options); + $options = $this->expandOptionArray($name, $label, $options); + $classes = $this->getHolderClasses($name); + $value = $this->fillFieldValue($name, $value); + $options['step'] = 'any'; + $defaultCurrency = $options['currency'] ?? app('amount')->getDefaultCurrency(); + + /** @var Collection $currencies */ + $currencies = app('amount')->getCurrencies(); + unset($options['currency'], $options['placeholder']); + // perhaps the currency has been sent to us in the field $amount_currency_id_$name (amount_currency_id_amount) + $preFilled = session('preFilled'); + if (!is_array($preFilled)) { + $preFilled = []; + } + $key = 'amount_currency_id_'.$name; + $sentCurrencyId = array_key_exists($key, $preFilled) ? (int)$preFilled[$key] : $defaultCurrency->id; + + app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); + + // find this currency in set of currencies: + foreach ($currencies as $currency) { + if ($currency->id === $sentCurrencyId) { + $defaultCurrency = $currency; + app('log')->debug(sprintf('default currency is now %s', $defaultCurrency->code)); + + break; + } + } + + // make sure value is formatted nicely: + if (null !== $value && '' !== $value) { + $value = app('steam')->bcround($value, $defaultCurrency->decimal_places); + } + + try { + $html = view('form.'.$view, compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); + } catch (\Throwable $e) { + app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); + $html = 'Could not render currencyField.'; + + throw new FireflyException($html, 0, $e); + } + + return $html; + } + + /** + * TODO describe and cleanup + * + * @param mixed $value + * + * @throws FireflyException + */ + protected function allCurrencyField(string $name, string $view, $value = null, array $options = null): string + { + $label = $this->label($name, $options); + $options = $this->expandOptionArray($name, $label, $options); + $classes = $this->getHolderClasses($name); + $value = $this->fillFieldValue($name, $value); + $options['step'] = 'any'; + $defaultCurrency = $options['currency'] ?? app('amount')->getDefaultCurrency(); + + /** @var Collection $currencies */ + $currencies = app('amount')->getAllCurrencies(); + unset($options['currency'], $options['placeholder']); + + // perhaps the currency has been sent to us in the field $amount_currency_id_$name (amount_currency_id_amount) + $preFilled = session('preFilled'); + if (!is_array($preFilled)) { + $preFilled = []; + } + $key = 'amount_currency_id_'.$name; + $sentCurrencyId = array_key_exists($key, $preFilled) ? (int)$preFilled[$key] : $defaultCurrency->id; + + app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); + + // find this currency in set of currencies: + foreach ($currencies as $currency) { + if ($currency->id === $sentCurrencyId) { + $defaultCurrency = $currency; + app('log')->debug(sprintf('default currency is now %s', $defaultCurrency->code)); + + break; + } + } + + // make sure value is formatted nicely: + if (null !== $value && '' !== $value) { + $value = app('steam')->bcround($value, $defaultCurrency->decimal_places); + } + + try { + $html = view('form.'.$view, compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); + } catch (\Throwable $e) { + app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); + $html = 'Could not render currencyField.'; + + throw new FireflyException($html, 0, $e); + } + + return $html; + } } diff --git a/app/Support/Form/FormSupport.php b/app/Support/Form/FormSupport.php index 7aa8825d3e..df00b6e727 100644 --- a/app/Support/Form/FormSupport.php +++ b/app/Support/Form/FormSupport.php @@ -27,7 +27,6 @@ use Carbon\Carbon; use Carbon\Exceptions\InvalidDateException; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use Illuminate\Support\MessageBag; -use Throwable; /** * Trait FormSupport @@ -35,12 +34,7 @@ use Throwable; trait FormSupport { /** - * @param string $name - * @param array|null $list - * @param mixed $selected - * @param array|null $options - * - * @return string + * @param mixed $selected */ public function select(string $name, array $list = null, $selected = null, array $options = null): string { @@ -50,9 +44,10 @@ trait FormSupport $classes = $this->getHolderClasses($name); $selected = $this->fillFieldValue($name, $selected); unset($options['autocomplete'], $options['placeholder']); + try { $html = view('form.select', compact('classes', 'name', 'label', 'selected', 'options', 'list'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Could not render select(): %s', $e->getMessage())); $html = 'Could not render select.'; } @@ -60,12 +55,6 @@ trait FormSupport return $html; } - /** - * @param string $name - * @param array|null $options - * - * @return string - */ protected function label(string $name, array $options = null): string { $options ??= []; @@ -74,37 +63,28 @@ trait FormSupport } $name = str_replace('[]', '', $name); - return (string)trans('form.' . $name); + return (string)trans('form.'.$name); } /** - * @param string $name - * @param mixed $label - * @param array|null $options - * - * @return array + * @param mixed $label */ protected function expandOptionArray(string $name, $label, array $options = null): array { $options ??= []; $name = str_replace('[]', '', $name); $options['class'] = 'form-control'; - $options['id'] = 'ffInput_' . $name; + $options['id'] = 'ffInput_'.$name; $options['autocomplete'] = 'off'; $options['placeholder'] = ucfirst($label); return $options; } - /** - * @param string $name - * - * @return string - */ protected function getHolderClasses(string $name): string { // Get errors from session: - /** @var MessageBag|null $errors */ + /** @var null|MessageBag $errors */ $errors = session('errors'); $classes = 'form-group'; @@ -116,8 +96,7 @@ trait FormSupport } /** - * @param string $name - * @param mixed|null $value + * @param null|mixed $value * * @return mixed */ @@ -139,21 +118,16 @@ trait FormSupport return $value; } - /** - * @return AccountRepositoryInterface - */ protected function getAccountRepository(): AccountRepositoryInterface { return app(AccountRepositoryInterface::class); } - /** - * @return Carbon - */ protected function getDate(): Carbon { /** @var Carbon $date */ $date = null; + try { $date = today(config('app.timezone')); } catch (InvalidDateException $e) { // @phpstan-ignore-line diff --git a/app/Support/Form/PiggyBankForm.php b/app/Support/Form/PiggyBankForm.php index f7b9ab5cea..fa4f9b415a 100644 --- a/app/Support/Form/PiggyBankForm.php +++ b/app/Support/Form/PiggyBankForm.php @@ -38,11 +38,7 @@ class PiggyBankForm /** * TODO cleanup and describe * - * @param string $name - * @param mixed $value - * @param array|null $options - * - * @return string + * @param mixed $value */ public function piggyBankList(string $name, $value = null, array $options = null): string { @@ -62,6 +58,7 @@ class PiggyBankForm ], ], ]; + /** @var PiggyBank $piggy */ foreach ($piggyBanks as $piggy) { $group = $piggy->objectGroups->first(); diff --git a/app/Support/Form/RuleForm.php b/app/Support/Form/RuleForm.php index 3353371b43..3b1b3e5583 100644 --- a/app/Support/Form/RuleForm.php +++ b/app/Support/Form/RuleForm.php @@ -25,7 +25,6 @@ namespace FireflyIII\Support\Form; use FireflyIII\Models\RuleGroup; use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; -use Form; /** * Class RuleForm @@ -35,13 +34,6 @@ class RuleForm { use FormSupport; - /** - * @param string $name - * @param mixed $value - * @param array|null $options - * - * @return string - */ public function ruleGroupList(string $name, mixed $value = null, array $options = null): string { /** @var RuleGroupRepositoryInterface $groupRepos */ @@ -50,6 +42,7 @@ class RuleForm // get all currencies: $list = $groupRepos->get(); $array = []; + /** @var RuleGroup $group */ foreach ($list as $group) { $array[$group->id] = $group->title; @@ -59,16 +52,13 @@ class RuleForm } /** - * @param string $name - * @param null $value - * @param array|null $options - * - * @return string + * @param null $value */ public function ruleGroupListWithEmpty(string $name, $value = null, array $options = null): string { $options ??= []; $options['class'] = 'form-control'; + /** @var RuleGroupRepositoryInterface $groupRepos */ $groupRepos = app(RuleGroupRepositoryInterface::class); @@ -77,6 +67,7 @@ class RuleForm $array = [ 0 => (string)trans('firefly.none_in_select_list'), ]; + /** @var RuleGroup $group */ foreach ($list as $group) { if (array_key_exists('hidden', $options) && (int)$options['hidden'] !== $group->id) { diff --git a/app/Support/Http/Api/AccountBalanceGrouped.php b/app/Support/Http/Api/AccountBalanceGrouped.php new file mode 100644 index 0000000000..9cdd38b6b6 --- /dev/null +++ b/app/Support/Http/Api/AccountBalanceGrouped.php @@ -0,0 +1,243 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Support\Http\Api; + +use Carbon\Carbon; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Models\TransactionType; +use Illuminate\Support\Collection; + +/** + * Class AccountBalanceGrouped + */ +class AccountBalanceGrouped +{ + private array $accountIds; + private string $carbonFormat; + private array $currencies = []; + private array $data = []; + private TransactionCurrency $default; + private Carbon $end; + private array $journals = []; + private string $preferredRange; + private Carbon $start; + + /** + * Convert the given input to a chart compatible array. + */ + public function convertToChartData(): array + { + $chartData = []; + + // loop2: loop this data, make chart bars for each currency: + /** @var array $currency */ + foreach ($this->data as $currency) { + // income and expense array prepped: + $income = [ + 'label' => 'earned', + 'currency_id' => (string)$currency['currency_id'], + 'currency_symbol' => $currency['currency_symbol'], + 'currency_code' => $currency['currency_code'], + 'currency_decimal_places' => $currency['currency_decimal_places'], + 'native_id' => (string)$currency['native_id'], + 'native_symbol' => $currency['native_symbol'], + 'native_code' => $currency['native_code'], + 'native_decimal_places' => $currency['native_decimal_places'], + 'start' => $this->start->toAtomString(), + 'end' => $this->end->toAtomString(), + 'period' => $this->preferredRange, + 'entries' => [], + 'native_entries' => [], + ]; + $expense = [ + 'label' => 'spent', + 'currency_id' => (string)$currency['currency_id'], + 'currency_symbol' => $currency['currency_symbol'], + 'currency_code' => $currency['currency_code'], + 'currency_decimal_places' => $currency['currency_decimal_places'], + 'native_id' => (string)$currency['native_id'], + 'native_symbol' => $currency['native_symbol'], + 'native_code' => $currency['native_code'], + 'native_decimal_places' => $currency['native_decimal_places'], + 'start' => $this->start->toAtomString(), + 'end' => $this->end->toAtomString(), + 'period' => $this->preferredRange, + 'entries' => [], + 'native_entries' => [], + ]; + // loop all possible periods between $start and $end, and add them to the correct dataset. + $currentStart = clone $this->start; + while ($currentStart <= $this->end) { + $key = $currentStart->format($this->carbonFormat); + $label = $currentStart->toAtomString(); + // normal entries + $income['entries'][$label] = app('steam')->bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); + $expense['entries'][$label] = app('steam')->bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']); + + // converted entries + $income['native_entries'][$label] = app('steam')->bcround($currency[$key]['native_earned'] ?? '0', $currency['native_decimal_places']); + $expense['native_entries'][$label] = app('steam')->bcround($currency[$key]['native_spent'] ?? '0', $currency['native_decimal_places']); + + // next loop + $currentStart = app('navigation')->addPeriod($currentStart, $this->preferredRange, 0); + } + + $chartData[] = $income; + $chartData[] = $expense; + } + + return $chartData; + } + + /** + * Group the given journals by currency and then by period. + * If they are part of a set of accounts this basically means it's balance chart. + */ + public function groupByCurrencyAndPeriod(): void + { + $converter = new ExchangeRateConverter(); + + // loop. group by currency and by period. + /** @var array $journal */ + foreach ($this->journals as $journal) { + // format the date according to the period + $period = $journal['date']->format($this->carbonFormat); + $currencyId = (int)$journal['currency_id']; + $currency = $this->currencies[$currencyId] ?? TransactionCurrency::find($currencyId); + $this->currencies[$currencyId] = $currency; // may just re-assign itself, don't mind. + + // set the array with monetary info, if it does not exist. + $this->data[$currencyId] ??= [ + 'currency_id' => (string)$currencyId, + 'currency_symbol' => $journal['currency_symbol'], + 'currency_code' => $journal['currency_code'], + 'currency_name' => $journal['currency_name'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + // native currency info (could be the same) + 'native_id' => (string)$this->default->id, + 'native_code' => $this->default->code, + 'native_symbol' => $this->default->symbol, + 'native_decimal_places' => $this->default->decimal_places, + ]; + + // set the array (in monetary info) with spent/earned in this $period, if it does not exist. + $this->data[$currencyId][$period] ??= [ + 'period' => $period, + 'spent' => '0', + 'earned' => '0', + 'native_spent' => '0', + 'native_earned' => '0', + ]; + // is this journal's amount in- our outgoing? + $key = 'spent'; + $amount = app('steam')->negative($journal['amount']); + // deposit = incoming + // transfer or reconcile or opening balance, and these accounts are the destination. + if ( + TransactionType::DEPOSIT === $journal['transaction_type_type'] + + || ( + ( + TransactionType::TRANSFER === $journal['transaction_type_type'] + || TransactionType::RECONCILIATION === $journal['transaction_type_type'] + || TransactionType::OPENING_BALANCE === $journal['transaction_type_type'] + ) + && in_array($journal['destination_account_id'], $this->accountIds, true) + ) + ) { + $key = 'earned'; + $amount = app('steam')->positive($journal['amount']); + } + + // get conversion rate + try { + $rate = $converter->getCurrencyRate($currency, $this->default, $journal['date']); + } catch (FireflyException $e) { + app('log')->error($e->getMessage()); + $rate = '1'; + } + $amountConverted = bcmul($amount, $rate); + + // perhaps transaction already has the foreign amount in the native currency. + if ((int)$journal['foreign_currency_id'] === $this->default->id) { + $amountConverted = $journal['foreign_amount'] ?? '0'; + $amountConverted = 'earned' === $key ? app('steam')->positive($amountConverted) : app('steam')->negative($amountConverted); + } + + // add normal entry + $this->data[$currencyId][$period][$key] = bcadd($this->data[$currencyId][$period][$key], $amount); + + // add converted entry + $convertedKey = sprintf('native_%s', $key); + $this->data[$currencyId][$period][$convertedKey] = bcadd($this->data[$currencyId][$period][$convertedKey], $amountConverted); + } + $converter->summarize(); + } + + public function setAccounts(Collection $accounts): void + { + $this->accountIds = $accounts->pluck('id')->toArray(); + } + + public function setDefault(TransactionCurrency $default): void + { + $this->default = $default; + $defaultCurrencyId = $default->id; + $this->currencies = [$default->id => $default]; // currency cache + $this->data[$defaultCurrencyId] = [ + 'currency_id' => (string)$defaultCurrencyId, + 'currency_symbol' => $default->symbol, + 'currency_code' => $default->code, + 'currency_name' => $default->name, + 'currency_decimal_places' => $default->decimal_places, + 'native_id' => (string)$defaultCurrencyId, + 'native_symbol' => $default->symbol, + 'native_code' => $default->code, + 'native_name' => $default->name, + 'native_decimal_places' => $default->decimal_places, + ]; + } + + public function setEnd(Carbon $end): void + { + $this->end = $end; + } + + public function setJournals(array $journals): void + { + $this->journals = $journals; + } + + public function setPreferredRange(string $preferredRange): void + { + $this->preferredRange = $preferredRange; + $this->carbonFormat = app('navigation')->preferredCarbonFormatByPeriod($preferredRange); + } + + public function setStart(Carbon $start): void + { + $this->start = $start; + } +} diff --git a/app/Support/Http/Api/AccountFilter.php b/app/Support/Http/Api/AccountFilter.php index 5aeeec5cd1..7c41a9d6c5 100644 --- a/app/Support/Http/Api/AccountFilter.php +++ b/app/Support/Http/Api/AccountFilter.php @@ -27,17 +27,11 @@ use FireflyIII\Models\AccountType; /** * Trait AccountFilter - * - */ trait AccountFilter { /** * All the available types. - * - * @param string $type - * - * @return array */ protected function mapAccountTypes(string $type): array { @@ -56,11 +50,11 @@ trait AccountFilter AccountType::DEBT, AccountType::MORTGAGE, ], - 'asset' => [AccountType::DEFAULT, AccountType::ASSET,], - 'cash' => [AccountType::CASH,], - 'expense' => [AccountType::EXPENSE, AccountType::BENEFICIARY,], - 'revenue' => [AccountType::REVENUE,], - 'special' => [AccountType::CASH, AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION,], + 'asset' => [AccountType::DEFAULT, AccountType::ASSET], + 'cash' => [AccountType::CASH], + 'expense' => [AccountType::EXPENSE, AccountType::BENEFICIARY], + 'revenue' => [AccountType::REVENUE], + 'special' => [AccountType::CASH, AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION], 'hidden' => [AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION], 'liability' => [AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD], 'liabilities' => [AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD], diff --git a/app/Support/Http/Api/ApiSupport.php b/app/Support/Http/Api/ApiSupport.php index 04a6037de2..76d371657e 100644 --- a/app/Support/Http/Api/ApiSupport.php +++ b/app/Support/Http/Api/ApiSupport.php @@ -28,17 +28,11 @@ use Illuminate\Support\Collection; /** * Trait ApiSupport - * - */ trait ApiSupport { /** * Small helper function for the revenue and expense account charts. - * - * @param array $names - * - * @return array */ protected function expandNames(array $names): array { @@ -52,14 +46,11 @@ trait ApiSupport /** * Small helper function for the revenue and expense account charts. - * - * @param Collection $accounts - * - * @return array */ protected function extractNames(Collection $accounts): array { $return = []; + /** @var Account $account */ foreach ($accounts as $account) { $return[$account->id] = $account->name; diff --git a/app/Support/Http/Api/CleansChartData.php b/app/Support/Http/Api/CleansChartData.php index bda87b5a7c..c5ed68b85b 100644 --- a/app/Support/Http/Api/CleansChartData.php +++ b/app/Support/Http/Api/CleansChartData.php @@ -1,6 +1,5 @@ enabled = false; if (false === $this->enabled) { $set['converted'] = false; + return $set; } $set['converted'] = true; + /** @var TransactionCurrency $native */ $native = app('amount')->getDefaultCurrency(); $currency = $this->getCurrency((int)$set['currency_id']); @@ -64,50 +62,24 @@ trait ConvertsExchangeRates $set['native_code'] = $currency->code; $set['native_symbol'] = $currency->symbol; $set['native_decimal_places'] = $currency->decimal_places; + return $set; } foreach ($set['entries'] as $date => $entry) { - $carbon = Carbon::createFromFormat(DateTimeInterface::ATOM, $date); + $carbon = Carbon::createFromFormat(\DateTimeInterface::ATOM, $date); $rate = $this->getRate($currency, $native, $carbon); $rate = '0' === $rate ? '1' : $rate; app('log')->debug(sprintf('bcmul("%s", "%s")', (string)$entry, $rate)); $set['entries'][$date] = (float)bcmul((string)$entry, $rate); } + return $set; } - /** - * @return void - * @deprecated - */ - private function getPreference(): void - { - $this->enabled = config('cer.currency_conversion'); - } - - /** - * @param int $currencyId - * - * @return TransactionCurrency - * @deprecated - */ - private function getCurrency(int $currencyId): TransactionCurrency - { - $result = TransactionCurrency::find($currencyId); - if (null === $result) { - return app('amount')->getDefaultCurrency(); - } - return $result; - } - - /** * For a sum of entries, get the exchange rate to the native currency of * the user. * - * @param array $entries - * - * @return array * @deprecated */ public function cerSum(array $entries): array @@ -119,18 +91,20 @@ trait ConvertsExchangeRates // if false, return the same array without conversion info if (false === $this->enabled) { $return = []; + /** @var array $entry */ foreach ($entries as $entry) { $entry['converted'] = false; $return[] = $entry; } + return $return; } - /** @var TransactionCurrency $native */ $native = app('amount')->getDefaultCurrency(); $return = []; + /** @var array $entry */ foreach ($entries as $entry) { $currency = $this->getCurrency((int)$entry['id']); @@ -155,17 +129,32 @@ trait ConvertsExchangeRates } $return[] = $entry; } + return $return; } /** - * @param string $amount - * @param TransactionCurrency $from - * @param TransactionCurrency $to - * @param Carbon|null $date - * - * @return string - * + * @deprecated + */ + private function getPreference(): void + { + $this->enabled = config('cer.currency_conversion'); + } + + /** + * @deprecated + */ + private function getCurrency(int $currencyId): TransactionCurrency + { + $result = TransactionCurrency::find($currencyId); + if (null === $result) { + return app('amount')->getDefaultCurrency(); + } + + return $result; + } + + /** * @deprecated */ private function convertAmount(string $amount, TransactionCurrency $from, TransactionCurrency $to, ?Carbon $date = null): string diff --git a/app/Support/Http/Api/ExchangeRateConverter.php b/app/Support/Http/Api/ExchangeRateConverter.php index 8e9ec07ed9..ee94070233 100644 --- a/app/Support/Http/Api/ExchangeRateConverter.php +++ b/app/Support/Http/Api/ExchangeRateConverter.php @@ -1,6 +1,5 @@ getCurrencyRate($from, $to, $date); + return bcmul($amount, $rate); } + public function prepare(TransactionCurrency $from, TransactionCurrency $to, Carbon $start, Carbon $end): void + { + $start->startOfDay(); + $end->endOfDay(); + Log::debug(sprintf('Preparing for %s to %s between %s and %s', $from->code, $to->code, $start->format('Y-m-d'), $end->format('Y-m-d'))); + $set = auth()->user() + ->currencyExchangeRates() + ->where('from_currency_id', $from->id) + ->where('to_currency_id', $to->id) + ->where('date', '<=', $end->format('Y-m-d')) + ->where('date', '>=', $start->format('Y-m-d')) + ->orderBy('date', 'DESC')->get() + ; + $fallback = $this->getRate($from, $to, $start); + ++$this->queryCount; + if (0 === $set->count()) { + Log::debug('No rates found in this period.'); + + return; + } + /* + * Je moet een fallback rate hebben van voor de periode zodat je die altijd kan gebruiken. + * Dus de laatste met de meest recente datum voor je periode. + * Dan moet je gaan loopen per dag, en als er iets in $temp zit toevallig gebruik je die. + */ + $temp = []; + $count = 0; + foreach ($set as $rate) { + $date = $rate->date->format('Y-m-d'); + $temp[$date] ??= [ + $from->id => [ + $to->id => $rate->rate, + ], + ]; + ++$count; + } + Log::debug(sprintf('Found %d rates in this period.', $count)); + $currentStart = clone $start; + while ($currentStart->lte($end)) { + $currentDate = $currentStart->format('Y-m-d'); + $this->prepared[$currentDate] ??= []; + $fallback = $temp[$currentDate][$from->id][$to->id] ?? $fallback; + if (0 === count($this->prepared[$currentDate]) && 0 !== bccomp('0', $fallback)) { + // fill from temp or fallback or from temp (see before) + $this->prepared[$currentDate][$from->id][$to->id] = $fallback; + } + $currentStart->addDay(); + } + } + /** - * @param TransactionCurrency $from - * @param TransactionCurrency $to - * @param Carbon $date - * - * @return string * @throws FireflyException */ public function getCurrencyRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string { $rate = $this->getRate($from, $to, $date); + return '0' === $rate ? '1' : $rate; } + public function summarize(): void + { + Log::info(sprintf('ExchangeRateConverter ran %d queries.', $this->queryCount)); + } + /** - * @param TransactionCurrency $from - * @param TransactionCurrency $to - * @param Carbon $date - * - * @return string * @throws FireflyException */ private function getRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string @@ -93,25 +136,30 @@ class ExchangeRateConverter $second = $this->getEuroRate($to, $date); // combined (if present), they can be used to calculate the necessary conversion rate. - if ('0' === $first || '0' === $second) { + if (0 === bccomp('0', $first) || 0 === bccomp('0', $second)) { + Log::warning(sprintf('$first is "%s" and $second is "%s"', $first, $second)); + return '0'; } $second = bcdiv('1', $second); + return bcmul($first, $second); } - /** - * @param int $from - * @param int $to - * @param string $date - * - * @return string|null - */ private function getFromDB(int $from, int $to, string $date): ?string { + if ($from === $to) { + return '1'; + } $key = sprintf('cer-%d-%d-%s', $from, $to, $date); + // perhaps the rate has been cached during this particular run + $preparedRate = $this->prepared[$date][$from][$to] ?? null; + if (null !== $preparedRate && '0' !== $preparedRate) { + return $preparedRate; + } + $cache = new CacheProperties(); $cache->addProperty($key); if ($cache->has()) { @@ -119,34 +167,50 @@ class ExchangeRateConverter if ('' === $rate) { return null; } + return $rate; } app('log')->debug(sprintf('Going to get rate #%d->#%d (%s) from DB.', $from, $to, $date)); - /** @var CurrencyExchangeRate|null $result */ + /** @var null|CurrencyExchangeRate $result */ $result = auth()->user() - ->currencyExchangeRates() - ->where('from_currency_id', $from) - ->where('to_currency_id', $to) - ->where('date', '<=', $date) - ->orderBy('date', 'DESC') - ->first(); - $rate = (string)$result?->rate; - $cache->store($rate); + ->currencyExchangeRates() + ->where('from_currency_id', $from) + ->where('to_currency_id', $to) + ->where('date', '<=', $date) + ->orderBy('date', 'DESC') + ->first() + ; + ++$this->queryCount; + $rate = (string) $result?->rate; + if ('' === $rate) { return null; } + if('0' === $rate) { + return null; + } + + $cache->store($rate); + + // if the rate has not been cached during this particular run, save it + $this->prepared[$date] ??= [ + $from => [ + $to => $rate, + ], + ]; + // also save the exchange rate the other way around: + $this->prepared[$date] ??= [ + $to => [ + $from => bcdiv('1', $rate), + ], + ]; + return $rate; } - /** - * @param TransactionCurrency $currency - * @param Carbon $date - * - * @return string * @throws FireflyException - * */ private function getEuroRate(TransactionCurrency $currency, Carbon $date): string { @@ -164,23 +228,21 @@ class ExchangeRateConverter if (null !== $rate) { return bcdiv('1', $rate); // app('log')->debug(sprintf('Inverted rate for %s to EUR is %s.', $currency->code, $rate)); - //return $rate; + // return $rate; } // grab backup values from config file: $backup = config(sprintf('cer.rates.%s', $currency->code)); if (null !== $backup) { - return bcdiv('1', (string)$backup); + return bcdiv('1', (string) $backup); // app('log')->debug(sprintf('Backup rate for %s to EUR is %s.', $currency->code, $backup)); - //return $backup; + // return $backup; } // app('log')->debug(sprintf('No rate for %s to EUR.', $currency->code)); return '0'; } - /** - * @return int * @throws FireflyException */ private function getEuroId(): int @@ -188,13 +250,15 @@ class ExchangeRateConverter $cache = new CacheProperties(); $cache->addProperty('cer-euro-id'); if ($cache->has()) { - return (int)$cache->get(); + return (int) $cache->get(); } $euro = TransactionCurrency::whereCode('EUR')->first(); + ++$this->queryCount; if (null === $euro) { throw new FireflyException('Cannot find EUR in system, cannot do currency conversion.'); } $cache->store($euro->id); + return $euro->id; } } diff --git a/app/Support/Http/Api/SummaryBalanceGrouped.php b/app/Support/Http/Api/SummaryBalanceGrouped.php new file mode 100644 index 0000000000..29f8b1153b --- /dev/null +++ b/app/Support/Http/Api/SummaryBalanceGrouped.php @@ -0,0 +1,136 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Support\Http\Api; + +use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; + +class SummaryBalanceGrouped +{ + private const string SUM = 'sum'; + private TransactionCurrency $default; + private array $amounts = []; + private array $keys; + private array $currencies; + private CurrencyRepositoryInterface $currencyRepository; + + public function __construct() + { + $this->keys = [self::SUM]; + $this->currencies = []; + $this->currencyRepository = app(CurrencyRepositoryInterface::class); + } + + public function groupTransactions(string $key, array $journals): void + { + \Log::debug(sprintf('Now in groupTransactions with key "%s" and %d journal(s)', $key, count($journals))); + $converter = new ExchangeRateConverter(); + $this->keys[] = $key; + $multiplier = 'income' === $key ? '-1' : '1'; + + /** @var array $journal */ + foreach ($journals as $journal) { + // transaction info: + $currencyId = (int)$journal['currency_id']; + $amount = bcmul($journal['amount'], $multiplier); + $currency = $this->currencies[$currencyId] ?? TransactionCurrency::find($currencyId); + $this->currencies[$currencyId] = $currency; + $nativeAmount = $converter->convert($currency, $this->default, $journal['date'], $amount); + if ((int)$journal['foreign_currency_id'] === $this->default->id) { + // use foreign amount instead + $nativeAmount = $journal['foreign_amount']; + } + // prep the arrays + $this->amounts[$key] ??= []; + $this->amounts[$key][$currencyId] ??= '0'; + $this->amounts[$key]['native'] ??= '0'; + $this->amounts[self::SUM][$currencyId] ??= '0'; + $this->amounts[self::SUM]['native'] ??= '0'; + + // add values: + $this->amounts[$key][$currencyId] = bcadd($this->amounts[$key][$currencyId], $amount); + $this->amounts[self::SUM][$currencyId] = bcadd($this->amounts[self::SUM][$currencyId], $amount); + $this->amounts[$key]['native'] = bcadd($this->amounts[$key]['native'], $nativeAmount); + $this->amounts[self::SUM]['native'] = bcadd($this->amounts[self::SUM]['native'], $nativeAmount); + } + $converter->summarize(); + } + + public function groupData(): array + { + \Log::debug('Now going to group data.'); + $return = []; + foreach ($this->keys as $key) { + $title = match ($key) { + 'sum' => 'balance', + 'expense' => 'spent', + 'income' => 'earned', + default => 'something' + }; + $return[] = [ + 'key' => sprintf('%s-in-native', $title), + 'value' => $this->amounts[$key]['native'], + 'currency_id' => (string)$this->default->id, + 'currency_code' => $this->default->code, + 'currency_symbol' => $this->default->symbol, + 'currency_decimal_places' => $this->default->decimal_places, + ]; + } + // loop 3: format amounts: + $currencyIds = array_keys($this->amounts[self::SUM]); + foreach ($currencyIds as $currencyId) { + if ('native' === $currencyId) { + // skip native entries. + continue; + } + $currencyId = (int)$currencyId; + $currency = $this->currencies[$currencyId] ?? $this->currencyRepository->find($currencyId); + $this->currencies[$currencyId] = $currency; + // create objects for big array. + foreach ($this->keys as $key) { + $title = match ($key) { + 'sum' => 'balance', + 'expense' => 'spent', + 'income' => 'earned', + default => 'something' + }; + $return[] = [ + 'key' => sprintf('%s-in-%s', $title, $currency->code), + 'value' => $this->amounts[$key][$currencyId] ?? '0', + 'currency_id' => (string)$currency->id, + 'currency_code' => $currency->code, + 'currency_symbol' => $currency->symbol, + 'currency_decimal_places' => $currency->decimal_places, + ]; + } + } + + return $return; + } + + public function setDefault(TransactionCurrency $default): void + { + $this->default = $default; + } +} diff --git a/app/Support/Http/Api/TransactionFilter.php b/app/Support/Http/Api/TransactionFilter.php index 3542749cdd..0e5695c035 100644 --- a/app/Support/Http/Api/TransactionFilter.php +++ b/app/Support/Http/Api/TransactionFilter.php @@ -27,17 +27,11 @@ use FireflyIII\Models\TransactionType; /** * Trait TransactionFilter - * - */ trait TransactionFilter { /** * All the types you can request. - * - * @param string $type - * - * @return array */ protected function mapTransactionTypes(string $type): array { @@ -49,27 +43,28 @@ trait TransactionFilter TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION, ], - 'withdrawal' => [TransactionType::WITHDRAWAL,], - 'withdrawals' => [TransactionType::WITHDRAWAL,], - 'expense' => [TransactionType::WITHDRAWAL,], - 'expenses' => [TransactionType::WITHDRAWAL,], - 'income' => [TransactionType::DEPOSIT,], - 'deposit' => [TransactionType::DEPOSIT,], - 'deposits' => [TransactionType::DEPOSIT,], - 'transfer' => [TransactionType::TRANSFER,], - 'transfers' => [TransactionType::TRANSFER,], - 'opening_balance' => [TransactionType::OPENING_BALANCE,], - 'reconciliation' => [TransactionType::RECONCILIATION,], - 'reconciliations' => [TransactionType::RECONCILIATION,], - 'special' => [TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,], - 'specials' => [TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,], - 'default' => [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER,], + 'withdrawal' => [TransactionType::WITHDRAWAL], + 'withdrawals' => [TransactionType::WITHDRAWAL], + 'expense' => [TransactionType::WITHDRAWAL], + 'expenses' => [TransactionType::WITHDRAWAL], + 'income' => [TransactionType::DEPOSIT], + 'deposit' => [TransactionType::DEPOSIT], + 'deposits' => [TransactionType::DEPOSIT], + 'transfer' => [TransactionType::TRANSFER], + 'transfers' => [TransactionType::TRANSFER], + 'opening_balance' => [TransactionType::OPENING_BALANCE], + 'reconciliation' => [TransactionType::RECONCILIATION], + 'reconciliations' => [TransactionType::RECONCILIATION], + 'special' => [TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION], + 'specials' => [TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION], + 'default' => [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER], ]; $return = []; $parts = explode(',', $type); foreach ($parts as $part) { $return = array_merge($return, $types[$part] ?? $types['default']); } + return array_unique($return); } } diff --git a/app/Support/Http/Api/ValidatesUserGroupTrait.php b/app/Support/Http/Api/ValidatesUserGroupTrait.php index 69041f154e..0f1e01609b 100644 --- a/app/Support/Http/Api/ValidatesUserGroupTrait.php +++ b/app/Support/Http/Api/ValidatesUserGroupTrait.php @@ -37,32 +37,35 @@ trait ValidatesUserGroupTrait /** * This check does not validate which rights the user has, that comes later. * - * @param Request $request - * - * @return UserGroup|null * @throws FireflyException */ protected function validateUserGroup(Request $request): ?UserGroup { if (!auth()->check()) { app('log')->debug('validateUserGroup: user is not logged in, return NULL.'); + return null; } + /** @var User $user */ $user = auth()->user(); if (!$request->has('user_group_id')) { $group = $user->userGroup; app('log')->debug(sprintf('validateUserGroup: no user group submitted, return default group #%d.', $group?->id)); + return $group; } $groupId = (int)$request->get('user_group_id'); - /** @var GroupMembership|null $membership */ + + /** @var null|GroupMembership $membership */ $membership = $user->groupMemberships()->where('user_group_id', $groupId)->first(); if (null === $membership) { app('log')->debug('validateUserGroup: user has no access to this group.'); + throw new FireflyException((string)trans('validation.belongs_user_or_user_group')); } app('log')->debug(sprintf('validateUserGroup: user has role "%s" in group #%d.', $membership->userRole->title, $membership->userGroup->id)); + return $membership->userGroup; } } diff --git a/app/Support/Http/Controllers/AugumentData.php b/app/Support/Http/Controllers/AugumentData.php index aa5493ef77..47822de750 100644 --- a/app/Support/Http/Controllers/AugumentData.php +++ b/app/Support/Http/Controllers/AugumentData.php @@ -40,22 +40,18 @@ use Illuminate\Support\Collection; /** * Trait AugumentData - * */ trait AugumentData { /** * Searches for the opposing account. - * - * @param Collection $accounts - * - * @return array */ protected function combineAccounts(Collection $accounts): array // filter + group data { /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); $combined = []; + /** @var Account $expenseAccount */ foreach ($accounts as $expenseAccount) { $collection = new Collection(); @@ -74,9 +70,7 @@ trait AugumentData /** * Small helper function for the revenue and expense account charts. * - * @param array $names - * - * @return array + * @param array $names */ protected function expandNames(array $names): array { @@ -90,14 +84,11 @@ trait AugumentData /** * Small helper function for the revenue and expense account charts. - * - * @param Collection $accounts - * - * @return array */ protected function extractNames(Collection $accounts): array { $return = []; + /** @var Account $account */ foreach ($accounts as $account) { $return[$account->id] = $account->name; @@ -108,10 +99,6 @@ trait AugumentData /** * Get the account names belonging to a bunch of account ID's. - * - * @param array $accountIds - * - * @return array */ protected function getAccountNames(array $accountIds): array // extract info from array. { @@ -134,10 +121,6 @@ trait AugumentData /** * Get the budget names from a set of budget ID's. - * - * @param array $budgetIds - * - * @return array */ protected function getBudgetNames(array $budgetIds): array // extract info from array. { @@ -158,10 +141,6 @@ trait AugumentData /** * Get the category names from a set of category ID's. Small helper function for some of the charts. - * - * @param array $categoryIds - * - * @return array */ protected function getCategoryNames(array $categoryIds): array // extract info from array. { @@ -184,12 +163,6 @@ trait AugumentData /** * Gets all budget limits for a budget. - * - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * - * @return Collection */ protected function getLimits(Budget $budget, Carbon $start, Carbon $end): Collection // get data + augment with info { @@ -243,15 +216,12 @@ trait AugumentData /** * Group set of transactions by name of opposing account. - * - * @param array $array - * - * @return array */ protected function groupByName(array $array): array // filter + group data { // group by opposing account name. $grouped = []; + /** @var array $journal */ foreach ($array as $journal) { $name = '(no name)'; @@ -271,13 +241,6 @@ trait AugumentData /** * Spent in a period. - * - * @param Collection $assets - * @param Collection $opposing - * @param Carbon $start - * @param Carbon $end - * - * @return array */ protected function spentInPeriod(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info { diff --git a/app/Support/Http/Controllers/BasicDataSupport.php b/app/Support/Http/Controllers/BasicDataSupport.php index 360634330b..47d70225a7 100644 --- a/app/Support/Http/Controllers/BasicDataSupport.php +++ b/app/Support/Http/Controllers/BasicDataSupport.php @@ -27,16 +27,12 @@ use Carbon\Carbon; /** * Trait BasicDataSupport - * */ trait BasicDataSupport { /** * Find the ID in a given array. Return '0' if not there (amount). * - * @param array $array - * @param int $entryId - * * @return null|mixed */ protected function isInArray(array $array, int $entryId) @@ -46,11 +42,6 @@ trait BasicDataSupport /** * Find the ID in a given array. Return null if not there (amount). - * - * @param array $array - * @param int $entryId - * - * @return null|Carbon */ protected function isInArrayDate(array $array, int $entryId): ?Carbon { diff --git a/app/Support/Http/Controllers/ChartGeneration.php b/app/Support/Http/Controllers/ChartGeneration.php index 16cc521d5d..fd0c32255a 100644 --- a/app/Support/Http/Controllers/ChartGeneration.php +++ b/app/Support/Http/Controllers/ChartGeneration.php @@ -30,7 +30,6 @@ use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; -use JsonException; /** * Trait ChartGeneration @@ -40,13 +39,7 @@ trait ChartGeneration /** * Shows an overview of the account balances for a set of accounts. * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array * @throws FireflyException - * @throws JsonException */ protected function accountBalanceChart(Collection $accounts, Carbon $start, Carbon $end): array // chart helper method. { @@ -61,6 +54,7 @@ trait ChartGeneration } app('log')->debug('Regenerate chart.account.account-balance-chart from scratch.'); $locale = app('steam')->getLocale(); + /** @var GeneratorInterface $generator */ $generator = app(GeneratorInterface::class); @@ -69,6 +63,7 @@ trait ChartGeneration $default = app('amount')->getDefaultCurrency(); $chartData = []; + /** @var Account $account */ foreach ($accounts as $account) { // TODO we can use getAccountCurrency instead. diff --git a/app/Support/Http/Controllers/CreateStuff.php b/app/Support/Http/Controllers/CreateStuff.php index 14192fbd63..d340a1c28c 100644 --- a/app/Support/Http/Controllers/CreateStuff.php +++ b/app/Support/Http/Controllers/CreateStuff.php @@ -34,17 +34,11 @@ use phpseclib3\Crypt\RSA; /** * Trait CreateStuff - * */ trait CreateStuff { /** * Creates an asset account. - * - * @param NewUserFormRequest $request - * @param TransactionCurrency $currency - * - * @return bool */ protected function createAssetAccount(NewUserFormRequest $request, TransactionCurrency $currency): bool // create stuff { @@ -70,11 +64,6 @@ trait CreateStuff /** * Creates a cash wallet. - * - * @param TransactionCurrency $currency - * @param string $language - * - * @return bool */ protected function createCashWalletAccount(TransactionCurrency $currency, string $language): bool // create stuff { @@ -122,12 +111,6 @@ trait CreateStuff /** * Create a savings account. - * - * @param NewUserFormRequest $request - * @param TransactionCurrency $currency - * @param string $language - * - * @return bool */ protected function createSavingsAccount(NewUserFormRequest $request, TransactionCurrency $currency, string $language): bool // create stuff { @@ -152,10 +135,6 @@ trait CreateStuff /** * Create a new user instance after a valid registration. - * - * @param array $data - * - * @return User */ protected function createUser(array $data): User // create object { diff --git a/app/Support/Http/Controllers/CronRunner.php b/app/Support/Http/Controllers/CronRunner.php index dc0b337bd1..5fb7326bfa 100644 --- a/app/Support/Http/Controllers/CronRunner.php +++ b/app/Support/Http/Controllers/CronRunner.php @@ -29,28 +29,19 @@ use FireflyIII\Support\Cronjobs\AutoBudgetCronjob; use FireflyIII\Support\Cronjobs\BillWarningCronjob; use FireflyIII\Support\Cronjobs\ExchangeRatesCronjob; use FireflyIII\Support\Cronjobs\RecurringCronjob; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Trait CronRunner */ trait CronRunner { - /** - * @param bool $force - * @param Carbon $date - * - * @return array - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ protected function billWarningCronJob(bool $force, Carbon $date): array { /** @var BillWarningCronjob $billWarning */ $billWarning = app(BillWarningCronjob::class); $billWarning->setForce($force); $billWarning->setDate($date); + try { $billWarning->fire(); } catch (FireflyException $e) { @@ -70,18 +61,13 @@ trait CronRunner ]; } - /** - * @param bool $force - * @param Carbon $date - * - * @return array - */ protected function exchangeRatesCronJob(bool $force, Carbon $date): array { /** @var ExchangeRatesCronjob $exchangeRates */ $exchangeRates = app(ExchangeRatesCronjob::class); $exchangeRates->setForce($force); $exchangeRates->setDate($date); + try { $exchangeRates->fire(); } catch (FireflyException $e) { @@ -101,18 +87,13 @@ trait CronRunner ]; } - /** - * @param bool $force - * @param Carbon $date - * - * @return array - */ protected function runAutoBudget(bool $force, Carbon $date): array { /** @var AutoBudgetCronjob $autoBudget */ $autoBudget = app(AutoBudgetCronjob::class); $autoBudget->setForce($force); $autoBudget->setDate($date); + try { $autoBudget->fire(); } catch (FireflyException $e) { @@ -132,20 +113,13 @@ trait CronRunner ]; } - /** - * @param bool $force - * @param Carbon $date - * - * @return array - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ protected function runRecurring(bool $force, Carbon $date): array { /** @var RecurringCronjob $recurring */ $recurring = app(RecurringCronjob::class); $recurring->setForce($force); $recurring->setDate($date); + try { $recurring->fire(); } catch (FireflyException $e) { diff --git a/app/Support/Http/Controllers/DateCalculation.php b/app/Support/Http/Controllers/DateCalculation.php index ae01a4135e..c846eaeec6 100644 --- a/app/Support/Http/Controllers/DateCalculation.php +++ b/app/Support/Http/Controllers/DateCalculation.php @@ -27,7 +27,6 @@ use Carbon\Carbon; /** * Trait DateCalculation - * */ trait DateCalculation { @@ -37,11 +36,6 @@ trait DateCalculation * * If both are in the past OR both are in the future, simply return the number of days in the period with a minimum * of 1 - * - * @param Carbon $start - * @param Carbon $end - * - * @return int */ public function activeDaysLeft(Carbon $start, Carbon $end): int { @@ -59,11 +53,6 @@ trait DateCalculation * Calculate the number of days passed between two dates. Will take the current moment into consideration. * * If both are in the past OR both are in the future, simply return the period between them with a minimum of 1 - * - * @param Carbon $start - * @param Carbon $end - * - * @return int */ protected function activeDaysPassed(Carbon $start, Carbon $end): int { @@ -77,12 +66,6 @@ trait DateCalculation return $difference; } - /** - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ protected function calculateStep(Carbon $start, Carbon $end): string { $step = '1D'; @@ -103,16 +86,12 @@ trait DateCalculation /** * Get a list of the periods that will occur after this date. For example, * March 2018, April 2018, etc. - * - * @param Carbon $date - * @param string $range - * - * @return array */ protected function getNextPeriods(Carbon $date, string $range): array { // select thing for next 12 periods: $loop = []; + /** @var Carbon $current */ $current = app('navigation')->startOfPeriod($date, $range); $current = app('navigation')->endOfPeriod($current, $range); @@ -139,16 +118,12 @@ trait DateCalculation /** * Get a list of the periods that occurred before the start date. For example, * March 2018, February 2018, etc. - * - * @param Carbon $date - * @param string $range - * - * @return array */ protected function getPreviousPeriods(Carbon $date, string $range): array { // select thing for last 12 periods: $loop = []; + /** @var Carbon $current */ $current = app('navigation')->startOfPeriod($date, $range); $count = 0; diff --git a/app/Support/Http/Controllers/GetConfigurationData.php b/app/Support/Http/Controllers/GetConfigurationData.php index eebadefb10..c40f469917 100644 --- a/app/Support/Http/Controllers/GetConfigurationData.php +++ b/app/Support/Http/Controllers/GetConfigurationData.php @@ -25,21 +25,14 @@ namespace FireflyIII\Support\Http\Controllers; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Trait GetConfigurationData - * */ trait GetConfigurationData { /** * Some common combinations. - * - * @param int $value - * - * @return string */ protected function errorReporting(int $value): string // get configuration { @@ -58,10 +51,6 @@ trait GetConfigurationData /** * Get the basic steps from config. - * - * @param string $route - * - * @return array */ protected function getBasicSteps(string $route): array // get config values { @@ -73,7 +62,7 @@ trait GetConfigurationData $currentStep = $options; // get the text: - $currentStep['intro'] = (string)trans('intro.' . $route . '_' . $key); + $currentStep['intro'] = (string)trans('intro.'.$route.'_'.$key); // save in array: $steps[] = $currentStep; @@ -87,18 +76,18 @@ trait GetConfigurationData /** * Get config for date range. * - * @return array * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ protected function getDateRangeConfig(): array // get configuration + get preferences. { $viewRange = app('navigation')->getViewRange(false); + /** @var Carbon $start */ $start = session('start'); + /** @var Carbon $end */ $end = session('end'); + /** @var Carbon $first */ $first = session('first'); $title = sprintf('%s - %s', $start->isoFormat($this->monthAndDayFormat), $end->isoFormat($this->monthAndDayFormat)); @@ -134,6 +123,7 @@ trait GetConfigurationData // today: /** @var Carbon $todayStart */ $todayStart = app('navigation')->startOfPeriod($today, $viewRange); + /** @var Carbon $todayEnd */ $todayEnd = app('navigation')->endOfPeriod($todayStart, $viewRange); if ($todayStart->ne($start) || $todayEnd->ne($end)) { @@ -181,12 +171,6 @@ trait GetConfigurationData /** * Get specific info for special routes. - * - * @param string $route - * @param string $specificPage - * - * @return array - * */ protected function getSpecificSteps(string $route, string $specificPage): array // get config values { @@ -196,13 +180,13 @@ trait GetConfigurationData // user is on page with specific instructions: if ('' !== $specificPage) { $routeKey = str_replace('.', '_', $route); - $elements = config(sprintf('intro.%s', $routeKey . '_' . $specificPage)); + $elements = config(sprintf('intro.%s', $routeKey.'_'.$specificPage)); if (is_array($elements) && count($elements) > 0) { foreach ($elements as $key => $options) { $currentStep = $options; // get the text: - $currentStep['intro'] = (string)trans('intro.' . $route . '_' . $specificPage . '_' . $key); + $currentStep['intro'] = (string)trans('intro.'.$route.'_'.$specificPage.'_'.$key); // save in array: $steps[] = $currentStep; @@ -214,9 +198,6 @@ trait GetConfigurationData return $steps; } - /** - * - */ protected function verifyRecurringCronJob(): void { $config = app('fireflyconfig')->get('last_rt_job', 0); diff --git a/app/Support/Http/Controllers/ModelInformation.php b/app/Support/Http/Controllers/ModelInformation.php index 0491a8c741..c0f3eff785 100644 --- a/app/Support/Http/Controllers/ModelInformation.php +++ b/app/Support/Http/Controllers/ModelInformation.php @@ -30,20 +30,15 @@ use FireflyIII\Models\Tag; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use Throwable; /** * Trait ModelInformation - * */ trait ModelInformation { /** * Get actions based on a bill. * - * @param Bill $bill - * - * @return array * @throws FireflyException */ protected function getActionsForBill(Bill $bill): array // get info and augument @@ -58,10 +53,11 @@ trait ModelInformation 'count' => 1, ] )->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Throwable was thrown in getActionsForBill(): %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); $result = 'Could not render view. See log files.'; + throw new FireflyException($result, 0, $e); } @@ -69,7 +65,6 @@ trait ModelInformation } /** - * * @return string[] * * @psalm-return array @@ -78,31 +73,31 @@ trait ModelInformation { /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); + // types of liability: /** @var AccountType $debt */ $debt = $repository->getAccountTypeByType(AccountType::DEBT); + /** @var AccountType $loan */ $loan = $repository->getAccountTypeByType(AccountType::LOAN); + /** @var AccountType $mortgage */ $mortgage = $repository->getAccountTypeByType(AccountType::MORTGAGE); $liabilityTypes = [ - $debt->id => (string)trans(sprintf('firefly.account_type_%s', AccountType::DEBT)), - $loan->id => (string)trans(sprintf('firefly.account_type_%s', AccountType::LOAN)), - $mortgage->id => (string)trans(sprintf('firefly.account_type_%s', AccountType::MORTGAGE)), + $debt->id => (string) trans(sprintf('firefly.account_type_%s', AccountType::DEBT)), + $loan->id => (string) trans(sprintf('firefly.account_type_%s', AccountType::LOAN)), + $mortgage->id => (string) trans(sprintf('firefly.account_type_%s', AccountType::MORTGAGE)), ]; asort($liabilityTypes); return $liabilityTypes; } - /** - * @return array - */ protected function getRoles(): array { $roles = []; foreach (config('firefly.accountRoles') as $role) { - $roles[$role] = (string)trans(sprintf('firefly.account_role_%s', $role)); + $roles[$role] = (string) trans(sprintf('firefly.account_role_%s', $role)); } return $roles; @@ -111,9 +106,6 @@ trait ModelInformation /** * Create fake triggers to match the bill's properties * - * @param Bill $bill - * - * @return array * @throws FireflyException */ protected function getTriggersForBill(Bill $bill): array // get info and augument @@ -123,7 +115,7 @@ trait ModelInformation $triggers = []; foreach ($operators as $key => $operator) { if ('user_action' !== $key && false === $operator['alias']) { - $triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key)); + $triggers[$key] = (string) trans(sprintf('firefly.rule_trigger_%s_choice', $key)); } } asort($triggers); @@ -148,9 +140,10 @@ trait ModelInformation 'triggers' => $triggers, ] )->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Throwable was thrown in getTriggersForBill(): %s', $e->getMessage())); app('log')->debug($e->getTraceAsString()); + throw new FireflyException(sprintf('Could not render trigger: %s', $e->getMessage()), 0, $e); } if ('' !== $string) { @@ -162,10 +155,9 @@ trait ModelInformation } /** - * @param TransactionJournal $journal - * - * @return array * @throws FireflyException + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ private function getTriggersForJournal(TransactionJournal $journal): array { @@ -174,7 +166,7 @@ trait ModelInformation $triggers = []; foreach ($operators as $key => $operator) { if ('user_action' !== $key && false === $operator['alias']) { - $triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key)); + $triggers[$key] = (string) trans(sprintf('firefly.rule_trigger_%s_choice', $key)); } } asort($triggers); @@ -183,11 +175,13 @@ trait ModelInformation $journalTriggers = []; $values = []; $index = 0; + // amount, description, category, budget, tags, source, destination, notes, currency type - //,type - /** @var Transaction|null $source */ + // ,type + /** @var null|Transaction $source */ $source = $journal->transactions()->where('amount', '<', 0)->first(); - /** @var Transaction|null $destination */ + + /** @var null|Transaction $destination */ $destination = $journal->transactions()->where('amount', '>', 0)->first(); if (null === $destination || null === $source) { return $result; @@ -195,54 +189,55 @@ trait ModelInformation // type $journalTriggers[$index] = 'transaction_type'; $values[$index] = $journal->transactionType->type; - $index++; + ++$index; // currency $journalTriggers[$index] = 'currency_is'; $values[$index] = sprintf('%s (%s)', $journal->transactionCurrency?->name, $journal->transactionCurrency?->code); - $index++; + ++$index; // amount_exactly: $journalTriggers[$index] = 'amount_exactly'; $values[$index] = $destination->amount; - $index++; + ++$index; // description_is: $journalTriggers[$index] = 'description_is'; $values[$index] = $journal->description; - $index++; + ++$index; // from_account_is $journalTriggers[$index] = 'source_account_is'; $values[$index] = $source->account->name; - $index++; + ++$index; // to_account_is $journalTriggers[$index] = 'destination_account_is'; $values[$index] = $destination->account->name; - $index++; + ++$index; // category (if) $category = $journal->categories()->first(); if (null !== $category) { $journalTriggers[$index] = 'category_is'; $values[$index] = $category->name; - $index++; + ++$index; } // budget (if) $budget = $journal->budgets()->first(); if (null !== $budget) { $journalTriggers[$index] = 'budget_is'; $values[$index] = $budget->name; - $index++; + ++$index; } // tags (if) $tags = $journal->tags()->get(); + /** @var Tag $tag */ foreach ($tags as $tag) { $journalTriggers[$index] = 'tag_is'; $values[$index] = $tag->tag; - $index++; + ++$index; } // notes (if) $notes = $journal->notes()->first(); @@ -261,15 +256,17 @@ trait ModelInformation 'triggers' => $triggers, ]; $string = view('rules.partials.trigger', $renderInfo)->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Throwable was thrown in getTriggersForJournal(): %s', $e->getMessage())); app('log')->debug($e->getTraceAsString()); + throw new FireflyException(sprintf('Could not render trigger: %s', $e->getMessage()), 0, $e); } if ('' !== $string) { $result[] = $string; } } + return $result; } } diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 0d4045bed6..698140971c 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -33,8 +33,6 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; /** * Trait PeriodOverview. @@ -62,7 +60,6 @@ use Psr\Container\NotFoundExceptionInterface; * amount: -1234 (str) * count: 23 * ] - * */ trait PeriodOverview { @@ -73,14 +70,7 @@ trait PeriodOverview * and for each period, the amount of money spent and earned. This is a complex operation which is cached for * performance reasons. * - * @param Account $account - * @param Carbon $start - * @param Carbon $end - * - * @return array * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array { @@ -96,6 +86,7 @@ trait PeriodOverview if ($cache->has()) { return $cache->get(); } + /** @var array $dates */ $dates = app('navigation')->blockPeriods($start, $end, $range); $entries = []; @@ -133,143 +124,25 @@ trait PeriodOverview $transferredIn = $this->filterTransferredIn($account, $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end'])); $entries[] = [ - 'title' => $title, - 'route' => - route('accounts.show', [$account->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'title' => $title, + 'route' => route('accounts.show', [$account->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferredAway) + count($transferredIn), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred_away' => $this->groupByCurrency($transferredAway), - 'transferred_in' => $this->groupByCurrency($transferredIn), - ]; + 'total_transactions' => count($spent) + count($earned) + count($transferredAway) + count($transferredIn), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred_away' => $this->groupByCurrency($transferredAway), + 'transferred_in' => $this->groupByCurrency($transferredIn), + ]; } $cache->store($entries); return $entries; } - /** - * Filter a list of journals by a set of dates, and then group them by currency. - * - * @param array $array - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - private function filterJournalsByDate(array $array, Carbon $start, Carbon $end): array - { - $result = []; - /** @var array $journal */ - foreach ($array as $journal) { - if ($journal['date'] <= $end && $journal['date'] >= $start) { - $result[] = $journal; - } - } - - return $result; - } - - /** - * Return only transactions where $account is the source. - * - * @param Account $account - * @param array $journals - * - * @return array - */ - private function filterTransferredAway(Account $account, array $journals): array - { - $return = []; - /** @var array $journal */ - foreach ($journals as $journal) { - if ($account->id === (int)$journal['source_account_id']) { - $return[] = $journal; - } - } - - return $return; - } - - /** - * Return only transactions where $account is the source. - * - * @param Account $account - * @param array $journals - * - * @return array - */ - private function filterTransferredIn(Account $account, array $journals): array - { - $return = []; - /** @var array $journal */ - foreach ($journals as $journal) { - if ($account->id === (int)$journal['destination_account_id']) { - $return[] = $journal; - } - } - - return $return; - } - - /** - * @param array $journals - * - * @return array - */ - private function groupByCurrency(array $journals): array - { - $return = []; - /** @var array $journal */ - foreach ($journals as $journal) { - $currencyId = (int)$journal['currency_id']; - $foreignCurrencyId = $journal['foreign_currency_id']; - if (!array_key_exists($currencyId, $return)) { - $return[$currencyId] = [ - 'amount' => '0', - 'count' => 0, - 'currency_id' => $currencyId, - 'currency_name' => $journal['currency_name'], - 'currency_code' => $journal['currency_code'], - 'currency_symbol' => $journal['currency_symbol'], - 'currency_decimal_places' => $journal['currency_decimal_places'], - ]; - } - $return[$currencyId]['amount'] = bcadd($return[$currencyId]['amount'], $journal['amount'] ?? '0'); - $return[$currencyId]['count']++; - - if (null !== $foreignCurrencyId && null !== $journal['foreign_amount']) { - if (!array_key_exists($foreignCurrencyId, $return)) { - $return[$foreignCurrencyId] = [ - 'amount' => '0', - 'count' => 0, - 'currency_id' => (int)$foreignCurrencyId, - 'currency_name' => $journal['foreign_currency_name'], - 'currency_code' => $journal['foreign_currency_code'], - 'currency_symbol' => $journal['foreign_currency_symbol'], - 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], - ]; - } - $return[$foreignCurrencyId]['count']++; - $return[$foreignCurrencyId]['amount'] = bcadd($return[$foreignCurrencyId]['amount'], $journal['foreign_amount']); - } - } - - return $return; - } - /** * Overview for single category. Has been refactored recently. * - * @param Category $category - * @param Carbon $start - * @param Carbon $end - * - * @return array * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array { @@ -287,6 +160,7 @@ trait PeriodOverview if ($cache->has()) { return $cache->get(); } + /** @var array $dates */ $dates = app('navigation')->blockPeriods($start, $end, $range); $entries = []; @@ -321,17 +195,17 @@ trait PeriodOverview $title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'categories.show', - [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'categories.show', + [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } $cache->store($entries); @@ -343,13 +217,7 @@ trait PeriodOverview * * This method has been refactored recently. * - * @param Carbon $start - * @param Carbon $end - * - * @return array * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array { @@ -369,6 +237,7 @@ trait PeriodOverview /** @var array $dates */ $dates = app('navigation')->blockPeriods($start, $end, $range); $entries = []; + // get all expenses without a budget. /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); @@ -380,14 +249,14 @@ trait PeriodOverview $title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($set), - 'spent' => $this->groupByCurrency($set), - 'earned' => [], - 'transferred_away' => [], - 'transferred_in' => [], - ]; + 'title' => $title, + 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($set), + 'spent' => $this->groupByCurrency($set), + 'earned' => [], + 'transferred_away' => [], + 'transferred_in' => [], + ]; } $cache->store($entries); @@ -399,12 +268,7 @@ trait PeriodOverview * * Show period overview for no category view. * - * @param Carbon $theDate - * - * @return array * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ protected function getNoCategoryPeriodOverview(Carbon $theDate): array { @@ -453,13 +317,13 @@ trait PeriodOverview $title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'title' => $title, - 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'title' => $title, + 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } app('log')->debug('End of loops'); @@ -469,14 +333,7 @@ trait PeriodOverview /** * This shows a period overview for a tag. It goes back in time and lists all relevant transactions and sums. * - * @param Tag $tag - * @param Carbon $start - * @param Carbon $end - * - * @return array * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags. { @@ -492,6 +349,7 @@ trait PeriodOverview if ($cache->has()) { return $cache->get(); } + /** @var array $dates */ $dates = app('navigation')->blockPeriods($start, $end, $range); $entries = []; @@ -527,31 +385,24 @@ trait PeriodOverview $title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']); $entries[] = [ - 'transactions' => 0, - 'title' => $title, - 'route' => route( - 'tags.show', - [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] - ), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'transactions' => 0, + 'title' => $title, + 'route' => route( + 'tags.show', + [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] + ), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } return $entries; } /** - * @param string $transactionType - * @param Carbon $start - * @param Carbon $end - * - * @return array * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array { @@ -568,6 +419,7 @@ trait PeriodOverview if ($cache->has()) { return $cache->get(); } + /** @var array $dates */ $dates = app('navigation')->blockPeriods($start, $end, $range); $entries = []; @@ -595,16 +447,108 @@ trait PeriodOverview } $entries[] = [ - 'title' => $title, - 'route' => - route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), - 'total_transactions' => count($spent) + count($earned) + count($transferred), - 'spent' => $this->groupByCurrency($spent), - 'earned' => $this->groupByCurrency($earned), - 'transferred' => $this->groupByCurrency($transferred), - ]; + 'title' => $title, + 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), + 'total_transactions' => count($spent) + count($earned) + count($transferred), + 'spent' => $this->groupByCurrency($spent), + 'earned' => $this->groupByCurrency($earned), + 'transferred' => $this->groupByCurrency($transferred), + ]; } return $entries; } + + /** + * Filter a list of journals by a set of dates, and then group them by currency. + */ + private function filterJournalsByDate(array $array, Carbon $start, Carbon $end): array + { + $result = []; + + /** @var array $journal */ + foreach ($array as $journal) { + if ($journal['date'] <= $end && $journal['date'] >= $start) { + $result[] = $journal; + } + } + + return $result; + } + + /** + * Return only transactions where $account is the source. + */ + private function filterTransferredAway(Account $account, array $journals): array + { + $return = []; + + /** @var array $journal */ + foreach ($journals as $journal) { + if ($account->id === (int)$journal['source_account_id']) { + $return[] = $journal; + } + } + + return $return; + } + + /** + * Return only transactions where $account is the source. + */ + private function filterTransferredIn(Account $account, array $journals): array + { + $return = []; + + /** @var array $journal */ + foreach ($journals as $journal) { + if ($account->id === (int)$journal['destination_account_id']) { + $return[] = $journal; + } + } + + return $return; + } + + private function groupByCurrency(array $journals): array + { + $return = []; + + /** @var array $journal */ + foreach ($journals as $journal) { + $currencyId = (int)$journal['currency_id']; + $foreignCurrencyId = $journal['foreign_currency_id']; + if (!array_key_exists($currencyId, $return)) { + $return[$currencyId] = [ + 'amount' => '0', + 'count' => 0, + 'currency_id' => $currencyId, + 'currency_name' => $journal['currency_name'], + 'currency_code' => $journal['currency_code'], + 'currency_symbol' => $journal['currency_symbol'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + ]; + } + $return[$currencyId]['amount'] = bcadd($return[$currencyId]['amount'], $journal['amount'] ?? '0'); + ++$return[$currencyId]['count']; + + if (null !== $foreignCurrencyId && null !== $journal['foreign_amount']) { + if (!array_key_exists($foreignCurrencyId, $return)) { + $return[$foreignCurrencyId] = [ + 'amount' => '0', + 'count' => 0, + 'currency_id' => (int)$foreignCurrencyId, + 'currency_name' => $journal['foreign_currency_name'], + 'currency_code' => $journal['foreign_currency_code'], + 'currency_symbol' => $journal['foreign_currency_symbol'], + 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], + ]; + } + ++$return[$foreignCurrencyId]['count']; + $return[$foreignCurrencyId]['amount'] = bcadd($return[$foreignCurrencyId]['amount'], $journal['foreign_amount']); + } + } + + return $return; + } } diff --git a/app/Support/Http/Controllers/RenderPartialViews.php b/app/Support/Http/Controllers/RenderPartialViews.php index 38820f78fc..2873235c56 100644 --- a/app/Support/Http/Controllers/RenderPartialViews.php +++ b/app/Support/Http/Controllers/RenderPartialViews.php @@ -36,20 +36,15 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\Search\OperatorQuerySearch; -use Throwable; /** * Trait RenderPartialViews - * */ trait RenderPartialViews { /** * View for transactions in a budget for an account. * - * @param array $attributes - * - * @return string * @throws FireflyException */ protected function budgetEntry(array $attributes): string // generate view for report. @@ -72,9 +67,10 @@ trait RenderPartialViews try { $view = view('popup.report.balance-amount', compact('journals', 'budget', 'account'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; + throw new FireflyException($view, 0, $e); } @@ -84,7 +80,6 @@ trait RenderPartialViews /** * Get options for budget report. * - * @return string * @throws FireflyException */ protected function budgetReportOptions(): string // render a view @@ -95,9 +90,10 @@ trait RenderPartialViews try { $result = view('reports.options.budget', compact('budgets'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage())); $result = 'Could not render view.'; + throw new FireflyException($result, 0, $e); } @@ -107,9 +103,6 @@ trait RenderPartialViews /** * View for spent in a single budget. * - * @param array $attributes - * - * @return string * @throws FireflyException */ protected function budgetSpentAmount(array $attributes): string // generate view for report. @@ -128,9 +121,10 @@ trait RenderPartialViews try { $view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; + throw new FireflyException($view, 0, $e); } @@ -140,9 +134,6 @@ trait RenderPartialViews /** * View for transactions in a category. * - * @param array $attributes - * - * @return string * @throws FireflyException */ protected function categoryEntry(array $attributes): string // generate view for report. @@ -157,9 +148,10 @@ trait RenderPartialViews try { $view = view('popup.report.category-entry', compact('journals', 'category'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; + throw new FireflyException($view, 0, $e); } @@ -169,7 +161,6 @@ trait RenderPartialViews /** * Get options for category report. * - * @return string * @throws FireflyException */ protected function categoryReportOptions(): string // render a view @@ -180,9 +171,10 @@ trait RenderPartialViews try { $result = view('reports.options.category', compact('categories'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render reports.options.category: %s', $e->getMessage())); $result = 'Could not render view.'; + throw new FireflyException($result, 0, $e); } @@ -192,7 +184,6 @@ trait RenderPartialViews /** * Get options for double report. * - * @return string * @throws FireflyException */ protected function doubleReportOptions(): string // render a view @@ -220,12 +211,12 @@ trait RenderPartialViews } } - try { $result = view('reports.options.double', compact('set'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage())); $result = 'Could not render view.'; + throw new FireflyException($result, 0, $e); } @@ -235,9 +226,6 @@ trait RenderPartialViews /** * Returns all the expenses that went to the given expense account. * - * @param array $attributes - * - * @return string * @throws FireflyException */ protected function expenseEntry(array $attributes): string // generate view for report. @@ -258,9 +246,10 @@ trait RenderPartialViews try { $view = view('popup.report.expense-entry', compact('journals', 'account'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; + throw new FireflyException($view, 0, $e); } @@ -270,9 +259,6 @@ trait RenderPartialViews /** * Get current (from system) rule actions. * - * @param Rule $rule - * - * @return array * @throws FireflyException */ protected function getCurrentActions(Rule $rule): array // get info from object and present. @@ -281,9 +267,11 @@ trait RenderPartialViews $actions = []; // must be repos $currentActions = $rule->ruleActions()->orderBy('order', 'ASC')->get(); + /** @var RuleAction $entry */ foreach ($currentActions as $entry) { $count = ($index + 1); + try { $actions[] = view( 'rules.partials.action', @@ -294,9 +282,10 @@ trait RenderPartialViews 'count' => $count, ] )->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Throwable was thrown in getCurrentActions(): %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); + throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e); } @@ -309,9 +298,6 @@ trait RenderPartialViews /** * Get current (from DB) rule triggers. * - * @param Rule $rule - * - * @return array * @throws FireflyException */ protected function getCurrentTriggers(Rule $rule): array // get info from object and present. @@ -329,10 +315,12 @@ trait RenderPartialViews $renderedEntries = []; // must be repos $currentTriggers = $rule->ruleTriggers()->orderBy('order', 'ASC')->get(); + /** @var RuleTrigger $entry */ foreach ($currentTriggers as $entry) { if ('user_action' !== $entry->trigger_type) { $count = ($index + 1); + try { $rootOperator = OperatorQuerySearch::getRootOperator((string)$entry->trigger_type); if (str_starts_with($rootOperator, '-')) { @@ -349,9 +337,10 @@ trait RenderPartialViews 'triggers' => $triggers, ] )->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Throwable was thrown in getCurrentTriggers(): %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); + throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e); } @@ -365,9 +354,6 @@ trait RenderPartialViews /** * Returns all the incomes that went to the given asset account. * - * @param array $attributes - * - * @return string * @throws FireflyException */ protected function incomeEntry(array $attributes): string // generate view for report. @@ -387,9 +373,10 @@ trait RenderPartialViews try { $view = view('popup.report.income-entry', compact('journals', 'account'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; + throw new FireflyException($view, 0, $e); } @@ -399,16 +386,16 @@ trait RenderPartialViews /** * Get options for default report. * - * @return string * @throws FireflyException */ protected function noReportOptions(): string // render a view { try { $result = view('reports.options.no-options')->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render reports.options.no-options: %s', $e->getMessage())); $result = 'Could not render view.'; + throw new FireflyException($result, 0, $e); } @@ -418,7 +405,6 @@ trait RenderPartialViews /** * Get options for tag report. * - * @return string * @throws FireflyException */ protected function tagReportOptions(): string // render a view @@ -427,12 +413,12 @@ trait RenderPartialViews $repository = app(TagRepositoryInterface::class); $tags = $repository->get(); - try { $result = view('reports.options.tag', compact('tags'))->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage())); $result = 'Could not render view.'; + throw new FireflyException($result, 0, $e); } diff --git a/app/Support/Http/Controllers/RequestInformation.php b/app/Support/Http/Controllers/RequestInformation.php index be19d03318..77cfd2ec0d 100644 --- a/app/Support/Http/Controllers/RequestInformation.php +++ b/app/Support/Http/Controllers/RequestInformation.php @@ -25,29 +25,22 @@ namespace FireflyIII\Support\Http\Controllers; use Carbon\Carbon; use FireflyIII\Exceptions\ValidationException; -use FireflyIII\Helpers\Help\HelpInterface; use FireflyIII\Http\Requests\RuleFormRequest; use FireflyIII\Http\Requests\TestRuleFormRequest; use FireflyIII\Support\Binder\AccountList; use FireflyIII\User; -use Hash; use Illuminate\Contracts\Validation\Validator as ValidatorContract; use Illuminate\Routing\Route; use Illuminate\Support\Facades\Validator; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; use Route as RouteFacade; /** * Trait RequestInformation - * */ trait RequestInformation { /** * Get the domain of FF system. - * - * @return string */ final protected function getDomain(): string // get request info { @@ -59,10 +52,6 @@ trait RequestInformation /** * Get a list of triggers. - * - * @param TestRuleFormRequest $request - * - * @return array */ final protected function getValidTriggerList(TestRuleFormRequest $request): array // process input { @@ -80,15 +69,12 @@ trait RequestInformation $triggers[] = $current; } } + return $triggers; } /** * Returns if user has seen demo. - * - * @return bool - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ final protected function hasSeenDemo(): bool // get request info + get preference { @@ -113,9 +99,6 @@ trait RequestInformation return $shownDemo; } - /** - * @return string - */ final protected function getPageName(): string // get request info { return str_replace('.', '_', RouteFacade::currentRouteName()); @@ -123,28 +106,23 @@ trait RequestInformation /** * Get the specific name of a page for intro. - * - * @return string */ final protected function getSpecificPageName(): string // get request info { - /** @var string|null $param */ + /** @var null|string $param */ $param = RouteFacade::current()->parameter('objectType'); + return null === $param ? '' : sprintf('_%s', $param); } /** * Check if date is outside session range. - * - * @param Carbon $date - * - * @return bool - * */ final protected function notInSessionRange(Carbon $date): bool // Validate a preference { /** @var Carbon $start */ $start = session('start', today(config('app.timezone'))->startOfMonth()); + /** @var Carbon $end */ $end = session('end', today(config('app.timezone'))->endOfMonth()); $result = false; @@ -161,10 +139,6 @@ trait RequestInformation /** * Parses attributes from URL - * - * @param array $attributes - * - * @return array */ final protected function parseAttributes(array $attributes): array // parse input + return result { @@ -184,24 +158,17 @@ trait RequestInformation $date2->endOfDay(); $attributes['endDate'] = $date2; - return $attributes; } /** * Validate users new password. * - * @param User $user - * @param string $current - * @param string $new - * - * @return bool - * * @throws ValidationException */ - final protected function validatePassword(User $user, string $current, string $new): bool //get request info + final protected function validatePassword(User $user, string $current, string $new): bool // get request info { - if (!Hash::check($current, $user->password)) { + if (!\Hash::check($current, $user->password)) { throw new ValidationException((string)trans('firefly.invalid_current_password')); } @@ -214,10 +181,6 @@ trait RequestInformation /** * Get a validator for an incoming registration request. - * - * @param array $data - * - * @return ValidatorContract */ final protected function validator(array $data): ValidatorContract { diff --git a/app/Support/Http/Controllers/RuleManagement.php b/app/Support/Http/Controllers/RuleManagement.php index 5d06fd0fd1..f3aae1afc0 100644 --- a/app/Support/Http/Controllers/RuleManagement.php +++ b/app/Support/Http/Controllers/RuleManagement.php @@ -27,18 +27,13 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use FireflyIII\Support\Search\OperatorQuerySearch; use Illuminate\Http\Request; -use Throwable; /** * Trait RuleManagement - * */ trait RuleManagement { /** - * @param Request $request - * - * @return array * @throws FireflyException */ protected function getPreviousActions(Request $request): array @@ -58,12 +53,13 @@ trait RuleManagement 'count' => $index + 1, ] )->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->error(sprintf('Throwable was thrown in getPreviousActions(): %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); + throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e); } - $index++; + ++$index; } } @@ -71,9 +67,6 @@ trait RuleManagement } /** - * @param Request $request - * - * @return array * @throws FireflyException */ protected function getPreviousTriggers(Request $request): array @@ -98,19 +91,20 @@ trait RuleManagement 'rules.partials.trigger', [ 'oldTrigger' => OperatorQuerySearch::getRootOperator($oldTrigger['type']), - 'oldValue' => $oldTrigger['value'], + 'oldValue' => $oldTrigger['value'] ?? '', 'oldChecked' => 1 === (int)($oldTrigger['stop_processing'] ?? '0'), 'oldProhibited' => 1 === (int)($oldTrigger['prohibited'] ?? '0'), 'count' => $index + 1, 'triggers' => $triggers, ] )->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); + throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e); } - $index++; + ++$index; } } @@ -118,9 +112,6 @@ trait RuleManagement } /** - * @param array $submittedOperators - * - * @return array * @throws FireflyException */ protected function parseFromOperators(array $submittedOperators): array @@ -140,6 +131,7 @@ trait RuleManagement foreach ($submittedOperators as $operator) { $rootOperator = OperatorQuerySearch::getRootOperator($operator['type']); $needsContext = (bool)config(sprintf('search.operators.%s.needs_context', $rootOperator)); + try { $renderedEntries[] = view( 'rules.partials.trigger', @@ -152,20 +144,18 @@ trait RuleManagement 'triggers' => $triggers, ] )->render(); - } catch (Throwable $e) { + } catch (\Throwable $e) { app('log')->debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); + throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e); } - $index++; + ++$index; } return $renderedEntries; } - /** - * - */ private function createDefaultRuleGroup(): void { /** @var RuleGroupRepositoryInterface $repository */ diff --git a/app/Support/Http/Controllers/TransactionCalculation.php b/app/Support/Http/Controllers/TransactionCalculation.php index c17c26a7a5..e43fa9f847 100644 --- a/app/Support/Http/Controllers/TransactionCalculation.php +++ b/app/Support/Http/Controllers/TransactionCalculation.php @@ -30,19 +30,11 @@ use Illuminate\Support\Collection; /** * Trait TransactionCalculation - * */ trait TransactionCalculation { /** * Get all expenses for a set of accounts. - * - * @param Collection $accounts - * @param Collection $opposing - * @param Carbon $start - * @param Carbon $end - * - * @return array */ protected function getExpensesForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): array { @@ -51,23 +43,16 @@ trait TransactionCalculation /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts($total) - ->setRange($start, $end) - ->withAccountInformation() - ->setTypes([TransactionType::WITHDRAWAL]); + ->setRange($start, $end) + ->withAccountInformation() + ->setTypes([TransactionType::WITHDRAWAL]) + ; return $collector->getExtractedJournals(); } /** * Get all expenses by tags. - * - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * - * @return array - * */ protected function getExpensesForTags(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): array { @@ -75,40 +60,28 @@ trait TransactionCalculation $collector = app(GroupCollectorInterface::class); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) - ->setTags($tags)->withAccountInformation(); + ->setTags($tags)->withAccountInformation() + ; return $collector->getExtractedJournals(); } /** * Helper function that collects expenses for the given budgets. - * - * @param Collection $accounts - * @param Collection $budgets - * @param Carbon $start - * @param Carbon $end - * - * @return array */ protected function getExpensesInBudgets(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end): array { /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) - ->setBudgets($budgets)->withAccountInformation(); + ->setBudgets($budgets)->withAccountInformation() + ; return $collector->getExtractedJournals(); } /** * Get all expenses in a period for categories. - * - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * - * @return array */ protected function getExpensesInCategories(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): array { @@ -119,44 +92,33 @@ trait TransactionCalculation ->setRange($start, $end) ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) ->setCategories($categories) - ->withAccountInformation(); + ->withAccountInformation() + ; return $collector->getExtractedJournals(); } /** * Get all income for a period and a bunch of categories. - * - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * - * @return array */ protected function getIncomeForCategories(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): array { /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) - ->setCategories($categories)->withAccountInformation(); + ->setCategories($categories)->withAccountInformation() + ; return $collector->getExtractedJournals(); } /** * Get the income for a set of accounts. - * - * @param Collection $accounts - * @param Collection $opposing - * @param Carbon $start - * @param Carbon $end - * - * @return array */ protected function getIncomeForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): array { $total = $accounts->merge($opposing); + /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts($total)->setRange($start, $end)->withAccountInformation()->setTypes([TransactionType::DEPOSIT]); @@ -166,20 +128,14 @@ trait TransactionCalculation /** * Get all income by tag. - * - * @param Collection $accounts - * @param Collection $tags - * @param Carbon $start - * @param Carbon $end - * - * @return array */ protected function getIncomeForTags(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): array { /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) - ->setTags($tags)->withAccountInformation(); + ->setTags($tags)->withAccountInformation() + ; return $collector->getExtractedJournals(); } diff --git a/app/Support/Http/Controllers/UserNavigation.php b/app/Support/Http/Controllers/UserNavigation.php index e6fbf90b48..f60fc97bd3 100644 --- a/app/Support/Http/Controllers/UserNavigation.php +++ b/app/Support/Http/Controllers/UserNavigation.php @@ -34,7 +34,6 @@ use Illuminate\Routing\Redirector; /** * Trait UserNavigation - * */ trait UserNavigation { @@ -45,10 +44,6 @@ trait UserNavigation * returned but instead the index (/) will be returned. * - If the remembered url contains "jscript/" the remembered url will not be returned but instead the index (/) * will be returned. - * - * @param string $identifier - * - * @return string */ final protected function getPreviousUrl(string $identifier): string { @@ -61,10 +56,6 @@ trait UserNavigation /** * Will return false if you cant edit this account type. - * - * @param Account $account - * - * @return bool */ final protected function isEditableAccount(Account $account): bool { @@ -74,14 +65,9 @@ trait UserNavigation return in_array($type, $editable, true); } - /** - * @param TransactionGroup $group - * - * @return bool - */ final protected function isEditableGroup(TransactionGroup $group): bool { - /** @var TransactionJournal|null $journal */ + /** @var null|TransactionJournal $journal */ $journal = $group->transactionJournals()->first(); if (null === $journal) { return false; @@ -93,9 +79,7 @@ trait UserNavigation } /** - * @param Account $account - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ final protected function redirectAccountToAccount(Account $account) { @@ -103,7 +87,7 @@ trait UserNavigation if (AccountType::RECONCILIATION === $type || AccountType::INITIAL_BALANCE === $type || AccountType::LIABILITY_CREDIT === $type) { // reconciliation must be stored somewhere in this account's transactions. - /** @var Transaction|null $transaction */ + /** @var null|Transaction $transaction */ $transaction = $account->transactions()->first(); if (null === $transaction) { app('log')->error(sprintf('Account #%d has no transactions. Dont know where it belongs.', $account->id)); @@ -112,7 +96,8 @@ trait UserNavigation return redirect(route('index')); } $journal = $transaction->transactionJournal; - /** @var Transaction|null $other */ + + /** @var null|Transaction $other */ $other = $journal->transactions()->where('id', '!=', $transaction->id)->first(); if (null === $other) { app('log')->error(sprintf('Account #%d has no valid journals. Dont know where it belongs.', $account->id)); @@ -128,13 +113,11 @@ trait UserNavigation } /** - * @param TransactionGroup $group - * - * @return RedirectResponse|Redirector + * @return Redirector|RedirectResponse */ final protected function redirectGroupToAccount(TransactionGroup $group) { - /** @var TransactionJournal|null $journal */ + /** @var null|TransactionJournal $journal */ $journal = $group->transactionJournals()->first(); if (null === $journal) { app('log')->error(sprintf('No journals in group #%d', $group->id)); @@ -144,6 +127,7 @@ trait UserNavigation // prefer redirect to everything but expense and revenue: $transactions = $journal->transactions; $ignore = [AccountType::REVENUE, AccountType::EXPENSE, AccountType::RECONCILIATION, AccountType::INITIAL_BALANCE]; + /** @var Transaction $transaction */ foreach ($transactions as $transaction) { $type = $transaction->account->accountType->type; @@ -155,11 +139,6 @@ trait UserNavigation return redirect(route('index')); } - /** - * @param string $identifier - * - * @return string|null - */ final protected function rememberPreviousUrl(string $identifier): ?string { $return = app('steam')->getSafePreviousUrl(); diff --git a/app/Support/Logging/AuditLogger.php b/app/Support/Logging/AuditLogger.php index eeac766f53..ff40619bfe 100644 --- a/app/Support/Logging/AuditLogger.php +++ b/app/Support/Logging/AuditLogger.php @@ -30,21 +30,16 @@ use Monolog\Handler\AbstractProcessingHandler; /** * Class AuditLogger - * - */ class AuditLogger { /** * Customize the given logger instance. - * - * @param Logger $logger - * - * @return void */ - public function __invoke(Logger $logger) + public function __invoke(Logger $logger): void { $processor = new AuditProcessor(); + /** @var AbstractProcessingHandler $handler */ foreach ($logger->getHandlers() as $handler) { $formatter = new LineFormatter("[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"); diff --git a/app/Support/Logging/AuditProcessor.php b/app/Support/Logging/AuditProcessor.php index d23d8b32fe..5f8f823c13 100644 --- a/app/Support/Logging/AuditProcessor.php +++ b/app/Support/Logging/AuditProcessor.php @@ -28,16 +28,9 @@ use Monolog\LogRecord; /** * Class AuditProcessor - * - */ class AuditProcessor { - /** - * @param LogRecord $record - * - * @return LogRecord - */ public function __invoke(LogRecord $record): LogRecord { if (auth()->check()) { @@ -49,6 +42,7 @@ class AuditProcessor request()->method(), request()->url() ); + return new LogRecord($record->datetime, $record->channel, $record->level, $message, $record->context, $record->extra, $record->formatted); } diff --git a/app/Support/Models/BillDateCalculator.php b/app/Support/Models/BillDateCalculator.php index c64cee19f0..e2683a4a8f 100644 --- a/app/Support/Models/BillDateCalculator.php +++ b/app/Support/Models/BillDateCalculator.php @@ -32,23 +32,17 @@ class BillDateCalculator /** * Returns the dates a bill needs to be paid. * - * @param Carbon $earliest - * @param Carbon $latest - * @param Carbon $billStart - * @param string $period - * @param int $skip - * @param Carbon|null $lastPaid - * - * @return array * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function getPayDates(Carbon $earliest, Carbon $latest, Carbon $billStart, string $period, int $skip, ?Carbon $lastPaid): array { + $earliest->startOfDay(); + $latest->endOfDay(); + $billStart->startOfDay(); Log::debug('Now in BillDateCalculator::getPayDates()'); Log::debug(sprintf('Dates must be between %s and %s.', $earliest->format('Y-m-d'), $latest->format('Y-m-d'))); Log::debug(sprintf('Bill started on %s, period is "%s", skip is %d, last paid = "%s".', $billStart->format('Y-m-d'), $period, $skip, $lastPaid?->format('Y-m-d'))); - $set = new Collection(); $currentStart = clone $earliest; @@ -66,10 +60,12 @@ class BillDateCalculator Log::debug('Next expected match is after $latest.'); if ($set->count() > 0) { Log::debug(sprintf('Already have %d date(s), so we can safely break.', $set->count())); + break; } Log::debug('Add date to set anyway, since we had no dates yet.'); $set->push(clone $nextExpectedMatch); + continue; } @@ -88,9 +84,10 @@ class BillDateCalculator $nextExpectedMatch->addDay(); $currentStart = clone $nextExpectedMatch; - $loop++; + ++$loop; if ($loop > 12) { Log::debug('Loop is more than 12, so we break.'); + break; } } @@ -103,7 +100,6 @@ class BillDateCalculator Log::debug(sprintf('Found %d pay dates', $set->count()), $simple->toArray()); return $simple->toArray(); - } /** @@ -111,31 +107,25 @@ class BillDateCalculator * transaction given the earliest date this could happen. * * That date must be AFTER $billStartDate, as a sanity check. - * - * @param Carbon $earliest - * @param Carbon $billStartDate - * @param string $period - * @param int $skip - * - * @return Carbon */ protected function nextDateMatch(Carbon $earliest, Carbon $billStartDate, string $period, int $skip): Carbon { Log::debug(sprintf('Bill start date is %s', $billStartDate->format('Y-m-d'))); if ($earliest->lt($billStartDate)) { Log::debug('Earliest possible date is after bill start, so just return bill start date.'); + return $billStartDate; } $steps = app('navigation')->diffInPeriods($period, $skip, $earliest, $billStartDate); $result = clone $billStartDate; if ($steps > 0) { - $steps -= 1; + --$steps; Log::debug(sprintf('Steps is %d, because addPeriod already adds 1.', $steps)); $result = app('navigation')->addPeriod($billStartDate, $period, $steps); } Log::debug(sprintf('Number of steps is %d, added to %s, result is %s', $steps, $billStartDate->format('Y-m-d'), $result->format('Y-m-d'))); + return $result; } - } diff --git a/app/Support/Models/ReturnsIntegerIdTrait.php b/app/Support/Models/ReturnsIntegerIdTrait.php index d84925c07f..5d0d501b53 100644 --- a/app/Support/Models/ReturnsIntegerIdTrait.php +++ b/app/Support/Models/ReturnsIntegerIdTrait.php @@ -32,14 +32,13 @@ trait ReturnsIntegerIdTrait { /** * Get the ID - * @SuppressWarnings(PHPMD.ShortMethodName) * - * @return Attribute + * @SuppressWarnings(PHPMD.ShortMethodName) */ protected function id(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Support/Models/ReturnsIntegerUserIdTrait.php b/app/Support/Models/ReturnsIntegerUserIdTrait.php index 08e4d30484..77f1d8d18f 100644 --- a/app/Support/Models/ReturnsIntegerUserIdTrait.php +++ b/app/Support/Models/ReturnsIntegerUserIdTrait.php @@ -32,23 +32,18 @@ trait ReturnsIntegerUserIdTrait { /** * Get the user group ID - * - * @return Attribute */ protected function userGroupId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } - /** - * @return Attribute - */ protected function userId(): Attribute { return Attribute::make( - get: static fn($value) => (int)$value, + get: static fn ($value) => (int)$value, ); } } diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index 712deef93d..cb37988a42 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -30,9 +30,6 @@ use FireflyIII\Helpers\Fiscal\FiscalHelperInterface; use FireflyIII\Support\Calendar\Calculator; use FireflyIII\Support\Calendar\Periodicity; use Illuminate\Support\Facades\Log; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; -use Throwable; /** * Class Navigation. @@ -41,21 +38,11 @@ class Navigation { private Calculator $calculator; - /** - * @param Calculator|null $calculator - */ public function __construct(Calculator $calculator = null) { $this->calculator = $calculator instanceof Calculator ? $calculator : new Calculator(); } - /** - * @param Carbon $theDate - * @param string $repeatFreq - * @param int $skip - * - * @return Carbon - */ public function addPeriod(Carbon $theDate, string $repeatFreq, int $skip = 0): Carbon { $date = clone $theDate; @@ -93,26 +80,20 @@ class Navigation $repeatFreq, implode(', ', array_keys($functionMap)) )); + return $theDate; } return $this->nextDateByInterval($date, $functionMap[$repeatFreq], $skip); } - /** - * @param Carbon $epoch - * @param Periodicity $periodicity - * @param int $skipInterval - * - * @return Carbon - */ public function nextDateByInterval(Carbon $epoch, Periodicity $periodicity, int $skipInterval = 0): Carbon { try { return $this->calculator->nextDateByInterval($epoch, $periodicity, $skipInterval); } catch (IntervalException $exception) { Log::warning($exception->getMessage(), ['exception' => $exception]); - } catch (Throwable $exception) { // @phpstan-ignore-line + } catch (\Throwable $exception) { // @phpstan-ignore-line Log::error($exception->getMessage(), ['exception' => $exception]); } @@ -124,14 +105,6 @@ class Navigation return $epoch; } - /** - * @param Carbon $start - * @param Carbon $end - * @param string $range - * - * @return array - * - */ public function blockPeriods(Carbon $start, Carbon $end, string $range): array { if ($end < $start) { @@ -158,7 +131,7 @@ class Navigation } // skip to the next period: $workStart->subDay()->startOfDay(); - $loopCount++; + ++$loopCount; } // if $workEnd is still before $start, continue on a yearly basis: $loopCount = 0; @@ -178,19 +151,13 @@ class Navigation } // skip to the next period: $workStart->subDay()->startOfDay(); - $loopCount++; + ++$loopCount; } } return $periods; } - /** - * @param Carbon $theDate - * @param string $repeatFreq - * - * @return Carbon - */ public function startOfPeriod(Carbon $theDate, string $repeatFreq): Carbon { $date = clone $theDate; @@ -213,7 +180,7 @@ class Navigation ]; if (array_key_exists($repeatFreq, $functionMap)) { $function = $functionMap[$repeatFreq]; - $date->$function(); // @phpstan-ignore-line + $date->{$function}(); // @phpstan-ignore-line return $date; } @@ -238,7 +205,6 @@ class Navigation return $result; } - if ('custom' === $repeatFreq) { return $date; // the date is already at the start. } @@ -247,12 +213,6 @@ class Navigation return $theDate; } - /** - * @param Carbon $end - * @param string $repeatFreq - * - * @return Carbon - */ public function endOfPeriod(Carbon $end, string $repeatFreq): Carbon { $currentEnd = clone $end; @@ -293,6 +253,7 @@ class Navigation if ('custom' === $repeatFreq) { /** @var Carbon $tStart */ $tStart = session('start', today(config('app.timezone'))->startOfMonth()); + /** @var Carbon $tEnd */ $tEnd = session('end', today(config('app.timezone'))->endOfMonth()); $diffInDays = $tStart->diffInDays($tEnd); @@ -316,7 +277,6 @@ class Navigation } unset($result); - if (!array_key_exists($repeatFreq, $functionMap)) { Log::error(sprintf('Cannot do endOfPeriod for $repeat_freq "%s"', $repeatFreq)); @@ -325,7 +285,7 @@ class Navigation $function = $functionMap[$repeatFreq]; if (array_key_exists($repeatFreq, $modifierMap)) { - $currentEnd->$function($modifierMap[$repeatFreq]); // @phpstan-ignore-line + $currentEnd->{$function}($modifierMap[$repeatFreq]); // @phpstan-ignore-line if (in_array($repeatFreq, $subDay, true)) { $currentEnd->subDay(); } @@ -333,7 +293,7 @@ class Navigation return $currentEnd; } - $currentEnd->$function(); // @phpstan-ignore-line + $currentEnd->{$function}(); // @phpstan-ignore-line $currentEnd->endOfDay(); if (in_array($repeatFreq, $subDay, true)) { $currentEnd->subDay(); @@ -342,13 +302,6 @@ class Navigation return $currentEnd; } - /** - * @param string $period - * @param Carbon $beginning - * @param Carbon $end - * - * @return int - */ public function diffInPeriods(string $period, int $skip, Carbon $beginning, Carbon $end): int { Log::debug(sprintf( @@ -368,12 +321,12 @@ class Navigation ]; if (!array_key_exists($period, $map)) { Log::warning(sprintf('No diffInPeriods for period "%s"', $period)); + return 1; } $func = $map[$period]; // first do the diff - $floatDiff = $beginning->$func($end); // @phpstan-ignore-line - + $floatDiff = $beginning->{$func}($end); // @phpstan-ignore-line // then correct for quarterly or half-year if ('quarterly' === $period) { @@ -404,13 +357,6 @@ class Navigation return (int)$diff; } - /** - * @param Carbon $theCurrentEnd - * @param string $repeatFreq - * @param Carbon|null $maxDate - * - * @return Carbon - */ public function endOfX(Carbon $theCurrentEnd, string $repeatFreq, ?Carbon $maxDate): Carbon { $functionMap = [ @@ -434,7 +380,7 @@ class Navigation if (array_key_exists($repeatFreq, $functionMap)) { $function = $functionMap[$repeatFreq]; - $currentEnd->$function(); // @phpstan-ignore-line + $currentEnd->{$function}(); // @phpstan-ignore-line } if (null !== $maxDate && $currentEnd > $maxDate) { @@ -447,12 +393,6 @@ class Navigation /** * Returns the user's view range and if necessary, corrects the dynamic view * range to a normal range. - * - * @param bool $correct - * - * @return string - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function getViewRange(bool $correct): string { @@ -464,17 +404,22 @@ class Navigation if (!$correct) { return $range; } + switch ($range) { default: return $range; + case 'last7': return '1W'; + case 'last30': case 'MTD': return '1M'; + case 'last90': case 'QTD': return '3M'; + case 'last365': case 'YTD': return '1Y'; @@ -482,10 +427,6 @@ class Navigation } /** - * @param Carbon $start - * @param Carbon $end - * - * @return array * @throws FireflyException */ public function listOfPeriods(Carbon $start, Carbon $end): array @@ -512,7 +453,7 @@ class Navigation $formatted = $begin->format($format); $displayed = $begin->isoFormat($displayFormat); $entries[$formatted] = $displayed; - $begin->$increment(); // @phpstan-ignore-line + $begin->{$increment}(); // @phpstan-ignore-line } return $entries; @@ -521,11 +462,6 @@ class Navigation /** * If the date difference between start and end is less than a month, method returns "Y-m-d". If the difference is * less than a year, method returns "Y-m". If the date difference is larger, method returns "Y". - * - * @param Carbon $start - * @param Carbon $end - * - * @return string */ public function preferredCarbonFormat(Carbon $start, Carbon $end): string { @@ -541,12 +477,6 @@ class Navigation return $format; } - /** - * @param Carbon $theDate - * @param string $repeatFrequency - * - * @return string - */ public function periodShow(Carbon $theDate, string $repeatFrequency): string { $date = clone $theDate; @@ -583,16 +513,12 @@ class Navigation /** * Same as preferredCarbonFormat but by string - * - * @param string $period - * - * @return string */ public function preferredCarbonFormatByPeriod(string $period): string { return match ($period) { default => 'Y-m-d', - //'1D' => 'Y-m-d', + // '1D' => 'Y-m-d', '1W' => '\WW,Y', '1M' => 'Y-m', '3M', '6M' => '\QQ,Y', @@ -604,11 +530,6 @@ class Navigation * If the date difference between start and end is less than a month, method returns trans(config.month_and_day). * If the difference is less than a year, method returns "config.month". If the date difference is larger, method * returns "config.year". - * - * @param Carbon $start - * @param Carbon $end - * - * @return string */ public function preferredCarbonLocalizedFormat(Carbon $start, Carbon $end): string { @@ -628,11 +549,6 @@ class Navigation /** * If the date difference between start and end is less than a month, method returns "endOfDay". If the difference * is less than a year, method returns "endOfMonth". If the date difference is larger, method returns "endOfYear". - * - * @param Carbon $start - * @param Carbon $end - * - * @return string */ public function preferredEndOfPeriod(Carbon $start, Carbon $end): string { @@ -651,11 +567,6 @@ class Navigation /** * If the date difference between start and end is less than a month, method returns "1D". If the difference is * less than a year, method returns "1M". If the date difference is larger, method returns "1Y". - * - * @param Carbon $start - * @param Carbon $end - * - * @return string */ public function preferredRangeFormat(Carbon $start, Carbon $end): string { @@ -674,11 +585,6 @@ class Navigation /** * If the date difference between start and end is less than a month, method returns "%Y-%m-%d". If the difference * is less than a year, method returns "%Y-%m". If the date difference is larger, method returns "%Y". - * - * @param Carbon $start - * @param Carbon $end - * - * @return string */ public function preferredSqlFormat(Carbon $start, Carbon $end): string { @@ -695,12 +601,6 @@ class Navigation } /** - * @param Carbon $theDate - * @param string $repeatFreq - * @param int|null $subtract - * - * @return Carbon - * * @throws FireflyException */ public function subtractPeriod(Carbon $theDate, string $repeatFreq, int $subtract = null): Carbon @@ -730,7 +630,7 @@ class Navigation ]; if (array_key_exists($repeatFreq, $functionMap)) { $function = $functionMap[$repeatFreq]; - $date->$function($subtract); // @phpstan-ignore-line + $date->{$function}($subtract); // @phpstan-ignore-line return $date; } @@ -746,6 +646,7 @@ class Navigation if ('custom' === $repeatFreq) { /** @var Carbon $tStart */ $tStart = session('start', today(config('app.timezone'))->startOfMonth()); + /** @var Carbon $tEnd */ $tEnd = session('end', today(config('app.timezone'))->endOfMonth()); $diffInDays = $tStart->diffInDays($tEnd); @@ -753,42 +654,51 @@ class Navigation return $date; } + switch ($repeatFreq) { default: break; + case 'last7': $date->subDays(7); + return $date; + case 'last30': $date->subDays(30); + return $date; + case 'last90': $date->subDays(90); + return $date; + case 'last365': $date->subDays(365); + return $date; + case 'YTD': $date->subYear(); + return $date; + case 'QTD': $date->subQuarter(); + return $date; + case 'MTD': $date->subMonth(); + return $date; } - throw new FireflyException(sprintf('Cannot do subtractPeriod for $repeat_freq "%s"', $repeatFreq)); } /** - * @param string $range - * @param Carbon $start - * - * @return Carbon - * * @throws FireflyException */ public function updateEndDate(string $range, Carbon $start): Carbon @@ -805,7 +715,7 @@ class Navigation if (array_key_exists($range, $functionMap)) { $function = $functionMap[$range]; - $end->$function(); // @phpstan-ignore-line + $end->{$function}(); // @phpstan-ignore-line return $end; } @@ -840,6 +750,7 @@ class Navigation $end = today(config('app.timezone')); $end->endOfDay(); Log::debug(sprintf('updateEndDate returns "%s"', $end->format('Y-m-d'))); + return $end; } @@ -847,11 +758,6 @@ class Navigation } /** - * @param string $range - * @param Carbon $start - * - * @return Carbon - * * @throws FireflyException */ public function updateStartDate(string $range, Carbon $start): Carbon @@ -866,7 +772,7 @@ class Navigation ]; if (array_key_exists($range, $functionMap)) { $function = $functionMap[$range]; - $start->$function(); // @phpstan-ignore-line + $start->{$function}(); // @phpstan-ignore-line return $start; } @@ -888,31 +794,47 @@ class Navigation return $fiscalHelper->startOfFiscalYear($start); } + switch ($range) { default: break; + case 'last7': $start->subDays(7); + return $start; + case 'last30': $start->subDays(30); + return $start; + case 'last90': $start->subDays(90); + return $start; + case 'last365': $start->subDays(365); + return $start; + case 'YTD': $start->startOfYear(); + return $start; + case 'QTD': $start->startOfQuarter(); + return $start; + case 'MTD': $start->startOfMonth(); + return $start; } + throw new FireflyException(sprintf('updateStartDate cannot handle range "%s"', $range)); } } diff --git a/app/Support/Notifications/UrlValidator.php b/app/Support/Notifications/UrlValidator.php index 5f6106ce9d..355f45e4ad 100644 --- a/app/Support/Notifications/UrlValidator.php +++ b/app/Support/Notifications/UrlValidator.php @@ -1,6 +1,5 @@ parseDefaultDate($date); } @@ -110,23 +105,64 @@ class ParseDateString // maybe a date range if (10 === strlen($date) && (str_contains($date, 'xx') || str_contains($date, 'xxxx'))) { app('log')->debug(sprintf('[c] Detected a date range ("%s"), return a fake date.', $date)); + // very lazy way to parse the date without parsing it, because this specific function // cant handle date ranges. return new Carbon('1984-09-17'); } // maybe a year, nothing else? - if (4 === strlen($date) && is_numeric($date) && (int)$date > 1000 && (int)$date <= 3000) { + if (4 === strlen($date) && is_numeric($date) && (int) $date > 1000 && (int) $date <= 3000) { return new Carbon(sprintf('%d-01-01', $date)); } throw new FireflyException(sprintf('[d] Not a recognised date format: "%s"', $date)); } - /** - * @param string $keyword - * - * @return Carbon - */ + public function parseRange(string $date): array + { + // several types of range can be submitted + $result = [ + 'exact' => new Carbon('1984-09-17'), + ]; + + switch (true) { + default: + break; + + case $this->isDayRange($date): + $result = $this->parseDayRange($date); + + break; + + case $this->isMonthRange($date): + $result = $this->parseMonthRange($date); + + break; + + case $this->isYearRange($date): + $result = $this->parseYearRange($date); + + break; + + case $this->isMonthDayRange($date): + $result = $this->parseMonthDayRange($date); + + break; + + case $this->isDayYearRange($date): + $result = $this->parseDayYearRange($date); + + break; + + case $this->isMonthYearRange($date): + $result = $this->parseMonthYearRange($date); + + break; + } + + return $result; + } + protected function parseKeyword(string $keyword): Carbon { $today = today(config('app.timezone'))->startOfDay(); @@ -146,14 +182,10 @@ class ParseDateString }; } - /** - * @param string $date - * - * @return Carbon - */ protected function parseDefaultDate(string $date): Carbon { $result = false; + try { $result = Carbon::createFromFormat('Y-m-d', $date); } catch (InvalidFormatException $e) { // @phpstan-ignore-line @@ -162,14 +194,10 @@ class ParseDateString if (false === $result) { $result = today(config('app.timezone'))->startOfDay(); } + return $result; } - /** - * @param string $date - * - * @return Carbon - */ protected function parseRelativeDate(string $date): Carbon { app('log')->debug(sprintf('Now in parseRelativeDate("%s")', $date)); @@ -198,21 +226,23 @@ class ParseDateString // verify if correct $pattern = '/[+-]\d+[wqmdy]/'; - $res = preg_match($pattern, $part); - if (0 === $res || false === $res) { + $result = preg_match($pattern, $part); + if (0 === $result || false === $result) { app('log')->error(sprintf('Part "%s" does not match regular expression. Will be skipped.', $part)); + continue; } $direction = str_starts_with($part, '+') ? 1 : 0; $period = $part[strlen($part) - 1]; - $number = (int)substr($part, 1, -1); + $number = (int) substr($part, 1, -1); if (!array_key_exists($period, $functions[$direction])) { app('log')->error(sprintf('No method for direction %d and period "%s".', $direction, $period)); + continue; } $func = $functions[$direction][$period]; app('log')->debug(sprintf('Will now do %s(%d) on %s', $func, $number, $today->format('Y-m-d'))); - $today->$func($number); // @phpstan-ignore-line + $today->{$func}($number); // @phpstan-ignore-line app('log')->debug(sprintf('Resulting date is %s', $today->format('Y-m-d'))); } @@ -220,52 +250,13 @@ class ParseDateString } /** - * @param string $date - * - * @return array - */ - public function parseRange(string $date): array - { - // several types of range can be submitted - $result = [ - 'exact' => new Carbon('1984-09-17'), - ]; - switch (true) { - default: - break; - case $this->isDayRange($date): - $result = $this->parseDayRange($date); - break; - case $this->isMonthRange($date): - $result = $this->parseMonthRange($date); - break; - case $this->isYearRange($date): - $result = $this->parseYearRange($date); - break; - case $this->isMonthDayRange($date): - $result = $this->parseMonthDayRange($date); - break; - case $this->isDayYearRange($date): - $result = $this->parseDayYearRange($date); - break; - case $this->isMonthYearRange($date): - $result = $this->parseMonthYearRange($date); - break; - } - - return $result; - } - - /** - * @param string $date - * - * @return bool + * Returns true if this matches regex for xxxx-xx-DD: */ protected function isDayRange(string $date): bool { - // if regex for xxxx-xx-DD: $pattern = '/^xxxx-xx-(0[1-9]|[12]\d|3[01])$/'; - if (false !== preg_match($pattern, $date)) { + $result = preg_match($pattern, $date); + if (false !== $result && 0 !== $result) { app('log')->debug(sprintf('"%s" is a day range.', $date)); return true; @@ -277,10 +268,6 @@ class ParseDateString /** * format of string is xxxx-xx-DD - * - * @param string $date - * - * @return array */ protected function parseDayRange(string $date): array { @@ -291,16 +278,12 @@ class ParseDateString ]; } - /** - * @param string $date - * - * @return bool - */ protected function isMonthRange(string $date): bool { // if regex for xxxx-MM-xx: $pattern = '/^xxxx-(0[1-9]|1[012])-xx$/'; - if (false !== preg_match($pattern, $date)) { + $result = preg_match($pattern, $date); + if (false !== $result && 0 !== $result) { app('log')->debug(sprintf('"%s" is a month range.', $date)); return true; @@ -312,10 +295,6 @@ class ParseDateString /** * format of string is xxxx-MM-xx - * - * @param string $date - * - * @return array */ protected function parseMonthRange(string $date): array { @@ -327,16 +306,12 @@ class ParseDateString ]; } - /** - * @param string $date - * - * @return bool - */ protected function isYearRange(string $date): bool { // if regex for YYYY-xx-xx: $pattern = '/^(19|20)\d\d-xx-xx$/'; - if (false !== preg_match($pattern, $date)) { + $result = preg_match($pattern, $date); + if (false !== $result && 0 !== $result) { app('log')->debug(sprintf('"%s" is a year range.', $date)); return true; @@ -348,10 +323,6 @@ class ParseDateString /** * format of string is YYYY-xx-xx - * - * @param string $date - * - * @return array */ protected function parseYearRange(string $date): array { @@ -363,16 +334,12 @@ class ParseDateString ]; } - /** - * @param string $date - * - * @return bool - */ protected function isMonthDayRange(string $date): bool { // if regex for xxxx-MM-DD: $pattern = '/^xxxx-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])$/'; - if (false !== preg_match($pattern, $date)) { + $result = preg_match($pattern, $date); + if (false !== $result && 0 !== $result) { app('log')->debug(sprintf('"%s" is a month/day range.', $date)); return true; @@ -382,12 +349,52 @@ class ParseDateString return false; } + protected function isDayYearRange(string $date): bool + { + // if regex for YYYY-xx-DD: + $pattern = '/^(19|20)\d\d-xx-(0[1-9]|[12]\d|3[01])$/'; + $result = preg_match($pattern, $date); + if (false !== $result && 0 !== $result) { + app('log')->debug(sprintf('"%s" is a day/year range.', $date)); + + return true; + } + app('log')->debug(sprintf('"%s" is not a day/year range.', $date)); + + return false; + } + + protected function isMonthYearRange(string $date): bool + { + // if regex for YYYY-MM-xx: + $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-xx$/'; + $result = preg_match($pattern, $date); + if (false !== $result && 0 !== $result) { + app('log')->debug(sprintf('"%s" is a month/year range.', $date)); + + return true; + } + app('log')->debug(sprintf('"%s" is not a month/year range.', $date)); + + return false; + } + + /** + * format of string is YYYY-MM-xx + */ + protected function parseMonthYearRange(string $date): array + { + app('log')->debug(sprintf('parseMonthYearRange: Parsed "%s".', $date)); + $parts = explode('-', $date); + + return [ + 'year' => $parts[0], + 'month' => $parts[1], + ]; + } + /** * format of string is xxxx-MM-DD - * - * @param string $date - * - * @return array */ private function parseMonthDayRange(string $date): array { @@ -400,31 +407,8 @@ class ParseDateString ]; } - /** - * @param string $date - * - * @return bool - */ - protected function isDayYearRange(string $date): bool - { - // if regex for YYYY-xx-DD: - $pattern = '/^(19|20)\d\d-xx-(0[1-9]|[12]\d|3[01])$/'; - if (false !== preg_match($pattern, $date)) { - app('log')->debug(sprintf('"%s" is a day/year range.', $date)); - - return true; - } - app('log')->debug(sprintf('"%s" is not a day/year range.', $date)); - - return false; - } - /** * format of string is YYYY-xx-DD - * - * @param string $date - * - * @return array */ private function parseDayYearRange(string $date): array { @@ -436,41 +420,4 @@ class ParseDateString 'day' => $parts[2], ]; } - - /** - * @param string $date - * - * @return bool - */ - protected function isMonthYearRange(string $date): bool - { - // if regex for YYYY-MM-xx: - $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-xx$/'; - if (false !== preg_match($pattern, $date)) { - app('log')->debug(sprintf('"%s" is a month/year range.', $date)); - - return true; - } - app('log')->debug(sprintf('"%s" is not a month/year range.', $date)); - - return false; - } - - /** - * format of string is YYYY-MM-xx - * - * @param string $date - * - * @return array - */ - protected function parseMonthYearRange(string $date): array - { - app('log')->debug(sprintf('parseMonthYearRange: Parsed "%s".', $date)); - $parts = explode('-', $date); - - return [ - 'year' => $parts[0], - 'month' => $parts[1], - ]; - } } diff --git a/app/Support/Preferences.php b/app/Support/Preferences.php index 2cc76ef3bf..3a78f0b551 100644 --- a/app/Support/Preferences.php +++ b/app/Support/Preferences.php @@ -23,24 +23,16 @@ declare(strict_types=1); namespace FireflyIII\Support; -use Cache; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Preference; use FireflyIII\User; use Illuminate\Support\Collection; -use PDOException; -use Session; /** * Class Preferences. - * - */ class Preferences { - /** - * @return Collection - */ public function all(): Collection { $user = auth()->user(); @@ -52,10 +44,8 @@ class Preferences } /** - * @param string $name - * @param mixed $default + * @param mixed $default * - * @return Preference|null * @throws FireflyException */ public function get(string $name, $default = null): ?Preference @@ -63,7 +53,8 @@ class Preferences if ('currencyPreference' === $name) { throw new FireflyException('No longer supports "currencyPreference", please refactor me.'); } - /** @var User|null $user */ + + /** @var null|User $user */ $user = auth()->user(); if (null === $user) { $preference = new Preference(); @@ -76,14 +67,9 @@ class Preferences } /** - * @param User $user - * @param string $name - * @param null|string|int|bool|array $default - * - * @return Preference|null * @throws FireflyException */ - public function getForUser(User $user, string $name, string | int | bool | null | array $default = null): ?Preference + public function getForUser(User $user, string $name, null|array|bool|int|string $default = null): ?Preference { if ('currencyPreference' === $name) { throw new FireflyException('No longer supports "currencyPreference", please refactor me.'); @@ -107,9 +93,6 @@ class Preferences } /** - * @param string $name - * - * @return bool * @throws FireflyException */ public function delete(string $name): bool @@ -118,34 +101,27 @@ class Preferences throw new FireflyException('No longer supports "currencyPreference", please refactor me.'); } $fullName = sprintf('preference%s%s', auth()->user()->id, $name); - if (Cache::has($fullName)) { - Cache::forget($fullName); + if (\Cache::has($fullName)) { + \Cache::forget($fullName); } Preference::where('user_id', auth()->user()->id)->where('name', $name)->delete(); return true; } - /** - * @param User $user - * @param string $name - */ public function forget(User $user, string $name): void { if ('currencyPreference' === $name) { throw new FireflyException('No longer supports "currencyPreference", please refactor me.'); } $key = sprintf('preference%s%s', $user->id, $name); - Cache::forget($key); - Cache::put($key, '', 5); + \Cache::forget($key); + \Cache::put($key, '', 5); } /** - * @param User $user - * @param string $name - * @param mixed $value + * @param mixed $value * - * @return Preference * @throws FireflyException */ public function setForUser(User $user, string $name, $value): Preference @@ -154,8 +130,9 @@ class Preferences throw new FireflyException('No longer supports "currencyPreference", please refactor me.'); } $fullName = sprintf('preference%s%s', $user->id, $name); - Cache::forget($fullName); - /** @var Preference|null $pref */ + \Cache::forget($fullName); + + /** @var null|Preference $pref */ $pref = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data', 'updated_at', 'created_at']); if (null !== $pref && null === $value) { @@ -172,50 +149,36 @@ class Preferences $pref->name = $name; } $pref->data = $value; + try { $pref->save(); - } catch (PDOException $e) { + } catch (\PDOException $e) { throw new FireflyException(sprintf('Could not save preference: %s', $e->getMessage()), 0, $e); } - Cache::forever($fullName, $pref); + \Cache::forever($fullName, $pref); return $pref; } - /** - * @param User $user - * @param string $search - * - * @return Collection - */ public function beginsWith(User $user, string $search): Collection { - return Preference::where('user_id', $user->id)->where('name', 'LIKE', $search . '%')->get(); + return Preference::where('user_id', $user->id)->where('name', 'LIKE', $search.'%')->get(); } - /** - * @param string $name - * - * @return Collection - */ public function findByName(string $name): Collection { if ('currencyPreference' === $name) { throw new FireflyException('No longer supports "currencyPreference", please refactor me.'); } + return Preference::where('name', $name)->get(); } - /** - * @param User $user - * @param array $list - * - * @return array - */ public function getArrayForUser(User $user, array $list): array { $result = []; $preferences = Preference::where('user_id', $user->id)->whereIn('name', $list)->get(['id', 'name', 'data']); + /** @var Preference $preference */ foreach ($preferences as $preference) { $result[$preference->name] = $preference->data; @@ -230,10 +193,8 @@ class Preferences } /** - * @param string $name - * @param mixed $default + * @param mixed $default * - * @return Preference|null * @throws FireflyException */ public function getFresh(string $name, $default = null): ?Preference @@ -241,7 +202,8 @@ class Preferences if ('currencyPreference' === $name) { throw new FireflyException('No longer supports "currencyPreference", please refactor me.'); } - /** @var User|null $user */ + + /** @var null|User $user */ $user = auth()->user(); if (null === $user) { $preference = new Preference(); @@ -254,12 +216,11 @@ class Preferences } /** - * @param User $user - * @param string $name - * @param null $default + * @param null $default + * + * @return null|preference + * TODO remove me * - * @return Preference|null - * TODO remove me. * @throws FireflyException */ public function getFreshForUser(User $user, string $name, $default = null): ?Preference @@ -267,11 +228,11 @@ class Preferences if ('currencyPreference' === $name) { throw new FireflyException('No longer supports "currencyPreference", please refactor me.'); } + return $this->getForUser($user, $name, $default); } /** - * @return string * @throws FireflyException */ public function lastActivity(): string @@ -289,20 +250,15 @@ class Preferences return hash('sha256', (string)$lastActivity); } - /** - * - */ public function mark(): void { $this->set('lastActivity', microtime()); - Session::forget('first'); + \Session::forget('first'); } /** - * @param string $name - * @param mixed $value + * @param mixed $value * - * @return Preference * @throws FireflyException */ public function set(string $name, $value): Preference diff --git a/app/Support/Report/Budget/BudgetReportGenerator.php b/app/Support/Report/Budget/BudgetReportGenerator.php index 3ffc6ee0c3..80b123baf4 100644 --- a/app/Support/Report/Budget/BudgetReportGenerator.php +++ b/app/Support/Report/Budget/BudgetReportGenerator.php @@ -35,7 +35,6 @@ use FireflyIII\Repositories\Budget\NoBudgetRepositoryInterface; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; use FireflyIII\User; use Illuminate\Support\Collection; -use JsonException; /** * Class BudgetReportGenerator @@ -74,6 +73,7 @@ class BudgetReportGenerator { $spent = $this->opsRepository->listExpenses($this->start, $this->end, $this->accounts, $this->budgets); $this->report = []; + /** @var Account $account */ foreach ($this->accounts as $account) { $accountId = $account->id; @@ -91,48 +91,6 @@ class BudgetReportGenerator } } - /** - * Process each row of expenses collected for the "Account per budget" partial - * - * @param array $expenses - */ - private function processExpenses(array $expenses): void - { - foreach ($expenses['budgets'] as $budget) { - $this->processBudgetExpenses($expenses, $budget); - } - } - - /** - * Process each set of transactions for each row of expenses. - * - * @param array $expenses - * @param array $budget - */ - private function processBudgetExpenses(array $expenses, array $budget): void - { - $budgetId = (int)$budget['id']; - $currencyId = (int)$expenses['currency_id']; - foreach ($budget['transaction_journals'] as $journal) { - $sourceAccountId = $journal['source_account_id']; - - $this->report[$sourceAccountId]['currencies'][$currencyId] - ??= [ - 'currency_id' => $expenses['currency_id'], - 'currency_symbol' => $expenses['currency_symbol'], - 'currency_name' => $expenses['currency_name'], - 'currency_decimal_places' => $expenses['currency_decimal_places'], - 'budgets' => [], - ]; - - $this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId] - ??= '0'; - - $this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId] - = bcadd($this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId], $journal['amount']); - } - } - /** * Generates the data necessary to create the card that displays * the budget overview in the general report. @@ -149,12 +107,87 @@ class BudgetReportGenerator $this->percentageReport(); } + public function getReport(): array + { + return $this->report; + } + + public function setAccounts(Collection $accounts): void + { + $this->accounts = $accounts; + } + + public function setBudgets(Collection $budgets): void + { + $this->budgets = $budgets; + } + + public function setEnd(Carbon $end): void + { + $this->end = $end; + } + + public function setStart(Carbon $start): void + { + $this->start = $start; + } + + /** + * @throws FireflyException + */ + public function setUser(User $user): void + { + $this->repository->setUser($user); + $this->blRepository->setUser($user); + $this->opsRepository->setUser($user); + $this->nbRepository->setUser($user); + $this->currency = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); + } + + /** + * Process each row of expenses collected for the "Account per budget" partial + */ + private function processExpenses(array $expenses): void + { + foreach ($expenses['budgets'] as $budget) { + $this->processBudgetExpenses($expenses, $budget); + } + } + + /** + * Process each set of transactions for each row of expenses. + */ + private function processBudgetExpenses(array $expenses, array $budget): void + { + $budgetId = (int)$budget['id']; + $currencyId = (int)$expenses['currency_id']; + foreach ($budget['transaction_journals'] as $journal) { + $sourceAccountId = $journal['source_account_id']; + + $this->report[$sourceAccountId]['currencies'][$currencyId] + ??= [ + 'currency_id' => $expenses['currency_id'], + 'currency_symbol' => $expenses['currency_symbol'], + 'currency_name' => $expenses['currency_name'], + 'currency_decimal_places' => $expenses['currency_decimal_places'], + 'budgets' => [], + ]; + + $this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId] + ??= '0'; + + $this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId] + = bcadd($this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId], $journal['amount']); + } + } + /** * Start the budgets block on the default report by processing every budget. */ private function generalBudgetReport(): void { $budgetList = $this->repository->getBudgets(); + /** @var Budget $budget */ foreach ($budgetList as $budget) { $this->processBudget($budget); @@ -163,8 +196,6 @@ class BudgetReportGenerator /** * Process expenses etc. for a single budget for the budgets block on the default report. - * - * @param Budget $budget */ private function processBudget(Budget $budget): void { @@ -178,6 +209,7 @@ class BudgetReportGenerator // get all budget limits for budget in period: $limits = $this->blRepository->getBudgetLimits($budget, $this->start, $this->end); + /** @var BudgetLimit $limit */ foreach ($limits as $limit) { $this->processLimit($budget, $limit); @@ -186,9 +218,6 @@ class BudgetReportGenerator /** * Process a single budget limit for the budgets block on the default report. - * - * @param Budget $budget - * @param BudgetLimit $limit */ private function processLimit(Budget $budget, BudgetLimit $limit): void { @@ -221,16 +250,16 @@ class BudgetReportGenerator // make sum information: $this->report['sums'][$currencyId] ??= [ - 'budgeted' => '0', - 'spent' => '0', - 'left' => '0', - 'overspent' => '0', - 'currency_id' => $currencyId, - 'currency_code' => $limitCurrency->code, - 'currency_name' => $limitCurrency->name, - 'currency_symbol' => $limitCurrency->symbol, - 'currency_decimal_places' => $limitCurrency->decimal_places, - ]; + 'budgeted' => '0', + 'spent' => '0', + 'left' => '0', + 'overspent' => '0', + 'currency_id' => $currencyId, + 'currency_code' => $limitCurrency->code, + 'currency_name' => $limitCurrency->name, + 'currency_symbol' => $limitCurrency->symbol, + 'currency_decimal_places' => $limitCurrency->decimal_places, + ]; $this->report['sums'][$currencyId]['budgeted'] = bcadd($this->report['sums'][$currencyId]['budgeted'], $limit->amount); $this->report['sums'][$currencyId]['spent'] = bcadd($this->report['sums'][$currencyId]['spent'], $spent); $this->report['sums'][$currencyId]['left'] = bcadd($this->report['sums'][$currencyId]['left'], bcadd($limit->amount, $spent)); @@ -320,59 +349,4 @@ class BudgetReportGenerator } } } - - /** - * @return array - */ - public function getReport(): array - { - return $this->report; - } - - /** - * @param Collection $accounts - */ - public function setAccounts(Collection $accounts): void - { - $this->accounts = $accounts; - } - - /** - * @param Collection $budgets - */ - public function setBudgets(Collection $budgets): void - { - $this->budgets = $budgets; - } - - /** - * @param Carbon $end - */ - public function setEnd(Carbon $end): void - { - $this->end = $end; - } - - /** - * @param Carbon $start - */ - public function setStart(Carbon $start): void - { - $this->start = $start; - } - - /** - * @param User $user - * - * @throws FireflyException - * @throws JsonException - */ - public function setUser(User $user): void - { - $this->repository->setUser($user); - $this->blRepository->setUser($user); - $this->opsRepository->setUser($user); - $this->nbRepository->setUser($user); - $this->currency = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); - } } diff --git a/app/Support/Report/Category/CategoryReportGenerator.php b/app/Support/Report/Category/CategoryReportGenerator.php index 6ac8709201..67b4b1fe18 100644 --- a/app/Support/Report/Category/CategoryReportGenerator.php +++ b/app/Support/Report/Category/CategoryReportGenerator.php @@ -50,9 +50,6 @@ class CategoryReportGenerator $this->noCatRepository = app(NoCategoryRepositoryInterface::class); } - /** - * @return array - */ public function getReport(): array { return $this->report; @@ -85,10 +82,29 @@ class CategoryReportGenerator } } + public function setAccounts(Collection $accounts): void + { + $this->accounts = $accounts; + } + + public function setEnd(Carbon $end): void + { + $this->end = $end; + } + + public function setStart(Carbon $start): void + { + $this->start = $start; + } + + public function setUser(User $user): void + { + $this->noCatRepository->setUser($user); + $this->opsRepository->setUser($user); + } + /** * Process one of the spent arrays from the operations method. - * - * @param array $data */ private function processOpsArray(array $data): void { @@ -101,10 +117,6 @@ class CategoryReportGenerator } } - /** - * @param int $currencyId - * @param array $currencyRow - */ private function processCurrencyArray(int $currencyId, array $currencyRow): void { $this->report['sums'][$currencyId] ??= [ @@ -127,12 +139,6 @@ class CategoryReportGenerator } } - /** - * @param int $currencyId - * @param array $currencyRow - * @param int $categoryId - * @param array $categoryRow - */ private function processCategoryRow(int $currencyId, array $currencyRow, int $categoryId, array $categoryRow): void { $key = sprintf('%s-%s', $currencyId, $categoryId); @@ -177,37 +183,4 @@ class CategoryReportGenerator ) : $this->report['categories'][$key]['earned']; } } - - /** - * @param Collection $accounts - */ - public function setAccounts(Collection $accounts): void - { - $this->accounts = $accounts; - } - - /** - * @param Carbon $end - */ - public function setEnd(Carbon $end): void - { - $this->end = $end; - } - - /** - * @param Carbon $start - */ - public function setStart(Carbon $start): void - { - $this->start = $start; - } - - /** - * @param User $user - */ - public function setUser(User $user): void - { - $this->noCatRepository->setUser($user); - $this->opsRepository->setUser($user); - } } diff --git a/app/Support/Repositories/Recurring/CalculateRangeOccurrences.php b/app/Support/Repositories/Recurring/CalculateRangeOccurrences.php index 1cbb6f3920..4ca2c8c29e 100644 --- a/app/Support/Repositories/Recurring/CalculateRangeOccurrences.php +++ b/app/Support/Repositories/Recurring/CalculateRangeOccurrences.php @@ -34,12 +34,6 @@ trait CalculateRangeOccurrences /** * Get the number of daily occurrences for a recurring transaction until date $end is reached. Will skip every * $skipMod-1 occurrences. - * - * @param Carbon $start - * @param Carbon $end - * @param int $skipMod - * - * @return array */ protected function getDailyInRange(Carbon $start, Carbon $end, int $skipMod): array { @@ -50,7 +44,7 @@ trait CalculateRangeOccurrences $return[] = clone $start; } $start->addDay(); - $attempts++; + ++$attempts; } return $return; @@ -59,14 +53,6 @@ trait CalculateRangeOccurrences /** * Get the number of daily occurrences for a recurring transaction until date $end is reached. Will skip every * $skipMod-1 occurrences. - * - * @param Carbon $start - * @param Carbon $end - * @param int $skipMod - * @param string $moment - * - * @return array - * */ protected function getMonthlyInRange(Carbon $start, Carbon $end, int $skipMod, string $moment): array { @@ -83,7 +69,7 @@ trait CalculateRangeOccurrences if (0 === $attempts % $skipMod && $start->lte($start) && $end->gte($start)) { $return[] = clone $start; } - $attempts++; + ++$attempts; $start->endOfMonth()->startOfDay()->addDay(); } @@ -93,13 +79,6 @@ trait CalculateRangeOccurrences /** * Get the number of daily occurrences for a recurring transaction until date $end is reached. Will skip every * $skipMod-1 occurrences. - * - * @param Carbon $start - * @param Carbon $end - * @param int $skipMod - * @param string $moment - * - * @return array */ protected function getNdomInRange(Carbon $start, Carbon $end, int $skipMod, string $moment): array { @@ -107,8 +86,8 @@ trait CalculateRangeOccurrences $attempts = 0; $start->startOfMonth(); // this feels a bit like a cop out but why reinvent the wheel? - $counters = [1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth',]; - $daysOfWeek = [1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday',]; + $counters = [1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth']; + $daysOfWeek = [1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday']; $parts = explode(',', $moment); while ($start <= $end) { $string = sprintf('%s %s of %s %s', $counters[$parts[0]], $daysOfWeek[$parts[1]], $start->format('F'), $start->format('Y')); @@ -116,7 +95,7 @@ trait CalculateRangeOccurrences if (0 === $attempts % $skipMod) { $return[] = clone $newCarbon; } - $attempts++; + ++$attempts; $start->endOfMonth()->addDay(); } @@ -126,14 +105,6 @@ trait CalculateRangeOccurrences /** * Get the number of daily occurrences for a recurring transaction until date $end is reached. Will skip every * $skipMod-1 occurrences. - * - * @param Carbon $start - * @param Carbon $end - * @param int $skipMod - * @param string $moment - * - * @return array - * */ protected function getWeeklyInRange(Carbon $start, Carbon $end, int $skipMod, string $moment): array { @@ -160,7 +131,7 @@ trait CalculateRangeOccurrences app('log')->debug('Date is in range of start+end, add to set.'); $return[] = clone $start; } - $attempts++; + ++$attempts; $start->addWeek(); app('log')->debug(sprintf('Mutator is now (end of loop): %s', $start->format('Y-m-d'))); } @@ -171,14 +142,6 @@ trait CalculateRangeOccurrences /** * Get the number of daily occurrences for a recurring transaction until date $end is reached. Will skip every * $skipMod-1 occurrences. - * - * @param Carbon $start - * @param Carbon $end - * @param int $skipMod - * @param string $moment - * - * @return array - * */ protected function getYearlyInRange(Carbon $start, Carbon $end, int $skipMod, string $moment): array { @@ -198,8 +161,8 @@ trait CalculateRangeOccurrences $return[] = clone $obj; } $obj->addYears(); - $count++; - $attempts++; + ++$count; + ++$attempts; } return $return; diff --git a/app/Support/Repositories/Recurring/CalculateXOccurrences.php b/app/Support/Repositories/Recurring/CalculateXOccurrences.php index eae0b5eaa5..602cb03d02 100644 --- a/app/Support/Repositories/Recurring/CalculateXOccurrences.php +++ b/app/Support/Repositories/Recurring/CalculateXOccurrences.php @@ -34,12 +34,6 @@ trait CalculateXOccurrences /** * Calculates the number of daily occurrences for a recurring transaction, starting at the date, until $count is * reached. It will skip over $skipMod -1 recurrences. - * - * @param Carbon $date - * @param int $count - * @param int $skipMod - * - * @return array */ protected function getXDailyOccurrences(Carbon $date, int $count, int $skipMod): array { @@ -51,9 +45,9 @@ trait CalculateXOccurrences $mutator->addDay(); if (0 === $attempts % $skipMod) { $return[] = clone $mutator; - $total++; + ++$total; } - $attempts++; + ++$attempts; } return $return; @@ -62,13 +56,6 @@ trait CalculateXOccurrences /** * Calculates the number of monthly occurrences for a recurring transaction, starting at the date, until $count is * reached. It will skip over $skipMod -1 recurrences. - * - * @param Carbon $date - * @param int $count - * @param int $skipMod - * @param string $moment - * - * @return array */ protected function getXMonthlyOccurrences(Carbon $date, int $count, int $skipMod, string $moment): array { @@ -87,9 +74,9 @@ trait CalculateXOccurrences $mutator->day = $domCorrected; if (0 === $attempts % $skipMod) { $return[] = clone $mutator; - $total++; + ++$total; } - $attempts++; + ++$attempts; $mutator->endOfMonth()->addDay(); } @@ -99,13 +86,6 @@ trait CalculateXOccurrences /** * Calculates the number of NDOM occurrences for a recurring transaction, starting at the date, until $count is * reached. It will skip over $skipMod -1 recurrences. - * - * @param Carbon $date - * @param int $count - * @param int $skipMod - * @param string $moment - * - * @return array */ protected function getXNDomOccurrences(Carbon $date, int $count, int $skipMod, string $moment): array { @@ -116,8 +96,8 @@ trait CalculateXOccurrences $mutator->addDay(); // always assume today has passed. $mutator->startOfMonth(); // this feels a bit like a cop out but why reinvent the wheel? - $counters = [1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth',]; - $daysOfWeek = [1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday',]; + $counters = [1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth']; + $daysOfWeek = [1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday']; $parts = explode(',', $moment); while ($total < $count) { @@ -125,9 +105,9 @@ trait CalculateXOccurrences $newCarbon = new Carbon($string); if (0 === $attempts % $skipMod) { $return[] = clone $newCarbon; - $total++; + ++$total; } - $attempts++; + ++$attempts; $mutator->endOfMonth()->addDay(); } @@ -137,13 +117,6 @@ trait CalculateXOccurrences /** * Calculates the number of weekly occurrences for a recurring transaction, starting at the date, until $count is * reached. It will skip over $skipMod -1 recurrences. - * - * @param Carbon $date - * @param int $count - * @param int $skipMod - * @param string $moment - * - * @return array */ protected function getXWeeklyOccurrences(Carbon $date, int $count, int $skipMod, string $moment): array { @@ -167,9 +140,9 @@ trait CalculateXOccurrences while ($total < $count) { if (0 === $attempts % $skipMod) { $return[] = clone $mutator; - $total++; + ++$total; } - $attempts++; + ++$attempts; $mutator->addWeek(); } @@ -179,13 +152,6 @@ trait CalculateXOccurrences /** * Calculates the number of yearly occurrences for a recurring transaction, starting at the date, until $count is * reached. It will skip over $skipMod -1 recurrences. - * - * @param Carbon $date - * @param int $count - * @param int $skipMod - * @param string $moment - * - * @return array */ protected function getXYearlyOccurrences(Carbon $date, int $count, int $skipMod, string $moment): array { @@ -202,10 +168,10 @@ trait CalculateXOccurrences while ($total < $count) { if (0 === $attempts % $skipMod) { $return[] = clone $obj; - $total++; + ++$total; } $obj->addYears(); - $attempts++; + ++$attempts; } return $return; diff --git a/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php b/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php index 66139faa7e..c5e5924601 100644 --- a/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php +++ b/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php @@ -34,13 +34,6 @@ trait CalculateXOccurrencesSince /** * Calculates the number of daily occurrences for a recurring transaction, starting at the date, until $count is * reached. It will skip over $skipMod -1 recurrences. - * - * @param Carbon $date - * @param Carbon $afterDate - * @param int $count - * @param int $skipMod - * - * @return array */ protected function getXDailyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod): array { @@ -52,10 +45,10 @@ trait CalculateXOccurrencesSince while ($total < $count) { if (0 === $attempts % $skipMod && $mutator->gt($afterDate)) { $return[] = clone $mutator; - $total++; + ++$total; } $mutator->addDay(); - $attempts++; + ++$attempts; } return $return; @@ -65,13 +58,6 @@ trait CalculateXOccurrencesSince * Calculates the number of monthly occurrences for a recurring transaction, starting at the date, until $count is * reached. It will skip over $skipMod -1 recurrences. * - * @param Carbon $date - * @param Carbon $afterDate - * @param int $count - * @param int $skipMod - * @param string $moment - * - * @return array * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ protected function getXMonthlyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array @@ -95,9 +81,9 @@ trait CalculateXOccurrencesSince $mutator->day = $domCorrected; if (0 === $attempts % $skipMod && $mutator->gte($afterDate)) { $return[] = clone $mutator; - $total++; + ++$total; } - $attempts++; + ++$attempts; $mutator = $mutator->endOfMonth()->addDay(); app('log')->debug(sprintf('Mutator is now %s', $mutator->format('Y-m-d'))); } @@ -109,13 +95,6 @@ trait CalculateXOccurrencesSince * Calculates the number of NDOM occurrences for a recurring transaction, starting at the date, until $count is * reached. It will skip over $skipMod -1 recurrences. * - * @param Carbon $date - * @param Carbon $afterDate - * @param int $count - * @param int $skipMod - * @param string $moment - * - * @return array * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ protected function getXNDomOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array @@ -128,8 +107,8 @@ trait CalculateXOccurrencesSince $mutator->addDay(); // always assume today has passed. $mutator->startOfMonth(); // this feels a bit like a cop out but why reinvent the wheel? - $counters = [1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth',]; - $daysOfWeek = [1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday',]; + $counters = [1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth']; + $daysOfWeek = [1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday']; $parts = explode(',', $moment); while ($total < $count) { @@ -137,9 +116,9 @@ trait CalculateXOccurrencesSince $newCarbon = new Carbon($string); if (0 === $attempts % $skipMod && $mutator->gte($afterDate)) { $return[] = clone $newCarbon; - $total++; + ++$total; } - $attempts++; + ++$attempts; $mutator->endOfMonth()->addDay(); } @@ -150,13 +129,6 @@ trait CalculateXOccurrencesSince * Calculates the number of weekly occurrences for a recurring transaction, starting at the date, until $count is * reached. It will skip over $skipMod -1 recurrences. * - * @param Carbon $date - * @param Carbon $afterDate - * @param int $count - * @param int $skipMod - * @param string $moment - * - * @return array * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ protected function getXWeeklyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array @@ -169,7 +141,7 @@ trait CalculateXOccurrencesSince // monday = 1 // sunday = 7 // Removed assumption today has passed, see issue https://github.com/firefly-iii/firefly-iii/issues/4798 - //$mutator->addDay(); // always assume today has passed. + // $mutator->addDay(); // always assume today has passed. $dayOfWeek = (int)$moment; if ($mutator->dayOfWeekIso > $dayOfWeek) { // day has already passed this week, add one week: @@ -183,9 +155,9 @@ trait CalculateXOccurrencesSince while ($total < $count) { if (0 === $attempts % $skipMod && $mutator->gte($afterDate)) { $return[] = clone $mutator; - $total++; + ++$total; } - $attempts++; + ++$attempts; $mutator->addWeek(); } @@ -196,13 +168,6 @@ trait CalculateXOccurrencesSince * Calculates the number of yearly occurrences for a recurring transaction, starting at the date, until $count is * reached. It will skip over $skipMod -1 recurrences. * - * @param Carbon $date - * @param Carbon $afterDate - * @param int $count - * @param int $skipMod - * @param string $moment - * - * @return array * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ protected function getXYearlyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array @@ -229,10 +194,10 @@ trait CalculateXOccurrencesSince if (0 === $attempts % $skipMod && $obj->gte($afterDate)) { app('log')->debug('All conditions true, add obj.'); $return[] = clone $obj; - $total++; + ++$total; } $obj->addYears(); - $attempts++; + ++$attempts; } return $return; diff --git a/app/Support/Repositories/Recurring/FiltersWeekends.php b/app/Support/Repositories/Recurring/FiltersWeekends.php index efc792a1cc..552115a3a9 100644 --- a/app/Support/Repositories/Recurring/FiltersWeekends.php +++ b/app/Support/Repositories/Recurring/FiltersWeekends.php @@ -35,53 +35,51 @@ trait FiltersWeekends { /** * Filters out all weekend entries, if necessary. - * - * @param RecurrenceRepetition $repetition - * @param array $dates - * - * @return array - * */ protected function filterWeekends(RecurrenceRepetition $repetition, array $dates): array { app('log')->debug(sprintf('Now in %s', __METHOD__)); - if ($repetition->weekend === RecurrenceRepetition::WEEKEND_DO_NOTHING) { + if (RecurrenceRepetition::WEEKEND_DO_NOTHING === $repetition->weekend) { app('log')->debug('Repetition will not be filtered on weekend days.'); return $dates; } $return = []; + /** @var Carbon $date */ foreach ($dates as $date) { $isWeekend = $date->isWeekend(); if (!$isWeekend) { $return[] = clone $date; - //app('log')->debug(sprintf('Date is %s, not a weekend date.', $date->format('D d M Y'))); + + // app('log')->debug(sprintf('Date is %s, not a weekend date.', $date->format('D d M Y'))); continue; } // is weekend and must set back to Friday? - if ($repetition->weekend === RecurrenceRepetition::WEEKEND_TO_FRIDAY) { + if (RecurrenceRepetition::WEEKEND_TO_FRIDAY === $repetition->weekend) { $clone = clone $date; $clone->addDays(5 - $date->dayOfWeekIso); app('log')->debug( sprintf('Date is %s, and this is in the weekend, so corrected to %s (Friday).', $date->format('D d M Y'), $clone->format('D d M Y')) ); $return[] = clone $clone; + continue; } // postpone to Monday? - if ($repetition->weekend === RecurrenceRepetition::WEEKEND_TO_MONDAY) { + if (RecurrenceRepetition::WEEKEND_TO_MONDAY === $repetition->weekend) { $clone = clone $date; $clone->addDays(8 - $date->dayOfWeekIso); app('log')->debug( sprintf('Date is %s, and this is in the weekend, so corrected to %s (Monday).', $date->format('D d M Y'), $clone->format('D d M Y')) ); $return[] = $clone; + continue; } - //app('log')->debug(sprintf('Date is %s, removed from final result', $date->format('D d M Y'))); + // app('log')->debug(sprintf('Date is %s, removed from final result', $date->format('D d M Y'))); } // filter unique dates diff --git a/app/Support/Repositories/UserGroup/UserGroupTrait.php b/app/Support/Repositories/UserGroup/UserGroupTrait.php index 30ca44bb27..a4c12d5d62 100644 --- a/app/Support/Repositories/UserGroup/UserGroupTrait.php +++ b/app/Support/Repositories/UserGroup/UserGroupTrait.php @@ -1,6 +1,5 @@ userGroup; @@ -49,10 +45,6 @@ trait UserGroupTrait /** * TODO This method does not check if the user has access to this particular user group. - * - * @param UserGroup $userGroup - * - * @return void */ public function setUserGroup(UserGroup $userGroup): void { @@ -60,12 +52,9 @@ trait UserGroupTrait } /** - * @param Authenticatable|User|null $user - * - * @return void * @throws FireflyException */ - public function setUser(Authenticatable | User | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; @@ -77,25 +66,23 @@ trait UserGroupTrait } /** - * @param int $userGroupId - * * @throws FireflyException */ public function setUserGroupById(int $userGroupId): void { $memberships = GroupMembership::where('user_id', $this->user->id) - ->where('user_group_id', $userGroupId) - ->count(); + ->where('user_group_id', $userGroupId) + ->count() + ; if (0 === $memberships) { throw new FireflyException(sprintf('User #%d has no access to administration #%d', $this->user->id, $userGroupId)); } - /** @var UserGroup|null $userGroup */ + + /** @var null|UserGroup $userGroup */ $userGroup = UserGroup::find($userGroupId); if (null === $userGroup) { throw new FireflyException(sprintf('Cannot find administration for user #%d', $this->user->id)); } $this->userGroup = $userGroup; } - - } diff --git a/app/Support/Request/AppendsLocationData.php b/app/Support/Request/AppendsLocationData.php index b6e21bf76f..ed6ec863ff 100644 --- a/app/Support/Request/AppendsLocationData.php +++ b/app/Support/Request/AppendsLocationData.php @@ -31,20 +31,42 @@ trait AppendsLocationData /** * Abstract method. * - * @param $key + * @param mixed $key * * @return bool */ abstract public function has($key); + /** + * Abstract method. + * + * @return string + */ + abstract public function method(); + + /** + * Abstract method. + * + * @param mixed ...$patterns + * + * @return mixed + */ + abstract public function routeIs(...$patterns); + + /** + * Abstract method stolen from "InteractsWithInput". + * + * @param null $key + * @param bool $default + * + * @return mixed + * + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) + */ + abstract public function boolean($key = null, $default = false); + /** * Read the submitted Request data and add new or updated Location data to the array. - * - * @param array $data - * - * @param string|null $prefix - * - * @return array */ protected function appendLocationData(array $data, ?string $prefix): array { @@ -97,12 +119,6 @@ trait AppendsLocationData return $data; } - /** - * @param string|null $prefix - * @param string $key - * - * @return string - */ private function getLocationKey(?string $prefix, string $key): string { if (null === $prefix) { @@ -112,11 +128,6 @@ trait AppendsLocationData return sprintf('%s_%s', $prefix, $key); } - /** - * @param string|null $prefix - * - * @return bool - */ private function isValidPost(?string $prefix): bool { app('log')->debug('Now in isValidPost()'); @@ -155,38 +166,6 @@ trait AppendsLocationData return false; } - /** - * Abstract method. - * - * @return string - */ - abstract public function method(); - - /** - * Abstract method. - * - * @param mixed ...$patterns - * - * @return mixed - */ - abstract public function routeIs(...$patterns); - - /** - * Abstract method stolen from "InteractsWithInput". - * - * @param null $key - * @param bool $default - * - * @return mixed - * @SuppressWarnings(PHPMD.BooleanArgumentFlag) - */ - abstract public function boolean($key = null, $default = false); - - /** - * @param string|null $prefix - * - * @return bool - */ private function isValidPUT(?string $prefix): bool { $longitudeKey = $this->getLocationKey($prefix, 'longitude'); @@ -227,11 +206,6 @@ trait AppendsLocationData return false; } - /** - * @param string|null $prefix - * - * @return bool - */ private function isValidEmptyPUT(?string $prefix): bool { $longitudeKey = $this->getLocationKey($prefix, 'longitude'); @@ -239,9 +213,9 @@ trait AppendsLocationData $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); return ( - null === $this->get($longitudeKey) - && null === $this->get($latitudeKey) - && null === $this->get($zoomLevelKey)) + 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 fa610fb7c5..aaddbb603e 100644 --- a/app/Support/Request/ChecksLogin.php +++ b/app/Support/Request/ChecksLogin.php @@ -34,8 +34,6 @@ trait ChecksLogin { /** * Verify the request. - * - * @return bool */ public function authorize(): bool { @@ -47,13 +45,16 @@ trait ChecksLogin } if (!property_exists($this, 'acceptedRoles')) { // @phpstan-ignore-line app('log')->debug('Request class has no acceptedRoles array'); + return true; // check for false already took place. } + /** @var User $user */ $user = auth()->user(); $userGroup = $this->getUserGroup(); if (null === $userGroup) { app('log')->error('User has no valid user group submitted or otherwise.'); + return false; } @@ -65,21 +66,21 @@ trait ChecksLogin return true; } } + return false; } /** * Return the user group or NULL if none is set. * Will throw exception if invalid. - * - * @return UserGroup|null */ public function getUserGroup(): ?UserGroup { /** @var User $user */ $user = auth()->user(); app('log')->debug('Now in getUserGroup()'); - /** @var UserGroup|null $userGroup */ + + /** @var null|UserGroup $userGroup */ $userGroup = $this->route()?->parameter('userGroup'); if (null === $userGroup) { app('log')->debug('Request class has no userGroup parameter, but perhaps there is a parameter.'); @@ -91,10 +92,12 @@ trait ChecksLogin $userGroup = UserGroup::find($userGroupId); if (null === $userGroup) { app('log')->error(sprintf('Request class has user_group_id (#%d), but group does not exist.', $userGroupId)); + return null; } app('log')->debug('Request class has valid user_group_id.'); } + return $userGroup; } } diff --git a/app/Support/Request/ConvertAPIDataTypes.php b/app/Support/Request/ConvertAPIDataTypes.php index 837e906847..0c9732e80c 100644 --- a/app/Support/Request/ConvertAPIDataTypes.php +++ b/app/Support/Request/ConvertAPIDataTypes.php @@ -26,6 +26,4 @@ namespace FireflyIII\Support\Request; /** * Trait ConvertAPIDataTypes */ -trait ConvertAPIDataTypes -{ -} +trait ConvertAPIDataTypes {} diff --git a/app/Support/Request/ConvertsDataTypes.php b/app/Support/Request/ConvertsDataTypes.php index 59765c94c4..b98ff19295 100644 --- a/app/Support/Request/ConvertsDataTypes.php +++ b/app/Support/Request/ConvertsDataTypes.php @@ -89,10 +89,6 @@ trait ConvertsDataTypes /** * Return integer value. - * - * @param string $field - * - * @return int */ public function convertInteger(string $field): int { @@ -102,20 +98,11 @@ trait ConvertsDataTypes /** * Abstract method that always exists in the Request classes that use this * trait, OR a stub needs to be added by any other class that uses this train. - * - * @param string $key - * @param mixed|null $default - * - * @return mixed */ abstract public function get(string $key, mixed $default = null): mixed; /** * Return string value. - * - * @param string $field - * - * @return string */ public function convertString(string $field): string { @@ -123,14 +110,10 @@ trait ConvertsDataTypes if (!is_scalar($entry)) { return ''; } + return (string)$this->clearString((string)$entry); } - /** - * @param string|null $string - * - * @return string|null - */ public function clearString(?string $string): ?string { $string = $this->clearStringKeepNewlines($string); @@ -148,11 +131,6 @@ trait ConvertsDataTypes return trim($string); } - /** - * @param string|null $string - * - * @return string|null - */ public function clearStringKeepNewlines(?string $string): ?string { if (null === $string) { @@ -173,8 +151,6 @@ trait ConvertsDataTypes * TODO duplicate, see SelectTransactionsRequest * * Validate list of accounts. This one is for V2 endpoints, so it searches for groups, not users. - * - * @return Collection */ public function getAccountList(): Collection { @@ -207,10 +183,6 @@ trait ConvertsDataTypes /** * Return string value with newlines. - * - * @param string $field - * - * @return string */ public function stringWithNewlines(string $field): string { @@ -218,9 +190,17 @@ trait ConvertsDataTypes } /** - * @param mixed $array + * Abstract method that always exists in the Request classes that use this + * trait, OR a stub needs to be added by any other class that uses this train. * - * @return array|null + * @param mixed $key + * + * @return mixed + */ + abstract public function has($key); + + /** + * @param mixed $array */ protected function arrayFromValue($array): ?array { @@ -237,11 +217,6 @@ trait ConvertsDataTypes return null; } - /** - * @param string|null $value - * - * @return bool - */ protected function convertBoolean(?string $value): bool { if (null === $value) { @@ -263,11 +238,6 @@ trait ConvertsDataTypes return false; } - /** - * @param string|null $string - * - * @return Carbon|null - */ protected function convertDateTime(?string $string): ?Carbon { $value = $this->get((string)$string); @@ -283,6 +253,7 @@ trait ConvertsDataTypes $carbon = Carbon::createFromFormat('Y-m-d', $value); } catch (InvalidDateException $e) { // @phpstan-ignore-line app('log')->error(sprintf('[1] "%s" is not a valid date: %s', $value, $e->getMessage())); + return null; } catch (InvalidFormatException $e) { // @phpstan-ignore-line app('log')->error(sprintf('[2] "%s" is of an invalid format: %s', $value, $e->getMessage())); @@ -291,10 +262,13 @@ trait ConvertsDataTypes } if (false === $carbon) { app('log')->error(sprintf('[2] "%s" is of an invalid format.', $value)); + return null; } + return $carbon; } + // is an atom string, I hope? try { $carbon = Carbon::parse($value); @@ -307,15 +281,12 @@ trait ConvertsDataTypes return null; } + return $carbon; } /** * Return floating value. - * - * @param string $field - * - * @return float|null */ protected function convertFloat(string $field): ?float { @@ -327,11 +298,6 @@ trait ConvertsDataTypes return (float)$res; } - /** - * @param string|null $string - * - * @return Carbon|null - */ protected function dateFromValue(?string $string): ?Carbon { if (null === $string) { @@ -341,6 +307,7 @@ trait ConvertsDataTypes return null; } $carbon = null; + try { $carbon = new Carbon($string, config('app.timezone')); } catch (InvalidFormatException $e) { @@ -359,10 +326,6 @@ trait ConvertsDataTypes /** * Returns all data in the request, or omits the field if not set, * according to the config from the request. This is the way. - * - * @param array $fields - * - * @return array */ protected function getAllData(array $fields): array { @@ -370,33 +333,20 @@ trait ConvertsDataTypes foreach ($fields as $field => $info) { if (true === $this->has($info[0])) { $method = $info[1]; - $return[$field] = $this->$method($info[0]); // @phpstan-ignore-line + $return[$field] = $this->{$method}($info[0]); // @phpstan-ignore-line } } return $return; } - /** - * Abstract method that always exists in the Request classes that use this - * trait, OR a stub needs to be added by any other class that uses this train. - * - * @param mixed $key - * - * @return mixed - */ - abstract public function has($key); - /** * Return date or NULL. - * - * @param string $field - * - * @return Carbon|null */ protected function getCarbonDate(string $field): ?Carbon { $result = null; + try { $result = '' !== (string)$this->get($field) ? new Carbon((string)$this->get($field), config('app.timezone')) : null; } catch (InvalidFormatException $e) { @@ -411,10 +361,6 @@ trait ConvertsDataTypes /** * Parse to integer - * - * @param string|null $string - * - * @return int|null */ protected function integerFromValue(?string $string): ?int { @@ -430,10 +376,6 @@ trait ConvertsDataTypes /** * Return integer value, or NULL when it's not set. - * - * @param string $field - * - * @return int|null */ protected function nullableInteger(string $field): ?int { diff --git a/app/Support/Request/GetRecurrenceData.php b/app/Support/Request/GetRecurrenceData.php index d26b9e702e..4c956a66cf 100644 --- a/app/Support/Request/GetRecurrenceData.php +++ b/app/Support/Request/GetRecurrenceData.php @@ -28,67 +28,27 @@ namespace FireflyIII\Support\Request; */ trait GetRecurrenceData { - /** - * @param array $transaction - * - * @return array - */ protected function getSingleTransactionData(array $transaction): array { - $return = []; + $return = []; + $stringKeys = ['id']; + $intKeys = ['currency_id', 'foreign_currency_id', 'source_id', 'destination_id', 'bill_id', 'piggy_bank_id', 'bill_id', 'budget_id', 'category_id']; + $keys = ['amount', 'currency_code', 'foreign_amount', 'foreign_currency_code', 'description', 'tags']; - if (array_key_exists('id', $transaction)) { - $return['id'] = (string)$transaction['id']; + foreach ($stringKeys as $key) { + if (array_key_exists($key, $transaction)) { + $return[$key] = (string) $transaction[$key]; + } } - - // amount + currency - if (array_key_exists('amount', $transaction)) { - $return['amount'] = $transaction['amount']; + foreach ($intKeys as $key) { + if (array_key_exists($key, $transaction)) { + $return[$key] = (int) $transaction[$key]; + } } - 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('bill_id', $transaction)) { - $return['bill_id'] = (int)$transaction['bill_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']; + foreach ($keys as $key) { + if (array_key_exists($key, $transaction)) { + $return[$key] = $transaction[$key]; + } } return $return; diff --git a/app/Support/Request/GetRuleConfiguration.php b/app/Support/Request/GetRuleConfiguration.php index 776d966ea6..d1fa61edc2 100644 --- a/app/Support/Request/GetRuleConfiguration.php +++ b/app/Support/Request/GetRuleConfiguration.php @@ -28,17 +28,11 @@ namespace FireflyIII\Support\Request; */ trait GetRuleConfiguration { - /** - * @return array - */ protected function getTriggers(): array { return array_keys(config('search.operators')); } - /** - * @return array - */ protected function getTriggersWithContext(): array { $list = config('search.operators'); diff --git a/app/Support/Search/AccountSearch.php b/app/Support/Search/AccountSearch.php index 2f2c838d27..1e5bcb25f3 100644 --- a/app/Support/Search/AccountSearch.php +++ b/app/Support/Search/AccountSearch.php @@ -36,12 +36,16 @@ class AccountSearch implements GenericSearchInterface { /** @var string */ public const string SEARCH_ALL = 'all'; + /** @var string */ public const string SEARCH_IBAN = 'iban'; + /** @var string */ public const string SEARCH_ID = 'id'; + /** @var string */ public const string SEARCH_NAME = 'name'; + /** @var string */ public const string SEARCH_NUMBER = 'number'; private string $field; @@ -54,22 +58,21 @@ class AccountSearch implements GenericSearchInterface $this->types = []; } - /** - * @return Collection - */ public function search(): Collection { $searchQuery = $this->user->accounts() - ->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id') - ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') - ->whereIn('account_types.type', $this->types); + ->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id') + ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') + ->whereIn('account_types.type', $this->types) + ; $like = sprintf('%%%s%%', $this->query); $originalQuery = $this->query; + switch ($this->field) { default: case self::SEARCH_ALL: $searchQuery->where( - static function (Builder $q) use ($like) { // @phpstan-ignore-line + static function (Builder $q) use ($like): void { // @phpstan-ignore-line $q->where('accounts.id', 'LIKE', $like); $q->orWhere('accounts.name', 'LIKE', $like); $q->orWhere('accounts.iban', 'LIKE', $like); @@ -77,67 +80,62 @@ class AccountSearch implements GenericSearchInterface ); // meta data: $searchQuery->orWhere( - static function (Builder $q) use ($originalQuery) { // @phpstan-ignore-line + static function (Builder $q) use ($originalQuery): void { // @phpstan-ignore-line $json = json_encode($originalQuery, JSON_THROW_ON_ERROR); $q->where('account_meta.name', '=', 'account_number'); $q->where('account_meta.data', 'LIKE', $json); } ); + break; + case self::SEARCH_ID: $searchQuery->where('accounts.id', '=', (int)$originalQuery); + break; + case self::SEARCH_NAME: $searchQuery->where('accounts.name', 'LIKE', $like); + break; + case self::SEARCH_IBAN: $searchQuery->where('accounts.iban', 'LIKE', $like); + break; + case self::SEARCH_NUMBER: // meta data: $searchQuery->Where( - static function (Builder $q) use ($originalQuery) { // @phpstan-ignore-line + static function (Builder $q) use ($originalQuery): void { // @phpstan-ignore-line $json = json_encode($originalQuery, JSON_THROW_ON_ERROR); $q->where('account_meta.name', 'account_number'); $q->where('account_meta.data', $json); } ); + break; } return $searchQuery->distinct()->get(['accounts.*']); } - /** - * @param string $field - */ public function setField(string $field): void { $this->field = $field; } - /** - * @param string $query - */ public function setQuery(string $query): void { $this->query = $query; } - /** - * @param array $types - */ public function setTypes(array $types): void { $this->types = $types; } - /** - * @param User|Authenticatable|null $user - * - * @return void - */ - public function setUser(User | Authenticatable | null $user): void + public function setUser(null|Authenticatable|User $user): void { if ($user instanceof User) { $this->user = $user; diff --git a/app/Support/Search/GenericSearchInterface.php b/app/Support/Search/GenericSearchInterface.php index e7bb9005c3..1c765b459e 100644 --- a/app/Support/Search/GenericSearchInterface.php +++ b/app/Support/Search/GenericSearchInterface.php @@ -31,8 +31,5 @@ use Illuminate\Support\Collection; */ interface GenericSearchInterface { - /** - * @return Collection - */ public function search(): Collection; } diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php index 083c0e3d83..797268ac94 100644 --- a/app/Support/Search/OperatorQuerySearch.php +++ b/app/Support/Search/OperatorQuerySearch.php @@ -56,13 +56,11 @@ use Gdbots\QueryParser\Node\Word; use Gdbots\QueryParser\QueryParser; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; -use LogicException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; -use TypeError; /** * Class OperatorQuerySearch + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class OperatorQuerySearch implements SearchInterface { @@ -73,8 +71,8 @@ class OperatorQuerySearch implements SearchInterface private CategoryRepositoryInterface $categoryRepository; private GroupCollectorInterface $collector; private CurrencyRepositoryInterface $currencyRepository; - private array $excludeTags; - private array $includeTags; + private array $excludeTags; + private array $includeTags; private array $invalidOperators; private int $limit; private Collection $operators; @@ -87,8 +85,6 @@ class OperatorQuerySearch implements SearchInterface /** * OperatorQuerySearch constructor. - * - */ public function __construct() { @@ -111,40 +107,27 @@ class OperatorQuerySearch implements SearchInterface $this->currencyRepository = app(CurrencyRepositoryInterface::class); } - /** - * @return array - */ public function getInvalidOperators(): array { return $this->invalidOperators; } - /** - * @inheritDoc - */ public function getModifiers(): Collection { return $this->getOperators(); } - /** - * @inheritDoc - */ public function getOperators(): Collection { return $this->operators; } - /** - * @inheritDoc - */ public function getWordsAsString(): string { return implode(' ', $this->words); } /** - * @inheritDoc * @throws FireflyException */ public function hasModifiers(): bool @@ -153,18 +136,19 @@ class OperatorQuerySearch implements SearchInterface } /** - * @inheritDoc * @throws FireflyException */ public function parseQuery(string $query): void { app('log')->debug(sprintf('Now in parseQuery(%s)', $query)); $parser = new QueryParser(); + try { $query1 = $parser->parse($query); - } catch (TypeError | LogicException $e) { + } catch (\LogicException|\TypeError $e) { app('log')->error($e->getMessage()); app('log')->error(sprintf('Could not parse search: "%s".', $query)); + throw new FireflyException(sprintf('Invalid search value "%s". See the logs.', e($query)), 0, $e); } @@ -174,1238 +158,11 @@ class OperatorQuerySearch implements SearchInterface } $this->parseTagInstructions(); - $this->collector->setSearchWords($this->words); $this->collector->excludeSearchWords($this->prohibitedWords); } /** - * @param Node $searchNode - * - * @throws FireflyException - */ - private function handleSearchNode(Node $searchNode): void - { - $class = get_class($searchNode); - app('log')->debug(sprintf('Now in handleSearchNode(%s)', $class)); - switch ($class) { - default: - app('log')->error(sprintf('Cannot handle node %s', $class)); - 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) { // @phpstan-ignore-line PHPStan thinks getNodes() does not exist but it does. - $this->handleSearchNode($subNode); // let's hope it's not too recursive - } - break; - case Word::class: - case Phrase::class: - case Numbr::class: - case Url::class: - case Date::class: - case Hashtag::class: - case Emoticon::class: - case Emoji::class: - case Mention::class: - $allWords = (string)$searchNode->getValue(); - app('log')->debug(sprintf('Add words "%s" to search string, because Node class is "%s"', $allWords, $class)); - $this->words[] = $allWords; - break; - case Field::class: - app('log')->debug(sprintf('Now handle Node class %s', $class)); - /** @var Field $searchNode */ - // used to search for x:y - $operator = strtolower($searchNode->getValue()); - $value = $searchNode->getNode()->getValue(); - $prohibited = $searchNode->getBoolOperator() === BoolOperator::PROHIBITED; - $context = config(sprintf('search.operators.%s.needs_context', $operator)); - - // is an operator that needs no context, and value is false, then prohibited = true. - if ('false' === $value && in_array($operator, $this->validOperators, true) && false === $context && !$prohibited) { - $prohibited = true; - $value = 'true'; - } - // if the operator is prohibited, but the value is false, do an uno reverse - if ('false' === $value && $prohibited && in_array($operator, $this->validOperators, true) && false === $context) { - $prohibited = false; - $value = 'true'; - } - - // must be valid operator: - if ( - in_array($operator, $this->validOperators, true) && - $this->updateCollector($operator, (string)$value, $prohibited)) { - $this->operators->push( - [ - 'type' => self::getRootOperator($operator), - 'value' => (string)$value, - 'prohibited' => $prohibited, - ] - ); - app('log')->debug(sprintf('Added operator type "%s"', $operator)); - } - if (!in_array($operator, $this->validOperators, true)) { - app('log')->debug(sprintf('Added INVALID operator type "%s"', $operator)); - $this->invalidOperators[] = [ - 'type' => $operator, - 'value' => (string)$value, - ]; - } - } - } - - /** - * - * @throws FireflyException - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - private function updateCollector(string $operator, string $value, bool $prohibited): bool - { - if ($prohibited) { - app('log')->debug(sprintf('Operator "%s" is now "%s"', $operator, sprintf('-%s', $operator))); - $operator = sprintf('-%s', $operator); - } - - app('log')->debug(sprintf('Now in updateCollector("%s", "%s")', $operator, $value)); - - // check if alias, replace if necessary: - $operator = self::getRootOperator($operator); - - switch ($operator) { - default: - app('log')->error(sprintf('No such operator: %s', $operator)); - throw new FireflyException(sprintf('Unsupported search operator: "%s"', $operator)); - // some search operators are ignored, basically: - case 'user_action': - app('log')->info(sprintf('Ignore search operator "%s"', $operator)); - - return false; - // - // all account related searches: - // - case 'account_is': - $this->searchAccount($value, SearchDirection::BOTH, StringPosition::IS); - break; - case '-account_is': - $this->searchAccount($value, SearchDirection::BOTH, StringPosition::IS, true); - break; - case 'account_contains': - $this->searchAccount($value, SearchDirection::BOTH, StringPosition::CONTAINS); - break; - case '-account_contains': - $this->searchAccount($value, SearchDirection::BOTH, StringPosition::CONTAINS, true); - break; - case 'account_ends': - $this->searchAccount($value, SearchDirection::BOTH, StringPosition::ENDS); - break; - case '-account_ends': - $this->searchAccount($value, SearchDirection::BOTH, StringPosition::ENDS, true); - break; - case 'account_starts': - $this->searchAccount($value, SearchDirection::BOTH, StringPosition::STARTS); - break; - case '-account_starts': - $this->searchAccount($value, SearchDirection::BOTH, StringPosition::STARTS, true); - break; - case 'account_nr_is': - $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::IS); - break; - case '-account_nr_is': - $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::IS, true); - break; - case 'account_nr_contains': - $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::CONTAINS); - break; - case '-account_nr_contains': - $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::CONTAINS, true); - break; - case 'account_nr_ends': - $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::ENDS); - break; - case '-account_nr_ends': - $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::ENDS, true); - break; - case 'account_nr_starts': - $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::STARTS); - break; - case '-account_nr_starts': - $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::STARTS, true); - break; - case 'source_account_starts': - $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::STARTS); - break; - case '-source_account_starts': - $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::STARTS, true); - break; - case 'source_account_ends': - $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::ENDS); - break; - case '-source_account_ends': - $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::ENDS, true); - break; - case 'source_account_is': - $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::IS); - break; - case '-source_account_is': - $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::IS, true); - break; - case 'source_account_nr_starts': - $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::STARTS); - break; - case '-source_account_nr_starts': - $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::STARTS, true); - break; - case 'source_account_nr_ends': - $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::ENDS); - break; - case '-source_account_nr_ends': - $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::ENDS, true); - break; - case 'source_account_nr_is': - $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::IS); - break; - case '-source_account_nr_is': - $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::IS, true); - break; - case 'source_account_nr_contains': - $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::CONTAINS); - break; - case '-source_account_nr_contains': - $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::CONTAINS, true); - break; - case 'source_account_contains': - $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::CONTAINS); - break; - case '-source_account_contains': - $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::CONTAINS, true); - break; - case 'source_account_id': - $account = $this->accountRepository->find((int)$value); - if (null !== $account) { - $this->collector->setSourceAccounts(new Collection([$account])); - } - if (null === $account) { - // since the source does not exist, cannot return results: - $this->collector->findNothing(); - } - break; - case '-source_account_id': - $account = $this->accountRepository->find((int)$value); - if (null !== $account) { - $this->collector->excludeSourceAccounts(new Collection([$account])); - } - if (null === $account) { - // since the source does not exist, cannot return results: - $this->collector->findNothing(); - } - break; - case 'journal_id': - $parts = explode(',', $value); - $this->collector->setJournalIds($parts); - break; - case '-journal_id': - $parts = explode(',', $value); - $this->collector->excludeJournalIds($parts); - break; - case 'id': - $parts = explode(',', $value); - $this->collector->setIds($parts); - break; - case '-id': - $parts = explode(',', $value); - $this->collector->excludeIds($parts); - break; - case 'destination_account_starts': - $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::STARTS); - break; - case '-destination_account_starts': - $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::STARTS, true); - break; - case 'destination_account_ends': - $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::ENDS); - break; - case '-destination_account_ends': - $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::ENDS, true); - break; - case 'destination_account_nr_starts': - $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::STARTS); - break; - case '-destination_account_nr_starts': - $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::STARTS, true); - break; - case 'destination_account_nr_ends': - $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::ENDS); - break; - case '-destination_account_nr_ends': - $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::ENDS, true); - break; - case 'destination_account_nr_is': - $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::IS); - break; - case '-destination_account_nr_is': - $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::IS, true); - break; - case 'destination_account_is': - $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::IS); - break; - case '-destination_account_is': - $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::IS, true); - break; - case 'destination_account_nr_contains': - $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::CONTAINS); - break; - case '-destination_account_nr_contains': - $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::CONTAINS, true); - break; - case 'destination_account_contains': - $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::CONTAINS); - break; - case '-destination_account_contains': - $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::CONTAINS, true); - break; - case 'destination_account_id': - $account = $this->accountRepository->find((int)$value); - if (null !== $account) { - $this->collector->setDestinationAccounts(new Collection([$account])); - } - if (null === $account) { - $this->collector->findNothing(); - } - break; - case '-destination_account_id': - $account = $this->accountRepository->find((int)$value); - if (null !== $account) { - $this->collector->excludeDestinationAccounts(new Collection([$account])); - } - if (null === $account) { - $this->collector->findNothing(); - } - break; - case 'account_id': - $parts = explode(',', $value); - $collection = new Collection(); - foreach ($parts as $accountId) { - $account = $this->accountRepository->find((int)$accountId); - if (null !== $account) { - $collection->push($account); - } - } - if ($collection->count() > 0) { - $this->collector->setAccounts($collection); - } - if (0 === $collection->count()) { - $this->collector->findNothing(); - } - break; - case '-account_id': - $parts = explode(',', $value); - $collection = new Collection(); - foreach ($parts as $accountId) { - $account = $this->accountRepository->find((int)$accountId); - if (null !== $account) { - $collection->push($account); - } - } - if ($collection->count() > 0) { - $this->collector->setNotAccounts($collection); - } - if (0 === $collection->count()) { - $this->collector->findNothing(); - } - break; - // - // cash account - // - case 'source_is_cash': - $account = $this->getCashAccount(); - $this->collector->setSourceAccounts(new Collection([$account])); - break; - case '-source_is_cash': - $account = $this->getCashAccount(); - $this->collector->excludeSourceAccounts(new Collection([$account])); - break; - case 'destination_is_cash': - $account = $this->getCashAccount(); - $this->collector->setDestinationAccounts(new Collection([$account])); - break; - case '-destination_is_cash': - $account = $this->getCashAccount(); - $this->collector->excludeDestinationAccounts(new Collection([$account])); - break; - case 'account_is_cash': - $account = $this->getCashAccount(); - $this->collector->setAccounts(new Collection([$account])); - break; - case '-account_is_cash': - $account = $this->getCashAccount(); - $this->collector->excludeAccounts(new Collection([$account])); - break; - // - // description - // - case 'description_starts': - $this->collector->descriptionStarts([$value]); - break; - case '-description_starts': - $this->collector->descriptionDoesNotStart([$value]); - break; - case 'description_ends': - $this->collector->descriptionEnds([$value]); - break; - case '-description_ends': - $this->collector->descriptionDoesNotEnd([$value]); - break; - case 'description_contains': - $this->words[] = $value; - - return false; - case '-description_contains': - $this->prohibitedWords[] = $value; - - break; - case 'description_is': - $this->collector->descriptionIs($value); - break; - case '-description_is': - $this->collector->descriptionIsNot($value); - break; - // - // currency - // - case 'currency_is': - $currency = $this->findCurrency($value); - if (null !== $currency) { - $this->collector->setCurrency($currency); - } - if (null === $currency) { - $this->collector->findNothing(); - } - break; - case '-currency_is': - $currency = $this->findCurrency($value); - if (null !== $currency) { - $this->collector->excludeCurrency($currency); - } - if (null === $currency) { - $this->collector->findNothing(); - } - break; - case 'foreign_currency_is': - $currency = $this->findCurrency($value); - if (null !== $currency) { - $this->collector->setForeignCurrency($currency); - } - if (null === $currency) { - $this->collector->findNothing(); - } - break; - case '-foreign_currency_is': - $currency = $this->findCurrency($value); - if (null !== $currency) { - $this->collector->excludeForeignCurrency($currency); - } - if (null === $currency) { - $this->collector->findNothing(); - } - break; - // - // attachments - // - case 'has_attachments': - case '-has_no_attachments': - app('log')->debug('Set collector to filter on attachments.'); - $this->collector->hasAttachments(); - break; - case 'has_no_attachments': - case '-has_attachments': - app('log')->debug('Set collector to filter on NO attachments.'); - $this->collector->hasNoAttachments(); - break; - // - // categories - case '-has_any_category': - case 'has_no_category': - $this->collector->withoutCategory(); - break; - case '-has_no_category': - case 'has_any_category': - $this->collector->withCategory(); - break; - case 'category_is': - $category = $this->categoryRepository->findByName($value); - if (null !== $category) { - $this->collector->setCategory($category); - break; - } - $this->collector->findNothing(); - break; - case '-category_is': - $category = $this->categoryRepository->findByName($value); - if (null !== $category) { - $this->collector->excludeCategory($category); - break; - } - break; - case 'category_ends': - $result = $this->categoryRepository->categoryEndsWith($value, 1337); - if ($result->count() > 0) { - $this->collector->setCategories($result); - } - if (0 === $result->count()) { - $this->collector->findNothing(); - } - break; - case '-category_ends': - $result = $this->categoryRepository->categoryEndsWith($value, 1337); - if ($result->count() > 0) { - $this->collector->excludeCategories($result); - } - if (0 === $result->count()) { - $this->collector->findNothing(); - } - break; - case 'category_starts': - $result = $this->categoryRepository->categoryStartsWith($value, 1337); - if ($result->count() > 0) { - $this->collector->setCategories($result); - } - if (0 === $result->count()) { - $this->collector->findNothing(); - } - break; - case '-category_starts': - $result = $this->categoryRepository->categoryStartsWith($value, 1337); - if ($result->count() > 0) { - $this->collector->excludeCategories($result); - } - if (0 === $result->count()) { - $this->collector->findNothing(); - } - break; - case 'category_contains': - $result = $this->categoryRepository->searchCategory($value, 1337); - if ($result->count() > 0) { - $this->collector->setCategories($result); - } - if (0 === $result->count()) { - $this->collector->findNothing(); - } - break; - case '-category_contains': - $result = $this->categoryRepository->searchCategory($value, 1337); - if ($result->count() > 0) { - $this->collector->excludeCategories($result); - } - if (0 === $result->count()) { - $this->collector->findNothing(); - } - break; - // - // budgets - // - case '-has_any_budget': - case 'has_no_budget': - $this->collector->withoutBudget(); - break; - case 'has_any_budget': - case '-has_no_budget': - $this->collector->withBudget(); - break; - case 'budget_contains': - $result = $this->budgetRepository->searchBudget($value, 1337); - if ($result->count() > 0) { - $this->collector->setBudgets($result); - } - if (0 === $result->count()) { - $this->collector->findNothing(); - } - break; - case '-budget_contains': - $result = $this->budgetRepository->searchBudget($value, 1337); - if ($result->count() > 0) { - $this->collector->excludeBudgets($result); - } - if (0 === $result->count()) { - $this->collector->findNothing(); - } - break; - case 'budget_is': - $budget = $this->budgetRepository->findByName($value); - if (null !== $budget) { - $this->collector->setBudget($budget); - break; - } - $this->collector->findNothing(); - break; - case '-budget_is': - $budget = $this->budgetRepository->findByName($value); - if (null !== $budget) { - $this->collector->excludeBudget($budget); - break; - } - $this->collector->findNothing(); - break; - case 'budget_ends': - $result = $this->budgetRepository->budgetEndsWith($value, 1337); - if ($result->count() > 0) { - $this->collector->setBudgets($result); - } - if (0 === $result->count()) { - $this->collector->findNothing(); - } - break; - case '-budget_ends': - $result = $this->budgetRepository->budgetEndsWith($value, 1337); - if ($result->count() > 0) { - $this->collector->excludeBudgets($result); - } - if (0 === $result->count()) { - $this->collector->findNothing(); - } - break; - case 'budget_starts': - $result = $this->budgetRepository->budgetStartsWith($value, 1337); - if ($result->count() > 0) { - $this->collector->setBudgets($result); - } - if (0 === $result->count()) { - $this->collector->findNothing(); - } - break; - case '-budget_starts': - $result = $this->budgetRepository->budgetStartsWith($value, 1337); - if ($result->count() > 0) { - $this->collector->excludeBudgets($result); - } - if (0 === $result->count()) { - $this->collector->findNothing(); - } - break; - // - // bill - // - case '-has_any_bill': - case 'has_no_bill': - $this->collector->withoutBill(); - break; - case '-has_no_bill': - case 'has_any_bill': - $this->collector->withBill(); - break; - case 'bill_contains': - $result = $this->billRepository->searchBill($value, 1337); - if ($result->count() > 0) { - $this->collector->setBills($result); - break; - } - $this->collector->findNothing(); - break; - case '-bill_contains': - $result = $this->billRepository->searchBill($value, 1337); - if ($result->count() > 0) { - $this->collector->excludeBills($result); - break; - } - $this->collector->findNothing(); - break; - case 'bill_is': - $bill = $this->billRepository->findByName($value); - if (null !== $bill) { - $this->collector->setBill($bill); - break; - } - $this->collector->findNothing(); - break; - case '-bill_is': - $bill = $this->billRepository->findByName($value); - if (null !== $bill) { - $this->collector->excludeBills(new Collection([$bill])); - break; - } - $this->collector->findNothing(); - break; - case 'bill_ends': - $result = $this->billRepository->billEndsWith($value, 1337); - if ($result->count() > 0) { - $this->collector->setBills($result); - } - if (0 === $result->count()) { - $this->collector->findNothing(); - } - break; - case '-bill_ends': - $result = $this->billRepository->billEndsWith($value, 1337); - if ($result->count() > 0) { - $this->collector->excludeBills($result); - } - if (0 === $result->count()) { - $this->collector->findNothing(); - } - break; - case 'bill_starts': - $result = $this->billRepository->billStartsWith($value, 1337); - if ($result->count() > 0) { - $this->collector->setBills($result); - } - if (0 === $result->count()) { - $this->collector->findNothing(); - } - break; - case '-bill_starts': - $result = $this->billRepository->billStartsWith($value, 1337); - if ($result->count() > 0) { - $this->collector->excludeBills($result); - } - if (0 === $result->count()) { - $this->collector->findNothing(); - } - break; - // - // tags - // - case '-has_any_tag': - case 'has_no_tag': - $this->collector->withoutTags(); - break; - case '-has_no_tag': - case 'has_any_tag': - $this->collector->hasAnyTag(); - break; - case '-tag_is_not': - case 'tag_is': - $result = $this->tagRepository->findByTag($value); - if (null !== $result) { - $this->includeTags[] = $result->id; - $this->includeTags = array_unique($this->includeTags); - } - // no tags found means search must result in nothing. - if (null === $result) { - app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); - $this->collector->findNothing(); - } - break; - case 'tag_contains': - $tags = $this->tagRepository->searchTag($value); - if (0 === $tags->count()) { - app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); - $this->collector->findNothing(); - } - if ($tags->count() > 0) { - $ids = array_values($tags->pluck('id')->toArray()); - $this->includeTags = array_unique(array_merge($this->includeTags, $ids)); - } - break; - case 'tag_starts': - $tags = $this->tagRepository->tagStartsWith($value); - if (0 === $tags->count()) { - app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); - $this->collector->findNothing(); - } - if ($tags->count() > 0) { - $ids = array_values($tags->pluck('id')->toArray()); - $this->includeTags = array_unique(array_merge($this->includeTags, $ids)); - } - break; - case '-tag_starts': - $tags = $this->tagRepository->tagStartsWith($value); - if (0 === $tags->count()) { - app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); - $this->collector->findNothing(); - } - if ($tags->count() > 0) { - $ids = array_values($tags->pluck('id')->toArray()); - $this->excludeTags = array_unique(array_merge($this->includeTags, $ids)); - } - break; - case 'tag_ends': - $tags = $this->tagRepository->tagEndsWith($value); - if (0 === $tags->count()) { - app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); - $this->collector->findNothing(); - } - if ($tags->count() > 0) { - $ids = array_values($tags->pluck('id')->toArray()); - $this->includeTags = array_unique(array_merge($this->includeTags, $ids)); - } - break; - case '-tag_ends': - $tags = $this->tagRepository->tagEndsWith($value); - if (0 === $tags->count()) { - app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); - $this->collector->findNothing(); - } - if ($tags->count() > 0) { - $ids = array_values($tags->pluck('id')->toArray()); - $this->excludeTags = array_unique(array_merge($this->includeTags, $ids)); - } - break; - case '-tag_contains': - $tags = $this->tagRepository->searchTag($value)->keyBy('id'); - - if (0 === $tags->count()) { - app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); - $this->collector->findNothing(); - } - if ($tags->count() > 0) { - $ids = array_values($tags->pluck('id')->toArray()); - $this->excludeTags = array_unique(array_merge($this->excludeTags, $ids)); - } - break; - case '-tag_is': - case 'tag_is_not': - $result = $this->tagRepository->findByTag($value); - if (null !== $result) { - $this->excludeTags[] = $result->id; - $this->excludeTags = array_unique($this->excludeTags); - } - break; - // - // notes - // - case 'notes_contains': - $this->collector->notesContain($value); - break; - case '-notes_contains': - $this->collector->notesDoNotContain($value); - break; - case 'notes_starts': - $this->collector->notesStartWith($value); - break; - case '-notes_starts': - $this->collector->notesDontStartWith($value); - break; - case 'notes_ends': - $this->collector->notesEndWith($value); - break; - case '-notes_ends': - $this->collector->notesDontEndWith($value); - break; - case 'notes_is': - $this->collector->notesExactly($value); - break; - case '-notes_is': - $this->collector->notesExactlyNot($value); - break; - case '-any_notes': - case 'no_notes': - $this->collector->withoutNotes(); - break; - case 'any_notes': - case '-no_notes': - $this->collector->withAnyNotes(); - break; - case 'reconciled': - $this->collector->isReconciled(); - break; - case '-reconciled': - $this->collector->isNotReconciled(); - break; - // - // amount - // - case 'amount_is': - // strip comma's, make dots. - app('log')->debug(sprintf('Original value "%s"', $value)); - $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); - app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); - $this->collector->amountIs($amount); - break; - case '-amount_is': - // strip comma's, make dots. - app('log')->debug(sprintf('Original value "%s"', $value)); - $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); - app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); - $this->collector->amountIsNot($amount); - break; - case 'foreign_amount_is': - - // strip comma's, make dots. - $value = str_replace(',', '.', $value); - - $amount = app('steam')->positive($value); - app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); - $this->collector->foreignAmountIs($amount); - break; - case '-foreign_amount_is': - - // strip comma's, make dots. - $value = str_replace(',', '.', $value); - - $amount = app('steam')->positive($value); - app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); - $this->collector->foreignAmountIsNot($amount); - break; - case '-amount_more': - case 'amount_less': - // strip comma's, make dots. - $value = str_replace(',', '.', $value); - - $amount = app('steam')->positive($value); - app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); - $this->collector->amountLess($amount); - break; - case '-foreign_amount_more': - case 'foreign_amount_less': - // strip comma's, make dots. - $value = str_replace(',', '.', $value); - - $amount = app('steam')->positive($value); - app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); - $this->collector->foreignAmountLess($amount); - break; - case '-amount_less': - case 'amount_more': - app('log')->debug(sprintf('Now handling operator "%s"', $operator)); - // strip comma's, make dots. - $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); - app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); - $this->collector->amountMore($amount); - break; - case '-foreign_amount_less': - case 'foreign_amount_more': - app('log')->debug(sprintf('Now handling operator "%s"', $operator)); - // strip comma's, make dots. - $value = str_replace(',', '.', $value); - $amount = app('steam')->positive($value); - app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); - $this->collector->foreignAmountMore($amount); - break; - // - // transaction type - // - case 'transaction_type': - $this->collector->setTypes([ucfirst($value)]); - app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - break; - case '-transaction_type': - $this->collector->excludeTypes([ucfirst($value)]); - app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - break; - // - // dates - // - case '-date_on': - case 'date_on': - $range = $this->parseDateRange($operator, $value); - $this->setExactDateParams($range, $prohibited); - return false; - case 'date_before': - case '-date_after': - $range = $this->parseDateRange($operator, $value); - $this->setDateBeforeParams($range); - return false; - case 'date_after': - case '-date_before': - $range = $this->parseDateRange($operator, $value); - $this->setDateAfterParams($range); - return false; - - case 'interest_date_on': - case '-interest_date_on': - $range = $this->parseDateRange($operator, $value); - $this->setExactMetaDateParams('interest_date', $range, $prohibited); - return false; - case 'interest_date_before': - case '-interest_date_after': - $range = $this->parseDateRange($operator, $value); - $this->setMetaDateBeforeParams('interest_date', $range); - return false; - case 'interest_date_after': - case '-interest_date_before': - $range = $this->parseDateRange($operator, $value); - $this->setMetaDateAfterParams('interest_date', $range); - return false; - - case 'book_date_on': - case '-book_date_on': - $range = $this->parseDateRange($operator, $value); - $this->setExactMetaDateParams('book_date', $range, $prohibited); - return false; - case 'book_date_before': - case '-book_date_after': - $range = $this->parseDateRange($operator, $value); - $this->setMetaDateBeforeParams('book_date', $range); - return false; - case 'book_date_after': - case '-book_date_before': - $range = $this->parseDateRange($operator, $value); - $this->setMetaDateAfterParams('book_date', $range); - return false; - - case 'process_date_on': - case '-process_date_on': - $range = $this->parseDateRange($operator, $value); - $this->setExactMetaDateParams('process_date', $range, $prohibited); - return false; - case 'process_date_before': - case '-process_date_after': - $range = $this->parseDateRange($operator, $value); - $this->setMetaDateBeforeParams('process_date', $range); - return false; - case 'process_date_after': - case '-process_date_before': - $range = $this->parseDateRange($operator, $value); - $this->setMetaDateAfterParams('process_date', $range); - return false; - - case 'due_date_on': - case '-due_date_on': - $range = $this->parseDateRange($operator, $value); - $this->setExactMetaDateParams('due_date', $range, $prohibited); - return false; - case 'due_date_before': - case '-due_date_after': - $range = $this->parseDateRange($operator, $value); - $this->setMetaDateBeforeParams('due_date', $range); - return false; - case 'due_date_after': - case '-due_date_before': - $range = $this->parseDateRange($operator, $value); - $this->setMetaDateAfterParams('due_date', $range); - return false; - - case 'payment_date_on': - case '-payment_date_on': - $range = $this->parseDateRange($operator, $value); - $this->setExactMetaDateParams('payment_date', $range, $prohibited); - return false; - case 'payment_date_before': - case '-payment_date_after': - $range = $this->parseDateRange($operator, $value); - $this->setMetaDateBeforeParams('payment_date', $range); - return false; - case 'payment_date_after': - case '-payment_date_before': - $range = $this->parseDateRange($operator, $value); - $this->setMetaDateAfterParams('payment_date', $range); - return false; - - case 'invoice_date_on': - case '-invoice_date_on': - $range = $this->parseDateRange($operator, $value); - $this->setExactMetaDateParams('invoice_date', $range, $prohibited); - return false; - case 'invoice_date_before': - case '-invoice_date_after': - $range = $this->parseDateRange($operator, $value); - $this->setMetaDateBeforeParams('invoice_date', $range); - return false; - case 'invoice_date_after': - case '-invoice_date_before': - $range = $this->parseDateRange($operator, $value); - $this->setMetaDateAfterParams('invoice_date', $range); - return false; - - case 'created_at_on': - case '-created_at_on': - app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); - $this->setExactObjectDateParams('created_at', $range, $prohibited); - return false; - case 'created_at_before': - case '-created_at_after': - app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); - $this->setObjectDateBeforeParams('created_at', $range); - return false; - case 'created_at_after': - case '-created_at_before': - app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); - $this->setObjectDateAfterParams('created_at', $range); - return false; - - case 'updated_at_on': - case '-updated_at_on': - app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); - $this->setExactObjectDateParams('updated_at', $range, $prohibited); - return false; - case 'updated_at_before': - case '-updated_at_after': - app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); - $this->setObjectDateBeforeParams('updated_at', $range); - return false; - case 'updated_at_after': - case '-updated_at_before': - app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); - $range = $this->parseDateRange($operator, $value); - $this->setObjectDateAfterParams('updated_at', $range); - return false; - // - // external URL - // - case '-any_external_url': - case 'no_external_url': - $this->collector->withoutExternalUrl(); - break; - case '-no_external_url': - case 'any_external_url': - $this->collector->withExternalUrl(); - break; - case '-any_external_id': - case 'no_external_id': - $this->collector->withoutExternalId(); - break; - case '-no_external_id': - case 'any_external_id': - $this->collector->withExternalId(); - break; - - case 'external_url_is': - $this->collector->setExternalUrl($value); - break; - case '-external_url_is': - $this->collector->excludeExternalUrl($value); - break; - case 'external_url_contains': - $this->collector->externalUrlContains($value); - break; - case '-external_url_contains': - $this->collector->externalUrlDoesNotContain($value); - break; - case 'external_url_starts': - $this->collector->externalUrlStarts($value); - break; - case '-external_url_starts': - $this->collector->externalUrlDoesNotStart($value); - break; - case 'external_url_ends': - $this->collector->externalUrlEnds($value); - break; - case '-external_url_ends': - $this->collector->externalUrlDoesNotEnd($value); - break; - - // - // other fields - // - case 'external_id_is': - $this->collector->setExternalId($value); - break; - case '-external_id_is': - $this->collector->excludeExternalId($value); - break; - case 'recurrence_id': - $this->collector->setRecurrenceId($value); - break; - case '-recurrence_id': - $this->collector->excludeRecurrenceId($value); - break; - case 'external_id_contains': - $this->collector->externalIdContains($value); - break; - case '-external_id_contains': - $this->collector->externalIdDoesNotContain($value); - break; - case 'external_id_starts': - $this->collector->externalIdStarts($value); - break; - case '-external_id_starts': - $this->collector->externalIdDoesNotStart($value); - break; - case 'external_id_ends': - $this->collector->externalIdEnds($value); - break; - case '-external_id_ends': - $this->collector->externalIdDoesNotEnd($value); - break; - - case 'internal_reference_is': - $this->collector->setInternalReference($value); - break; - case '-internal_reference_is': - $this->collector->excludeInternalReference($value); - break; - case 'internal_reference_contains': - $this->collector->internalReferenceContains($value); - break; - case '-internal_reference_contains': - $this->collector->internalReferenceDoesNotContain($value); - break; - case 'internal_reference_starts': - $this->collector->internalReferenceStarts($value); - break; - case '-internal_reference_starts': - $this->collector->internalReferenceDoesNotStart($value); - break; - case 'internal_reference_ends': - $this->collector->internalReferenceEnds($value); - break; - case '-internal_reference_ends': - $this->collector->internalReferenceDoesNotEnd($value); - break; - - case 'attachment_name_is': - $this->collector->attachmentNameIs($value); - break; - case '-attachment_name_is': - $this->collector->attachmentNameIsNot($value); - break; - case 'attachment_name_contains': - $this->collector->attachmentNameContains($value); - break; - case '-attachment_name_contains': - $this->collector->attachmentNameDoesNotContain($value); - break; - case 'attachment_name_starts': - $this->collector->attachmentNameStarts($value); - break; - case '-attachment_name_starts': - $this->collector->attachmentNameDoesNotStart($value); - break; - case 'attachment_name_ends': - $this->collector->attachmentNameEnds($value); - break; - case '-attachment_name_ends': - $this->collector->attachmentNameDoesNotEnd($value); - break; - - case 'attachment_notes_are': - $this->collector->attachmentNotesAre($value); - break; - case '-attachment_notes_are': - $this->collector->attachmentNotesAreNot($value); - break; - case 'attachment_notes_contains': - $this->collector->attachmentNotesContains($value); - break; - case '-attachment_notes_contains': - $this->collector->attachmentNotesDoNotContain($value); - break; - case 'attachment_notes_starts': - $this->collector->attachmentNotesStarts($value); - break; - case '-attachment_notes_starts': - $this->collector->attachmentNotesDoNotStart($value); - break; - case 'attachment_notes_ends': - $this->collector->attachmentNotesEnds($value); - break; - case '-attachment_notes_ends': - $this->collector->attachmentNotesDoNotEnd($value); - break; - case 'exists': - $this->collector->exists(); - break; - case '-exists': - $this->collector->findNothing(); - break; - case 'sepa_ct_is': - $this->collector->setSepaCT($value); - break; - } - return true; - } - - /** - * @param string $operator - * - * @return string * @throws FireflyException */ public static function getRootOperator(string $operator): string @@ -1435,14 +192,1718 @@ class OperatorQuerySearch implements SearchInterface return $original; } + public function searchTime(): float + { + return microtime(true) - $this->startTime; + } + + public function searchTransactions(): LengthAwarePaginator + { + if (0 === count($this->getWords()) && 0 === count($this->getOperators())) { + return new LengthAwarePaginator([], 0, 5, 1); + } + + return $this->collector->getPaginatedGroups(); + } + + public function getWords(): array + { + return $this->words; + } + + public function setDate(Carbon $date): void + { + $this->date = $date; + } + + public function setPage(int $page): void + { + $this->page = $page; + $this->collector->setPage($this->page); + } + + public function setUser(User $user): void + { + $this->accountRepository->setUser($user); + $this->billRepository->setUser($user); + $this->categoryRepository->setUser($user); + $this->budgetRepository->setUser($user); + $this->tagRepository->setUser($user); + $this->collector = app(GroupCollectorInterface::class); + $this->collector->setUser($user); + $this->collector->withAccountInformation()->withCategoryInformation()->withBudgetInformation(); + + $this->setLimit((int) app('preferences')->getForUser($user, 'listPageSize', 50)->data); + } + + public function setLimit(int $limit): void + { + $this->limit = $limit; + $this->collector->setLimit($this->limit); + } + + /** + * @throws FireflyException + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + private function handleSearchNode(Node $searchNode): void + { + $class = get_class($searchNode); + app('log')->debug(sprintf('Now in handleSearchNode(%s)', $class)); + + switch ($class) { + default: + app('log')->error(sprintf('Cannot handle node %s', $class)); + + 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) { // @phpstan-ignore-line PHPStan thinks getNodes() does not exist but it does. + $this->handleSearchNode($subNode); // let's hope it's not too recursive + } + + break; + + case Word::class: + case Phrase::class: + case Numbr::class: + case Url::class: + case Date::class: + case Hashtag::class: + case Emoticon::class: + case Emoji::class: + case Mention::class: + $allWords = (string) $searchNode->getValue(); + app('log')->debug(sprintf('Add words "%s" to search string, because Node class is "%s"', $allWords, $class)); + $this->words[] = $allWords; + + break; + + case Field::class: + app('log')->debug(sprintf('Now handle Node class %s', $class)); + + /** @var Field $searchNode */ + // used to search for x:y + $operator = strtolower($searchNode->getValue()); + $value = $searchNode->getNode()->getValue(); + $prohibited = BoolOperator::PROHIBITED === $searchNode->getBoolOperator(); + $context = config(sprintf('search.operators.%s.needs_context', $operator)); + + // is an operator that needs no context, and value is false, then prohibited = true. + if ('false' === $value && in_array($operator, $this->validOperators, true) && false === $context && !$prohibited) { + $prohibited = true; + $value = 'true'; + } + // if the operator is prohibited, but the value is false, do an uno reverse + if ('false' === $value && $prohibited && in_array($operator, $this->validOperators, true) && false === $context) { + $prohibited = false; + $value = 'true'; + } + + // must be valid operator: + if ( + in_array($operator, $this->validOperators, true) + && $this->updateCollector($operator, (string) $value, $prohibited)) { + $this->operators->push( + [ + 'type' => self::getRootOperator($operator), + 'value' => (string) $value, + 'prohibited' => $prohibited, + ] + ); + app('log')->debug(sprintf('Added operator type "%s"', $operator)); + } + if (!in_array($operator, $this->validOperators, true)) { + app('log')->debug(sprintf('Added INVALID operator type "%s"', $operator)); + $this->invalidOperators[] = [ + 'type' => $operator, + 'value' => (string) $value, + ]; + } + } + } + + /** + * @throws FireflyException + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + private function updateCollector(string $operator, string $value, bool $prohibited): bool + { + if ($prohibited) { + app('log')->debug(sprintf('Operator "%s" is now "%s"', $operator, sprintf('-%s', $operator))); + $operator = sprintf('-%s', $operator); + } + + app('log')->debug(sprintf('Now in updateCollector("%s", "%s")', $operator, $value)); + + // check if alias, replace if necessary: + $operator = self::getRootOperator($operator); + + switch ($operator) { + default: + app('log')->error(sprintf('No such operator: %s', $operator)); + + throw new FireflyException(sprintf('Unsupported search operator: "%s"', $operator)); + + // some search operators are ignored, basically: + case 'user_action': + app('log')->info(sprintf('Ignore search operator "%s"', $operator)); + + return false; + + // + // all account related searches: + // + case 'account_is': + $this->searchAccount($value, SearchDirection::BOTH, StringPosition::IS); + + break; + + case '-account_is': + $this->searchAccount($value, SearchDirection::BOTH, StringPosition::IS, true); + + break; + + case 'account_contains': + $this->searchAccount($value, SearchDirection::BOTH, StringPosition::CONTAINS); + + break; + + case '-account_contains': + $this->searchAccount($value, SearchDirection::BOTH, StringPosition::CONTAINS, true); + + break; + + case 'account_ends': + $this->searchAccount($value, SearchDirection::BOTH, StringPosition::ENDS); + + break; + + case '-account_ends': + $this->searchAccount($value, SearchDirection::BOTH, StringPosition::ENDS, true); + + break; + + case 'account_starts': + $this->searchAccount($value, SearchDirection::BOTH, StringPosition::STARTS); + + break; + + case '-account_starts': + $this->searchAccount($value, SearchDirection::BOTH, StringPosition::STARTS, true); + + break; + + case 'account_nr_is': + $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::IS); + + break; + + case '-account_nr_is': + $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::IS, true); + + break; + + case 'account_nr_contains': + $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::CONTAINS); + + break; + + case '-account_nr_contains': + $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::CONTAINS, true); + + break; + + case 'account_nr_ends': + $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::ENDS); + + break; + + case '-account_nr_ends': + $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::ENDS, true); + + break; + + case 'account_nr_starts': + $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::STARTS); + + break; + + case '-account_nr_starts': + $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::STARTS, true); + + break; + + case 'source_account_starts': + $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::STARTS); + + break; + + case '-source_account_starts': + $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::STARTS, true); + + break; + + case 'source_account_ends': + $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::ENDS); + + break; + + case '-source_account_ends': + $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::ENDS, true); + + break; + + case 'source_account_is': + $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::IS); + + break; + + case '-source_account_is': + $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::IS, true); + + break; + + case 'source_account_nr_starts': + $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::STARTS); + + break; + + case '-source_account_nr_starts': + $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::STARTS, true); + + break; + + case 'source_account_nr_ends': + $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::ENDS); + + break; + + case '-source_account_nr_ends': + $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::ENDS, true); + + break; + + case 'source_account_nr_is': + $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::IS); + + break; + + case '-source_account_nr_is': + $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::IS, true); + + break; + + case 'source_account_nr_contains': + $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::CONTAINS); + + break; + + case '-source_account_nr_contains': + $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::CONTAINS, true); + + break; + + case 'source_account_contains': + $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::CONTAINS); + + break; + + case '-source_account_contains': + $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::CONTAINS, true); + + break; + + case 'source_account_id': + $account = $this->accountRepository->find((int) $value); + if (null !== $account) { + $this->collector->setSourceAccounts(new Collection([$account])); + } + if (null === $account) { + // since the source does not exist, cannot return results: + $this->collector->findNothing(); + } + + break; + + case '-source_account_id': + $account = $this->accountRepository->find((int) $value); + if (null !== $account) { + $this->collector->excludeSourceAccounts(new Collection([$account])); + } + if (null === $account) { + // since the source does not exist, cannot return results: + $this->collector->findNothing(); + } + + break; + + case 'journal_id': + $parts = explode(',', $value); + $this->collector->setJournalIds($parts); + + break; + + case '-journal_id': + $parts = explode(',', $value); + $this->collector->excludeJournalIds($parts); + + break; + + case 'id': + $parts = explode(',', $value); + $this->collector->setIds($parts); + + break; + + case '-id': + $parts = explode(',', $value); + $this->collector->excludeIds($parts); + + break; + + case 'destination_account_starts': + $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::STARTS); + + break; + + case '-destination_account_starts': + $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::STARTS, true); + + break; + + case 'destination_account_ends': + $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::ENDS); + + break; + + case '-destination_account_ends': + $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::ENDS, true); + + break; + + case 'destination_account_nr_starts': + $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::STARTS); + + break; + + case '-destination_account_nr_starts': + $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::STARTS, true); + + break; + + case 'destination_account_nr_ends': + $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::ENDS); + + break; + + case '-destination_account_nr_ends': + $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::ENDS, true); + + break; + + case 'destination_account_nr_is': + $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::IS); + + break; + + case '-destination_account_nr_is': + $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::IS, true); + + break; + + case 'destination_account_is': + $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::IS); + + break; + + case '-destination_account_is': + $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::IS, true); + + break; + + case 'destination_account_nr_contains': + $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::CONTAINS); + + break; + + case '-destination_account_nr_contains': + $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::CONTAINS, true); + + break; + + case 'destination_account_contains': + $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::CONTAINS); + + break; + + case '-destination_account_contains': + $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::CONTAINS, true); + + break; + + case 'destination_account_id': + $account = $this->accountRepository->find((int) $value); + if (null !== $account) { + $this->collector->setDestinationAccounts(new Collection([$account])); + } + if (null === $account) { + $this->collector->findNothing(); + } + + break; + + case '-destination_account_id': + $account = $this->accountRepository->find((int) $value); + if (null !== $account) { + $this->collector->excludeDestinationAccounts(new Collection([$account])); + } + if (null === $account) { + $this->collector->findNothing(); + } + + break; + + case 'account_id': + $parts = explode(',', $value); + $collection = new Collection(); + foreach ($parts as $accountId) { + $account = $this->accountRepository->find((int) $accountId); + if (null !== $account) { + $collection->push($account); + } + } + if ($collection->count() > 0) { + $this->collector->setAccounts($collection); + } + if (0 === $collection->count()) { + $this->collector->findNothing(); + } + + break; + + case '-account_id': + $parts = explode(',', $value); + $collection = new Collection(); + foreach ($parts as $accountId) { + $account = $this->accountRepository->find((int) $accountId); + if (null !== $account) { + $collection->push($account); + } + } + if ($collection->count() > 0) { + $this->collector->setNotAccounts($collection); + } + if (0 === $collection->count()) { + $this->collector->findNothing(); + } + + break; + + // + // cash account + // + case 'source_is_cash': + $account = $this->getCashAccount(); + $this->collector->setSourceAccounts(new Collection([$account])); + + break; + + case '-source_is_cash': + $account = $this->getCashAccount(); + $this->collector->excludeSourceAccounts(new Collection([$account])); + + break; + + case 'destination_is_cash': + $account = $this->getCashAccount(); + $this->collector->setDestinationAccounts(new Collection([$account])); + + break; + + case '-destination_is_cash': + $account = $this->getCashAccount(); + $this->collector->excludeDestinationAccounts(new Collection([$account])); + + break; + + case 'account_is_cash': + $account = $this->getCashAccount(); + $this->collector->setAccounts(new Collection([$account])); + + break; + + case '-account_is_cash': + $account = $this->getCashAccount(); + $this->collector->excludeAccounts(new Collection([$account])); + + break; + + // + // description + // + case 'description_starts': + $this->collector->descriptionStarts([$value]); + + break; + + case '-description_starts': + $this->collector->descriptionDoesNotStart([$value]); + + break; + + case 'description_ends': + $this->collector->descriptionEnds([$value]); + + break; + + case '-description_ends': + $this->collector->descriptionDoesNotEnd([$value]); + + break; + + case 'description_contains': + $this->words[] = $value; + + return false; + + case '-description_contains': + $this->prohibitedWords[] = $value; + + break; + + case 'description_is': + $this->collector->descriptionIs($value); + + break; + + case '-description_is': + $this->collector->descriptionIsNot($value); + + break; + + // + // currency + // + case 'currency_is': + $currency = $this->findCurrency($value); + if (null !== $currency) { + $this->collector->setCurrency($currency); + } + if (null === $currency) { + $this->collector->findNothing(); + } + + break; + + case '-currency_is': + $currency = $this->findCurrency($value); + if (null !== $currency) { + $this->collector->excludeCurrency($currency); + } + if (null === $currency) { + $this->collector->findNothing(); + } + + break; + + case 'foreign_currency_is': + $currency = $this->findCurrency($value); + if (null !== $currency) { + $this->collector->setForeignCurrency($currency); + } + if (null === $currency) { + $this->collector->findNothing(); + } + + break; + + case '-foreign_currency_is': + $currency = $this->findCurrency($value); + if (null !== $currency) { + $this->collector->excludeForeignCurrency($currency); + } + if (null === $currency) { + $this->collector->findNothing(); + } + + break; + + // + // attachments + // + case 'has_attachments': + case '-has_no_attachments': + app('log')->debug('Set collector to filter on attachments.'); + $this->collector->hasAttachments(); + + break; + + case 'has_no_attachments': + case '-has_attachments': + app('log')->debug('Set collector to filter on NO attachments.'); + $this->collector->hasNoAttachments(); + + break; + + // + // categories + case '-has_any_category': + case 'has_no_category': + $this->collector->withoutCategory(); + + break; + + case '-has_no_category': + case 'has_any_category': + $this->collector->withCategory(); + + break; + + case 'category_is': + $category = $this->categoryRepository->findByName($value); + if (null !== $category) { + $this->collector->setCategory($category); + + break; + } + $this->collector->findNothing(); + + break; + + case '-category_is': + $category = $this->categoryRepository->findByName($value); + if (null !== $category) { + $this->collector->excludeCategory($category); + + break; + } + + break; + + case 'category_ends': + $result = $this->categoryRepository->categoryEndsWith($value, 1337); + if ($result->count() > 0) { + $this->collector->setCategories($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + + break; + + case '-category_ends': + $result = $this->categoryRepository->categoryEndsWith($value, 1337); + if ($result->count() > 0) { + $this->collector->excludeCategories($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + + break; + + case 'category_starts': + $result = $this->categoryRepository->categoryStartsWith($value, 1337); + if ($result->count() > 0) { + $this->collector->setCategories($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + + break; + + case '-category_starts': + $result = $this->categoryRepository->categoryStartsWith($value, 1337); + if ($result->count() > 0) { + $this->collector->excludeCategories($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + + break; + + case 'category_contains': + $result = $this->categoryRepository->searchCategory($value, 1337); + if ($result->count() > 0) { + $this->collector->setCategories($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + + break; + + case '-category_contains': + $result = $this->categoryRepository->searchCategory($value, 1337); + if ($result->count() > 0) { + $this->collector->excludeCategories($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + + break; + + // + // budgets + // + case '-has_any_budget': + case 'has_no_budget': + $this->collector->withoutBudget(); + + break; + + case 'has_any_budget': + case '-has_no_budget': + $this->collector->withBudget(); + + break; + + case 'budget_contains': + $result = $this->budgetRepository->searchBudget($value, 1337); + if ($result->count() > 0) { + $this->collector->setBudgets($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + + break; + + case '-budget_contains': + $result = $this->budgetRepository->searchBudget($value, 1337); + if ($result->count() > 0) { + $this->collector->excludeBudgets($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + + break; + + case 'budget_is': + $budget = $this->budgetRepository->findByName($value); + if (null !== $budget) { + $this->collector->setBudget($budget); + + break; + } + $this->collector->findNothing(); + + break; + + case '-budget_is': + $budget = $this->budgetRepository->findByName($value); + if (null !== $budget) { + $this->collector->excludeBudget($budget); + + break; + } + $this->collector->findNothing(); + + break; + + case 'budget_ends': + $result = $this->budgetRepository->budgetEndsWith($value, 1337); + if ($result->count() > 0) { + $this->collector->setBudgets($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + + break; + + case '-budget_ends': + $result = $this->budgetRepository->budgetEndsWith($value, 1337); + if ($result->count() > 0) { + $this->collector->excludeBudgets($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + + break; + + case 'budget_starts': + $result = $this->budgetRepository->budgetStartsWith($value, 1337); + if ($result->count() > 0) { + $this->collector->setBudgets($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + + break; + + case '-budget_starts': + $result = $this->budgetRepository->budgetStartsWith($value, 1337); + if ($result->count() > 0) { + $this->collector->excludeBudgets($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + + break; + + // + // bill + // + case '-has_any_bill': + case 'has_no_bill': + $this->collector->withoutBill(); + + break; + + case '-has_no_bill': + case 'has_any_bill': + $this->collector->withBill(); + + break; + + case 'bill_contains': + $result = $this->billRepository->searchBill($value, 1337); + if ($result->count() > 0) { + $this->collector->setBills($result); + + break; + } + $this->collector->findNothing(); + + break; + + case '-bill_contains': + $result = $this->billRepository->searchBill($value, 1337); + if ($result->count() > 0) { + $this->collector->excludeBills($result); + + break; + } + $this->collector->findNothing(); + + break; + + case 'bill_is': + $bill = $this->billRepository->findByName($value); + if (null !== $bill) { + $this->collector->setBill($bill); + + break; + } + $this->collector->findNothing(); + + break; + + case '-bill_is': + $bill = $this->billRepository->findByName($value); + if (null !== $bill) { + $this->collector->excludeBills(new Collection([$bill])); + + break; + } + $this->collector->findNothing(); + + break; + + case 'bill_ends': + $result = $this->billRepository->billEndsWith($value, 1337); + if ($result->count() > 0) { + $this->collector->setBills($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + + break; + + case '-bill_ends': + $result = $this->billRepository->billEndsWith($value, 1337); + if ($result->count() > 0) { + $this->collector->excludeBills($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + + break; + + case 'bill_starts': + $result = $this->billRepository->billStartsWith($value, 1337); + if ($result->count() > 0) { + $this->collector->setBills($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + + break; + + case '-bill_starts': + $result = $this->billRepository->billStartsWith($value, 1337); + if ($result->count() > 0) { + $this->collector->excludeBills($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + + break; + + // + // tags + // + case '-has_any_tag': + case 'has_no_tag': + $this->collector->withoutTags(); + + break; + + case '-has_no_tag': + case 'has_any_tag': + $this->collector->hasAnyTag(); + + break; + + case '-tag_is_not': + case 'tag_is': + $result = $this->tagRepository->findByTag($value); + if (null !== $result) { + $this->includeTags[] = $result->id; + $this->includeTags = array_unique($this->includeTags); + } + // no tags found means search must result in nothing. + if (null === $result) { + app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); + $this->collector->findNothing(); + } + + break; + + case 'tag_contains': + $tags = $this->tagRepository->searchTag($value); + if (0 === $tags->count()) { + app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); + $this->collector->findNothing(); + } + if ($tags->count() > 0) { + $ids = array_values($tags->pluck('id')->toArray()); + $this->includeTags = array_unique(array_merge($this->includeTags, $ids)); + } + + break; + + case 'tag_starts': + $tags = $this->tagRepository->tagStartsWith($value); + if (0 === $tags->count()) { + app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); + $this->collector->findNothing(); + } + if ($tags->count() > 0) { + $ids = array_values($tags->pluck('id')->toArray()); + $this->includeTags = array_unique(array_merge($this->includeTags, $ids)); + } + + break; + + case '-tag_starts': + $tags = $this->tagRepository->tagStartsWith($value); + if (0 === $tags->count()) { + app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); + $this->collector->findNothing(); + } + if ($tags->count() > 0) { + $ids = array_values($tags->pluck('id')->toArray()); + $this->excludeTags = array_unique(array_merge($this->includeTags, $ids)); + } + + break; + + case 'tag_ends': + $tags = $this->tagRepository->tagEndsWith($value); + if (0 === $tags->count()) { + app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); + $this->collector->findNothing(); + } + if ($tags->count() > 0) { + $ids = array_values($tags->pluck('id')->toArray()); + $this->includeTags = array_unique(array_merge($this->includeTags, $ids)); + } + + break; + + case '-tag_ends': + $tags = $this->tagRepository->tagEndsWith($value); + if (0 === $tags->count()) { + app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); + $this->collector->findNothing(); + } + if ($tags->count() > 0) { + $ids = array_values($tags->pluck('id')->toArray()); + $this->excludeTags = array_unique(array_merge($this->includeTags, $ids)); + } + + break; + + case '-tag_contains': + $tags = $this->tagRepository->searchTag($value)->keyBy('id'); + + if (0 === $tags->count()) { + app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); + $this->collector->findNothing(); + } + if ($tags->count() > 0) { + $ids = array_values($tags->pluck('id')->toArray()); + $this->excludeTags = array_unique(array_merge($this->excludeTags, $ids)); + } + + break; + + case '-tag_is': + case 'tag_is_not': + $result = $this->tagRepository->findByTag($value); + if (null !== $result) { + $this->excludeTags[] = $result->id; + $this->excludeTags = array_unique($this->excludeTags); + } + + break; + + // + // notes + // + case 'notes_contains': + $this->collector->notesContain($value); + + break; + + case '-notes_contains': + $this->collector->notesDoNotContain($value); + + break; + + case 'notes_starts': + $this->collector->notesStartWith($value); + + break; + + case '-notes_starts': + $this->collector->notesDontStartWith($value); + + break; + + case 'notes_ends': + $this->collector->notesEndWith($value); + + break; + + case '-notes_ends': + $this->collector->notesDontEndWith($value); + + break; + + case 'notes_is': + $this->collector->notesExactly($value); + + break; + + case '-notes_is': + $this->collector->notesExactlyNot($value); + + break; + + case '-any_notes': + case 'no_notes': + $this->collector->withoutNotes(); + + break; + + case 'any_notes': + case '-no_notes': + $this->collector->withAnyNotes(); + + break; + + case 'reconciled': + $this->collector->isReconciled(); + + break; + + case '-reconciled': + $this->collector->isNotReconciled(); + + break; + + // + // amount + // + case 'amount_is': + // strip comma's, make dots. + app('log')->debug(sprintf('Original value "%s"', $value)); + $value = str_replace(',', '.', $value); + $amount = app('steam')->positive($value); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); + $this->collector->amountIs($amount); + + break; + + case '-amount_is': + // strip comma's, make dots. + app('log')->debug(sprintf('Original value "%s"', $value)); + $value = str_replace(',', '.', $value); + $amount = app('steam')->positive($value); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); + $this->collector->amountIsNot($amount); + + break; + + case 'foreign_amount_is': + // strip comma's, make dots. + $value = str_replace(',', '.', $value); + + $amount = app('steam')->positive($value); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); + $this->collector->foreignAmountIs($amount); + + break; + + case '-foreign_amount_is': + // strip comma's, make dots. + $value = str_replace(',', '.', $value); + + $amount = app('steam')->positive($value); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); + $this->collector->foreignAmountIsNot($amount); + + break; + + case '-amount_more': + case 'amount_less': + // strip comma's, make dots. + $value = str_replace(',', '.', $value); + + $amount = app('steam')->positive($value); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); + $this->collector->amountLess($amount); + + break; + + case '-foreign_amount_more': + case 'foreign_amount_less': + // strip comma's, make dots. + $value = str_replace(',', '.', $value); + + $amount = app('steam')->positive($value); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); + $this->collector->foreignAmountLess($amount); + + break; + + case '-amount_less': + case 'amount_more': + app('log')->debug(sprintf('Now handling operator "%s"', $operator)); + // strip comma's, make dots. + $value = str_replace(',', '.', $value); + $amount = app('steam')->positive($value); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); + $this->collector->amountMore($amount); + + break; + + case '-foreign_amount_less': + case 'foreign_amount_more': + app('log')->debug(sprintf('Now handling operator "%s"', $operator)); + // strip comma's, make dots. + $value = str_replace(',', '.', $value); + $amount = app('steam')->positive($value); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); + $this->collector->foreignAmountMore($amount); + + break; + + // + // transaction type + // + case 'transaction_type': + $this->collector->setTypes([ucfirst($value)]); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); + + break; + + case '-transaction_type': + $this->collector->excludeTypes([ucfirst($value)]); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); + + break; + + // + // dates + // + case '-date_on': + case 'date_on': + $range = $this->parseDateRange($operator, $value); + $this->setExactDateParams($range, $prohibited); + + return false; + + case 'date_before': + case '-date_after': + $range = $this->parseDateRange($operator, $value); + $this->setDateBeforeParams($range); + + return false; + + case 'date_after': + case '-date_before': + $range = $this->parseDateRange($operator, $value); + $this->setDateAfterParams($range); + + return false; + + case 'interest_date_on': + case '-interest_date_on': + $range = $this->parseDateRange($operator, $value); + $this->setExactMetaDateParams('interest_date', $range, $prohibited); + + return false; + + case 'interest_date_before': + case '-interest_date_after': + $range = $this->parseDateRange($operator, $value); + $this->setMetaDateBeforeParams('interest_date', $range); + + return false; + + case 'interest_date_after': + case '-interest_date_before': + $range = $this->parseDateRange($operator, $value); + $this->setMetaDateAfterParams('interest_date', $range); + + return false; + + case 'book_date_on': + case '-book_date_on': + $range = $this->parseDateRange($operator, $value); + $this->setExactMetaDateParams('book_date', $range, $prohibited); + + return false; + + case 'book_date_before': + case '-book_date_after': + $range = $this->parseDateRange($operator, $value); + $this->setMetaDateBeforeParams('book_date', $range); + + return false; + + case 'book_date_after': + case '-book_date_before': + $range = $this->parseDateRange($operator, $value); + $this->setMetaDateAfterParams('book_date', $range); + + return false; + + case 'process_date_on': + case '-process_date_on': + $range = $this->parseDateRange($operator, $value); + $this->setExactMetaDateParams('process_date', $range, $prohibited); + + return false; + + case 'process_date_before': + case '-process_date_after': + $range = $this->parseDateRange($operator, $value); + $this->setMetaDateBeforeParams('process_date', $range); + + return false; + + case 'process_date_after': + case '-process_date_before': + $range = $this->parseDateRange($operator, $value); + $this->setMetaDateAfterParams('process_date', $range); + + return false; + + case 'due_date_on': + case '-due_date_on': + $range = $this->parseDateRange($operator, $value); + $this->setExactMetaDateParams('due_date', $range, $prohibited); + + return false; + + case 'due_date_before': + case '-due_date_after': + $range = $this->parseDateRange($operator, $value); + $this->setMetaDateBeforeParams('due_date', $range); + + return false; + + case 'due_date_after': + case '-due_date_before': + $range = $this->parseDateRange($operator, $value); + $this->setMetaDateAfterParams('due_date', $range); + + return false; + + case 'payment_date_on': + case '-payment_date_on': + $range = $this->parseDateRange($operator, $value); + $this->setExactMetaDateParams('payment_date', $range, $prohibited); + + return false; + + case 'payment_date_before': + case '-payment_date_after': + $range = $this->parseDateRange($operator, $value); + $this->setMetaDateBeforeParams('payment_date', $range); + + return false; + + case 'payment_date_after': + case '-payment_date_before': + $range = $this->parseDateRange($operator, $value); + $this->setMetaDateAfterParams('payment_date', $range); + + return false; + + case 'invoice_date_on': + case '-invoice_date_on': + $range = $this->parseDateRange($operator, $value); + $this->setExactMetaDateParams('invoice_date', $range, $prohibited); + + return false; + + case 'invoice_date_before': + case '-invoice_date_after': + $range = $this->parseDateRange($operator, $value); + $this->setMetaDateBeforeParams('invoice_date', $range); + + return false; + + case 'invoice_date_after': + case '-invoice_date_before': + $range = $this->parseDateRange($operator, $value); + $this->setMetaDateAfterParams('invoice_date', $range); + + return false; + + case 'created_at_on': + case '-created_at_on': + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); + $range = $this->parseDateRange($operator, $value); + $this->setExactObjectDateParams('created_at', $range, $prohibited); + + return false; + + case 'created_at_before': + case '-created_at_after': + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); + $range = $this->parseDateRange($operator, $value); + $this->setObjectDateBeforeParams('created_at', $range); + + return false; + + case 'created_at_after': + case '-created_at_before': + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); + $range = $this->parseDateRange($operator, $value); + $this->setObjectDateAfterParams('created_at', $range); + + return false; + + case 'updated_at_on': + case '-updated_at_on': + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); + $range = $this->parseDateRange($operator, $value); + $this->setExactObjectDateParams('updated_at', $range, $prohibited); + + return false; + + case 'updated_at_before': + case '-updated_at_after': + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); + $range = $this->parseDateRange($operator, $value); + $this->setObjectDateBeforeParams('updated_at', $range); + + return false; + + case 'updated_at_after': + case '-updated_at_before': + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); + $range = $this->parseDateRange($operator, $value); + $this->setObjectDateAfterParams('updated_at', $range); + + return false; + + // + // external URL + // + case '-any_external_url': + case 'no_external_url': + $this->collector->withoutExternalUrl(); + + break; + + case '-no_external_url': + case 'any_external_url': + $this->collector->withExternalUrl(); + + break; + + case '-any_external_id': + case 'no_external_id': + $this->collector->withoutExternalId(); + + break; + + case '-no_external_id': + case 'any_external_id': + $this->collector->withExternalId(); + + break; + + case 'external_url_is': + $this->collector->setExternalUrl($value); + + break; + + case '-external_url_is': + $this->collector->excludeExternalUrl($value); + + break; + + case 'external_url_contains': + $this->collector->externalUrlContains($value); + + break; + + case '-external_url_contains': + $this->collector->externalUrlDoesNotContain($value); + + break; + + case 'external_url_starts': + $this->collector->externalUrlStarts($value); + + break; + + case '-external_url_starts': + $this->collector->externalUrlDoesNotStart($value); + + break; + + case 'external_url_ends': + $this->collector->externalUrlEnds($value); + + break; + + case '-external_url_ends': + $this->collector->externalUrlDoesNotEnd($value); + + break; + + // + // other fields + // + case 'external_id_is': + $this->collector->setExternalId($value); + + break; + + case '-external_id_is': + $this->collector->excludeExternalId($value); + + break; + + case 'recurrence_id': + $this->collector->setRecurrenceId($value); + + break; + + case '-recurrence_id': + $this->collector->excludeRecurrenceId($value); + + break; + + case 'external_id_contains': + $this->collector->externalIdContains($value); + + break; + + case '-external_id_contains': + $this->collector->externalIdDoesNotContain($value); + + break; + + case 'external_id_starts': + $this->collector->externalIdStarts($value); + + break; + + case '-external_id_starts': + $this->collector->externalIdDoesNotStart($value); + + break; + + case 'external_id_ends': + $this->collector->externalIdEnds($value); + + break; + + case '-external_id_ends': + $this->collector->externalIdDoesNotEnd($value); + + break; + + case 'internal_reference_is': + $this->collector->setInternalReference($value); + + break; + + case '-internal_reference_is': + $this->collector->excludeInternalReference($value); + + break; + + case 'internal_reference_contains': + $this->collector->internalReferenceContains($value); + + break; + + case '-internal_reference_contains': + $this->collector->internalReferenceDoesNotContain($value); + + break; + + case 'internal_reference_starts': + $this->collector->internalReferenceStarts($value); + + break; + + case '-internal_reference_starts': + $this->collector->internalReferenceDoesNotStart($value); + + break; + + case 'internal_reference_ends': + $this->collector->internalReferenceEnds($value); + + break; + + case '-internal_reference_ends': + $this->collector->internalReferenceDoesNotEnd($value); + + break; + + case 'attachment_name_is': + $this->collector->attachmentNameIs($value); + + break; + + case '-attachment_name_is': + $this->collector->attachmentNameIsNot($value); + + break; + + case 'attachment_name_contains': + $this->collector->attachmentNameContains($value); + + break; + + case '-attachment_name_contains': + $this->collector->attachmentNameDoesNotContain($value); + + break; + + case 'attachment_name_starts': + $this->collector->attachmentNameStarts($value); + + break; + + case '-attachment_name_starts': + $this->collector->attachmentNameDoesNotStart($value); + + break; + + case 'attachment_name_ends': + $this->collector->attachmentNameEnds($value); + + break; + + case '-attachment_name_ends': + $this->collector->attachmentNameDoesNotEnd($value); + + break; + + case 'attachment_notes_are': + $this->collector->attachmentNotesAre($value); + + break; + + case '-attachment_notes_are': + $this->collector->attachmentNotesAreNot($value); + + break; + + case 'attachment_notes_contains': + $this->collector->attachmentNotesContains($value); + + break; + + case '-attachment_notes_contains': + $this->collector->attachmentNotesDoNotContain($value); + + break; + + case 'attachment_notes_starts': + $this->collector->attachmentNotesStarts($value); + + break; + + case '-attachment_notes_starts': + $this->collector->attachmentNotesDoNotStart($value); + + break; + + case 'attachment_notes_ends': + $this->collector->attachmentNotesEnds($value); + + break; + + case '-attachment_notes_ends': + $this->collector->attachmentNotesDoNotEnd($value); + + break; + + case 'exists': + $this->collector->exists(); + + break; + + case '-exists': + $this->collector->findNothing(); + + break; + + case 'sepa_ct_is': + $this->collector->setSepaCT($value); + + break; + } + + return true; + } + /** * searchDirection: 1 = source (default), 2 = destination, 3 = both * stringPosition: 1 = start (default), 2 = end, 3 = contains, 4 = is * - * @param string $value - * @param SearchDirection $searchDirection - * @param StringPosition $stringPosition - * @param bool $prohibited * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function searchAccount(string $value, SearchDirection $searchDirection, StringPosition $stringPosition, bool $prohibited = false): void @@ -1509,7 +1970,7 @@ class OperatorQuerySearch implements SearchInterface return; } app('log')->debug(sprintf('Left with %d, set as %s().', $filtered->count(), $collectorMethod)); - $this->collector->$collectorMethod($filtered); // @phpstan-ignore-line + $this->collector->{$collectorMethod}($filtered); // @phpstan-ignore-line } /** @@ -1517,11 +1978,8 @@ class OperatorQuerySearch implements SearchInterface * searchDirection: 1 = source (default), 2 = destination, 3 = both * stringPosition: 1 = start (default), 2 = end, 3 = contains, 4 = is * - * @param string $value - * @param SearchDirection $searchDirection - * @param StringPosition $stringPosition - * @param bool $prohibited * @SuppressWarnings(PHPMD.BooleanArgumentFlag) + * @SuppressWarnings(PHPMD.NPathComplexity) */ private function searchAccountNr(string $value, SearchDirection $searchDirection, StringPosition $stringPosition, bool $prohibited = false): void { @@ -1581,8 +2039,9 @@ class OperatorQuerySearch implements SearchInterface $filtered = $accounts->filter( static function (Account $account) use ($value, $stringMethod) { // either IBAN or account number - $ibanMatch = $stringMethod(strtolower((string)$account->iban), strtolower($value)); + $ibanMatch = $stringMethod(strtolower((string) $account->iban), strtolower($value)); $accountNrMatch = false; + /** @var AccountMeta $meta */ foreach ($account->accountMeta as $meta) { if ('account_number' === $meta->name && $stringMethod(strtolower($meta->data), strtolower($value))) { @@ -1601,22 +2060,14 @@ class OperatorQuerySearch implements SearchInterface return; } app('log')->debug(sprintf('Left with %d, set as %s().', $filtered->count(), $collectorMethod)); - $this->collector->$collectorMethod($filtered);// @phpstan-ignore-line + $this->collector->{$collectorMethod}($filtered); // @phpstan-ignore-line } - /** - * @return Account - */ private function getCashAccount(): Account { return $this->accountRepository->getCashAccount(); } - /** - * @param string $value - * - * @return TransactionCurrency|null - */ private function findCurrency(string $value): ?TransactionCurrency { if (str_contains($value, '(') && str_contains($value, ')')) { @@ -1633,9 +2084,6 @@ class OperatorQuerySearch implements SearchInterface } /** - * @param string $value - * - * @return array * @throws FireflyException */ private function parseDateRange(string $type, string $value): array @@ -1644,6 +2092,7 @@ class OperatorQuerySearch implements SearchInterface if ($parser->isDateRange($value)) { return $parser->parseRange($value); } + try { $parsedDate = $parser->parseDate($value); } catch (FireflyException $e) { @@ -1652,6 +2101,7 @@ class OperatorQuerySearch implements SearchInterface 'type' => $type, 'value' => $value, ]; + return []; } @@ -1661,8 +2111,8 @@ class OperatorQuerySearch implements SearchInterface } /** - * * @throws FireflyException + * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setExactDateParams(array $range, bool $prohibited = false): void @@ -1673,71 +2123,88 @@ class OperatorQuerySearch implements SearchInterface */ foreach ($range as $key => $value) { $key = $prohibited ? sprintf('%s_not', $key) : $key; + switch ($key) { default: throw new FireflyException(sprintf('Cannot handle key "%s" in setExactParameters()', $key)); + case 'exact': if ($value instanceof Carbon) { app('log')->debug(sprintf('Set date_is_exact value "%s"', $value->format('Y-m-d'))); $this->collector->setRange($value, $value); - $this->operators->push(['type' => 'date_on', 'value' => $value->format('Y-m-d'),]); + $this->operators->push(['type' => 'date_on', 'value' => $value->format('Y-m-d')]); } + break; + case 'exact_not': if ($value instanceof Carbon) { $this->collector->excludeRange($value, $value); - $this->operators->push(['type' => 'not_date_on', 'value' => $value->format('Y-m-d'),]); + $this->operators->push(['type' => 'not_date_on', 'value' => $value->format('Y-m-d')]); } + break; + case 'year': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_exact YEAR value "%s"', $value)); $this->collector->yearIs($value); - $this->operators->push(['type' => 'date_on_year', 'value' => $value,]); + $this->operators->push(['type' => 'date_on_year', 'value' => $value]); } + break; + case 'year_not': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_exact_not YEAR value "%s"', $value)); $this->collector->yearIsNot($value); - $this->operators->push(['type' => 'not_date_on_year', 'value' => $value,]); + $this->operators->push(['type' => 'not_date_on_year', 'value' => $value]); } + break; + case 'month': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_exact MONTH value "%s"', $value)); $this->collector->monthIs($value); - $this->operators->push(['type' => 'date_on_month', 'value' => $value,]); + $this->operators->push(['type' => 'date_on_month', 'value' => $value]); } + break; + case 'month_not': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_exact not MONTH value "%s"', $value)); $this->collector->monthIsNot($value); - $this->operators->push(['type' => 'not_date_on_month', 'value' => $value,]); + $this->operators->push(['type' => 'not_date_on_month', 'value' => $value]); } + break; + case 'day': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_exact DAY value "%s"', $value)); $this->collector->dayIs($value); - $this->operators->push(['type' => 'date_on_day', 'value' => $value,]); + $this->operators->push(['type' => 'date_on_day', 'value' => $value]); } + break; + case 'day_not': if (is_string($value)) { app('log')->debug(sprintf('Set not date_is_exact DAY value "%s"', $value)); $this->collector->dayIsNot($value); - $this->operators->push(['type' => 'not_date_on_day', 'value' => $value,]); + $this->operators->push(['type' => 'not_date_on_day', 'value' => $value]); } + break; } } } /** - * * @throws FireflyException + * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setDateBeforeParams(array $range, bool $prohibited = false): void @@ -1748,43 +2215,52 @@ class OperatorQuerySearch implements SearchInterface */ foreach ($range as $key => $value) { $key = $prohibited ? sprintf('%s_not', $key) : $key; + switch ($key) { default: throw new FireflyException(sprintf('Cannot handle key "%s" in setDateBeforeParams()', $key)); + case 'exact': if ($value instanceof Carbon) { $this->collector->setBefore($value); - $this->operators->push(['type' => 'date_before', 'value' => $value->format('Y-m-d'),]); + $this->operators->push(['type' => 'date_before', 'value' => $value->format('Y-m-d')]); } + break; + case 'year': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_before YEAR value "%s"', $value)); $this->collector->yearBefore($value); - $this->operators->push(['type' => 'date_before_year', 'value' => $value,]); + $this->operators->push(['type' => 'date_before_year', 'value' => $value]); } + break; + case 'month': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_before MONTH value "%s"', $value)); $this->collector->monthBefore($value); - $this->operators->push(['type' => 'date_before_month', 'value' => $value,]); + $this->operators->push(['type' => 'date_before_month', 'value' => $value]); } + break; + case 'day': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_before DAY value "%s"', $value)); $this->collector->dayBefore($value); - $this->operators->push(['type' => 'date_before_day', 'value' => $value,]); + $this->operators->push(['type' => 'date_before_day', 'value' => $value]); } + break; } } } /** - * * @throws FireflyException + * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setDateAfterParams(array $range, bool $prohibited = false): void @@ -1795,35 +2271,44 @@ class OperatorQuerySearch implements SearchInterface */ foreach ($range as $key => $value) { $key = $prohibited ? sprintf('%s_not', $key) : $key; + switch ($key) { default: throw new FireflyException(sprintf('Cannot handle key "%s" in setDateAfterParams()', $key)); + case 'exact': if ($value instanceof Carbon) { $this->collector->setAfter($value); - $this->operators->push(['type' => 'date_after', 'value' => $value->format('Y-m-d'),]); + $this->operators->push(['type' => 'date_after', 'value' => $value->format('Y-m-d')]); } + break; + case 'year': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_after YEAR value "%s"', $value)); $this->collector->yearAfter($value); - $this->operators->push(['type' => 'date_after_year', 'value' => $value,]); + $this->operators->push(['type' => 'date_after_year', 'value' => $value]); } + break; + case 'month': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_after MONTH value "%s"', $value)); $this->collector->monthAfter($value); - $this->operators->push(['type' => 'date_after_month', 'value' => $value,]); + $this->operators->push(['type' => 'date_after_month', 'value' => $value]); } + break; + case 'day': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_after DAY value "%s"', $value)); $this->collector->dayAfter($value); - $this->operators->push(['type' => 'date_after_day', 'value' => $value,]); + $this->operators->push(['type' => 'date_after_day', 'value' => $value]); } + break; } } @@ -1831,75 +2316,94 @@ class OperatorQuerySearch implements SearchInterface /** * @throws FireflyException + * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setExactMetaDateParams(string $field, array $range, bool $prohibited = false): void { app('log')->debug('Now in setExactMetaDateParams()'); + /** * @var string $key * @var Carbon|string $value */ foreach ($range as $key => $value) { $key = $prohibited ? sprintf('%s_not', $key) : $key; + switch ($key) { default: throw new FireflyException(sprintf('Cannot handle key "%s" in setExactMetaDateParams()', $key)); + case 'exact': if ($value instanceof Carbon) { app('log')->debug(sprintf('Set %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); $this->collector->setMetaDateRange($value, $value, $field); - $this->operators->push(['type' => sprintf('%s_on', $field), 'value' => $value->format('Y-m-d'),]); + $this->operators->push(['type' => sprintf('%s_on', $field), 'value' => $value->format('Y-m-d')]); } + break; + case 'exact_not': if ($value instanceof Carbon) { app('log')->debug(sprintf('Set NOT %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); $this->collector->excludeMetaDateRange($value, $value, $field); - $this->operators->push(['type' => sprintf('not_%s_on', $field), 'value' => $value->format('Y-m-d'),]); + $this->operators->push(['type' => sprintf('not_%s_on', $field), 'value' => $value->format('Y-m-d')]); } + break; + case 'year': if (is_string($value)) { app('log')->debug(sprintf('Set %s_is_exact YEAR value "%s"', $field, $value)); $this->collector->metaYearIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_year', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_on_year', $field), 'value' => $value]); } + break; + case 'year_not': if (is_string($value)) { app('log')->debug(sprintf('Set NOT %s_is_exact YEAR value "%s"', $field, $value)); $this->collector->metaYearIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_year', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('not_%s_on_year', $field), 'value' => $value]); } + break; + case 'month': if (is_string($value)) { app('log')->debug(sprintf('Set %s_is_exact MONTH value "%s"', $field, $value)); $this->collector->metaMonthIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_month', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_on_month', $field), 'value' => $value]); } + break; + case 'month_not': if (is_string($value)) { app('log')->debug(sprintf('Set NOT %s_is_exact MONTH value "%s"', $field, $value)); $this->collector->metaMonthIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_month', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('not_%s_on_month', $field), 'value' => $value]); } + break; + case 'day': if (is_string($value)) { app('log')->debug(sprintf('Set %s_is_exact DAY value "%s"', $field, $value)); $this->collector->metaDayIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_day', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_on_day', $field), 'value' => $value]); } + break; + case 'day_not': if (is_string($value)) { app('log')->debug(sprintf('Set NOT %s_is_exact DAY value "%s"', $field, $value)); $this->collector->metaDayIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_day', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('not_%s_on_day', $field), 'value' => $value]); } + break; } } @@ -1907,6 +2411,7 @@ class OperatorQuerySearch implements SearchInterface /** * @throws FireflyException + * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setMetaDateBeforeParams(string $field, array $range, bool $prohibited = false): void @@ -1917,35 +2422,44 @@ class OperatorQuerySearch implements SearchInterface */ foreach ($range as $key => $value) { $key = $prohibited ? sprintf('%s_not', $key) : $key; + switch ($key) { default: throw new FireflyException(sprintf('Cannot handle key "%s" in setMetaDateBeforeParams()', $key)); + case 'exact': if ($value instanceof Carbon) { $this->collector->setMetaBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before', $field), 'value' => $value->format('Y-m-d'),]); + $this->operators->push(['type' => sprintf('%s_before', $field), 'value' => $value->format('Y-m-d')]); } + break; + case 'year': if (is_string($value)) { app('log')->debug(sprintf('Set %s_is_before YEAR value "%s"', $field, $value)); $this->collector->metaYearBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_year', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_before_year', $field), 'value' => $value]); } + break; + case 'month': if (is_string($value)) { app('log')->debug(sprintf('Set %s_is_before MONTH value "%s"', $field, $value)); $this->collector->metaMonthBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_month', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_before_month', $field), 'value' => $value]); } + break; + case 'day': if (is_string($value)) { app('log')->debug(sprintf('Set %s_is_before DAY value "%s"', $field, $value)); $this->collector->metaDayBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_day', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_before_day', $field), 'value' => $value]); } + break; } } @@ -1953,6 +2467,7 @@ class OperatorQuerySearch implements SearchInterface /** * @throws FireflyException + * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setMetaDateAfterParams(string $field, array $range, bool $prohibited = false): void @@ -1963,35 +2478,44 @@ class OperatorQuerySearch implements SearchInterface */ foreach ($range as $key => $value) { $key = $prohibited ? sprintf('%s_not', $key) : $key; + switch ($key) { default: throw new FireflyException(sprintf('Cannot handle key "%s" in setMetaDateAfterParams()', $key)); + case 'exact': if ($value instanceof Carbon) { $this->collector->setMetaAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after', $field), 'value' => $value->format('Y-m-d'),]); + $this->operators->push(['type' => sprintf('%s_after', $field), 'value' => $value->format('Y-m-d')]); } + break; + case 'year': if (is_string($value)) { app('log')->debug(sprintf('Set %s_is_after YEAR value "%s"', $field, $value)); $this->collector->metaYearAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_year', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_after_year', $field), 'value' => $value]); } + break; + case 'month': if (is_string($value)) { app('log')->debug(sprintf('Set %s_is_after MONTH value "%s"', $field, $value)); $this->collector->metaMonthAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_month', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_after_month', $field), 'value' => $value]); } + break; + case 'day': if (is_string($value)) { app('log')->debug(sprintf('Set %s_is_after DAY value "%s"', $field, $value)); $this->collector->metaDayAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_day', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_after_day', $field), 'value' => $value]); } + break; } } @@ -1999,6 +2523,7 @@ class OperatorQuerySearch implements SearchInterface /** * @throws FireflyException + * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setExactObjectDateParams(string $field, array $range, bool $prohibited = false): void @@ -2009,72 +2534,89 @@ class OperatorQuerySearch implements SearchInterface */ foreach ($range as $key => $value) { $key = $prohibited ? sprintf('%s_not', $key) : $key; + switch ($key) { default: throw new FireflyException(sprintf('Cannot handle key "%s" in setExactObjectDateParams()', $key)); + case 'exact': if ($value instanceof Carbon) { app('log')->debug(sprintf('Set %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); $this->collector->setObjectRange($value, clone $value, $field); - $this->operators->push(['type' => sprintf('%s_on', $field), 'value' => $value->format('Y-m-d'),]); + $this->operators->push(['type' => sprintf('%s_on', $field), 'value' => $value->format('Y-m-d')]); } + break; + case 'exact_not': if ($value instanceof Carbon) { app('log')->debug(sprintf('Set NOT %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); $this->collector->excludeObjectRange($value, clone $value, $field); - $this->operators->push(['type' => sprintf('not_%s_on', $field), 'value' => $value->format('Y-m-d'),]); + $this->operators->push(['type' => sprintf('not_%s_on', $field), 'value' => $value->format('Y-m-d')]); } + break; + case 'year': if (is_string($value)) { app('log')->debug(sprintf('Set %s_is_exact YEAR value "%s"', $field, $value)); $this->collector->objectYearIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_year', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_on_year', $field), 'value' => $value]); } + break; + case 'year_not': if (is_string($value)) { app('log')->debug(sprintf('Set NOT %s_is_exact YEAR value "%s"', $field, $value)); $this->collector->objectYearIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_year', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('not_%s_on_year', $field), 'value' => $value]); } + break; + case 'month': if (is_string($value)) { app('log')->debug(sprintf('Set %s_is_exact MONTH value "%s"', $field, $value)); $this->collector->objectMonthIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_month', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_on_month', $field), 'value' => $value]); } + break; + case 'month_not': if (is_string($value)) { app('log')->debug(sprintf('Set NOT %s_is_exact MONTH value "%s"', $field, $value)); $this->collector->objectMonthIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_month', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('not_%s_on_month', $field), 'value' => $value]); } + break; + case 'day': if (is_string($value)) { app('log')->debug(sprintf('Set %s_is_exact DAY value "%s"', $field, $value)); $this->collector->objectDayIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_day', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_on_day', $field), 'value' => $value]); } + break; + case 'day_not': if (is_string($value)) { app('log')->debug(sprintf('Set NOT %s_is_exact DAY value "%s"', $field, $value)); $this->collector->objectDayIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_day', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('not_%s_on_day', $field), 'value' => $value]); } + break; } } } /** - * * @throws FireflyException + * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setObjectDateBeforeParams(string $field, array $range, bool $prohibited = false): void @@ -2085,43 +2627,52 @@ class OperatorQuerySearch implements SearchInterface */ foreach ($range as $key => $value) { $key = $prohibited ? sprintf('%s_not', $key) : $key; + switch ($key) { default: throw new FireflyException(sprintf('Cannot handle key "%s" in setObjectDateBeforeParams()', $key)); + case 'exact': if ($value instanceof Carbon) { $this->collector->setObjectBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before', $field), 'value' => $value->format('Y-m-d'),]); + $this->operators->push(['type' => sprintf('%s_before', $field), 'value' => $value->format('Y-m-d')]); } + break; + case 'year': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_before YEAR value "%s"', $value)); $this->collector->objectYearBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_year', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_before_year', $field), 'value' => $value]); } + break; + case 'month': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_before MONTH value "%s"', $value)); $this->collector->objectMonthBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_month', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_before_month', $field), 'value' => $value]); } + break; + case 'day': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_before DAY value "%s"', $value)); $this->collector->objectDayBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_day', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_before_day', $field), 'value' => $value]); } + break; } } } /** - * * @throws FireflyException + * * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setObjectDateAfterParams(string $field, array $range, bool $prohibited = false): void @@ -2132,52 +2683,56 @@ class OperatorQuerySearch implements SearchInterface */ foreach ($range as $key => $value) { $key = $prohibited ? sprintf('%s_not', $key) : $key; + switch ($key) { default: throw new FireflyException(sprintf('Cannot handle key "%s" in setObjectDateAfterParams()', $key)); + case 'exact': if ($value instanceof Carbon) { $this->collector->setObjectAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after', $field), 'value' => $value->format('Y-m-d'),]); + $this->operators->push(['type' => sprintf('%s_after', $field), 'value' => $value->format('Y-m-d')]); } + break; + case 'year': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_after YEAR value "%s"', $value)); $this->collector->objectYearAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_year', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_after_year', $field), 'value' => $value]); } + break; + case 'month': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_after MONTH value "%s"', $value)); $this->collector->objectMonthAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_month', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_after_month', $field), 'value' => $value]); } + break; + case 'day': if (is_string($value)) { app('log')->debug(sprintf('Set date_is_after DAY value "%s"', $value)); $this->collector->objectDayAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_day', $field), 'value' => $value,]); + $this->operators->push(['type' => sprintf('%s_after_day', $field), 'value' => $value]); } + break; } } } - /** - * @return void - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ private function parseTagInstructions(): void { app('log')->debug('Now in parseTagInstructions()'); // if exclude tags, remove excluded tags. if (count($this->excludeTags) > 0) { app('log')->debug(sprintf('%d exclude tag(s)', count($this->excludeTags))); - $collection = new Collection; + $collection = new Collection(); foreach ($this->excludeTags as $tagId) { $tag = $this->tagRepository->find($tagId); if (null !== $tag) { @@ -2191,7 +2746,7 @@ class OperatorQuerySearch implements SearchInterface // if include tags, include them: if (count($this->includeTags) > 0) { app('log')->debug(sprintf('%d include tag(s)', count($this->includeTags))); - $collection = new Collection; + $collection = new Collection(); foreach ($this->includeTags as $tagId) { $tag = $this->tagRepository->find($tagId); if (null !== $tag) { @@ -2201,77 +2756,5 @@ class OperatorQuerySearch implements SearchInterface } $this->collector->setTags($collection); } - - } - - /** - * @inheritDoc - */ - public function searchTime(): float - { - return microtime(true) - $this->startTime; - } - - /** - * @inheritDoc - */ - public function searchTransactions(): LengthAwarePaginator - { - if (0 === count($this->getWords()) && 0 === count($this->getOperators())) { - return new LengthAwarePaginator([], 0, 5, 1); - } - - return $this->collector->getPaginatedGroups(); - } - - /** - * @return array - */ - public function getWords(): array - { - return $this->words; - } - - /** - * @param Carbon $date - */ - public function setDate(Carbon $date): void - { - $this->date = $date; - } - - /** - * @inheritDoc - */ - public function setPage(int $page): void - { - $this->page = $page; - $this->collector->setPage($this->page); - } - - /** - * @inheritDoc - */ - public function setUser(User $user): void - { - $this->accountRepository->setUser($user); - $this->billRepository->setUser($user); - $this->categoryRepository->setUser($user); - $this->budgetRepository->setUser($user); - $this->tagRepository->setUser($user); - $this->collector = app(GroupCollectorInterface::class); - $this->collector->setUser($user); - $this->collector->withAccountInformation()->withCategoryInformation()->withBudgetInformation(); - - $this->setLimit((int)app('preferences')->getForUser($user, 'listPageSize', 50)->data); - } - - /** - * @param int $limit - */ - public function setLimit(int $limit): void - { - $this->limit = $limit; - $this->collector->setLimit($this->limit); } } diff --git a/app/Support/Search/SearchInterface.php b/app/Support/Search/SearchInterface.php index 3ce60fd9bf..b9197e9daf 100644 --- a/app/Support/Search/SearchInterface.php +++ b/app/Support/Search/SearchInterface.php @@ -33,63 +33,27 @@ use Illuminate\Support\Collection; */ interface SearchInterface { - /** - * @return array - */ public function getInvalidOperators(): array; - /** - * @return Collection - */ public function getModifiers(): Collection; - /** - * @return Collection - */ public function getOperators(): Collection; - /** - * @return string - */ public function getWordsAsString(): string; - /** - * @return bool - */ public function hasModifiers(): bool; - /** - * @param string $query - */ public function parseQuery(string $query): void; - /** - * @return float - */ public function searchTime(): float; - /** - * @return LengthAwarePaginator - */ public function searchTransactions(): LengthAwarePaginator; - /** - * @param Carbon $date - */ public function setDate(Carbon $date): void; - /** - * @param int $limit - */ public function setLimit(int $limit): void; - /** - * @param int $page - */ public function setPage(int $page): void; - /** - * @param User $user - */ public function setUser(User $user): void; } diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 59faf20c14..a4dbd3405c 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -24,8 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Support; use Carbon\Carbon; -use DB; -use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\Transaction; @@ -34,64 +32,48 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; -use JsonException; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; -use stdClass; -use Str; -use ValueError; /** * Class Steam. - * - */ class Steam { - /** - * @param Account $account - * @param Carbon $date - * - * @return string - */ public function balanceIgnoreVirtual(Account $account, Carbon $date): string { /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); $repository->setUser($account->user); - $currencyId = (int)$repository->getMetaValue($account, 'currency_id'); + $currencyId = (int) $repository->getMetaValue($account, 'currency_id'); $transactions = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) - ->where('transactions.transaction_currency_id', $currencyId) - ->get(['transactions.amount'])->toArray(); + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) + ->where('transactions.transaction_currency_id', $currencyId) + ->get(['transactions.amount'])->toArray() + ; $nativeBalance = $this->sumTransactions($transactions, 'amount'); // get all balances in foreign currency: $transactions = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) - ->where('transactions.foreign_currency_id', $currencyId) - ->where('transactions.transaction_currency_id', '!=', $currencyId) - ->get(['transactions.foreign_amount'])->toArray(); + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) + ->where('transactions.foreign_currency_id', $currencyId) + ->where('transactions.transaction_currency_id', '!=', $currencyId) + ->get(['transactions.foreign_amount'])->toArray() + ; $foreignBalance = $this->sumTransactions($transactions, 'foreign_amount'); + return bcadd($nativeBalance, $foreignBalance); } - /** - * @param array $transactions - * @param string $key - * - * @return string - */ public function sumTransactions(array $transactions, string $key): string { $sum = '0'; + /** @var array $transaction */ foreach ($transactions as $transaction) { - $value = (string)($transaction[$key] ?? '0'); + $value = (string) ($transaction[$key] ?? '0'); $value = '' === $value ? '0' : $value; $sum = bcadd($sum, $value); } @@ -104,14 +86,7 @@ class Steam * * [yyyy-mm-dd] => 123,2 * - * @param Account $account - * @param Carbon $start - * @param Carbon $end - * @param TransactionCurrency|null $currency - * - * @return array * @throws FireflyException - * @throws JsonException */ public function balanceInRange(Account $account, Carbon $start, Carbon $end, ?TransactionCurrency $currency = null): array { @@ -143,40 +118,42 @@ class Steam // query! $set = $account->transactions() - ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59')) - ->groupBy('transaction_journals.date') - ->groupBy('transactions.transaction_currency_id') - ->groupBy('transactions.foreign_currency_id') - ->orderBy('transaction_journals.date', 'ASC') - ->whereNull('transaction_journals.deleted_at') - ->get( - [ // @phpstan-ignore-line - 'transaction_journals.date', - 'transactions.transaction_currency_id', - DB::raw('SUM(transactions.amount) AS modified'), - 'transactions.foreign_currency_id', - DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'), - ] - ); + ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59')) + ->groupBy('transaction_journals.date') + ->groupBy('transactions.transaction_currency_id') + ->groupBy('transactions.foreign_currency_id') + ->orderBy('transaction_journals.date', 'ASC') + ->whereNull('transaction_journals.deleted_at') + ->get( + [ // @phpstan-ignore-line + 'transaction_journals.date', + 'transactions.transaction_currency_id', + \DB::raw('SUM(transactions.amount) AS modified'), + 'transactions.foreign_currency_id', + \DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'), + ] + ) + ; $currentBalance = $startBalance; + /** @var Transaction $entry */ foreach ($set as $entry) { // normal amount and foreign amount - $modified = (string)(null === $entry->modified ? '0' : $entry->modified); - $foreignModified = (string)(null === $entry->modified_foreign ? '0' : $entry->modified_foreign); + $modified = (string) (null === $entry->modified ? '0' : $entry->modified); + $foreignModified = (string) (null === $entry->modified_foreign ? '0' : $entry->modified_foreign); $amount = '0'; - if ($currencyId === (int)$entry->transaction_currency_id || 0 === $currencyId) { + if ($currencyId === (int) $entry->transaction_currency_id || 0 === $currencyId) { // use normal amount: $amount = $modified; } - if ($currencyId === (int)$entry->foreign_currency_id) { + if ($currencyId === (int) $entry->foreign_currency_id) { // use foreign amount: $amount = $foreignModified; } - Log::debug(sprintf('Trying to add %s and %s.', var_export($currentBalance, true), var_export($amount, true))); + // Log::debug(sprintf('Trying to add %s and %s.', var_export($currentBalance, true), var_export($amount, true))); $currentBalance = bcadd($currentBalance, $amount); $carbon = new Carbon($entry->date, config('app.timezone')); $date = $carbon->format('Y-m-d'); @@ -191,11 +168,6 @@ class Steam /** * Gets balance at the end of current month by default * - * @param Account $account - * @param Carbon $date - * @param TransactionCurrency|null $currency - * - * @return string * @throws FireflyException */ public function balance(Account $account, Carbon $date, ?TransactionCurrency $currency = null): string @@ -209,6 +181,7 @@ class Steam if ($cache->has()) { return $cache->get(); } + /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); if (null === $currency) { @@ -216,18 +189,20 @@ class Steam } // first part: get all balances in own currency: $transactions = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) - ->where('transactions.transaction_currency_id', $currency->id) - ->get(['transactions.amount'])->toArray(); + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) + ->where('transactions.transaction_currency_id', $currency->id) + ->get(['transactions.amount'])->toArray() + ; $nativeBalance = $this->sumTransactions($transactions, 'amount'); // get all balances in foreign currency: $transactions = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) - ->where('transactions.foreign_currency_id', $currency->id) - ->where('transactions.transaction_currency_id', '!=', $currency->id) - ->get(['transactions.foreign_amount'])->toArray(); + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) + ->where('transactions.foreign_currency_id', $currency->id) + ->where('transactions.transaction_currency_id', '!=', $currency->id) + ->get(['transactions.foreign_amount'])->toArray() + ; $foreignBalance = $this->sumTransactions($transactions, 'foreign_amount'); $balance = bcadd($nativeBalance, $foreignBalance); $virtual = null === $account->virtual_balance ? '0' : $account->virtual_balance; @@ -239,16 +214,12 @@ class Steam } /** - * @param Account $account - * @param Carbon $start - * @param Carbon $end - * - * @return array * @throws FireflyException + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function balanceInRangeConverted(Account $account, Carbon $start, Carbon $end, TransactionCurrency $native): array { - $cache = new CacheProperties(); $cache->addProperty($account->id); $cache->addProperty('balance-in-range-converted'); @@ -276,23 +247,25 @@ class Steam // grab all transactions between start and end: $set = $account->transactions() - ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59')) - ->orderBy('transaction_journals.date', 'ASC') - ->whereNull('transaction_journals.deleted_at') - ->get( - [ - 'transaction_journals.date', - 'transactions.transaction_currency_id', - 'transactions.amount', - 'transactions.foreign_currency_id', - 'transactions.foreign_amount', - ] - )->toArray(); + ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59')) + ->orderBy('transaction_journals.date', 'ASC') + ->whereNull('transaction_journals.deleted_at') + ->get( + [ + 'transaction_journals.date', + 'transactions.transaction_currency_id', + 'transactions.amount', + 'transactions.foreign_currency_id', + 'transactions.foreign_amount', + ] + )->toArray() + ; // loop the set and convert if necessary: $currentBalance = $startBalance; + /** @var Transaction $transaction */ foreach ($set as $transaction) { $day = Carbon::createFromFormat('Y-m-d H:i:s', $transaction['date'], config('app.timezone')); @@ -301,22 +274,24 @@ class Steam } $format = $day->format('Y-m-d'); // if the transaction is in the expected currency, change nothing. - if ((int)$transaction['transaction_currency_id'] === $native->id) { + if ((int) $transaction['transaction_currency_id'] === $native->id) { // change the current balance, set it to today, continue the loop. $currentBalance = bcadd($currentBalance, $transaction['amount']); $balances[$format] = $currentBalance; app('log')->debug(sprintf('%s: transaction in %s, new balance is %s.', $format, $native->code, $currentBalance)); + continue; } // if foreign currency is in the expected currency, do nothing: - if ((int)$transaction['foreign_currency_id'] === $native->id) { + if ((int) $transaction['foreign_currency_id'] === $native->id) { $currentBalance = bcadd($currentBalance, $transaction['foreign_amount']); $balances[$format] = $currentBalance; app('log')->debug(sprintf('%s: transaction in %s (foreign), new balance is %s.', $format, $native->code, $currentBalance)); + continue; } // otherwise, convert 'amount' to the necessary currency: - $currencyId = (int)$transaction['transaction_currency_id']; + $currencyId = (int) $transaction['transaction_currency_id']; $currency = $currencies[$currencyId] ?? TransactionCurrency::find($currencyId); $currencies[$currencyId] = $currency; @@ -335,147 +310,155 @@ class Steam $native->code, $convertedAmount )); - - } $cache->store($balances); + $converter->summarize(); return $balances; } /** + * selection of transactions + * 1: all normal transactions. No foreign currency info. In $currency. Need conversion. + * 2: all normal transactions. No foreign currency info. In $native. Need NO conversion. + * 3: all normal transactions. No foreign currency info. In neither currency. Need conversion. + * Then, select everything with foreign currency info: + * 4. All transactions with foreign currency info in $native. Normal currency value is ignored. Do not need + * conversion. + * 5. All transactions with foreign currency info NOT in $native, but currency info in $currency. Need conversion. + * 6. All transactions with foreign currency info NOT in $native, and currency info NOT in $currency. Need + * conversion. + * * Gets balance at the end of current month by default. Returns the balance converted * to the indicated currency ($native). * - * @param Account $account - * @param Carbon $date - * @param TransactionCurrency $native - * - * @return string * @throws FireflyException + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function balanceConverted(Account $account, Carbon $date, TransactionCurrency $native): string { app('log')->debug(sprintf('Now in balanceConverted (%s) for account #%d, converting to %s', $date->format('Y-m-d'), $account->id, $native->code)); - // abuse chart properties: $cache = new CacheProperties(); $cache->addProperty($account->id); $cache->addProperty('balance'); $cache->addProperty($date); $cache->addProperty($native->id); if ($cache->has()) { + Log::debug('Cached!'); + return $cache->get(); } + /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); $currency = $repository->getAccountCurrency($account); $currency = null === $currency ? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup) : $currency; if ($native->id === $currency->id) { + // Log::debug('No conversion necessary!'); return $this->balance($account, $date); } - /** - * selection of transactions - * 1: all normal transactions. No foreign currency info. In $currency. Need conversion. - * 2: all normal transactions. No foreign currency info. In $native. Need NO conversion. - * 3: all normal transactions. No foreign currency info. In neither currency. Need conversion. - * Then, select everything with foreign currency info: - * 4. All transactions with foreign currency info in $native. Normal currency value is ignored. Do not need conversion. - * 5. All transactions with foreign currency info NOT in $native, but currency info in $currency. Need conversion. - * 6. All transactions with foreign currency info NOT in $native, and currency info NOT in $currency. Need conversion. - * - */ $new = []; $existing = []; - // 1 - $new[] = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) - ->where('transactions.transaction_currency_id', $currency->id) - ->whereNull('transactions.foreign_currency_id') - ->get(['transaction_journals.date', 'transactions.amount'])->toArray(); + $new[] = $account->transactions() // 1 + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) + ->where('transactions.transaction_currency_id', $currency->id) + ->whereNull('transactions.foreign_currency_id') + ->get(['transaction_journals.date', 'transactions.amount'])->toArray() + ; app('log')->debug(sprintf('%d transaction(s) in set #1', count($new[0]))); - // 2 - $existing[] = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) - ->where('transactions.transaction_currency_id', $native->id) - ->whereNull('transactions.foreign_currency_id') - ->get(['transactions.amount'])->toArray(); + $existing[] = $account->transactions() // 2 + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) + ->where('transactions.transaction_currency_id', $native->id) + ->whereNull('transactions.foreign_currency_id') + ->get(['transactions.amount'])->toArray() + ; app('log')->debug(sprintf('%d transaction(s) in set #2', count($existing[0]))); - // 3 - $new[] = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) - ->where('transactions.transaction_currency_id', '!=', $currency->id) - ->where('transactions.transaction_currency_id', '!=', $native->id) - ->whereNull('transactions.foreign_currency_id') - ->get(['transaction_journals.date', 'transactions.amount'])->toArray(); + $new[] = $account->transactions() // 3 + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) + ->where('transactions.transaction_currency_id', '!=', $currency->id) + ->where('transactions.transaction_currency_id', '!=', $native->id) + ->whereNull('transactions.foreign_currency_id') + ->get(['transaction_journals.date', 'transactions.amount'])->toArray() + ; app('log')->debug(sprintf('%d transactions in set #3', count($new[1]))); - // 4 - $existing[] = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) - ->where('transactions.foreign_currency_id', $native->id) - ->whereNotNull('transactions.foreign_amount') - ->get(['transactions.foreign_amount'])->toArray(); + $existing[] = $account->transactions() // 4 + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) + ->where('transactions.foreign_currency_id', $native->id) + ->whereNotNull('transactions.foreign_amount') + ->get(['transactions.foreign_amount'])->toArray() + ; app('log')->debug(sprintf('%d transactions in set #4', count($existing[1]))); - // 5 - $new[] = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) - ->where('transactions.transaction_currency_id', $currency->id) - ->where('transactions.foreign_currency_id', '!=', $native->id) - ->whereNotNull('transactions.foreign_amount') - ->get(['transaction_journals.date', 'transactions.amount'])->toArray(); + $new[] = $account->transactions()// 5 + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) + ->where('transactions.transaction_currency_id', $currency->id) + ->where('transactions.foreign_currency_id', '!=', $native->id) + ->whereNotNull('transactions.foreign_amount') + ->get(['transaction_journals.date', 'transactions.amount'])->toArray() + ; app('log')->debug(sprintf('%d transactions in set #5', count($new[2]))); - // 6 - $new[] = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) - ->where('transactions.transaction_currency_id', '!=', $currency->id) - ->where('transactions.foreign_currency_id', '!=', $native->id) - ->whereNotNull('transactions.foreign_amount') - ->get(['transaction_journals.date', 'transactions.amount'])->toArray(); + $new[] = $account->transactions()// 6 + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) + ->where('transactions.transaction_currency_id', '!=', $currency->id) + ->where('transactions.foreign_currency_id', '!=', $native->id) + ->whereNotNull('transactions.foreign_amount') + ->get(['transaction_journals.date', 'transactions.amount'])->toArray() + ; app('log')->debug(sprintf('%d transactions in set #6', count($new[3]))); // process both sets of transactions. Of course, no need to convert set "existing". $balance = $this->sumTransactions($existing[0], 'amount'); $balance = bcadd($balance, $this->sumTransactions($existing[1], 'foreign_amount')); - //app('log')->debug(sprintf('Balance from set #2 and #4 is %f', $balance)); + // app('log')->debug(sprintf('Balance from set #2 and #4 is %f', $balance)); // need to convert the others. All sets use the "amount" value as their base (that's easy) // but we need to convert each transaction separately because the date difference may // incur huge currency changes. + $start = clone $date; + $end = clone $date; $converter = new ExchangeRateConverter(); foreach ($new as $set) { foreach ($set as $transaction) { - $date = Carbon::createFromFormat('Y-m-d H:i:s', $transaction['date']); - if (false === $date) { - $date = today(config('app.timezone')); + $currentDate = Carbon::createFromFormat('Y-m-d H:i:s', $transaction['date']); + if (false === $currentDate) { + $currentDate = today(config('app.timezone')); } - $rate = $converter->getCurrencyRate($currency, $native, $date); + if ($currentDate->lte($start)) { + $start = clone $currentDate; + } + } + } + unset($currentDate); + $converter->prepare($currency, $native, $start, $end); + + foreach ($new as $set) { + foreach ($set as $transaction) { + $currentDate = Carbon::createFromFormat('Y-m-d H:i:s', $transaction['date']); + if (false === $currentDate) { + $currentDate = today(config('app.timezone')); + } + $rate = $converter->getCurrencyRate($currency, $native, $currentDate); $convertedAmount = bcmul($transaction['amount'], $rate); $balance = bcadd($balance, $convertedAmount); - // app('log')->debug(sprintf('Date: %s, rate: %s, amount: %s %s, new: %s %s', - // $date->format('Y-m-d'), - // $rate, - // $currency->code, - // $transaction['amount'], - // $native->code, - // $convertedAmount - // )); } - //app('log')->debug(sprintf('Balance from new set #%d is %f', $index, $balance)); } // add virtual balance (also needs conversion) $virtual = null === $account->virtual_balance ? '0' : $account->virtual_balance; $virtual = $converter->convert($currency, $native, $account->created_at, $virtual); $balance = bcadd($balance, $virtual); + $converter->summarize(); $cache->store($balance); + $converter->summarize(); return $balance; } @@ -483,10 +466,6 @@ class Steam /** * This method always ignores the virtual balance. * - * @param Collection $accounts - * @param Carbon $date - * - * @return array * @throws FireflyException */ public function balancesByAccounts(Collection $accounts, Carbon $date): array @@ -503,6 +482,7 @@ class Steam // need to do this per account. $result = []; + /** @var Account $account */ foreach ($accounts as $account) { $result[$account->id] = $this->balance($account, $date); @@ -516,10 +496,6 @@ class Steam /** * This method always ignores the virtual balance. * - * @param Collection $accounts - * @param Carbon $date - * - * @return array * @throws FireflyException */ public function balancesByAccountsConverted(Collection $accounts, Carbon $date): array @@ -534,17 +510,17 @@ class Steam // return $cache->get(); } - // need to do this per account. $result = []; + /** @var Account $account */ foreach ($accounts as $account) { $default = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); $result[$account->id] = [ - 'balance' => $this->balance($account, $date), - 'native_balance' => $this->balanceConverted($account, $date, $default), - ]; + 'balance' => $this->balance($account, $date), + 'native_balance' => $this->balanceConverted($account, $date, $default), + ]; } $cache->store($result); @@ -554,11 +530,6 @@ class Steam /** * Same as above, but also groups per currency. - * - * @param Collection $accounts - * @param Carbon $date - * - * @return array */ public function balancesPerCurrencyByAccounts(Collection $accounts, Carbon $date): array { @@ -574,6 +545,7 @@ class Steam // need to do this per account. $result = []; + /** @var Account $account */ foreach ($accounts as $account) { $result[$account->id] = $this->balancePerCurrency($account, $date); @@ -584,12 +556,6 @@ class Steam return $result; } - /** - * @param Account $account - * @param Carbon $date - * - * @return array - */ public function balancePerCurrency(Account $account, Carbon $date): array { // abuse chart properties: @@ -601,14 +567,16 @@ class Steam return $cache->get(); } $query = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) - ->groupBy('transactions.transaction_currency_id'); - $balances = $query->get(['transactions.transaction_currency_id', DB::raw('SUM(transactions.amount) as sum_for_currency')]); // @phpstan-ignore-line - $return = []; - /** @var stdClass $entry */ + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) + ->groupBy('transactions.transaction_currency_id') + ; + $balances = $query->get(['transactions.transaction_currency_id', \DB::raw('SUM(transactions.amount) as sum_for_currency')]); // @phpstan-ignore-line + $return = []; + + /** @var \stdClass $entry */ foreach ($balances as $entry) { - $return[(int)$entry->transaction_currency_id] = (string)$entry->sum_for_currency; + $return[(int) $entry->transaction_currency_id] = (string) $entry->sum_for_currency; } $cache->store($return); @@ -617,11 +585,6 @@ class Steam /** * https://stackoverflow.com/questions/1642614/how-to-ceil-floor-and-round-bcmath-numbers - * - * @param null|string $number - * @param int $precision - * - * @return string */ public function bcround(?string $number, int $precision = 0): string { @@ -638,21 +601,16 @@ class Steam // app('log')->debug(sprintf('Trying bcround("%s",%d)', $number, $precision)); if (str_contains($number, '.')) { - if ($number[0] !== '-') { - return bcadd($number, '0.' . str_repeat('0', $precision) . '5', $precision); + if ('-' !== $number[0]) { + return bcadd($number, '0.'.str_repeat('0', $precision).'5', $precision); } - return bcsub($number, '0.' . str_repeat('0', $precision) . '5', $precision); + return bcsub($number, '0.'.str_repeat('0', $precision).'5', $precision); } return $number; } - /** - * @param string $string - * - * @return string - */ public function filterSpaces(string $string): string { $search = [ @@ -711,34 +669,28 @@ class Steam } /** - * @param string $ipAddress - * - * @return string * @throws FireflyException */ public function getHostName(string $ipAddress): string { try { $hostName = gethostbyaddr($ipAddress); - } catch (Exception $e) { // intentional generic exception + } catch (\Exception $e) { // intentional generic exception throw new FireflyException($e->getMessage(), 0, $e); } - return (string)$hostName; + + return (string) $hostName; } - /** - * @param array $accounts - * - * @return array - */ public function getLastActivities(array $accounts): array { $list = []; $set = auth()->user()->transactions() - ->whereIn('transactions.account_id', $accounts) - ->groupBy(['transactions.account_id', 'transaction_journals.user_id']) - ->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) AS max_date')]); // @phpstan-ignore-line + ->whereIn('transactions.account_id', $accounts) + ->groupBy(['transactions.account_id', 'transaction_journals.user_id']) + ->get(['transactions.account_id', \DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line + ; /** @var Transaction $entry */ foreach ($set as $entry) { @@ -752,11 +704,6 @@ class Steam /** * Get user's locale. - * - * @return string - * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function getLocale(): string // get preference { @@ -767,11 +714,10 @@ class Steam if ('equal' === $locale) { $locale = $this->getLanguage(); } - $locale = (string)$locale; - + $locale = (string) $locale; // Check for Windows to replace the locale correctly. - if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + if ('WIN' === strtoupper(substr(PHP_OS, 0, 3))) { $locale = str_replace('_', '-', $locale); } @@ -781,10 +727,7 @@ class Steam /** * Get user's language. * - * @return string * @throws FireflyException - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function getLanguage(): string // get preference { @@ -792,14 +735,10 @@ class Steam if (!is_string($preference)) { throw new FireflyException(sprintf('Preference "language" must be a string, but is unexpectedly a "%s".', gettype($preference))); } + return str_replace('-', '_', $preference); } - /** - * @param string $locale - * - * @return array - */ public function getLocaleArray(string $locale): array { return [ @@ -817,26 +756,19 @@ class Steam * Uses the session's previousUrl() function as inspired by GitHub user @z1r0- * * session()->previousUrl() uses getSafeUrl() so we can safely return it: - * - * @return string */ public function getSafePreviousUrl(): string { - //app('log')->debug(sprintf('getSafePreviousUrl: "%s"', session()->previousUrl())); + // app('log')->debug(sprintf('getSafePreviousUrl: "%s"', session()->previousUrl())); return session()->previousUrl() ?? route('index'); } /** * Make sure URL is safe. - * - * @param string $unknownUrl - * @param string $safeUrl - * - * @return string */ public function getSafeUrl(string $unknownUrl, string $safeUrl): string { - //app('log')->debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl)); + // app('log')->debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl)); $returnUrl = $safeUrl; $unknownHost = parse_url($unknownUrl, PHP_URL_HOST); $safeHost = parse_url($safeUrl, PHP_URL_HOST); @@ -847,18 +779,13 @@ class Steam // URL must not lead to weird pages $forbiddenWords = ['jscript', 'json', 'debug', 'serviceworker', 'offline', 'delete', '/login', '/attachments/view']; - if (Str::contains($returnUrl, $forbiddenWords)) { + if (\Str::contains($returnUrl, $forbiddenWords)) { $returnUrl = $safeUrl; } return $returnUrl; } - /** - * @param string $amount - * - * @return string - */ public function negative(string $amount): string { if ('' === $amount) { @@ -878,10 +805,6 @@ class Steam * * Convert a scientific notation to float * Additionally fixed a problem with PHP <= 5.2.x with big integers - * - * @param string $value - * - * @return string */ public function floatalize(string $value): string { @@ -890,25 +813,22 @@ class Steam return $value; } - $number = substr($value, 0, (int)strpos($value, 'E')); + $number = substr($value, 0, (int) strpos($value, 'E')); if (str_contains($number, '.')) { - $post = strlen(substr($number, (int)strpos($number, '.') + 1)); - $mantis = substr($value, (int)strpos($value, 'E') + 1); + $post = strlen(substr($number, (int) strpos($number, '.') + 1)); + $mantis = substr($value, (int) strpos($value, 'E') + 1); if ($mantis < 0) { - $post += abs((int)$mantis); + $post += abs((int) $mantis); } + // TODO careless float could break financial math. - return number_format((float)$value, $post, '.', ''); + return number_format((float) $value, $post, '.', ''); } + // TODO careless float could break financial math. - return number_format((float)$value, 0, '.', ''); + return number_format((float) $value, 0, '.', ''); } - /** - * @param string|null $amount - * - * @return string|null - */ public function opposite(string $amount = null): ?string { if (null === $amount) { @@ -918,11 +838,6 @@ class Steam return bcmul($amount, '-1'); } - /** - * @param string $string - * - * @return int - */ public function phpBytes(string $string): int { $string = str_replace(['kb', 'mb', 'gb'], ['k', 'm', 'g'], strtolower($string)); @@ -931,43 +846,40 @@ class Steam // has a K in it, remove the K and multiply by 1024. $bytes = bcmul(rtrim($string, 'k'), '1024'); - return (int)$bytes; + return (int) $bytes; } if (false !== stripos($string, 'm')) { // has a M in it, remove the M and multiply by 1048576. $bytes = bcmul(rtrim($string, 'm'), '1048576'); - return (int)$bytes; + return (int) $bytes; } if (false !== stripos($string, 'g')) { // has a G in it, remove the G and multiply by (1024)^3. $bytes = bcmul(rtrim($string, 'g'), '1073741824'); - return (int)$bytes; + return (int) $bytes; } - return (int)$string; + return (int) $string; } - /** - * @param string $amount - * - * @return string - */ public function positive(string $amount): string { if ('' === $amount) { return '0'; } + try { - if (bccomp($amount, '0') === -1) { + if (-1 === bccomp($amount, '0')) { $amount = bcmul($amount, '-1'); } - } catch (ValueError $e) { + } catch (\ValueError $e) { app('log')->error(sprintf('ValueError in Steam::positive("%s"): %s', $amount, $e->getMessage())); app('log')->error($e->getTraceAsString()); + return '0'; } diff --git a/app/Support/System/GeneratesInstallationId.php b/app/Support/System/GeneratesInstallationId.php index cdd981eb99..bd017f4f5a 100644 --- a/app/Support/System/GeneratesInstallationId.php +++ b/app/Support/System/GeneratesInstallationId.php @@ -32,9 +32,6 @@ use Ramsey\Uuid\Uuid; */ trait GeneratesInstallationId { - /** - * - */ protected function generateInstallationId(): void { try { diff --git a/app/Support/System/OAuthKeys.php b/app/Support/System/OAuthKeys.php index c548664acb..08f7c61a2a 100644 --- a/app/Support/System/OAuthKeys.php +++ b/app/Support/System/OAuthKeys.php @@ -24,8 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Support\System; -use Artisan; -use Crypt; use FireflyIII\Exceptions\FireflyException; use Illuminate\Contracts\Encryption\DecryptException; use Laravel\Passport\Console\KeysCommand; @@ -40,9 +38,6 @@ class OAuthKeys private const string PRIVATE_KEY = 'oauth_private_key'; private const string PUBLIC_KEY = 'oauth_public_key'; - /** - * - */ public static function verifyKeysRoutine(): void { if (!self::keysInDatabase() && !self::hasKeyFiles()) { @@ -61,9 +56,6 @@ class OAuthKeys } } - /** - * @return bool - */ public static function keysInDatabase(): bool { $privateKey = ''; @@ -73,7 +65,7 @@ class OAuthKeys try { $privateKey = (string)app('fireflyconfig')->get(self::PRIVATE_KEY)?->data; $publicKey = (string)app('fireflyconfig')->get(self::PUBLIC_KEY)?->data; - } catch (ContainerExceptionInterface | NotFoundExceptionInterface | FireflyException $e) { + } catch (ContainerExceptionInterface|FireflyException|NotFoundExceptionInterface $e) { app('log')->error(sprintf('Could not validate keysInDatabase(): %s', $e->getMessage())); app('log')->error($e->getTraceAsString()); } @@ -85,9 +77,6 @@ class OAuthKeys return false; } - /** - * @return bool - */ public static function hasKeyFiles(): bool { $private = storage_path('oauth-private.key'); @@ -96,39 +85,31 @@ class OAuthKeys return file_exists($private) && file_exists($public); } - /** - * - */ public static function generateKeys(): void { - Artisan::registerCommand(new KeysCommand()); - Artisan::call('passport:keys'); + \Artisan::registerCommand(new KeysCommand()); + \Artisan::call('passport:keys'); } - /** - * - */ public static function storeKeysInDB(): void { $private = storage_path('oauth-private.key'); $public = storage_path('oauth-public.key'); - app('fireflyconfig')->set(self::PRIVATE_KEY, Crypt::encrypt(file_get_contents($private))); - app('fireflyconfig')->set(self::PUBLIC_KEY, Crypt::encrypt(file_get_contents($public))); + app('fireflyconfig')->set(self::PRIVATE_KEY, \Crypt::encrypt(file_get_contents($private))); + app('fireflyconfig')->set(self::PUBLIC_KEY, \Crypt::encrypt(file_get_contents($public))); } /** - * @return bool - * @throws ContainerExceptionInterface * @throws FireflyException - * @throws NotFoundExceptionInterface */ public static function restoreKeysFromDB(): bool { $privateKey = (string)app('fireflyconfig')->get(self::PRIVATE_KEY)?->data; $publicKey = (string)app('fireflyconfig')->get(self::PUBLIC_KEY)?->data; + try { - $privateContent = Crypt::decrypt($privateKey); - $publicContent = Crypt::decrypt($publicKey); + $privateContent = \Crypt::decrypt($privateKey); + $publicContent = \Crypt::decrypt($publicKey); } catch (DecryptException $e) { app('log')->error('Could not decrypt pub/private keypair.'); app('log')->error($e->getMessage()); @@ -143,6 +124,7 @@ class OAuthKeys $public = storage_path('oauth-public.key'); file_put_contents($private, $privateContent); file_put_contents($public, $publicContent); + return true; } } diff --git a/app/Support/Twig/AmountFormat.php b/app/Support/Twig/AmountFormat.php index 3b150652c4..ad9d4d8fbe 100644 --- a/app/Support/Twig/AmountFormat.php +++ b/app/Support/Twig/AmountFormat.php @@ -35,9 +35,6 @@ use Twig\TwigFunction; */ class AmountFormat extends AbstractExtension { - /** - * {@inheritdoc} - */ public function getFilters(): array { return [ @@ -46,9 +43,15 @@ class AmountFormat extends AbstractExtension ]; } - /** - * @return TwigFilter - */ + public function getFunctions(): array + { + return [ + $this->formatAmountByAccount(), + $this->formatAmountBySymbol(), + $this->formatAmountByCurrency(), + ]; + } + protected function formatAmount(): TwigFilter { return new TwigFilter( @@ -62,9 +65,6 @@ class AmountFormat extends AbstractExtension ); } - /** - * @return TwigFilter - */ protected function formatAmountPlain(): TwigFilter { return new TwigFilter( @@ -78,23 +78,11 @@ class AmountFormat extends AbstractExtension ); } - /** - * {@inheritdoc} - */ - public function getFunctions(): array - { - return [ - $this->formatAmountByAccount(), - $this->formatAmountBySymbol(), - $this->formatAmountByCurrency(), - ]; - } - /** * Will format the amount by the currency related to the given account. * - * @return TwigFunction - * TODO remove me when layout v1 is deprecated. + * @return twigFunction + * TODO remove me when layout v1 is deprecated */ protected function formatAmountByAccount(): TwigFunction { @@ -102,6 +90,7 @@ class AmountFormat extends AbstractExtension 'formatAmountByAccount', static function (AccountModel $account, string $amount, bool $coloured = null): string { $coloured ??= true; + /** @var AccountRepositoryInterface $accountRepos */ $accountRepos = app(AccountRepositoryInterface::class); $currency = $accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency(); @@ -114,8 +103,6 @@ class AmountFormat extends AbstractExtension /** * Will format the amount by the currency related to the given account. - * - * @return TwigFunction */ protected function formatAmountBySymbol(): TwigFunction { @@ -136,8 +123,6 @@ class AmountFormat extends AbstractExtension /** * Will format the amount by the currency related to the given account. - * - * @return TwigFunction */ protected function formatAmountByCurrency(): TwigFunction { diff --git a/app/Support/Twig/General.php b/app/Support/Twig/General.php index 52d9a01aa8..e53c38b8ca 100644 --- a/app/Support/Twig/General.php +++ b/app/Support/Twig/General.php @@ -29,7 +29,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Support\Search\OperatorQuerySearch; use League\CommonMark\GithubFlavoredMarkdownConverter; -use Route; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; use Twig\TwigFunction; @@ -39,9 +38,6 @@ use Twig\TwigFunction; */ class General extends AbstractExtension { - /** - * @inheritDoc - */ public function getFilters(): array { return [ @@ -53,173 +49,6 @@ class General extends AbstractExtension ]; } - /** - * Show account balance. Only used on the front page of Firefly III. - * - * @return TwigFilter - */ - protected function balance(): TwigFilter - { - return new TwigFilter( - 'balance', - static function (?Account $account): string { - if (null === $account) { - return '0'; - } - /** @var Carbon $date */ - $date = session('end', today(config('app.timezone'))->endOfMonth()); - - return app('steam')->balance($account, $date); - } - ); - } - - /** - * Used to convert 1024 to 1kb etc. - * - * @return TwigFilter - */ - protected function formatFilesize(): TwigFilter - { - return new TwigFilter( - 'filesize', - static function (int $size): string { - // less than one GB, more than one MB - if ($size < (1024 * 1024 * 2014) && $size >= (1024 * 1024)) { - return round($size / (1024 * 1024), 2) . ' MB'; - } - - // less than one MB - if ($size < (1024 * 1024)) { - return round($size / 1024, 2) . ' KB'; - } - - return $size . ' bytes'; - } - ); - } - - /** - * Show icon with attachment. - * - * @return TwigFilter - */ - protected function mimeIcon(): TwigFilter - { - return new TwigFilter( - 'mimeIcon', - static function (string $string): string { - switch ($string) { - default: - return 'fa-file-o'; - case 'application/pdf': - return 'fa-file-pdf-o'; - /* image */ - case 'image/png': - case 'image/jpeg': - case 'image/svg+xml': - case 'image/heic': - case 'image/heic-sequence': - case 'application/vnd.oasis.opendocument.image': - return 'fa-file-image-o'; - /* MS word */ - case 'application/msword': - case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': - case 'application/vnd.openxmlformats-officedocument.wordprocessingml.template': - case 'application/x-iwork-pages-sffpages': - case 'application/vnd.sun.xml.writer': - case 'application/vnd.sun.xml.writer.template': - case 'application/vnd.sun.xml.writer.global': - case 'application/vnd.stardivision.writer': - case 'application/vnd.stardivision.writer-global': - case 'application/vnd.oasis.opendocument.text': - case 'application/vnd.oasis.opendocument.text-template': - case 'application/vnd.oasis.opendocument.text-web': - case 'application/vnd.oasis.opendocument.text-master': - return 'fa-file-word-o'; - /* MS excel */ - case 'application/vnd.ms-excel': - case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': - case 'application/vnd.openxmlformats-officedocument.spreadsheetml.template': - case 'application/vnd.sun.xml.calc': - case 'application/vnd.sun.xml.calc.template': - case 'application/vnd.stardivision.calc': - case 'application/vnd.oasis.opendocument.spreadsheet': - case 'application/vnd.oasis.opendocument.spreadsheet-template': - return 'fa-file-excel-o'; - /* MS powerpoint */ - case 'application/vnd.ms-powerpoint': - case 'application/vnd.openxmlformats-officedocument.presentationml.presentation': - case 'application/vnd.openxmlformats-officedocument.presentationml.template': - case 'application/vnd.openxmlformats-officedocument.presentationml.slideshow': - case 'application/vnd.sun.xml.impress': - case 'application/vnd.sun.xml.impress.template': - case 'application/vnd.stardivision.impress': - case 'application/vnd.oasis.opendocument.presentation': - case 'application/vnd.oasis.opendocument.presentation-template': - return 'fa-file-powerpoint-o'; - /* calc */ - case 'application/vnd.sun.xml.draw': - case 'application/vnd.sun.xml.draw.template': - case 'application/vnd.stardivision.draw': - case 'application/vnd.oasis.opendocument.chart': - return 'fa-paint-brush'; - case 'application/vnd.oasis.opendocument.graphics': - case 'application/vnd.oasis.opendocument.graphics-template': - case 'application/vnd.sun.xml.math': - case 'application/vnd.stardivision.math': - case 'application/vnd.oasis.opendocument.formula': - case 'application/vnd.oasis.opendocument.database': - return 'fa-calculator'; - } - }, - ['is_safe' => ['html']] - ); - } - - /** - * @return TwigFilter - */ - protected function markdown(): TwigFilter - { - return new TwigFilter( - 'markdown', - static function (string $text): string { - $converter = new GithubFlavoredMarkdownConverter( - [ - 'allow_unsafe_links' => false, - 'max_nesting_level' => 3, - 'html_input' => 'escape', - ] - ); - - return (string)$converter->convert($text); - }, - ['is_safe' => ['html']] - ); - } - - /** - * Show URL host name - * - * @return TwigFilter - */ - protected function phpHostName(): TwigFilter - { - return new TwigFilter( - 'phphost', - static function (string $string): string { - $proto = (string)parse_url($string, PHP_URL_SCHEME); - $host = (string)parse_url($string, PHP_URL_HOST); - - return e(sprintf('%s://%s', $proto, $host)); - } - ); - } - - /** - * {@inheritdoc} - */ public function getFunctions(): array { return [ @@ -237,9 +66,170 @@ class General extends AbstractExtension } /** - * Basic example thing for some views. + * Show account balance. Only used on the front page of Firefly III. + */ + protected function balance(): TwigFilter + { + return new TwigFilter( + 'balance', + static function (?Account $account): string { + if (null === $account) { + return '0'; + } + + /** @var Carbon $date */ + $date = session('end', today(config('app.timezone'))->endOfMonth()); + + return app('steam')->balance($account, $date); + } + ); + } + + /** + * Used to convert 1024 to 1kb etc. + */ + protected function formatFilesize(): TwigFilter + { + return new TwigFilter( + 'filesize', + static function (int $size): string { + // less than one GB, more than one MB + if ($size < (1024 * 1024 * 2014) && $size >= (1024 * 1024)) { + return round($size / (1024 * 1024), 2).' MB'; + } + + // less than one MB + if ($size < (1024 * 1024)) { + return round($size / 1024, 2).' KB'; + } + + return $size.' bytes'; + } + ); + } + + /** + * Show icon with attachment. * - * @return TwigFunction + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + protected function mimeIcon(): TwigFilter + { + return new TwigFilter( + 'mimeIcon', + static function (string $string): string { + switch ($string) { + default: + return 'fa-file-o'; + + case 'application/pdf': + return 'fa-file-pdf-o'; + + // image + case 'image/png': + case 'image/jpeg': + case 'image/svg+xml': + case 'image/heic': + case 'image/heic-sequence': + case 'application/vnd.oasis.opendocument.image': + return 'fa-file-image-o'; + + // MS word + case 'application/msword': + case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': + case 'application/vnd.openxmlformats-officedocument.wordprocessingml.template': + case 'application/x-iwork-pages-sffpages': + case 'application/vnd.sun.xml.writer': + case 'application/vnd.sun.xml.writer.template': + case 'application/vnd.sun.xml.writer.global': + case 'application/vnd.stardivision.writer': + case 'application/vnd.stardivision.writer-global': + case 'application/vnd.oasis.opendocument.text': + case 'application/vnd.oasis.opendocument.text-template': + case 'application/vnd.oasis.opendocument.text-web': + case 'application/vnd.oasis.opendocument.text-master': + return 'fa-file-word-o'; + + // MS excel + case 'application/vnd.ms-excel': + case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': + case 'application/vnd.openxmlformats-officedocument.spreadsheetml.template': + case 'application/vnd.sun.xml.calc': + case 'application/vnd.sun.xml.calc.template': + case 'application/vnd.stardivision.calc': + case 'application/vnd.oasis.opendocument.spreadsheet': + case 'application/vnd.oasis.opendocument.spreadsheet-template': + return 'fa-file-excel-o'; + + // MS powerpoint + case 'application/vnd.ms-powerpoint': + case 'application/vnd.openxmlformats-officedocument.presentationml.presentation': + case 'application/vnd.openxmlformats-officedocument.presentationml.template': + case 'application/vnd.openxmlformats-officedocument.presentationml.slideshow': + case 'application/vnd.sun.xml.impress': + case 'application/vnd.sun.xml.impress.template': + case 'application/vnd.stardivision.impress': + case 'application/vnd.oasis.opendocument.presentation': + case 'application/vnd.oasis.opendocument.presentation-template': + return 'fa-file-powerpoint-o'; + + // calc + case 'application/vnd.sun.xml.draw': + case 'application/vnd.sun.xml.draw.template': + case 'application/vnd.stardivision.draw': + case 'application/vnd.oasis.opendocument.chart': + return 'fa-paint-brush'; + + case 'application/vnd.oasis.opendocument.graphics': + case 'application/vnd.oasis.opendocument.graphics-template': + case 'application/vnd.sun.xml.math': + case 'application/vnd.stardivision.math': + case 'application/vnd.oasis.opendocument.formula': + case 'application/vnd.oasis.opendocument.database': + return 'fa-calculator'; + } + }, + ['is_safe' => ['html']] + ); + } + + protected function markdown(): TwigFilter + { + return new TwigFilter( + 'markdown', + static function (string $text): string { + $converter = new GithubFlavoredMarkdownConverter( + [ + 'allow_unsafe_links' => false, + 'max_nesting_level' => 3, + 'html_input' => 'escape', + ] + ); + + return (string) $converter->convert($text); + }, + ['is_safe' => ['html']] + ); + } + + /** + * Show URL host name + */ + protected function phpHostName(): TwigFilter + { + return new TwigFilter( + 'phphost', + static function (string $string): string { + $proto = (string) parse_url($string, PHP_URL_SCHEME); + $host = (string) parse_url($string, PHP_URL_HOST); + + return e(sprintf('%s://%s', $proto, $host)); + } + ); + } + + /** + * Basic example thing for some views. */ protected function phpdate(): TwigFunction { @@ -254,8 +244,6 @@ class General extends AbstractExtension /** * Will return "active" when the current route matches the given argument * exactly. - * - * @return TwigFunction */ protected function activeRouteStrict(): TwigFunction { @@ -265,7 +253,7 @@ class General extends AbstractExtension $args = func_get_args(); $route = $args[0]; // name of the route. - if (Route::getCurrentRoute()->getName() === $route) { + if (\Route::getCurrentRoute()->getName() === $route) { return 'active'; } @@ -277,8 +265,6 @@ class General extends AbstractExtension /** * Will return "active" when a part of the route matches the argument. * ie. "accounts" will match "accounts.index". - * - * @return TwigFunction */ protected function activeRoutePartial(): TwigFunction { @@ -287,7 +273,7 @@ class General extends AbstractExtension static function (): string { $args = func_get_args(); $route = $args[0]; // name of the route. - $name = Route::getCurrentRoute()->getName() ?? ''; + $name = \Route::getCurrentRoute()->getName() ?? ''; if (str_contains($name, $route)) { return 'active'; } @@ -300,8 +286,6 @@ class General extends AbstractExtension /** * This function will return "active" when the current route matches the first argument (even partly) * but, the variable $objectType has been set and matches the second argument. - * - * @return TwigFunction */ protected function activeRoutePartialObjectType(): TwigFunction { @@ -313,7 +297,7 @@ class General extends AbstractExtension if ($objectType === $activeObjectType && false !== stripos( - Route::getCurrentRoute()->getName(), + \Route::getCurrentRoute()->getName(), $route )) { return 'active'; @@ -328,8 +312,6 @@ class General extends AbstractExtension /** * Will return "menu-open" when a part of the route matches the argument. * ie. "accounts" will match "accounts.index". - * - * @return TwigFunction */ protected function menuOpenRoutePartial(): TwigFunction { @@ -338,7 +320,7 @@ class General extends AbstractExtension static function (): string { $args = func_get_args(); $route = $args[0]; // name of the route. - $name = Route::getCurrentRoute()->getName() ?? ''; + $name = \Route::getCurrentRoute()->getName() ?? ''; if (str_contains($name, $route)) { return 'menu-open'; } @@ -350,8 +332,6 @@ class General extends AbstractExtension /** * Formats a string as a thing by converting it to a Carbon first. - * - * @return TwigFunction */ protected function formatDate(): TwigFunction { @@ -366,8 +346,8 @@ class General extends AbstractExtension } /** - * @return TwigFunction - * TODO remove me when layout v1 is deprecated. + * @return twigFunction + * TODO remove me when layout v1 is deprecated */ protected function getMetaField(): TwigFunction { @@ -388,8 +368,6 @@ class General extends AbstractExtension /** * Will return true if the user is of role X. - * - * @return TwigFunction */ protected function hasRole(): TwigFunction { @@ -406,9 +384,6 @@ class General extends AbstractExtension ); } - /** - * @return TwigFunction - */ protected function getRootSearchOperator(): TwigFunction { return new TwigFunction( @@ -421,9 +396,6 @@ class General extends AbstractExtension ); } - /** - * @return TwigFunction - */ protected function carbonize(): TwigFunction { return new TwigFunction( diff --git a/app/Support/Twig/Rule.php b/app/Support/Twig/Rule.php index 44ecd420cd..c79031d807 100644 --- a/app/Support/Twig/Rule.php +++ b/app/Support/Twig/Rule.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Support\Twig; -use Config; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; @@ -32,9 +31,6 @@ use Twig\TwigFunction; */ class Rule extends AbstractExtension { - /** - * @inheritDoc - */ public function getFunctions(): array { return [ @@ -44,9 +40,6 @@ class Rule extends AbstractExtension ]; } - /** - * @return TwigFunction - */ public function allJournalTriggers(): TwigFunction { return new TwigFunction( @@ -60,9 +53,6 @@ class Rule extends AbstractExtension ); } - /** - * @return TwigFunction - */ public function allRuleTriggers(): TwigFunction { return new TwigFunction( @@ -72,7 +62,7 @@ class Rule extends AbstractExtension $possibleTriggers = []; foreach ($ruleTriggers as $key) { if ('user_action' !== $key) { - $possibleTriggers[$key] = (string)trans('firefly.rule_trigger_' . $key . '_choice'); + $possibleTriggers[$key] = (string)trans('firefly.rule_trigger_'.$key.'_choice'); } } unset($ruleTriggers); @@ -83,19 +73,16 @@ class Rule extends AbstractExtension ); } - /** - * @return TwigFunction - */ public function allActionTriggers(): TwigFunction { return new TwigFunction( 'allRuleActions', static function () { // array of valid values for actions - $ruleActions = array_keys(Config::get('firefly.rule-actions')); + $ruleActions = array_keys(\Config::get('firefly.rule-actions')); $possibleActions = []; foreach ($ruleActions as $key) { - $possibleActions[$key] = (string)trans('firefly.rule_action_' . $key . '_choice'); + $possibleActions[$key] = (string)trans('firefly.rule_action_'.$key.'_choice'); } unset($ruleActions); asort($possibleActions); diff --git a/app/Support/Twig/TransactionGroupTwig.php b/app/Support/Twig/TransactionGroupTwig.php index 09f09cec7e..21e081796d 100644 --- a/app/Support/Twig/TransactionGroupTwig.php +++ b/app/Support/Twig/TransactionGroupTwig.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Support\Twig; use Carbon\Carbon; -use DB; use FireflyIII\Models\AccountType; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; @@ -38,9 +37,6 @@ use Twig\TwigFunction; */ class TransactionGroupTwig extends AbstractExtension { - /** - * @inheritDoc - */ public function getFunctions(): array { return [ @@ -54,8 +50,6 @@ class TransactionGroupTwig extends AbstractExtension /** * Shows the amount for a single journal array. - * - * @return TwigFunction */ public function journalArrayAmount(): TwigFunction { @@ -76,91 +70,8 @@ class TransactionGroupTwig extends AbstractExtension ); } - /** - * Generate normal amount for transaction from a transaction group. - * - * @param array $array - * - * @return string - */ - private function normalJournalArrayAmount(array $array): string - { - $type = $array['transaction_type_type'] ?? TransactionType::WITHDRAWAL; - $amount = $array['amount'] ?? '0'; - $colored = true; - $sourceType = $array['source_account_type'] ?? 'invalid'; - $amount = $this->signAmount($amount, $type, $sourceType); - - if ($type === TransactionType::TRANSFER) { - $colored = false; - } - - $result = app('amount')->formatFlat($array['currency_symbol'], (int)$array['currency_decimal_places'], $amount, $colored); - if ($type === TransactionType::TRANSFER) { - $result = sprintf('%s', $result); - } - - return $result; - } - - /** - * @param string $amount - * @param string $transactionType - * @param string $sourceType - * - * @return string - */ - private function signAmount(string $amount, string $transactionType, string $sourceType): string - { - // withdrawals stay negative - if ($transactionType !== TransactionType::WITHDRAWAL) { - $amount = bcmul($amount, '-1'); - } - - // opening balance and it comes from initial balance? its expense. - if ($transactionType === TransactionType::OPENING_BALANCE && AccountType::INITIAL_BALANCE !== $sourceType) { - $amount = bcmul($amount, '-1'); - } - - // reconciliation and it comes from reconciliation? - if ($transactionType === TransactionType::RECONCILIATION && AccountType::RECONCILIATION !== $sourceType) { - $amount = bcmul($amount, '-1'); - } - - return $amount; - } - - /** - * Generate foreign amount for transaction from a transaction group. - * - * @param array $array - * - * @return string - */ - private function foreignJournalArrayAmount(array $array): string - { - $type = $array['transaction_type_type'] ?? TransactionType::WITHDRAWAL; - $amount = $array['foreign_amount'] ?? '0'; - $colored = true; - - $sourceType = $array['source_account_type'] ?? 'invalid'; - $amount = $this->signAmount($amount, $type, $sourceType); - - if ($type === TransactionType::TRANSFER) { - $colored = false; - } - $result = app('amount')->formatFlat($array['foreign_currency_symbol'], (int)$array['foreign_currency_decimal_places'], $amount, $colored); - if ($type === TransactionType::TRANSFER) { - $result = sprintf('%s', $result); - } - - return $result; - } - /** * Shows the amount for a single journal object. - * - * @return TwigFunction */ public function journalObjectAmount(): TwigFunction { @@ -180,12 +91,133 @@ class TransactionGroupTwig extends AbstractExtension ); } + public function journalHasMeta(): TwigFunction + { + return new TwigFunction( + 'journalHasMeta', + static function (int $journalId, string $metaField) { + $count = \DB::table('journal_meta') + ->where('name', $metaField) + ->where('transaction_journal_id', $journalId) + ->whereNull('deleted_at') + ->count() + ; + + return 1 === $count; + } + ); + } + + public function journalGetMetaDate(): TwigFunction + { + return new TwigFunction( + 'journalGetMetaDate', + static function (int $journalId, string $metaField) { + /** @var null|TransactionJournalMeta $entry */ + $entry = \DB::table('journal_meta') + ->where('name', $metaField) + ->where('transaction_journal_id', $journalId) + ->whereNull('deleted_at') + ->first() + ; + if (null === $entry) { + return today(config('app.timezone')); + } + + return new Carbon(json_decode($entry->data, false)); + } + ); + } + + public function journalGetMetaField(): TwigFunction + { + return new TwigFunction( + 'journalGetMetaField', + static function (int $journalId, string $metaField) { + /** @var null|TransactionJournalMeta $entry */ + $entry = \DB::table('journal_meta') + ->where('name', $metaField) + ->where('transaction_journal_id', $journalId) + ->whereNull('deleted_at') + ->first() + ; + if (null === $entry) { + return ''; + } + + return json_decode($entry->data, true); + } + ); + } + + /** + * Generate normal amount for transaction from a transaction group. + */ + private function normalJournalArrayAmount(array $array): string + { + $type = $array['transaction_type_type'] ?? TransactionType::WITHDRAWAL; + $amount = $array['amount'] ?? '0'; + $colored = true; + $sourceType = $array['source_account_type'] ?? 'invalid'; + $amount = $this->signAmount($amount, $type, $sourceType); + + if (TransactionType::TRANSFER === $type) { + $colored = false; + } + + $result = app('amount')->formatFlat($array['currency_symbol'], (int)$array['currency_decimal_places'], $amount, $colored); + if (TransactionType::TRANSFER === $type) { + $result = sprintf('%s', $result); + } + + return $result; + } + + private function signAmount(string $amount, string $transactionType, string $sourceType): string + { + // withdrawals stay negative + if (TransactionType::WITHDRAWAL !== $transactionType) { + $amount = bcmul($amount, '-1'); + } + + // opening balance and it comes from initial balance? its expense. + if (TransactionType::OPENING_BALANCE === $transactionType && AccountType::INITIAL_BALANCE !== $sourceType) { + $amount = bcmul($amount, '-1'); + } + + // reconciliation and it comes from reconciliation? + if (TransactionType::RECONCILIATION === $transactionType && AccountType::RECONCILIATION !== $sourceType) { + $amount = bcmul($amount, '-1'); + } + + return $amount; + } + + /** + * Generate foreign amount for transaction from a transaction group. + */ + private function foreignJournalArrayAmount(array $array): string + { + $type = $array['transaction_type_type'] ?? TransactionType::WITHDRAWAL; + $amount = $array['foreign_amount'] ?? '0'; + $colored = true; + + $sourceType = $array['source_account_type'] ?? 'invalid'; + $amount = $this->signAmount($amount, $type, $sourceType); + + if (TransactionType::TRANSFER === $type) { + $colored = false; + } + $result = app('amount')->formatFlat($array['foreign_currency_symbol'], (int)$array['foreign_currency_decimal_places'], $amount, $colored); + if (TransactionType::TRANSFER === $type) { + $result = sprintf('%s', $result); + } + + return $result; + } + /** * Generate normal amount for transaction from a transaction group. - * - * @param TransactionJournal $journal - * - * @return string */ private function normalJournalObjectAmount(TransactionJournal $journal): string { @@ -198,22 +230,17 @@ class TransactionGroupTwig extends AbstractExtension $amount = $this->signAmount($amount, $type, $sourceType); - if ($type === TransactionType::TRANSFER) { + if (TransactionType::TRANSFER === $type) { $colored = false; } $result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored); - if ($type === TransactionType::TRANSFER) { + if (TransactionType::TRANSFER === $type) { $result = sprintf('%s', $result); } return $result; } - /** - * @param TransactionJournal $journal - * - * @return bool - */ private function journalObjectHasForeign(TransactionJournal $journal): bool { /** @var Transaction $first */ @@ -224,14 +251,11 @@ class TransactionGroupTwig extends AbstractExtension /** * Generate foreign amount for journal from a transaction group. - * - * @param TransactionJournal $journal - * - * @return string */ private function foreignJournalObjectAmount(TransactionJournal $journal): string { $type = $journal->transactionType->type; + /** @var Transaction $first */ $first = $journal->transactions()->where('amount', '<', 0)->first(); $currency = $first->foreignCurrency; @@ -241,79 +265,14 @@ class TransactionGroupTwig extends AbstractExtension $amount = $this->signAmount($amount, $type, $sourceType); - if ($type === TransactionType::TRANSFER) { + if (TransactionType::TRANSFER === $type) { $colored = false; } $result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored); - if ($type === TransactionType::TRANSFER) { + if (TransactionType::TRANSFER === $type) { $result = sprintf('%s', $result); } return $result; } - - /** - * @return TwigFunction - */ - public function journalHasMeta(): TwigFunction - { - return new TwigFunction( - 'journalHasMeta', - static function (int $journalId, string $metaField) { - $count = DB::table('journal_meta') - ->where('name', $metaField) - ->where('transaction_journal_id', $journalId) - ->whereNull('deleted_at') - ->count(); - - return 1 === $count; - } - ); - } - - /** - * @return TwigFunction - */ - public function journalGetMetaDate(): TwigFunction - { - return new TwigFunction( - 'journalGetMetaDate', - static function (int $journalId, string $metaField) { - /** @var TransactionJournalMeta|null $entry */ - $entry = DB::table('journal_meta') - ->where('name', $metaField) - ->where('transaction_journal_id', $journalId) - ->whereNull('deleted_at') - ->first(); - if (null === $entry) { - return today(config('app.timezone')); - } - - return new Carbon(json_decode($entry->data, false)); - } - ); - } - - /** - * @return TwigFunction - */ - public function journalGetMetaField(): TwigFunction - { - return new TwigFunction( - 'journalGetMetaField', - static function (int $journalId, string $metaField) { - /** @var TransactionJournalMeta|null $entry */ - $entry = DB::table('journal_meta') - ->where('name', $metaField) - ->where('transaction_journal_id', $journalId) - ->whereNull('deleted_at') - ->first(); - if (null === $entry) { - return ''; - } - - return json_decode($entry->data, true); - } - ); - } } diff --git a/app/Support/Twig/Translation.php b/app/Support/Twig/Translation.php index 806d065b72..eaeac5ca6c 100644 --- a/app/Support/Twig/Translation.php +++ b/app/Support/Twig/Translation.php @@ -32,9 +32,6 @@ use Twig\TwigFunction; */ class Translation extends AbstractExtension { - /** - * @inheritDoc - */ public function getFilters(): array { return [ @@ -48,9 +45,6 @@ class Translation extends AbstractExtension ]; } - /** - * {@inheritdoc} - */ public function getFunctions(): array { return [ @@ -58,9 +52,6 @@ class Translation extends AbstractExtension ]; } - /** - * @return TwigFunction - */ public function journalLinkTranslation(): TwigFunction { return new TwigFunction( diff --git a/app/TransactionRules/Actions/ActionInterface.php b/app/TransactionRules/Actions/ActionInterface.php index c8bd82a992..8a1ffdd5ae 100644 --- a/app/TransactionRules/Actions/ActionInterface.php +++ b/app/TransactionRules/Actions/ActionInterface.php @@ -31,10 +31,6 @@ interface ActionInterface /** * 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/Actions/AddTag.php b/app/TransactionRules/Actions/AddTag.php index 6d8fcc961c..d231d732ba 100644 --- a/app/TransactionRules/Actions/AddTag.php +++ b/app/TransactionRules/Actions/AddTag.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Factory\TagFactory; @@ -40,22 +39,18 @@ class AddTag implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { // journal has this tag maybe? /** @var TagFactory $factory */ $factory = app(TagFactory::class); + /** @var User $user */ $user = User::find($journal['user_id']); $factory->setUser($user); @@ -68,19 +63,22 @@ class AddTag implements ActionInterface return false; } - $count = DB::table('tag_transaction_journal') - ->where('tag_id', $tag->id) - ->where('transaction_journal_id', $journal['transaction_journal_id']) - ->count(); + $count = \DB::table('tag_transaction_journal') + ->where('tag_id', $tag->id) + ->where('transaction_journal_id', $journal['transaction_journal_id']) + ->count() + ; if (0 === $count) { // add to journal: - DB::table('tag_transaction_journal')->insert(['tag_id' => $tag->id, 'transaction_journal_id' => $journal['transaction_journal_id']]); + \DB::table('tag_transaction_journal')->insert(['tag_id' => $tag->id, 'transaction_journal_id' => $journal['transaction_journal_id']]); app('log')->debug(sprintf('RuleAction AddTag. Added tag #%d ("%s") to journal %d.', $tag->id, $tag->tag, $journal['transaction_journal_id'])); + /** @var TransactionJournal $object */ $object = TransactionJournal::find($journal['transaction_journal_id']); // event for audit log entry event(new TriggeredAuditLog($this->action->rule, $object, 'add_tag', null, $tag->tag)); + return true; } app('log')->debug( diff --git a/app/TransactionRules/Actions/AppendDescription.php b/app/TransactionRules/Actions/AppendDescription.php index 57ac382767..1c460a3cd6 100644 --- a/app/TransactionRules/Actions/AppendDescription.php +++ b/app/TransactionRules/Actions/AppendDescription.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; @@ -37,21 +36,16 @@ class AppendDescription implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { $description = sprintf('%s %s', $journal['description'], $this->action->action_value); - DB::table('transaction_journals')->where('id', $journal['transaction_journal_id'])->limit(1)->update(['description' => $description]); + \DB::table('transaction_journals')->where('id', $journal['transaction_journal_id'])->limit(1)->update(['description' => $description]); // event for audit log entry /** @var TransactionJournal $object */ diff --git a/app/TransactionRules/Actions/AppendDescriptionToNotes.php b/app/TransactionRules/Actions/AppendDescriptionToNotes.php index 037aeacd27..8c6f385821 100644 --- a/app/TransactionRules/Actions/AppendDescriptionToNotes.php +++ b/app/TransactionRules/Actions/AppendDescriptionToNotes.php @@ -39,24 +39,20 @@ class AppendDescriptionToNotes implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { - /** @var TransactionJournal|null $object */ + /** @var null|TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { app('log')->error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id'])); event(new RuleActionFailedOnArray($this->action, $journal, (string)trans('rules.journal_other_user'))); + return false; } $note = $object->notes()->first(); @@ -78,6 +74,7 @@ class AppendDescriptionToNotes implements ActionInterface event(new TriggeredAuditLog($this->action->rule, $object, 'update_notes', $before, $after)); $note->save(); + return true; } } diff --git a/app/TransactionRules/Actions/AppendNotes.php b/app/TransactionRules/Actions/AppendNotes.php index 9d4758116a..196fe79f45 100644 --- a/app/TransactionRules/Actions/AppendNotes.php +++ b/app/TransactionRules/Actions/AppendNotes.php @@ -37,22 +37,18 @@ class AppendNotes implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { $dbNote = Note::where('noteable_id', (int)$journal['transaction_journal_id']) - ->where('noteable_type', TransactionJournal::class) - ->first(['notes.*']); + ->where('noteable_type', TransactionJournal::class) + ->first(['notes.*']) + ; if (null === $dbNote) { $dbNote = new Note(); $dbNote->noteable_id = (int)$journal['transaction_journal_id']; diff --git a/app/TransactionRules/Actions/AppendNotesToDescription.php b/app/TransactionRules/Actions/AppendNotesToDescription.php index 96379d8a7a..7ba8abd032 100644 --- a/app/TransactionRules/Actions/AppendNotesToDescription.php +++ b/app/TransactionRules/Actions/AppendNotesToDescription.php @@ -42,25 +42,22 @@ class AppendNotesToDescription implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { app('log')->debug('Now in AppendNotesToDescription'); - /** @var TransactionJournal|null $object */ + + /** @var null|TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { app('log')->error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user'))); + return false; } $note = $object->notes()->first(); @@ -73,7 +70,7 @@ class AppendNotesToDescription implements ActionInterface // only append if there is something to append if ('' !== $note->text) { $before = $object->description; - $object->description = trim(sprintf('%s %s', $object->description, (string)$this->clearString($note->text))); + $object->description = trim(sprintf('%s %s', $object->description, (string) $this->clearString($note->text))); $object->save(); app('log')->debug(sprintf('Journal description is updated to "%s".', $object->description)); @@ -82,11 +79,12 @@ class AppendNotesToDescription implements ActionInterface return true; } event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.new_notes_empty'))); + return false; } /** - * @inheritDoc + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function get(string $key, mixed $default = null): mixed { @@ -94,7 +92,7 @@ class AppendNotesToDescription implements ActionInterface } /** - * @inheritDoc + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function has(mixed $key): mixed { diff --git a/app/TransactionRules/Actions/ClearBudget.php b/app/TransactionRules/Actions/ClearBudget.php index a9fde4d6f2..b6ca1af2e0 100644 --- a/app/TransactionRules/Actions/ClearBudget.php +++ b/app/TransactionRules/Actions/ClearBudget.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; @@ -38,17 +37,12 @@ class ClearBudget implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { /** @var TransactionJournal $object */ @@ -57,10 +51,11 @@ class ClearBudget implements ActionInterface if (null === $budget) { app('log')->debug(sprintf('RuleAction ClearBudget, no budget in journal #%d.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_already_no_budget'))); + return false; } - DB::table('budget_transaction_journal')->where('transaction_journal_id', '=', $journal['transaction_journal_id'])->delete(); + \DB::table('budget_transaction_journal')->where('transaction_journal_id', '=', $journal['transaction_journal_id'])->delete(); event(new TriggeredAuditLog($this->action->rule, $object, 'clear_budget', $budget->name, null)); diff --git a/app/TransactionRules/Actions/ClearCategory.php b/app/TransactionRules/Actions/ClearCategory.php index 4c622a25ab..fe87dd3c3f 100644 --- a/app/TransactionRules/Actions/ClearCategory.php +++ b/app/TransactionRules/Actions/ClearCategory.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; @@ -38,17 +37,12 @@ class ClearCategory implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { /** @var TransactionJournal $object */ @@ -57,10 +51,11 @@ class ClearCategory implements ActionInterface if (null === $category) { app('log')->debug(sprintf('RuleAction ClearCategory, no category in journal #%d.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_already_no_category'))); + return false; } - DB::table('category_transaction_journal')->where('transaction_journal_id', '=', $journal['transaction_journal_id'])->delete(); + \DB::table('category_transaction_journal')->where('transaction_journal_id', '=', $journal['transaction_journal_id'])->delete(); event(new TriggeredAuditLog($this->action->rule, $object, 'clear_category', $category->name, null)); diff --git a/app/TransactionRules/Actions/ClearNotes.php b/app/TransactionRules/Actions/ClearNotes.php index 3215f776df..296198735b 100644 --- a/app/TransactionRules/Actions/ClearNotes.php +++ b/app/TransactionRules/Actions/ClearNotes.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Note; @@ -39,34 +38,32 @@ class ClearNotes implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); - /** @var Note|null $notes */ + + /** @var null|Note $notes */ $notes = $object->notes()->first(); if (null === $notes) { app('log')->debug(sprintf('RuleAction ClearNotes, journal #%d has no notes.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_already_no_notes'))); + return false; } $before = $notes->text; - DB::table('notes') - ->where('noteable_id', $journal['transaction_journal_id']) - ->where('noteable_type', TransactionJournal::class) - ->delete(); + \DB::table('notes') + ->where('noteable_id', $journal['transaction_journal_id']) + ->where('noteable_type', TransactionJournal::class) + ->delete() + ; app('log')->debug(sprintf('RuleAction ClearNotes removed all notes from journal #%d.', $journal['transaction_journal_id'])); event(new TriggeredAuditLog($this->action->rule, $object, 'clear_notes', $before, null)); diff --git a/app/TransactionRules/Actions/ConvertToDeposit.php b/app/TransactionRules/Actions/ConvertToDeposit.php index 33b0840d5f..874aaca2b8 100644 --- a/app/TransactionRules/Actions/ConvertToDeposit.php +++ b/app/TransactionRules/Actions/ConvertToDeposit.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Exceptions\FireflyException; @@ -35,10 +34,8 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use JsonException; /** - * * Class ConvertToDeposit */ class ConvertToDeposit implements ActionInterface @@ -47,31 +44,28 @@ class ConvertToDeposit implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { // make object from array (so the data is fresh). - /** @var TransactionJournal|null $object */ + /** @var null|TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { app('log')->error(sprintf('Cannot find journal #%d, cannot convert to deposit.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found'))); + return false; } $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); if ($groupCount > 1) { app('log')->error(sprintf('Group #%d has more than one transaction in it, cannot convert to deposit.', $journal['transaction_group_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group'))); + return false; } @@ -80,6 +74,7 @@ class ConvertToDeposit implements ActionInterface if (TransactionType::DEPOSIT === $type) { app('log')->error(sprintf('Journal #%d is already a deposit (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id)); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_already_deposit'))); + return false; } @@ -88,10 +83,11 @@ class ConvertToDeposit implements ActionInterface try { $res = $this->convertWithdrawalArray($object); - } catch (JsonException | FireflyException $e) { + } catch (FireflyException $e) { app('log')->debug('Could not convert withdrawal to deposit.'); app('log')->error($e->getMessage()); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error'))); + return false; } @@ -104,10 +100,11 @@ class ConvertToDeposit implements ActionInterface try { $res = $this->convertTransferArray($object); - } catch (JsonException | FireflyException $e) { + } catch (FireflyException $e) { app('log')->debug('Could not convert transfer to deposit.'); app('log')->error($e->getMessage()); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error'))); + return false; } event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::TRANSFER, TransactionType::DEPOSIT)); @@ -115,6 +112,7 @@ class ConvertToDeposit implements ActionInterface return $res; } event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.unsupported_transaction_type_deposit', ['type' => $type]))); + return false; } @@ -122,15 +120,12 @@ class ConvertToDeposit implements ActionInterface * Input is a withdrawal from A to B * Is converted to a deposit from C to A. * - * @param TransactionJournal $journal - * - * @return bool * @throws FireflyException - * @throws JsonException */ private function convertWithdrawalArray(TransactionJournal $journal): bool { $user = $journal->user; + // find or create revenue account. /** @var AccountFactory $factory */ $factory = app(AccountFactory::class); @@ -155,23 +150,26 @@ class ConvertToDeposit implements ActionInterface app('log')->debug(sprintf('ConvertToDeposit. Action value is "%s", new opposing name is "%s"', $this->action->action_value, $opposingAccount->name)); // update the source transaction and put in the new revenue ID. - DB::table('transactions') - ->where('transaction_journal_id', '=', $journal->id) - ->where('amount', '<', 0) - ->update(['account_id' => $opposingAccount->id]); + \DB::table('transactions') + ->where('transaction_journal_id', '=', $journal->id) + ->where('amount', '<', 0) + ->update(['account_id' => $opposingAccount->id]) + ; // update the destination transaction and put in the original source account ID. - DB::table('transactions') - ->where('transaction_journal_id', '=', $journal->id) - ->where('amount', '>', 0) - ->update(['account_id' => $sourceAccount->id]); + \DB::table('transactions') + ->where('transaction_journal_id', '=', $journal->id) + ->where('amount', '>', 0) + ->update(['account_id' => $sourceAccount->id]) + ; // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::DEPOSIT)->first(); - DB::table('transaction_journals') - ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); + \DB::table('transaction_journals') + ->where('id', '=', $journal->id) + ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]) + ; app('log')->debug('Converted withdrawal to deposit.'); @@ -179,34 +177,30 @@ class ConvertToDeposit implements ActionInterface } /** - * @param TransactionJournal $journal - * - * @return Account * @throws FireflyException */ private function getDestinationAccount(TransactionJournal $journal): Account { - /** @var Transaction|null $destAccount */ + /** @var null|Transaction $destAccount */ $destAccount = $journal->transactions()->where('amount', '>', 0)->first(); if (null === $destAccount) { throw new FireflyException(sprintf('Cannot find destination transaction for journal #%d', $journal->id)); } + return $destAccount->account; } /** - * @param TransactionJournal $journal - * - * @return Account * @throws FireflyException */ private function getSourceAccount(TransactionJournal $journal): Account { - /** @var Transaction|null $sourceTransaction */ + /** @var null|Transaction $sourceTransaction */ $sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first(); if (null === $sourceTransaction) { throw new FireflyException(sprintf('Cannot find source transaction for journal #%d', $journal->id)); } + return $sourceTransaction->account; } @@ -215,15 +209,12 @@ class ConvertToDeposit implements ActionInterface * Output is a deposit from C to B. * The source account is replaced. * - * @param TransactionJournal $journal - * - * @return bool * @throws FireflyException - * @throws JsonException */ private function convertTransferArray(TransactionJournal $journal): bool { $user = $journal->user; + // find or create revenue account. /** @var AccountFactory $factory */ $factory = app(AccountFactory::class); @@ -247,17 +238,19 @@ class ConvertToDeposit implements ActionInterface app('log')->debug(sprintf('ConvertToDeposit. Action value is "%s", revenue name is "%s"', $this->action->action_value, $opposingAccount->name)); // update source transaction(s) to be revenue account - DB::table('transactions') - ->where('transaction_journal_id', '=', $journal->id) - ->where('amount', '<', 0) - ->update(['account_id' => $opposingAccount->id]); + \DB::table('transactions') + ->where('transaction_journal_id', '=', $journal->id) + ->where('amount', '<', 0) + ->update(['account_id' => $opposingAccount->id]) + ; // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::DEPOSIT)->first(); - DB::table('transaction_journals') - ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); + \DB::table('transaction_journals') + ->where('id', '=', $journal->id) + ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]) + ; app('log')->debug('Converted transfer to deposit.'); diff --git a/app/TransactionRules/Actions/ConvertToTransfer.php b/app/TransactionRules/Actions/ConvertToTransfer.php index 704d63c637..9a2878186f 100644 --- a/app/TransactionRules/Actions/ConvertToTransfer.php +++ b/app/TransactionRules/Actions/ConvertToTransfer.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject; use FireflyIII\Events\TriggeredAuditLog; @@ -36,7 +35,6 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; /** - * * Class ConvertToTransfer */ class ConvertToTransfer implements ActionInterface @@ -45,8 +43,6 @@ class ConvertToTransfer implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { @@ -54,22 +50,25 @@ class ConvertToTransfer implements ActionInterface } /** - * @inheritDoc + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.NPathComplexity) */ public function actOnArray(array $journal): bool { // make object from array (so the data is fresh). - /** @var TransactionJournal|null $object */ + /** @var null|TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { app('log')->error(sprintf('Cannot find journal #%d, cannot convert to transfer.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found'))); + return false; } $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); if ($groupCount > 1) { app('log')->error(sprintf('Group #%d has more than one transaction in it, cannot convert to transfer.', $journal['transaction_group_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group'))); + return false; } @@ -86,6 +85,7 @@ class ConvertToTransfer implements ActionInterface } if (TransactionType::DEPOSIT !== $type && TransactionType::WITHDRAWAL !== $type) { event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.unsupported_transaction_type_transfer', ['type' => $type]))); + return false; } @@ -121,65 +121,65 @@ class ConvertToTransfer implements ActionInterface if (TransactionType::WITHDRAWAL === $type) { app('log')->debug('Going to transform a withdrawal to a transfer.'); + try { $res = $this->convertWithdrawalArray($object, $opposing); } catch (FireflyException $e) { app('log')->debug('Could not convert withdrawal to transfer.'); app('log')->error($e->getMessage()); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error'))); + return false; } if (false !== $res) { event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::WITHDRAWAL, TransactionType::TRANSFER)); } + return $res; } // can only be a deposit at this point. app('log')->debug('Going to transform a deposit to a transfer.'); + try { $res = $this->convertDepositArray($object, $opposing); } catch (FireflyException $e) { app('log')->debug('Could not convert deposit to transfer.'); app('log')->error($e->getMessage()); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error'))); + return false; } if (false !== $res) { event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::DEPOSIT, TransactionType::TRANSFER)); } + return $res; } - /** - * @param int $journalId - * - * @return string - */ private function getSourceType(int $journalId): string { - /** @var TransactionJournal|null $journal */ + /** @var null|TransactionJournal $journal */ $journal = TransactionJournal::find($journalId); if (null === $journal) { app('log')->error(sprintf('Journal #%d does not exist. Cannot convert to transfer.', $journalId)); + return ''; } - return (string)$journal->transactions()->where('amount', '<', 0)->first()?->account?->accountType?->type; + + return (string) $journal->transactions()->where('amount', '<', 0)->first()?->account?->accountType?->type; } - /** - * @param int $journalId - * - * @return string - */ private function getDestinationType(int $journalId): string { - /** @var TransactionJournal|null $journal */ + /** @var null|TransactionJournal $journal */ $journal = TransactionJournal::find($journalId); if (null === $journal) { app('log')->error(sprintf('Journal #%d does not exist. Cannot convert to transfer.', $journalId)); + return ''; } - return (string)$journal->transactions()->where('amount', '>', 0)->first()?->account?->accountType?->type; + + return (string) $journal->transactions()->where('amount', '>', 0)->first()?->account?->accountType?->type; } /** @@ -187,10 +187,6 @@ class ConvertToTransfer implements ActionInterface * We replace the Expense with another asset. * So this replaces the destination * - * @param TransactionJournal $journal - * @param Account $opposing - * - * @return bool * @throws FireflyException */ private function convertWithdrawalArray(TransactionJournal $journal, Account $opposing): bool @@ -209,17 +205,19 @@ class ConvertToTransfer implements ActionInterface } // update destination transaction: - DB::table('transactions') - ->where('transaction_journal_id', '=', $journal->id) - ->where('amount', '>', 0) - ->update(['account_id' => $opposing->id]); + \DB::table('transactions') + ->where('transaction_journal_id', '=', $journal->id) + ->where('amount', '>', 0) + ->update(['account_id' => $opposing->id]) + ; // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::TRANSFER)->first(); - DB::table('transaction_journals') - ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); + \DB::table('transaction_journals') + ->where('id', '=', $journal->id) + ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]) + ; app('log')->debug('Converted withdrawal to transfer.'); @@ -227,18 +225,16 @@ class ConvertToTransfer implements ActionInterface } /** - * @param TransactionJournal $journal - * - * @return Account * @throws FireflyException */ private function getSourceAccount(TransactionJournal $journal): Account { - /** @var Transaction|null $sourceTransaction */ + /** @var null|Transaction $sourceTransaction */ $sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first(); if (null === $sourceTransaction) { throw new FireflyException(sprintf('Cannot find source transaction for journal #%d', $journal->id)); } + return $sourceTransaction->account; } @@ -246,10 +242,6 @@ class ConvertToTransfer implements ActionInterface * A deposit is from Revenue to Asset. * We replace the Revenue with another asset. * - * @param TransactionJournal $journal - * @param Account $opposing - * - * @return bool * @throws FireflyException */ private function convertDepositArray(TransactionJournal $journal, Account $opposing): bool @@ -268,17 +260,19 @@ class ConvertToTransfer implements ActionInterface } // update source transaction: - DB::table('transactions') - ->where('transaction_journal_id', '=', $journal->id) - ->where('amount', '<', 0) - ->update(['account_id' => $opposing->id]); + \DB::table('transactions') + ->where('transaction_journal_id', '=', $journal->id) + ->where('amount', '<', 0) + ->update(['account_id' => $opposing->id]) + ; // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::TRANSFER)->first(); - DB::table('transaction_journals') - ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); + \DB::table('transaction_journals') + ->where('id', '=', $journal->id) + ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]) + ; app('log')->debug('Converted deposit to transfer.'); @@ -286,18 +280,16 @@ class ConvertToTransfer implements ActionInterface } /** - * @param TransactionJournal $journal - * - * @return Account * @throws FireflyException */ private function getDestinationAccount(TransactionJournal $journal): Account { - /** @var Transaction|null $destAccount */ + /** @var null|Transaction $destAccount */ $destAccount = $journal->transactions()->where('amount', '>', 0)->first(); if (null === $destAccount) { throw new FireflyException(sprintf('Cannot find destination transaction for journal #%d', $journal->id)); } + return $destAccount->account; } } diff --git a/app/TransactionRules/Actions/ConvertToWithdrawal.php b/app/TransactionRules/Actions/ConvertToWithdrawal.php index cfd82072a4..e9fe15633d 100644 --- a/app/TransactionRules/Actions/ConvertToWithdrawal.php +++ b/app/TransactionRules/Actions/ConvertToWithdrawal.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Exceptions\FireflyException; @@ -35,10 +34,8 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use JsonException; /** - * * Class ConvertToWithdrawal */ class ConvertToWithdrawal implements ActionInterface @@ -47,31 +44,28 @@ class ConvertToWithdrawal implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { // make object from array (so the data is fresh). - /** @var TransactionJournal|null $object */ + /** @var null|TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { app('log')->error(sprintf('Cannot find journal #%d, cannot convert to withdrawal.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found'))); + return false; } $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); if ($groupCount > 1) { app('log')->error(sprintf('Group #%d has more than one transaction in it, cannot convert to withdrawal.', $journal['transaction_group_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group'))); + return false; } @@ -79,20 +73,24 @@ class ConvertToWithdrawal implements ActionInterface if (TransactionType::WITHDRAWAL === $type) { app('log')->error(sprintf('Journal #%d is already a withdrawal (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id)); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_already_withdrawal'))); + return false; } if (TransactionType::DEPOSIT !== $type && TransactionType::TRANSFER !== $type) { event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.unsupported_transaction_type_withdrawal', ['type' => $type]))); + return false; } if (TransactionType::DEPOSIT === $type) { app('log')->debug('Going to transform a deposit to a withdrawal.'); + try { $res = $this->convertDepositArray($object); - } catch (JsonException | FireflyException $e) { + } catch (FireflyException $e) { app('log')->debug('Could not convert transfer to deposit.'); app('log')->error($e->getMessage()); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error'))); + return false; } event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::DEPOSIT, TransactionType::WITHDRAWAL)); @@ -104,10 +102,11 @@ class ConvertToWithdrawal implements ActionInterface try { $res = $this->convertTransferArray($object); - } catch (JsonException | FireflyException $e) { + } catch (FireflyException $e) { app('log')->debug('Could not convert transfer to deposit.'); app('log')->error($e->getMessage()); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error'))); + return false; } event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::TRANSFER, TransactionType::WITHDRAWAL)); @@ -116,15 +115,12 @@ class ConvertToWithdrawal implements ActionInterface } /** - * @param TransactionJournal $journal - * - * @return bool * @throws FireflyException - * @throws JsonException */ private function convertDepositArray(TransactionJournal $journal): bool { $user = $journal->user; + /** @var AccountFactory $factory */ $factory = app(AccountFactory::class); $factory->setUser($user); @@ -148,22 +144,25 @@ class ConvertToWithdrawal implements ActionInterface app('log')->debug(sprintf('ConvertToWithdrawal. Action value is "%s", expense name is "%s"', $this->action->action_value, $opposingName)); // update source transaction(s) to be the original destination account - DB::table('transactions') - ->where('transaction_journal_id', '=', $journal->id) - ->where('amount', '<', 0) - ->update(['account_id' => $destAccount->id]); + \DB::table('transactions') + ->where('transaction_journal_id', '=', $journal->id) + ->where('amount', '<', 0) + ->update(['account_id' => $destAccount->id]) + ; // update destination transaction(s) to be new expense account. - DB::table('transactions') - ->where('transaction_journal_id', '=', $journal->id) - ->where('amount', '>', 0) - ->update(['account_id' => $opposingAccount->id]); + \DB::table('transactions') + ->where('transaction_journal_id', '=', $journal->id) + ->where('amount', '>', 0) + ->update(['account_id' => $opposingAccount->id]) + ; // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); - DB::table('transaction_journals') - ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id]); + \DB::table('transaction_journals') + ->where('id', '=', $journal->id) + ->update(['transaction_type_id' => $newType->id]) + ; app('log')->debug('Converted deposit to withdrawal.'); @@ -171,34 +170,30 @@ class ConvertToWithdrawal implements ActionInterface } /** - * @param TransactionJournal $journal - * - * @return Account * @throws FireflyException */ private function getSourceAccount(TransactionJournal $journal): Account { - /** @var Transaction|null $sourceTransaction */ + /** @var null|Transaction $sourceTransaction */ $sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first(); if (null === $sourceTransaction) { throw new FireflyException(sprintf('Cannot find source transaction for journal #%d', $journal->id)); } + return $sourceTransaction->account; } /** - * @param TransactionJournal $journal - * - * @return Account * @throws FireflyException */ private function getDestinationAccount(TransactionJournal $journal): Account { - /** @var Transaction|null $destAccount */ + /** @var null|Transaction $destAccount */ $destAccount = $journal->transactions()->where('amount', '>', 0)->first(); if (null === $destAccount) { throw new FireflyException(sprintf('Cannot find destination transaction for journal #%d', $journal->id)); } + return $destAccount->account; } @@ -206,16 +201,13 @@ class ConvertToWithdrawal implements ActionInterface * Input is a transfer from A to B. * Output is a withdrawal from A to C. * - * @param TransactionJournal $journal - * - * @return bool * @throws FireflyException - * @throws JsonException */ private function convertTransferArray(TransactionJournal $journal): bool { // find or create expense account. $user = $journal->user; + /** @var AccountFactory $factory */ $factory = app(AccountFactory::class); $factory->setUser($user); @@ -238,16 +230,18 @@ class ConvertToWithdrawal implements ActionInterface app('log')->debug(sprintf('ConvertToWithdrawal. Action value is "%s", destination name is "%s"', $this->action->action_value, $opposingName)); // update destination transaction(s) to be new expense account. - DB::table('transactions') - ->where('transaction_journal_id', '=', $journal->id) - ->where('amount', '>', 0) - ->update(['account_id' => $opposingAccount->id]); + \DB::table('transactions') + ->where('transaction_journal_id', '=', $journal->id) + ->where('amount', '>', 0) + ->update(['account_id' => $opposingAccount->id]) + ; // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); - DB::table('transaction_journals') - ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id]); + \DB::table('transaction_journals') + ->where('id', '=', $journal->id) + ->update(['transaction_type_id' => $newType->id]) + ; app('log')->debug('Converted transfer to withdrawal.'); diff --git a/app/TransactionRules/Actions/DeleteTransaction.php b/app/TransactionRules/Actions/DeleteTransaction.php index fffa7c6118..0597695eac 100644 --- a/app/TransactionRules/Actions/DeleteTransaction.php +++ b/app/TransactionRules/Actions/DeleteTransaction.php @@ -39,17 +39,12 @@ class DeleteTransaction implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { $count = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); @@ -63,6 +58,7 @@ class DeleteTransaction implements ActionInterface $journal['description'] ) ); + /** @var TransactionGroup $group */ $group = TransactionGroup::find($journal['transaction_group_id']); $service = app(TransactionGroupDestroyService::class); @@ -77,7 +73,7 @@ class DeleteTransaction implements ActionInterface ); // trigger delete factory: - /** @var TransactionJournal|null $object */ + /** @var null|TransactionJournal $object */ $object = TransactionJournal::find($journal['transaction_journal_id']); if (null !== $object) { /** @var JournalDestroyService $service */ diff --git a/app/TransactionRules/Actions/LinkToBill.php b/app/TransactionRules/Actions/LinkToBill.php index 3cb703a5bc..91f8243a4a 100644 --- a/app/TransactionRules/Actions/LinkToBill.php +++ b/app/TransactionRules/Actions/LinkToBill.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; @@ -41,31 +40,27 @@ class LinkToBill implements ActionInterface /** * TriggerInterface constructor. - * - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { /** @var User $user */ $user = User::find($journal['user_id']); + /** @var BillRepositoryInterface $repository */ $repository = app(BillRepositoryInterface::class); $repository->setUser($user); $billName = (string)$this->action->action_value; $bill = $repository->findByName($billName); - if (null !== $bill && $journal['transaction_type_type'] === TransactionType::WITHDRAWAL) { - $count = DB::table('transaction_journals')->where('id', '=', $journal['transaction_journal_id']) - ->where('bill_id', $bill->id)->count(); + if (null !== $bill && TransactionType::WITHDRAWAL === $journal['transaction_type_type']) { + $count = \DB::table('transaction_journals')->where('id', '=', $journal['transaction_journal_id']) + ->where('bill_id', $bill->id)->count() + ; if (0 !== $count) { app('log')->error( sprintf( @@ -75,13 +70,14 @@ class LinkToBill implements ActionInterface ) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_linked_to_subscription', ['name' => $billName]))); + return false; } - - DB::table('transaction_journals') - ->where('id', '=', $journal['transaction_journal_id']) - ->update(['bill_id' => $bill->id]); + \DB::table('transaction_journals') + ->where('id', '=', $journal['transaction_journal_id']) + ->update(['bill_id' => $bill->id]) + ; app('log')->debug( sprintf('RuleAction LinkToBill set the bill of journal #%d to bill #%d ("%s").', $journal['transaction_journal_id'], $bill->id, $bill->name) ); @@ -101,6 +97,7 @@ class LinkToBill implements ActionInterface ) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_subscription', ['name' => $billName]))); + return false; } } diff --git a/app/TransactionRules/Actions/MoveDescriptionToNotes.php b/app/TransactionRules/Actions/MoveDescriptionToNotes.php index b064e3c314..0e5bda4381 100644 --- a/app/TransactionRules/Actions/MoveDescriptionToNotes.php +++ b/app/TransactionRules/Actions/MoveDescriptionToNotes.php @@ -39,28 +39,24 @@ class MoveDescriptionToNotes implements ActionInterface /** * TriggerInterface constructor. - * - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { - /** @var TransactionJournal|null $object */ + /** @var null|TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { app('log')->error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user'))); + return false; } - /** @var Note|null $note */ + + /** @var null|Note $note */ $note = $object->notes()->first(); if (null === $note) { $note = new Note(); @@ -84,6 +80,7 @@ class MoveDescriptionToNotes implements ActionInterface $note->save(); $object->save(); + return true; } } diff --git a/app/TransactionRules/Actions/MoveNotesToDescription.php b/app/TransactionRules/Actions/MoveNotesToDescription.php index e5ee1113cb..2a8b51fce7 100644 --- a/app/TransactionRules/Actions/MoveNotesToDescription.php +++ b/app/TransactionRules/Actions/MoveNotesToDescription.php @@ -45,30 +45,26 @@ class MoveNotesToDescription implements ActionInterface /** * TriggerInterface constructor. - * - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { - /** @var TransactionJournal|null $object */ + /** @var null|TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { app('log')->error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user'))); + return false; } $note = $object->notes()->first(); if (null === $note) { event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_notes_to_move'))); + // nothing to move, return null return false; } @@ -76,11 +72,12 @@ class MoveNotesToDescription implements ActionInterface // nothing to move, return null $note->delete(); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_notes_to_move'))); + return false; } $before = $object->description; $beforeNote = $note->text; - $object->description = (string)$this->clearString($note->text); + $object->description = (string) $this->clearString($note->text); $object->save(); $note->delete(); @@ -91,7 +88,7 @@ class MoveNotesToDescription implements ActionInterface } /** - * @inheritDoc + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function get(string $key, mixed $default = null): mixed { @@ -99,7 +96,7 @@ class MoveNotesToDescription implements ActionInterface } /** - * @inheritDoc + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function has(mixed $key): mixed { diff --git a/app/TransactionRules/Actions/PrependDescription.php b/app/TransactionRules/Actions/PrependDescription.php index c116370d9b..f00db5d501 100644 --- a/app/TransactionRules/Actions/PrependDescription.php +++ b/app/TransactionRules/Actions/PrependDescription.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; @@ -37,22 +36,17 @@ class PrependDescription implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { $before = $journal['description']; $after = sprintf('%s%s', $this->action->action_value, $journal['description']); - DB::table('transaction_journals')->where('id', $journal['transaction_journal_id'])->limit(1)->update(['description' => $after]); + \DB::table('transaction_journals')->where('id', $journal['transaction_journal_id'])->limit(1)->update(['description' => $after]); // journal /** @var TransactionJournal $object */ diff --git a/app/TransactionRules/Actions/PrependNotes.php b/app/TransactionRules/Actions/PrependNotes.php index 3e97f33076..6c03ea481b 100644 --- a/app/TransactionRules/Actions/PrependNotes.php +++ b/app/TransactionRules/Actions/PrependNotes.php @@ -37,22 +37,18 @@ class PrependNotes implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { $dbNote = Note::where('noteable_id', (int)$journal['transaction_journal_id']) - ->where('noteable_type', TransactionJournal::class) - ->first(['notes.*']); + ->where('noteable_type', TransactionJournal::class) + ->first(['notes.*']) + ; if (null === $dbNote) { $dbNote = new Note(); $dbNote->noteable_id = (int)$journal['transaction_journal_id']; diff --git a/app/TransactionRules/Actions/RemoveAllTags.php b/app/TransactionRules/Actions/RemoveAllTags.php index f7dbe97782..37da4d8768 100644 --- a/app/TransactionRules/Actions/RemoveAllTags.php +++ b/app/TransactionRules/Actions/RemoveAllTags.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; @@ -38,24 +37,20 @@ class RemoveAllTags implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { - DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->delete(); - $count = DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->count(); + \DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->delete(); + $count = \DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->count(); if (0 === $count) { app('log')->debug(sprintf('RuleAction RemoveAllTags, journal #%d has no tags.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_tags_to_remove'))); + return false; } app('log')->debug(sprintf('RuleAction RemoveAllTags removed all tags from journal %d.', $journal['transaction_journal_id'])); diff --git a/app/TransactionRules/Actions/RemoveTag.php b/app/TransactionRules/Actions/RemoveTag.php index 0d1fc2a872..207c7cdeeb 100644 --- a/app/TransactionRules/Actions/RemoveTag.php +++ b/app/TransactionRules/Actions/RemoveTag.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; @@ -39,21 +38,17 @@ class RemoveTag implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { // if tag does not exist, no need to continue: $name = $this->action->action_value; + /** @var User $user */ $user = User::find($journal['user_id']); $tag = $user->tags()->where('tag', $name)->first(); @@ -63,22 +58,25 @@ class RemoveTag implements ActionInterface sprintf('RuleAction RemoveTag tried to remove tag "%s" from journal #%d but no such tag exists.', $name, $journal['transaction_journal_id']) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_tag', ['tag' => $name]))); + return false; } - $count = DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->where('tag_id', $tag->id)->count(); + $count = \DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->where('tag_id', $tag->id)->count(); if (0 === $count) { app('log')->debug( sprintf('RuleAction RemoveTag tried to remove tag "%s" from journal #%d but no such tag is linked.', $name, $journal['transaction_journal_id']) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_unlink_tag', ['tag' => $name]))); + return false; } app('log')->debug(sprintf('RuleAction RemoveTag removed tag #%d ("%s") from journal #%d.', $tag->id, $tag->tag, $journal['transaction_journal_id'])); - DB::table('tag_transaction_journal') - ->where('transaction_journal_id', $journal['transaction_journal_id']) - ->where('tag_id', $tag->id) - ->delete(); + \DB::table('tag_transaction_journal') + ->where('transaction_journal_id', $journal['transaction_journal_id']) + ->where('tag_id', $tag->id) + ->delete() + ; /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); diff --git a/app/TransactionRules/Actions/SetBudget.php b/app/TransactionRules/Actions/SetBudget.php index 66804fed0a..0d616b50ba 100644 --- a/app/TransactionRules/Actions/SetBudget.php +++ b/app/TransactionRules/Actions/SetBudget.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; @@ -40,17 +39,12 @@ class SetBudget implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { /** @var User $user */ @@ -67,6 +61,7 @@ class SetBudget implements ActionInterface ) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_budget', ['name' => $search]))); + return false; } @@ -80,6 +75,7 @@ class SetBudget implements ActionInterface ) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_set_budget', ['type' => $journal['transaction_type_type'], 'name' => $search]))); + return false; } @@ -90,16 +86,16 @@ class SetBudget implements ActionInterface $oldBudgetName = $oldBudget?->name; if ((int)$oldBudget?->id === $budget->id) { event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_linked_to_budget', ['name' => $budget->name]))); + return false; } - app('log')->debug( sprintf('RuleAction SetBudget set the budget of journal #%d to budget #%d ("%s").', $journal['transaction_journal_id'], $budget->id, $budget->name) ); - DB::table('budget_transaction_journal')->where('transaction_journal_id', '=', $journal['transaction_journal_id'])->delete(); - DB::table('budget_transaction_journal')->insert(['transaction_journal_id' => $journal['transaction_journal_id'], 'budget_id' => $budget->id]); + \DB::table('budget_transaction_journal')->where('transaction_journal_id', '=', $journal['transaction_journal_id'])->delete(); + \DB::table('budget_transaction_journal')->insert(['transaction_journal_id' => $journal['transaction_journal_id'], 'budget_id' => $budget->id]); /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); diff --git a/app/TransactionRules/Actions/SetCategory.php b/app/TransactionRules/Actions/SetCategory.php index ba7106ac63..cc95ab919d 100644 --- a/app/TransactionRules/Actions/SetCategory.php +++ b/app/TransactionRules/Actions/SetCategory.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Factory\CategoryFactory; @@ -40,25 +39,21 @@ class SetCategory implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { - /** @var User|null $user */ + /** @var null|User $user */ $user = User::find($journal['user_id']); $search = $this->action->action_value; if (null === $user) { app('log')->error(sprintf('Journal has no valid user ID so action SetCategory("%s") cannot be applied', $search), $journal); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal'))); + return false; } @@ -75,6 +70,7 @@ class SetCategory implements ActionInterface ) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_category', ['name' => $search]))); + return false; } @@ -94,11 +90,12 @@ class SetCategory implements ActionInterface $oldCategoryName = $oldCategory?->name; if ((int)$oldCategory?->id === $category->id) { event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_linked_to_category', ['name' => $category->name]))); + return false; } - DB::table('category_transaction_journal')->where('transaction_journal_id', '=', $journal['transaction_journal_id'])->delete(); - DB::table('category_transaction_journal')->insert(['transaction_journal_id' => $journal['transaction_journal_id'], 'category_id' => $category->id]); + \DB::table('category_transaction_journal')->where('transaction_journal_id', '=', $journal['transaction_journal_id'])->delete(); + \DB::table('category_transaction_journal')->insert(['transaction_journal_id' => $journal['transaction_journal_id'], 'category_id' => $category->id]); /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); diff --git a/app/TransactionRules/Actions/SetDescription.php b/app/TransactionRules/Actions/SetDescription.php index 7da6199f67..3d5e392014 100644 --- a/app/TransactionRules/Actions/SetDescription.php +++ b/app/TransactionRules/Actions/SetDescription.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; @@ -37,26 +36,22 @@ class SetDescription implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $before = $object->description; - DB::table('transaction_journals') - ->where('id', '=', $journal['transaction_journal_id']) - ->update(['description' => $this->action->action_value]); + \DB::table('transaction_journals') + ->where('id', '=', $journal['transaction_journal_id']) + ->update(['description' => $this->action->action_value]) + ; app('log')->debug( sprintf( diff --git a/app/TransactionRules/Actions/SetDestinationAccount.php b/app/TransactionRules/Actions/SetDestinationAccount.php index 82f3a25c8a..e8e3750cc5 100644 --- a/app/TransactionRules/Actions/SetDestinationAccount.php +++ b/app/TransactionRules/Actions/SetDestinationAccount.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Account; @@ -44,28 +43,25 @@ class SetDestinationAccount implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { /** @var User $user */ $user = User::find($journal['user_id']); - /** @var TransactionJournal|null $object */ + + /** @var null|TransactionJournal $object */ $object = $user->transactionJournals()->find((int)$journal['transaction_journal_id']); $this->repository = app(AccountRepositoryInterface::class); if (null === $object) { app('log')->error('Could not find journal.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal'))); + return false; } $type = $object->transactionType->type; @@ -82,21 +78,24 @@ class SetDestinationAccount implements ActionInterface ) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_asset', ['name' => $this->action->action_value]))); + return false; } // new destination account must be different from the current source account: - /** @var Transaction|null $source */ + /** @var null|Transaction $source */ $source = $object->transactions()->where('amount', '<', 0)->first(); if (null === $source) { app('log')->error('Could not find source transaction.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_source_transaction'))); + return false; } // account must not be deleted (in the meantime): if (null === $source->account) { app('log')->error('Could not find source transaction account.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_source_transaction_account'))); + return false; } if (null !== $newAccount && $newAccount->id === $source->account_id) { @@ -109,6 +108,7 @@ class SetDestinationAccount implements ActionInterface ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_has_destination', ['name' => $newAccount->name]))); + return false; } @@ -123,21 +123,17 @@ class SetDestinationAccount implements ActionInterface event(new TriggeredAuditLog($this->action->rule, $object, 'set_destination', null, $newAccount->name)); // update destination transaction with new destination account: - DB::table('transactions') - ->where('transaction_journal_id', '=', $object->id) - ->where('amount', '>', 0) - ->update(['account_id' => $newAccount->id]); + \DB::table('transactions') + ->where('transaction_journal_id', '=', $object->id) + ->where('amount', '>', 0) + ->update(['account_id' => $newAccount->id]) + ; app('log')->debug(sprintf('Updated journal #%d (group #%d) and gave it new destination account ID.', $object->id, $object->transaction_group_id)); return true; } - /** - * @param string $type - * - * @return Account|null - */ private function findAssetAccount(string $type): ?Account { // switch on type: @@ -148,9 +144,6 @@ class SetDestinationAccount implements ActionInterface return $this->repository->findByName($this->action->action_value, $allowed); } - /** - * @return Account - */ private function findWithdrawalDestinationAccount(): Account { $allowed = config('firefly.expected_source_types.destination.Withdrawal'); diff --git a/app/TransactionRules/Actions/SetDestinationToCashAccount.php b/app/TransactionRules/Actions/SetDestinationToCashAccount.php index 549420e08b..6dfd024b6c 100644 --- a/app/TransactionRules/Actions/SetDestinationToCashAccount.php +++ b/app/TransactionRules/Actions/SetDestinationToCashAccount.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; @@ -42,34 +41,32 @@ class SetDestinationToCashAccount implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { /** @var User $user */ $user = User::find($journal['user_id']); - /** @var TransactionJournal|null $object */ + + /** @var null|TransactionJournal $object */ $object = $user->transactionJournals()->find((int)$journal['transaction_journal_id']); $repository = app(AccountRepositoryInterface::class); if (null === $object) { app('log')->error('Could not find journal.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal'))); + return false; } $type = $object->transactionType->type; if (TransactionType::WITHDRAWAL !== $type) { app('log')->error('Transaction must be withdrawal.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.not_withdrawal'))); + return false; } @@ -78,17 +75,19 @@ class SetDestinationToCashAccount implements ActionInterface $cashAccount = $repository->getCashAccount(); // new destination account must be different from the current source account: - /** @var Transaction|null $source */ + /** @var null|Transaction $source */ $source = $object->transactions()->where('amount', '<', 0)->first(); if (null === $source) { app('log')->error('Could not find source transaction.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_source_transaction'))); + return false; } // account must not be deleted (in the meantime): if (null === $source->account) { app('log')->error('Could not find source transaction account.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_source_transaction_account'))); + return false; } if ($cashAccount->id === $source->account_id) { @@ -101,20 +100,21 @@ class SetDestinationToCashAccount implements ActionInterface ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_has_destination', ['name' => $cashAccount->name]))); + return false; } event(new TriggeredAuditLog($this->action->rule, $object, 'set_destination', null, $cashAccount->name)); // update destination transaction with new destination account: - DB::table('transactions') - ->where('transaction_journal_id', '=', $object->id) - ->where('amount', '>', 0) - ->update(['account_id' => $cashAccount->id]); + \DB::table('transactions') + ->where('transaction_journal_id', '=', $object->id) + ->where('amount', '>', 0) + ->update(['account_id' => $cashAccount->id]) + ; app('log')->debug(sprintf('Updated journal #%d (group #%d) and gave it new destination account ID.', $object->id, $object->transaction_group_id)); return true; - } } diff --git a/app/TransactionRules/Actions/SetNotes.php b/app/TransactionRules/Actions/SetNotes.php index ec4431dbfa..4e0235baea 100644 --- a/app/TransactionRules/Actions/SetNotes.php +++ b/app/TransactionRules/Actions/SetNotes.php @@ -37,21 +37,17 @@ class SetNotes implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { $dbNote = Note::where('noteable_id', $journal['transaction_journal_id']) - ->where('noteable_type', TransactionJournal::class)->first(); + ->where('noteable_type', TransactionJournal::class)->first() + ; if (null === $dbNote) { $dbNote = new Note(); $dbNote->noteable_id = $journal['transaction_journal_id']; diff --git a/app/TransactionRules/Actions/SetSourceAccount.php b/app/TransactionRules/Actions/SetSourceAccount.php index 87af4fef4b..c957dfec9d 100644 --- a/app/TransactionRules/Actions/SetSourceAccount.php +++ b/app/TransactionRules/Actions/SetSourceAccount.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Account; @@ -44,27 +43,24 @@ class SetSourceAccount implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { /** @var User $user */ $user = User::find($journal['user_id']); - /** @var TransactionJournal|null $object */ + + /** @var null|TransactionJournal $object */ $object = $user->transactionJournals()->find((int)$journal['transaction_journal_id']); $this->repository = app(AccountRepositoryInterface::class); if (null === $object) { app('log')->error('Could not find journal.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal'))); + return false; } $type = $object->transactionType->type; @@ -77,21 +73,24 @@ class SetSourceAccount implements ActionInterface sprintf('Cant change source account of journal #%d because no asset account with name "%s" exists.', $object->id, $this->action->action_value) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_asset', ['name' => $this->action->action_value]))); + return false; } // new source account must be different from the current destination account: - /** @var Transaction|null $destination */ + /** @var null|Transaction $destination */ $destination = $object->transactions()->where('amount', '>', 0)->first(); if (null === $destination) { app('log')->error('Could not find destination transaction.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_destination_transaction'))); + return false; } // account must not be deleted (in the meantime): if (null === $destination->account) { app('log')->error('Could not find destination transaction account.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_destination_transaction_account'))); + return false; } if (null !== $newAccount && $newAccount->id === $destination->account_id) { @@ -103,6 +102,7 @@ class SetSourceAccount implements ActionInterface ) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_has_source', ['name' => $newAccount->name]))); + return false; } @@ -115,10 +115,11 @@ class SetSourceAccount implements ActionInterface app('log')->debug(sprintf('New source account is #%d ("%s").', $newAccount->id, $newAccount->name)); // update source transaction with new source account: - DB::table('transactions') - ->where('transaction_journal_id', '=', $object->id) - ->where('amount', '<', 0) - ->update(['account_id' => $newAccount->id]); + \DB::table('transactions') + ->where('transaction_journal_id', '=', $object->id) + ->where('amount', '<', 0) + ->update(['account_id' => $newAccount->id]) + ; event(new TriggeredAuditLog($this->action->rule, $object, 'set_source', null, $newAccount->name)); @@ -127,11 +128,6 @@ class SetSourceAccount implements ActionInterface return true; } - /** - * @param string $type - * - * @return Account|null - */ private function findAssetAccount(string $type): ?Account { // switch on type: @@ -142,9 +138,6 @@ class SetSourceAccount implements ActionInterface return $this->repository->findByName($this->action->action_value, $allowed); } - /** - * @return Account - */ private function findDepositSourceAccount(): Account { $allowed = config('firefly.expected_source_types.source.Deposit'); diff --git a/app/TransactionRules/Actions/SetSourceToCashAccount.php b/app/TransactionRules/Actions/SetSourceToCashAccount.php index 11339594fb..179ce4aab3 100644 --- a/app/TransactionRules/Actions/SetSourceToCashAccount.php +++ b/app/TransactionRules/Actions/SetSourceToCashAccount.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; -use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; @@ -42,34 +41,32 @@ class SetSourceToCashAccount implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { /** @var User $user */ $user = User::find($journal['user_id']); - /** @var TransactionJournal|null $object */ + + /** @var null|TransactionJournal $object */ $object = $user->transactionJournals()->find((int)$journal['transaction_journal_id']); $repository = app(AccountRepositoryInterface::class); if (null === $object) { app('log')->error('Could not find journal.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal'))); + return false; } $type = $object->transactionType->type; if (TransactionType::DEPOSIT !== $type) { app('log')->error('Transaction must be deposit.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.not_deposit'))); + return false; } @@ -78,17 +75,19 @@ class SetSourceToCashAccount implements ActionInterface $cashAccount = $repository->getCashAccount(); // new source account must be different from the current destination account: - /** @var Transaction|null $destination */ + /** @var null|Transaction $destination */ $destination = $object->transactions()->where('amount', '>', 0)->first(); if (null === $destination) { app('log')->error('Could not find destination transaction.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_destination_transaction'))); + return false; } // account must not be deleted (in the meantime): if (null === $destination->account) { app('log')->error('Could not find destination transaction account.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_destination_transaction_account'))); + return false; } if ($cashAccount->id === $destination->account_id) { @@ -101,16 +100,18 @@ class SetSourceToCashAccount implements ActionInterface ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_has_source', ['name' => $cashAccount->name]))); + return false; } event(new TriggeredAuditLog($this->action->rule, $object, 'set_source', null, $cashAccount->name)); // update destination transaction with new destination account: - DB::table('transactions') - ->where('transaction_journal_id', '=', $object->id) - ->where('amount', '<', 0) - ->update(['account_id' => $cashAccount->id]); + \DB::table('transactions') + ->where('transaction_journal_id', '=', $object->id) + ->where('amount', '<', 0) + ->update(['account_id' => $cashAccount->id]) + ; app('log')->debug(sprintf('Updated journal #%d (group #%d) and gave it new source account ID.', $object->id, $object->transaction_group_id)); diff --git a/app/TransactionRules/Actions/SwitchAccounts.php b/app/TransactionRules/Actions/SwitchAccounts.php index 8ea7dc5acd..a9bd99384b 100644 --- a/app/TransactionRules/Actions/SwitchAccounts.php +++ b/app/TransactionRules/Actions/SwitchAccounts.php @@ -31,7 +31,6 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; /** - * * Class SwitchAccounts */ class SwitchAccounts implements ActionInterface @@ -40,31 +39,28 @@ class SwitchAccounts implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { // make object from array (so the data is fresh). - /** @var TransactionJournal|null $object */ + /** @var null|TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { app('log')->error(sprintf('Cannot find journal #%d, cannot switch accounts.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal'))); + return false; } $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); if ($groupCount > 1) { app('log')->error(sprintf('Group #%d has more than one transaction in it, cannot switch accounts.', $journal['transaction_group_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group'))); + return false; } @@ -72,16 +68,19 @@ class SwitchAccounts implements ActionInterface if (TransactionType::TRANSFER !== $type) { app('log')->error(sprintf('Journal #%d is NOT a transfer (rule #%d), cannot switch accounts.', $journal['transaction_journal_id'], $this->action->rule_id)); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_not_transfer'))); + return false; } - /** @var Transaction|null $sourceTransaction */ + /** @var null|Transaction $sourceTransaction */ $sourceTransaction = $object->transactions()->where('amount', '<', 0)->first(); - /** @var Transaction|null $destTransaction */ + + /** @var null|Transaction $destTransaction */ $destTransaction = $object->transactions()->where('amount', '>', 0)->first(); if (null === $sourceTransaction || null === $destTransaction) { app('log')->error(sprintf('Journal #%d has no source or destination transaction (rule #%d), cannot switch accounts.', $journal['transaction_journal_id'], $this->action->rule_id)); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_accounts'))); + return false; } $sourceAccountId = $sourceTransaction->account_id; diff --git a/app/TransactionRules/Actions/UpdatePiggybank.php b/app/TransactionRules/Actions/UpdatePiggybank.php index 62fceba69b..6fabeb3923 100644 --- a/app/TransactionRules/Actions/UpdatePiggybank.php +++ b/app/TransactionRules/Actions/UpdatePiggybank.php @@ -42,17 +42,12 @@ class UpdatePiggybank implements ActionInterface /** * TriggerInterface constructor. - * - * @param RuleAction $action */ public function __construct(RuleAction $action) { $this->action = $action; } - /** - * @inheritDoc - */ public function actOnArray(array $journal): bool { app('log')->debug(sprintf('Triggered rule action UpdatePiggybank on journal #%d', $journal['transaction_journal_id'])); @@ -60,6 +55,7 @@ class UpdatePiggybank implements ActionInterface // refresh the transaction type. /** @var User $user */ $user = User::find($journal['user_id']); + /** @var TransactionJournal $journalObj */ $journalObj = $user->transactionJournals()->find($journal['transaction_journal_id']); @@ -69,6 +65,7 @@ class UpdatePiggybank implements ActionInterface sprintf('No piggy bank named "%s", cant execute action #%d of rule #%d', $this->action->action_value, $this->action->id, $this->action->rule_id) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_piggy', ['name' => $this->action->action_value]))); + return false; } @@ -76,6 +73,7 @@ class UpdatePiggybank implements ActionInterface /** @var Transaction $source */ $source = $journalObj->transactions()->where('amount', '<', 0)->first(); + /** @var Transaction $destination */ $destination = $journalObj->transactions()->where('amount', '>', 0)->first(); @@ -129,26 +127,15 @@ class UpdatePiggybank implements ActionInterface ) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_link_piggy', ['name' => $this->action->action_value]))); + return false; } - /** - * @param User $user - * - * @return PiggyBank|null - */ private function findPiggyBank(User $user): ?PiggyBank { return $user->piggyBanks()->where('piggy_banks.name', $this->action->action_value)->first(); } - /** - * @param PiggyBank $piggyBank - * @param TransactionJournal $journal - * @param string $amount - * - * @return void - */ private function removeAmount(PiggyBank $piggyBank, TransactionJournal $journal, string $amount): void { $repository = app(PiggyBankRepositoryInterface::class); @@ -180,13 +167,6 @@ class UpdatePiggybank implements ActionInterface $repository->removeAmount($piggyBank, $amount, $journal); } - /** - * @param PiggyBank $piggyBank - * @param TransactionJournal $journal - * @param string $amount - * - * @return void - */ private function addAmount(PiggyBank $piggyBank, TransactionJournal $journal, string $amount): void { $repository = app(PiggyBankRepositoryInterface::class); @@ -205,7 +185,6 @@ class UpdatePiggybank implements ActionInterface app('log')->debug('Target amount is zero, can add anything.'); } - // if amount is zero, stop. if (0 === bccomp('0', $amount)) { app('log')->warning('Amount left is zero, stop.'); diff --git a/app/TransactionRules/Engine/RuleEngineInterface.php b/app/TransactionRules/Engine/RuleEngineInterface.php index 687abf7627..3530bc4387 100644 --- a/app/TransactionRules/Engine/RuleEngineInterface.php +++ b/app/TransactionRules/Engine/RuleEngineInterface.php @@ -33,8 +33,6 @@ interface RuleEngineInterface { /** * Add operators added to each search by the rule engine. - * - * @param array $operator */ public function addOperator(array $operator): void; @@ -50,34 +48,20 @@ interface RuleEngineInterface /** * Return the number of changed transactions from the previous "fire" action. - * - * @return int */ public function getResults(): int; - /** - * @param bool $refreshTriggers - * - * @return void - */ public function setRefreshTriggers(bool $refreshTriggers): void; /** * Add entire rule groups for the engine to execute. - * - * @param Collection $ruleGroups */ public function setRuleGroups(Collection $ruleGroups): void; /** * Add rules for the engine to execute. - * - * @param Collection $rules */ public function setRules(Collection $rules): void; - /** - * @param User $user - */ public function setUser(User $user): void; } diff --git a/app/TransactionRules/Engine/SearchRuleEngine.php b/app/TransactionRules/Engine/SearchRuleEngine.php index ff720b8068..fcd0383c93 100644 --- a/app/TransactionRules/Engine/SearchRuleEngine.php +++ b/app/TransactionRules/Engine/SearchRuleEngine.php @@ -59,18 +59,12 @@ class SearchRuleEngine implements RuleEngineInterface $this->refreshTriggers = true; } - /** - * @inheritDoc - */ public function addOperator(array $operator): void { app('log')->debug('Add extra operator: ', $operator); $this->operators[] = $operator; } - /** - * - */ public function find(): Collection { app('log')->debug('SearchRuleEngine::find()'); @@ -89,12 +83,87 @@ class SearchRuleEngine implements RuleEngineInterface return $collection->unique(); } + public function setUser(User $user): void + { + $this->user = $user; + $this->operators = []; + } + + /** + * @throws FireflyException + */ + public function fire(): void + { + $this->resultCount = []; + app('log')->debug('SearchRuleEngine::fire()!'); + + // if rules and no rule groups, file each rule separately. + if (0 !== $this->rules->count()) { + app('log')->debug(sprintf('SearchRuleEngine:: found %d rule(s) to fire.', $this->rules->count())); + + /** @var Rule $rule */ + foreach ($this->rules as $rule) { + $result = $this->fireRule($rule); + if (true === $result && $rule->stop_processing) { + app('log')->debug(sprintf('Rule #%d has triggered and executed, but calls to stop processing. Since not in the context of a group, do not stop.', $rule->id)); + } + if (false === $result && $rule->stop_processing) { + app('log')->debug(sprintf('Rule #%d has triggered and changed nothing, but calls to stop processing. Do not stop.', $rule->id)); + } + } + app('log')->debug('SearchRuleEngine:: done processing all rules!'); + + return; + } + if (0 !== $this->groups->count()) { + app('log')->debug(sprintf('SearchRuleEngine:: found %d rule group(s) to fire.', $this->groups->count())); + + // fire each group: + /** @var RuleGroup $group */ + foreach ($this->groups as $group) { + $this->fireGroup($group); + } + } + app('log')->debug('SearchRuleEngine:: done processing all rules!'); + } + + /** + * Return the number of changed transactions from the previous "fire" action. + */ + public function getResults(): int + { + return count($this->resultCount); + } + + public function setRefreshTriggers(bool $refreshTriggers): void + { + $this->refreshTriggers = $refreshTriggers; + } + + public function setRuleGroups(Collection $ruleGroups): void + { + app('log')->debug(__METHOD__); + foreach ($ruleGroups as $group) { + if ($group instanceof RuleGroup) { + app('log')->debug(sprintf('Adding a rule group to the SearchRuleEngine: #%d ("%s")', $group->id, $group->title)); + $this->groups->push($group); + } + } + } + + public function setRules(Collection $rules): void + { + app('log')->debug(__METHOD__); + foreach ($rules as $rule) { + if ($rule instanceof Rule) { + app('log')->debug(sprintf('Adding a rule to the SearchRuleEngine: #%d ("%s")', $rule->id, $rule->title)); + $this->rules->push($rule); + } + } + } + /** * Finds the transactions a strict rule will execute on. - * - * @param Rule $rule - * - * @return Collection */ private function findStrictRule(Rule $rule): Collection { @@ -115,7 +184,7 @@ class SearchRuleEngine implements RuleEngineInterface } // if needs no context, value is different: - $needsContext = (bool)(config(sprintf('search.operators.%s.needs_context', $ruleTrigger->trigger_type)) ?? true); + $needsContext = (bool) (config(sprintf('search.operators.%s.needs_context', $ruleTrigger->trigger_type)) ?? true); if (false === $needsContext) { app('log')->debug(sprintf('SearchRuleEngine:: add a rule trigger (no context): %s:true', $ruleTrigger->trigger_type)); $searchArray[$ruleTrigger->trigger_type][] = 'true'; @@ -126,7 +195,6 @@ class SearchRuleEngine implements RuleEngineInterface } } - // add local operators: foreach ($this->operators as $operator) { app('log')->debug(sprintf('SearchRuleEngine:: add local added operator: %s:"%s"', $operator['type'], $operator['value'])); @@ -161,10 +229,6 @@ class SearchRuleEngine implements RuleEngineInterface * 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 { @@ -187,18 +251,13 @@ class SearchRuleEngine implements RuleEngineInterface return $result; } - /** - * @param array $array - * - * @return Carbon - */ private function setDateFromJournalTrigger(array $array): Carbon { app('log')->debug('Now in setDateFromJournalTrigger()'); $journalId = 0; foreach ($array as $triggerName => $values) { if ('journal_id' === $triggerName && is_array($values) && 1 === count($values)) { - $journalId = (int)trim(($values[0] ?? '"0"'), '"'); // follows format "123". + $journalId = (int) trim($values[0] ?? '"0"', '"'); // follows format "123". app('log')->debug(sprintf('Found journal ID #%d', $journalId)); } } @@ -218,20 +277,6 @@ class SearchRuleEngine implements RuleEngineInterface return today(config('app.timezone')); } - /** - * @inheritDoc - */ - public function setUser(User $user): void - { - $this->user = $user; - $this->operators = []; - } - - /** - * @param Rule $rule - * - * @return Collection - */ private function findNonStrictRule(Rule $rule): Collection { // start a search query for individual each trigger: @@ -252,6 +297,7 @@ class SearchRuleEngine implements RuleEngineInterface } if ('user_action' === $ruleTrigger->trigger_type) { app('log')->debug('Skip trigger type.'); + continue; } $searchArray = []; @@ -286,7 +332,13 @@ class SearchRuleEngine implements RuleEngineInterface app('log')->debug(sprintf('Found in this run, %d transactions', $collection->count())); $total = $total->merge($collection); app('log')->debug(sprintf('Total collection is now %d transactions', $total->count())); - $count++; + ++$count; + // if trigger says stop processing, do so. + if ($ruleTrigger->stop_processing && $collection->count() > 0) { + app('log')->debug('The trigger says to stop processing, so stop processing other triggers.'); + + break; + } } app('log')->debug(sprintf('Total collection is now %d transactions', $total->count())); app('log')->debug(sprintf('Done running %d trigger(s)', $count)); @@ -298,10 +350,9 @@ class SearchRuleEngine implements RuleEngineInterface foreach ($group['transactions'] as $transaction) { $str = sprintf('%s%d', $str, $transaction['transaction_journal_id']); } - $key = sprintf('%d%s', $group['id'], $str); - //app('log')->debug(sprintf('Return key: %s ', $key)); - return $key; + return sprintf('%d%s', $group['id'], $str); + // app('log')->debug(sprintf('Return key: %s ', $key)); } ); @@ -310,42 +361,9 @@ class SearchRuleEngine implements RuleEngineInterface return $unique; } - /** - * @inheritDoc - * @throws FireflyException - */ - public function fire(): void - { - $this->resultCount = []; - app('log')->debug('SearchRuleEngine::fire()!'); - - // if rules and no rule groups, file each rule separately. - if (0 !== $this->rules->count()) { - app('log')->debug(sprintf('SearchRuleEngine:: found %d rule(s) to fire.', $this->rules->count())); - foreach ($this->rules as $rule) { - $this->fireRule($rule); - } - app('log')->debug('SearchRuleEngine:: done processing all rules!'); - - return; - } - if (0 !== $this->groups->count()) { - app('log')->debug(sprintf('SearchRuleEngine:: found %d rule group(s) to fire.', $this->groups->count())); - // fire each group: - /** @var RuleGroup $group */ - foreach ($this->groups as $group) { - $this->fireGroup($group); - } - } - app('log')->debug('SearchRuleEngine:: done processing all rules!'); - } - /** * Returns true if the rule has been triggered. * - * @param Rule $rule - * - * @return bool * @throws FireflyException */ private function fireRule(Rule $rule): bool @@ -369,9 +387,6 @@ class SearchRuleEngine implements RuleEngineInterface /** * Return true if the rule is fired (the collection is larger than zero). * - * @param Rule $rule - * - * @return bool * @throws FireflyException */ private function fireStrictRule(Rule $rule): bool @@ -394,14 +409,12 @@ class SearchRuleEngine implements RuleEngineInterface } /** - * @param Rule $rule - * @param Collection $collection - * * @throws FireflyException */ private function processResults(Rule $rule, Collection $collection): void { app('log')->debug(sprintf('SearchRuleEngine:: Going to process %d results.', $collection->count())); + /** @var array $group */ foreach ($collection as $group) { $this->processTransactionGroup($rule, $group); @@ -409,14 +422,12 @@ class SearchRuleEngine implements RuleEngineInterface } /** - * @param Rule $rule - * @param array $group - * * @throws FireflyException */ private function processTransactionGroup(Rule $rule, array $group): void { app('log')->debug(sprintf('SearchRuleEngine:: Will now execute actions on transaction group #%d', $group['id'])); + /** @var array $transaction */ foreach ($group['transactions'] as $transaction) { $this->processTransactionJournal($rule, $transaction); @@ -424,15 +435,13 @@ class SearchRuleEngine implements RuleEngineInterface } /** - * @param Rule $rule - * @param array $transaction - * * @throws FireflyException */ private function processTransactionJournal(Rule $rule, array $transaction): void { app('log')->debug(sprintf('SearchRuleEngine:: Will now execute actions on transaction journal #%d', $transaction['transaction_journal_id'])); $actions = $rule->ruleActions()->orderBy('order', 'ASC')->get(); + /** @var RuleAction $ruleAction */ foreach ($actions as $ruleAction) { if (false === $ruleAction->active) { @@ -446,10 +455,6 @@ class SearchRuleEngine implements RuleEngineInterface } /** - * @param RuleAction $ruleAction - * @param array $transaction - * - * @return bool * @throws FireflyException */ private function processRuleAction(RuleAction $ruleAction, array $transaction): bool @@ -474,11 +479,14 @@ class SearchRuleEngine implements RuleEngineInterface } // pick up from the action if it actually acted or not: - if ($ruleAction->stop_processing) { - app('log')->debug(sprintf('Rule action "%s" asks to break, so break!', $ruleAction->action_type)); + if ($ruleAction->stop_processing && true === $result) { + app('log')->debug(sprintf('Rule action "%s" reports changes AND asks to break, so break!', $ruleAction->action_type)); return true; } + if ($ruleAction->stop_processing && false === $result) { + app('log')->debug(sprintf('Rule action "%s" reports NO changes AND asks to break, but we wont break!', $ruleAction->action_type)); + } return false; } @@ -486,9 +494,6 @@ class SearchRuleEngine implements RuleEngineInterface /** * Return true if the rule is fired (the collection is larger than zero). * - * @param Rule $rule - * - * @return bool * @throws FireflyException */ private function fireNonStrictRule(Rule $rule): bool @@ -503,14 +508,12 @@ class SearchRuleEngine implements RuleEngineInterface } /** - * @param RuleGroup $group - * - * @return void * @throws FireflyException */ private function fireGroup(RuleGroup $group): void { app('log')->debug(sprintf('Going to fire group #%d with %d rule(s)', $group->id, $group->rules->count())); + /** @var Rule $rule */ foreach ($group->rules as $rule) { app('log')->debug(sprintf('Going to fire rule #%d from group #%d', $rule->id, $group->id)); @@ -522,50 +525,4 @@ class SearchRuleEngine implements RuleEngineInterface } } } - - /** - * Return the number of changed transactions from the previous "fire" action. - * - * @return int - */ - public function getResults(): int - { - return count($this->resultCount); - } - - /** - * @param bool $refreshTriggers - */ - public function setRefreshTriggers(bool $refreshTriggers): void - { - $this->refreshTriggers = $refreshTriggers; - } - - /** - * @inheritDoc - */ - public function setRuleGroups(Collection $ruleGroups): void - { - app('log')->debug(__METHOD__); - foreach ($ruleGroups as $group) { - if ($group instanceof RuleGroup) { - app('log')->debug(sprintf('Adding a rule group to the SearchRuleEngine: #%d ("%s")', $group->id, $group->title)); - $this->groups->push($group); - } - } - } - - /** - * @inheritDoc - */ - public function setRules(Collection $rules): void - { - app('log')->debug(__METHOD__); - foreach ($rules as $rule) { - if ($rule instanceof Rule) { - app('log')->debug(sprintf('Adding a rule to the SearchRuleEngine: #%d ("%s")', $rule->id, $rule->title)); - $this->rules->push($rule); - } - } - } } diff --git a/app/TransactionRules/Factory/ActionFactory.php b/app/TransactionRules/Factory/ActionFactory.php index c9adcab529..4dda98e4c2 100644 --- a/app/TransactionRules/Factory/ActionFactory.php +++ b/app/TransactionRules/Factory/ActionFactory.php @@ -30,8 +30,6 @@ use FireflyIII\TransactionRules\Actions\ActionInterface; /** * Class ActionFactory can create actions. - * - */ class ActionFactory { @@ -44,10 +42,6 @@ class ActionFactory * with value "Groceries" this method will return a corresponding SetCategory object preset * to "Groceries". Any transaction journal then fed to this object will have its category changed. * - * @param RuleAction $action - * - * @return ActionInterface - * * @throws FireflyException */ public static function getAction(RuleAction $action): ActionInterface @@ -63,10 +57,6 @@ class ActionFactory * that will match the given action type (ie. "change_category") to the matching class name * (SetCategory) using the configuration (firefly.php). * - * @param string $actionType - * - * @return string - * * @throws FireflyException */ public static function getActionClass(string $actionType): string @@ -74,12 +64,12 @@ class ActionFactory $actionTypes = self::getActionTypes(); if (!array_key_exists($actionType, $actionTypes)) { - throw new FireflyException('No such action exists ("' . e($actionType) . '").'); + throw new FireflyException('No such action exists ("'.e($actionType).'").'); } $class = $actionTypes[$actionType]; if (!class_exists($class)) { - throw new FireflyException('Could not instantiate class for rule action type "' . e($actionType) . '" (' . e($class) . ').'); + throw new FireflyException('Could not instantiate class for rule action type "'.e($actionType).'" ('.e($class).').'); } return $class; @@ -87,8 +77,6 @@ class ActionFactory /** * Returns a map with actiontypes, mapped to the class representing that type. - * - * @return array */ protected static function getActionTypes(): array { diff --git a/app/Transformers/AbstractTransformer.php b/app/Transformers/AbstractTransformer.php index 1e76cf7ac1..ba6331d3ba 100644 --- a/app/Transformers/AbstractTransformer.php +++ b/app/Transformers/AbstractTransformer.php @@ -27,24 +27,17 @@ use League\Fractal\TransformerAbstract; use Symfony\Component\HttpFoundation\ParameterBag; /** - * * Class AbstractTransformer */ abstract class AbstractTransformer extends TransformerAbstract { protected ParameterBag $parameters; - /** - * @return ParameterBag - */ final public function getParameters(): ParameterBag { return $this->parameters; } - /** - * @param ParameterBag $parameters - */ final public function setParameters(ParameterBag $parameters): void { $this->parameters = $parameters; diff --git a/app/Transformers/AccountTransformer.php b/app/Transformers/AccountTransformer.php index 0c377df964..81c1b7e040 100644 --- a/app/Transformers/AccountTransformer.php +++ b/app/Transformers/AccountTransformer.php @@ -27,7 +27,6 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use JsonException; use Symfony\Component\HttpFoundation\ParameterBag; /** @@ -38,10 +37,7 @@ class AccountTransformer extends AbstractTransformer protected AccountRepositoryInterface $repository; /** - * * AccountTransformer constructor. - * - */ public function __construct() { @@ -52,11 +48,7 @@ class AccountTransformer extends AbstractTransformer /** * Transform the account. * - * @param Account $account - * - * @return array * @throws FireflyException - * @throws JsonException */ public function transform(Account $account): array { @@ -133,19 +125,12 @@ class AccountTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/accounts/' . $account->id, + 'uri' => '/accounts/'.$account->id, ], ], ]; } - /** - * @param Account $account - * - * @param string $accountType - * - * @return string|null - */ private function getAccountRole(Account $account, string $accountType): ?string { $accountRole = $this->repository->getMetaValue($account, 'account_role'); @@ -158,8 +143,6 @@ class AccountTransformer extends AbstractTransformer /** * TODO duplicated in the V2 transformer. - * - * @return Carbon */ private function getDate(): Carbon { @@ -172,11 +155,7 @@ class AccountTransformer extends AbstractTransformer } /** - * @param Account $account - * - * @return array * @throws FireflyException - * @throws JsonException */ private function getCurrency(Account $account): array { @@ -194,13 +173,6 @@ class AccountTransformer extends AbstractTransformer return [$currencyId, $currencyCode, $currencySymbol, $decimalPlaces]; } - /** - * @param Account $account - * @param string|null $accountRole - * @param string $accountType - * - * @return array - */ private function getCCInfo(Account $account, ?string $accountRole, string $accountType): array { $monthlyPaymentDate = null; @@ -227,12 +199,9 @@ class AccountTransformer extends AbstractTransformer } /** - * @param Account $account - * @param string $accountType - * * @return array * - * TODO refactor call to get~OpeningBalanceAmount / Date because it is a lot of queries. + * TODO refactor call to get~OpeningBalanceAmount / Date because it is a lot of queries */ private function getOpeningBalance(Account $account, string $accountType): array { @@ -249,18 +218,11 @@ class AccountTransformer extends AbstractTransformer $object = today(config('app.timezone')); } $openingBalanceDate = $object->toAtomString(); - } return [$openingBalance, $openingBalanceDate]; } - /** - * @param Account $account - * @param string $accountType - * - * @return array - */ private function getInterest(Account $account, string $accountType): array { $interest = null; diff --git a/app/Transformers/AttachmentTransformer.php b/app/Transformers/AttachmentTransformer.php index 2429d02438..e8b162bcd1 100644 --- a/app/Transformers/AttachmentTransformer.php +++ b/app/Transformers/AttachmentTransformer.php @@ -35,8 +35,6 @@ class AttachmentTransformer extends AbstractTransformer /** * BillTransformer constructor. - * - */ public function __construct() { @@ -45,10 +43,6 @@ class AttachmentTransformer extends AbstractTransformer /** * Transform attachment. - * - * @param Attachment $attachment - * - * @return array */ public function transform(Attachment $attachment): array { @@ -71,7 +65,7 @@ class AttachmentTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/attachment/' . $attachment->id, + 'uri' => '/attachment/'.$attachment->id, ], ], ]; diff --git a/app/Transformers/AvailableBudgetTransformer.php b/app/Transformers/AvailableBudgetTransformer.php index 5ffe94fab9..7c880200cd 100644 --- a/app/Transformers/AvailableBudgetTransformer.php +++ b/app/Transformers/AvailableBudgetTransformer.php @@ -39,8 +39,6 @@ class AvailableBudgetTransformer extends AbstractTransformer /** * CurrencyTransformer constructor. - * - */ public function __construct() { @@ -51,10 +49,6 @@ class AvailableBudgetTransformer extends AbstractTransformer /** * Transform the note. - * - * @param AvailableBudget $availableBudget - * - * @return array */ public function transform(AvailableBudget $availableBudget): array { @@ -77,7 +71,7 @@ class AvailableBudgetTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/available_budgets/' . $availableBudget->id, + 'uri' => '/available_budgets/'.$availableBudget->id, ], ], ]; @@ -91,9 +85,6 @@ class AvailableBudgetTransformer extends AbstractTransformer return $data; } - /** - * @return array - */ private function getSpentInBudgets(): array { $allActive = $this->repository->getActiveBudgets(); @@ -102,9 +93,6 @@ class AvailableBudgetTransformer extends AbstractTransformer return array_values($sums); } - /** - * @return array - */ private function spentOutsideBudgets(): array { $sums = $this->noBudgetRepository->sumExpenses($this->parameters->get('start'), $this->parameters->get('end')); diff --git a/app/Transformers/BillTransformer.php b/app/Transformers/BillTransformer.php index 4ba151783e..e0002fb637 100644 --- a/app/Transformers/BillTransformer.php +++ b/app/Transformers/BillTransformer.php @@ -42,8 +42,6 @@ class BillTransformer extends AbstractTransformer /** * BillTransformer constructor. - * - */ public function __construct() { @@ -54,28 +52,25 @@ class BillTransformer extends AbstractTransformer /** * Transform the bill. * - * @param Bill $bill - * - * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.NPathComplexity) */ public function transform(Bill $bill): array { $paidData = $this->paidData($bill); $lastPaidDate = $this->getLastPaidDate($paidData); - - // both params can be NULL, so just in case they are, add some wide margins: - $start = $this->parameters->get('start') ?? today()->subYears(10); - $end = $this->parameters->get('end') ?? today()->addYears(10); - $payDates = $this->calculator->getPayDates($start, $end, $bill->date, $bill->repeat_freq, $bill->skip, $lastPaidDate); - $currency = $bill->transactionCurrency; - $notes = $this->repository->getNoteText($bill); - $notes = '' === $notes ? null : $notes; - $this->repository->setUser($bill->user); - + $start = $this->parameters->get('start') ?? today()->subYears(10); + $end = $this->parameters->get('end') ?? today()->addYears(10); + $payDates = $this->calculator->getPayDates($start, $end, $bill->date, $bill->repeat_freq, $bill->skip, $lastPaidDate); + $currency = $bill->transactionCurrency; + $notes = $this->repository->getNoteText($bill); + $notes = '' === $notes ? null : $notes; $objectGroupId = null; $objectGroupOrder = null; $objectGroupTitle = null; - /** @var ObjectGroup|null $objectGroup */ + $this->repository->setUser($bill->user); + + /** @var null|ObjectGroup $objectGroup */ $objectGroup = $bill->objectGroups->first(); if (null !== $objectGroup) { $objectGroupId = $objectGroup->id; @@ -138,11 +133,12 @@ class BillTransformer extends AbstractTransformer } unset($temp2); } + return [ 'id' => $bill->id, 'created_at' => $bill->created_at->toAtomString(), 'updated_at' => $bill->updated_at->toAtomString(), - 'currency_id' => (string)$bill->transaction_currency_id, + 'currency_id' => (string) $bill->transaction_currency_id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => $currency->decimal_places, @@ -157,7 +153,7 @@ class BillTransformer extends AbstractTransformer 'active' => $bill->active, 'order' => $bill->order, 'notes' => $notes, - 'object_group_id' => null !== $objectGroupId ? (string)$objectGroupId : null, + 'object_group_id' => null !== $objectGroupId ? (string) $objectGroupId : null, 'object_group_order' => $objectGroupOrder, 'object_group_title' => $objectGroupTitle, @@ -169,7 +165,7 @@ class BillTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/bills/' . $bill->id, + 'uri' => '/bills/'.$bill->id, ], ], ]; @@ -177,10 +173,6 @@ class BillTransformer extends AbstractTransformer /** * Get the data the bill was paid and predict the next expected match. - * - * @param Bill $bill - * - * @return array */ protected function paidData(Bill $bill): array { @@ -190,6 +182,7 @@ class BillTransformer extends AbstractTransformer return []; } + // 2023-07-1 sub one day from the start date to fix a possible bug (see #7704) // 2023-07-18 this particular date is used to search for the last paid date. // 2023-07-18 the cloned $searchDate is used to grab the correct transactions. @@ -201,27 +194,21 @@ class BillTransformer extends AbstractTransformer app('log')->debug(sprintf('Parameters are start: %s end: %s', $start->format('Y-m-d'), $this->parameters->get('end')->format('Y-m-d'))); app('log')->debug(sprintf('Search parameters are: start: %s', $searchStart->format('Y-m-d'))); - /* - * Get from database when bill was paid. - */ + // Get from database when bill was paid. $set = $this->repository->getPaidDatesInRange($bill, $searchStart, $this->parameters->get('end')); app('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. - */ + // Grab from array the most recent payment. If none exist, fall back to the start date and pretend *that* was the last paid date. app('log')->debug(sprintf('Grab last paid date from function, return %s if it comes up with nothing.', $start->format('Y-m-d'))); $lastPaidDate = $this->lastPaidDate($set, $start); app('log')->debug(sprintf('Result of lastPaidDate is %s', $lastPaidDate->format('Y-m-d'))); - /* - * At this point the "next match" is exactly after the last time the bill was paid. - */ + // At this point the "next match" is exactly after the last time the bill was paid. $result = []; foreach ($set as $entry) { $result[] = [ - 'transaction_group_id' => (string)$entry->transaction_group_id, - 'transaction_journal_id' => (string)$entry->id, + 'transaction_group_id' => (string) $entry->transaction_group_id, + 'transaction_journal_id' => (string) $entry->id, 'date' => $entry->date->format('Y-m-d'), 'date_object' => $entry->date, ]; @@ -232,11 +219,6 @@ class BillTransformer extends AbstractTransformer /** * Returns the latest date in the set, or start when set is empty. - * - * @param Collection $dates - * @param Carbon $default - * - * @return Carbon */ protected function lastPaidDate(Collection $dates, Carbon $default): Carbon { @@ -244,6 +226,7 @@ class BillTransformer extends AbstractTransformer return $default; } $latest = $dates->first()->date; + /** @var TransactionJournal $journal */ foreach ($dates as $journal) { if ($journal->date->gte($latest)) { @@ -254,11 +237,6 @@ class BillTransformer extends AbstractTransformer return $latest; } - /** - * @param array $paidData - * - * @return Carbon|null - */ private function getLastPaidDate(array $paidData): ?Carbon { app('log')->debug('getLastPaidDate()'); @@ -279,7 +257,7 @@ class BillTransformer extends AbstractTransformer } } app('log')->debug(sprintf('Last paid date is: "%s"', $return?->format('Y-m-d'))); + return $return; } - } diff --git a/app/Transformers/BudgetLimitTransformer.php b/app/Transformers/BudgetLimitTransformer.php index bc93efb1d6..cc9fcd5b6a 100644 --- a/app/Transformers/BudgetLimitTransformer.php +++ b/app/Transformers/BudgetLimitTransformer.php @@ -41,8 +41,6 @@ class BudgetLimitTransformer extends AbstractTransformer /** * Include Budget * - * @param BudgetLimit $limit - * * @return Item */ public function includeBudget(BudgetLimit $limit) @@ -52,10 +50,6 @@ class BudgetLimitTransformer extends AbstractTransformer /** * Transform the note. - * - * @param BudgetLimit $budgetLimit - * - * @return array */ public function transform(BudgetLimit $budgetLimit): array { @@ -103,7 +97,7 @@ class BudgetLimitTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/budgets/limits/' . $budgetLimit->id, + 'uri' => '/budgets/limits/'.$budgetLimit->id, ], ], ]; diff --git a/app/Transformers/BudgetTransformer.php b/app/Transformers/BudgetTransformer.php index 75411190d7..44ef5c1870 100644 --- a/app/Transformers/BudgetTransformer.php +++ b/app/Transformers/BudgetTransformer.php @@ -40,8 +40,6 @@ class BudgetTransformer extends AbstractTransformer /** * BudgetTransformer constructor. - * - */ public function __construct() { @@ -52,10 +50,6 @@ class BudgetTransformer extends AbstractTransformer /** * Transform a budget. - * - * @param Budget $budget - * - * @return array */ public function transform(Budget $budget): array { @@ -106,17 +100,12 @@ class BudgetTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/budgets/' . $budget->id, + 'uri' => '/budgets/'.$budget->id, ], ], ]; } - /** - * @param array $array - * - * @return array - */ private function beautify(array $array): array { $return = []; diff --git a/app/Transformers/CategoryTransformer.php b/app/Transformers/CategoryTransformer.php index 03d8bb8e36..9404739e18 100644 --- a/app/Transformers/CategoryTransformer.php +++ b/app/Transformers/CategoryTransformer.php @@ -38,8 +38,6 @@ class CategoryTransformer extends AbstractTransformer /** * CategoryTransformer constructor. - * - */ public function __construct() { @@ -49,10 +47,6 @@ class CategoryTransformer extends AbstractTransformer /** * Convert category. - * - * @param Category $category - * - * @return array */ public function transform(Category $category): array { @@ -80,17 +74,12 @@ class CategoryTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/categories/' . $category->id, + 'uri' => '/categories/'.$category->id, ], ], ]; } - /** - * @param array $array - * - * @return array - */ private function beautify(array $array): array { $return = []; diff --git a/app/Transformers/CurrencyTransformer.php b/app/Transformers/CurrencyTransformer.php index e87de47aa0..6043291e56 100644 --- a/app/Transformers/CurrencyTransformer.php +++ b/app/Transformers/CurrencyTransformer.php @@ -32,10 +32,6 @@ class CurrencyTransformer extends AbstractTransformer { /** * Transform the currency. - * - * @param TransactionCurrency $currency - * - * @return array */ public function transform(TransactionCurrency $currency): array { @@ -52,7 +48,7 @@ class CurrencyTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/currencies/' . $currency->id, + 'uri' => '/currencies/'.$currency->id, ], ], ]; diff --git a/app/Transformers/LinkTypeTransformer.php b/app/Transformers/LinkTypeTransformer.php index 9002a2767d..3efe57546a 100644 --- a/app/Transformers/LinkTypeTransformer.php +++ b/app/Transformers/LinkTypeTransformer.php @@ -26,17 +26,12 @@ namespace FireflyIII\Transformers; use FireflyIII\Models\LinkType; /** - * * Class LinkTypeTransformer */ class LinkTypeTransformer extends AbstractTransformer { /** * Transform the currency. - * - * @param LinkType $linkType - * - * @return array */ public function transform(LinkType $linkType): array { @@ -51,7 +46,7 @@ class LinkTypeTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/link_types/' . $linkType->id, + 'uri' => '/link_types/'.$linkType->id, ], ], ]; diff --git a/app/Transformers/ObjectGroupTransformer.php b/app/Transformers/ObjectGroupTransformer.php index 87b1e2e815..c49275f849 100644 --- a/app/Transformers/ObjectGroupTransformer.php +++ b/app/Transformers/ObjectGroupTransformer.php @@ -34,10 +34,7 @@ class ObjectGroupTransformer extends AbstractTransformer protected ObjectGroupRepositoryInterface $repository; /** - * * AccountTransformer constructor. - * - */ public function __construct() { @@ -46,10 +43,6 @@ class ObjectGroupTransformer extends AbstractTransformer /** * Transform the account. - * - * @param ObjectGroup $objectGroup - * - * @return array */ public function transform(ObjectGroup $objectGroup): array { @@ -64,7 +57,7 @@ class ObjectGroupTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/object_groups/' . $objectGroup->id, + 'uri' => '/object_groups/'.$objectGroup->id, ], ], ]; diff --git a/app/Transformers/PiggyBankEventTransformer.php b/app/Transformers/PiggyBankEventTransformer.php index 3137e76269..44b07527c9 100644 --- a/app/Transformers/PiggyBankEventTransformer.php +++ b/app/Transformers/PiggyBankEventTransformer.php @@ -27,7 +27,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; -use JsonException; /** * Class PiggyBankEventTransformer @@ -39,8 +38,6 @@ class PiggyBankEventTransformer extends AbstractTransformer /** * PiggyBankEventTransformer constructor. - * - */ public function __construct() { @@ -51,11 +48,7 @@ class PiggyBankEventTransformer extends AbstractTransformer /** * Convert piggy bank event. * - * @param PiggyBankEvent $event - * - * @return array * @throws FireflyException - * @throws JsonException */ public function transform(PiggyBankEvent $event): array { @@ -91,7 +84,7 @@ class PiggyBankEventTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/piggy_bank_events/' . $event->id, + 'uri' => '/piggy_bank_events/'.$event->id, ], ], ]; diff --git a/app/Transformers/PiggyBankTransformer.php b/app/Transformers/PiggyBankTransformer.php index f098bba34d..7576128a3d 100644 --- a/app/Transformers/PiggyBankTransformer.php +++ b/app/Transformers/PiggyBankTransformer.php @@ -28,7 +28,6 @@ use FireflyIII\Models\ObjectGroup; use FireflyIII\Models\PiggyBank; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; -use JsonException; /** * Class PiggyBankTransformer @@ -40,8 +39,6 @@ class PiggyBankTransformer extends AbstractTransformer /** * PiggyBankTransformer constructor. - * - */ public function __construct() { @@ -52,11 +49,7 @@ class PiggyBankTransformer extends AbstractTransformer /** * Transform the piggy bank. * - * @param PiggyBank $piggyBank - * - * @return array * @throws FireflyException - * @throws JsonException */ public function transform(PiggyBank $piggyBank): array { @@ -76,7 +69,8 @@ class PiggyBankTransformer extends AbstractTransformer $objectGroupId = null; $objectGroupOrder = null; $objectGroupTitle = null; - /** @var ObjectGroup|null $objectGroup */ + + /** @var null|ObjectGroup $objectGroup */ $objectGroup = $piggyBank->objectGroups->first(); if (null !== $objectGroup) { $objectGroupId = $objectGroup->id; @@ -129,7 +123,7 @@ class PiggyBankTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/piggy_banks/' . $piggyBank->id, + 'uri' => '/piggy_banks/'.$piggyBank->id, ], ], ]; diff --git a/app/Transformers/PreferenceTransformer.php b/app/Transformers/PreferenceTransformer.php index 0d67f3db0d..a944a0bea9 100644 --- a/app/Transformers/PreferenceTransformer.php +++ b/app/Transformers/PreferenceTransformer.php @@ -32,10 +32,6 @@ class PreferenceTransformer extends AbstractTransformer { /** * Transform the preference - * - * @param Preference $preference - * - * @return array */ public function transform(Preference $preference): array { diff --git a/app/Transformers/RecurrenceTransformer.php b/app/Transformers/RecurrenceTransformer.php index 1cbedd0c18..622dc8ab4a 100644 --- a/app/Transformers/RecurrenceTransformer.php +++ b/app/Transformers/RecurrenceTransformer.php @@ -37,7 +37,6 @@ use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; /** - * * Class RecurringTransactionTransformer */ class RecurrenceTransformer extends AbstractTransformer @@ -50,8 +49,6 @@ class RecurrenceTransformer extends AbstractTransformer /** * RecurrenceTransformer constructor. - * - */ public function __construct() { @@ -65,9 +62,6 @@ class RecurrenceTransformer extends AbstractTransformer /** * Transform the recurring transaction. * - * @param Recurrence $recurrence - * - * @return array * @throws FireflyException */ public function transform(Recurrence $recurrence): array @@ -104,16 +98,13 @@ class RecurrenceTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/recurring/' . $recurrence->id, + 'uri' => '/recurring/'.$recurrence->id, ], ], ]; } /** - * @param Recurrence $recurrence - * - * @return array * @throws FireflyException */ private function getRepetitions(Recurrence $recurrence): array @@ -138,6 +129,7 @@ class RecurrenceTransformer extends AbstractTransformer // get the (future) occurrences for this specific type of repetition: $occurrences = $this->repository->getXOccurrencesSince($repetition, $fromDate, new Carbon(), 5); + /** @var Carbon $carbon */ foreach ($occurrences as $carbon) { $repetitionArray['occurrences'][] = $carbon->toAtomString(); @@ -150,21 +142,20 @@ class RecurrenceTransformer extends AbstractTransformer } /** - * @param Recurrence $recurrence - * - * @return array * @throws FireflyException */ private function getTransactions(Recurrence $recurrence): array { app('log')->debug(sprintf('Now in %s', __METHOD__)); $return = []; + // get all transactions: /** @var RecurrenceTransaction $transaction */ foreach ($recurrence->recurrenceTransactions()->get() as $transaction) { - /** @var Account|null $sourceAccount */ + /** @var null|Account $sourceAccount */ $sourceAccount = $transaction->sourceAccount; - /** @var Account|null $destinationAccount */ + + /** @var null|Account $destinationAccount */ $destinationAccount = $transaction->destinationAccount; $foreignCurrencyCode = null; $foreignCurrencySymbol = null; @@ -240,10 +231,6 @@ class RecurrenceTransformer extends AbstractTransformer } /** - * @param RecurrenceTransaction $transaction - * @param array $array - * - * @return array * @throws FireflyException */ private function getTransactionMeta(RecurrenceTransaction $transaction, array $array): array @@ -264,43 +251,55 @@ class RecurrenceTransformer extends AbstractTransformer switch ($transactionMeta->name) { default: throw new FireflyException(sprintf('Recurrence transformer cant handle field "%s"', $transactionMeta->name)); + case 'bill_id': $bill = $this->billRepos->find((int)$transactionMeta->value); if (null !== $bill) { $array['bill_id'] = (string)$bill->id; $array['bill_name'] = $bill->name; } + break; + case 'tags': $array['tags'] = json_decode($transactionMeta->value); + break; + case 'piggy_bank_id': $piggy = $this->piggyRepos->find((int)$transactionMeta->value); if (null !== $piggy) { $array['piggy_bank_id'] = (string)$piggy->id; $array['piggy_bank_name'] = $piggy->name; } + break; + case 'category_id': $category = $this->factory->findOrCreate((int)$transactionMeta->value, null); if (null !== $category) { $array['category_id'] = (string)$category->id; $array['category_name'] = $category->name; } + break; + case 'category_name': $category = $this->factory->findOrCreate(null, $transactionMeta->value); if (null !== $category) { $array['category_id'] = (string)$category->id; $array['category_name'] = $category->name; } + break; + case 'budget_id': $budget = $this->budgetRepos->find((int)$transactionMeta->value); if (null !== $budget) { $array['budget_id'] = (string)$budget->id; $array['budget_name'] = $budget->name; } + break; } } diff --git a/app/Transformers/RuleGroupTransformer.php b/app/Transformers/RuleGroupTransformer.php index 31fd555626..ee685516c2 100644 --- a/app/Transformers/RuleGroupTransformer.php +++ b/app/Transformers/RuleGroupTransformer.php @@ -32,10 +32,6 @@ class RuleGroupTransformer extends AbstractTransformer { /** * Transform the rule group - * - * @param RuleGroup $ruleGroup - * - * @return array */ public function transform(RuleGroup $ruleGroup): array { @@ -50,7 +46,7 @@ class RuleGroupTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/rule_groups/' . $ruleGroup->id, + 'uri' => '/rule_groups/'.$ruleGroup->id, ], ], ]; diff --git a/app/Transformers/RuleTransformer.php b/app/Transformers/RuleTransformer.php index 1c1837d091..2292999259 100644 --- a/app/Transformers/RuleTransformer.php +++ b/app/Transformers/RuleTransformer.php @@ -39,8 +39,6 @@ class RuleTransformer extends AbstractTransformer /** * CurrencyTransformer constructor. - * - */ public function __construct() { @@ -50,9 +48,6 @@ class RuleTransformer extends AbstractTransformer /** * Transform the rule. * - * @param Rule $rule - * - * @return array * @throws FireflyException */ public function transform(Rule $rule): array @@ -77,22 +72,20 @@ class RuleTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/rules/' . $rule->id, + 'uri' => '/rules/'.$rule->id, ], ], ]; } /** - * @param Rule $rule - * - * @return string * @throws FireflyException */ private function getRuleTrigger(Rule $rule): string { $moment = null; $triggers = $this->ruleRepository->getRuleTriggers($rule); + /** @var RuleTrigger $ruleTrigger */ foreach ($triggers as $ruleTrigger) { if ('user_action' === $ruleTrigger->trigger_type) { @@ -106,15 +99,11 @@ class RuleTransformer extends AbstractTransformer return $moment; } - /** - * @param Rule $rule - * - * @return array - */ private function triggers(Rule $rule): array { $result = []; $triggers = $this->ruleRepository->getRuleTriggers($rule); + /** @var RuleTrigger $ruleTrigger */ foreach ($triggers as $ruleTrigger) { if ('user_action' === $ruleTrigger->trigger_type) { @@ -135,15 +124,11 @@ class RuleTransformer extends AbstractTransformer return $result; } - /** - * @param Rule $rule - * - * @return array - */ private function actions(Rule $rule): array { $result = []; $actions = $this->ruleRepository->getRuleActions($rule); + /** @var RuleAction $ruleAction */ foreach ($actions as $ruleAction) { $result[] = [ diff --git a/app/Transformers/TagTransformer.php b/app/Transformers/TagTransformer.php index 6db04eba5c..cfd1f2ccb6 100644 --- a/app/Transformers/TagTransformer.php +++ b/app/Transformers/TagTransformer.php @@ -35,15 +35,12 @@ class TagTransformer extends AbstractTransformer * Transform a tag. * * TODO add spent, earned, transferred, etc. - * - * @param Tag $tag - * - * @return array */ public function transform(Tag $tag): array { $date = $tag->date?->format('Y-m-d'); - /** @var Location|null $location */ + + /** @var null|Location $location */ $location = $tag->locations()->first(); $latitude = null; $longitude = null; @@ -67,7 +64,7 @@ class TagTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/tags/' . $tag->id, + 'uri' => '/tags/'.$tag->id, ], ], ]; diff --git a/app/Transformers/TransactionGroupTransformer.php b/app/Transformers/TransactionGroupTransformer.php index ac9cad159d..598883dd54 100644 --- a/app/Transformers/TransactionGroupTransformer.php +++ b/app/Transformers/TransactionGroupTransformer.php @@ -48,8 +48,6 @@ class TransactionGroupTransformer extends AbstractTransformer /** * Constructor. - * - */ public function __construct() { @@ -76,37 +74,59 @@ class TransactionGroupTransformer extends AbstractTransformer $this->metaDateFields = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date']; } - /** - * @param array $group - * - * @return array - */ public function transform(array $group): array { $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' => (string)$data['user_id'], + 'user' => (string) $data['user_id'], 'group_title' => $data['title'], 'transactions' => $this->transformTransactions($data), 'links' => [ [ 'rel' => 'self', - 'uri' => '/transactions/' . $first['transaction_group_id'], + 'uri' => '/transactions/'.$first['transaction_group_id'], ], ], ]; } /** - * @param NullArrayObject $data - * - * @return array + * @throws FireflyException */ + public function transformObject(TransactionGroup $group): array + { + try { + $result = [ + 'id' => $group->id, + 'created_at' => $group->created_at->toAtomString(), + 'updated_at' => $group->updated_at->toAtomString(), + 'user' => $group->user_id, + 'group_title' => $group->title, + 'transactions' => $this->transformJournals($group->transactionJournals), + 'links' => [ + [ + 'rel' => 'self', + 'uri' => '/transactions/'.$group->id, + ], + ], + ]; + } catch (FireflyException $e) { + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); + + throw new FireflyException(sprintf('Transaction group #%d is broken. Please check out your log files.', $group->id), 0, $e); + } + + // do something else. + + return $result; + } + private function transformTransactions(NullArrayObject $data): array { $result = []; @@ -119,30 +139,27 @@ class TransactionGroupTransformer extends AbstractTransformer } /** - * @param array $transaction - * - * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ private function transformTransaction(array $transaction): array { $row = new NullArrayObject($transaction); // amount: - $amount = app('steam')->positive((string)($row['amount'] ?? '0')); + $amount = app('steam')->positive((string) ($row['amount'] ?? '0')); $foreignAmount = null; - if (null !== $row['foreign_amount'] && '' !== $row['foreign_amount'] && bccomp('0', $row['foreign_amount']) !== 0) { + if (null !== $row['foreign_amount'] && '' !== $row['foreign_amount'] && 0 !== bccomp('0', $row['foreign_amount'])) { $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); $type = $this->stringFromArray($transaction, 'transaction_type_type', TransactionType::WITHDRAWAL); - $longitude = null; $latitude = null; $zoomLevel = null; - $location = $this->getLocationById((int)$row['transaction_journal_id']); + $location = $this->getLocationById((int) $row['transaction_journal_id']); if (null !== $location) { $longitude = $location->longitude; $latitude = $location->latitude; @@ -150,17 +167,17 @@ class TransactionGroupTransformer extends AbstractTransformer } return [ - 'user' => (string)$row['user_id'], - 'transaction_journal_id' => (string)$row['transaction_journal_id'], + 'user' => (string) $row['user_id'], + 'transaction_journal_id' => (string) $row['transaction_journal_id'], 'type' => strtolower($type), 'date' => $row['date']->toAtomString(), 'order' => $row['order'], - 'currency_id' => (string)$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->stringFromArray($transaction, 'foreign_currency_id', null), 'foreign_currency_code' => $row['foreign_currency_code'], @@ -172,12 +189,12 @@ class TransactionGroupTransformer extends AbstractTransformer 'description' => $row['description'], - 'source_id' => (string)$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' => (string)$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'], @@ -192,8 +209,8 @@ class TransactionGroupTransformer extends AbstractTransformer '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'], @@ -226,17 +243,10 @@ class TransactionGroupTransformer extends AbstractTransformer 'latitude' => $latitude, 'zoom_level' => $zoomLevel, - 'has_attachments' => $this->hasAttachments((int)$row['transaction_journal_id']), + 'has_attachments' => $this->hasAttachments((int) $row['transaction_journal_id']), ]; } - /** - * @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) && null === $array[$key]) { @@ -249,7 +259,8 @@ class TransactionGroupTransformer extends AbstractTransformer if ('0' === $array[$key]) { return $default; } - return (string)$array[$key]; + + return (string) $array[$key]; } if (null !== $default) { @@ -259,47 +270,25 @@ class TransactionGroupTransformer extends AbstractTransformer return null; } - /** - * @param int $journalId - * - * @return Location|null - */ private function getLocationById(int $journalId): ?Location { return $this->groupRepos->getLocation($journalId); } - /** - * @param TransactionJournal $journal - * - * @return Location|null - */ private function getLocation(TransactionJournal $journal): ?Location { return $journal->locations()->first(); } - /** - * @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 { if (null === $object[$key]) { @@ -309,59 +298,18 @@ class TransactionGroupTransformer extends AbstractTransformer return $object[$key]->toAtomString(); } - /** - * @param int $journalId - * - * @return bool - */ private function hasAttachments(int $journalId): bool { return $this->groupRepos->countAttachments($journalId) > 0; } /** - * @param TransactionGroup $group - * - * @return array - * @throws FireflyException - */ - public function transformObject(TransactionGroup $group): array - { - try { - $result = [ - 'id' => $group->id, - 'created_at' => $group->created_at->toAtomString(), - 'updated_at' => $group->updated_at->toAtomString(), - 'user' => $group->user_id, - 'group_title' => $group->title, - 'transactions' => $this->transformJournals($group->transactionJournals), - 'links' => [ - [ - 'rel' => 'self', - 'uri' => '/transactions/' . $group->id, - ], - ], - ]; - } catch (FireflyException $e) { - app('log')->error($e->getMessage()); - app('log')->error($e->getTraceAsString()); - throw new FireflyException(sprintf('Transaction group #%d is broken. Please check out your log files.', $group->id), 0, $e); - } - - // do something else. - - return $result; - } - - /** - * @param Collection $transactionJournals - * - * @return array * @throws FireflyException */ private function transformJournals(Collection $transactionJournals): array { $result = []; + /** @var TransactionJournal $journal */ foreach ($transactionJournals as $journal) { $result[] = $this->transformJournal($journal); @@ -371,10 +319,9 @@ class TransactionGroupTransformer extends AbstractTransformer } /** - * @param TransactionJournal $journal - * - * @return array * @throws FireflyException + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ private function transformJournal(TransactionJournal $journal): array { @@ -481,16 +428,13 @@ class TransactionGroupTransformer extends AbstractTransformer } /** - * @param TransactionJournal $journal - * - * @return Transaction * @throws FireflyException */ private function getSourceTransaction(TransactionJournal $journal): Transaction { $result = $journal->transactions->first( static function (Transaction $transaction) { - return (float)$transaction->amount < 0; // lame but it works. + return (float) $transaction->amount < 0; // lame but it works. } ); if (null === $result) { @@ -501,16 +445,13 @@ class TransactionGroupTransformer extends AbstractTransformer } /** - * @param TransactionJournal $journal - * - * @return Transaction * @throws FireflyException */ private function getDestinationTransaction(TransactionJournal $journal): Transaction { $result = $journal->transactions->first( static function (Transaction $transaction) { - return (float)$transaction->amount > 0; // lame but it works + return (float) $transaction->amount > 0; // lame but it works } ); if (null === $result) { @@ -520,36 +461,21 @@ class TransactionGroupTransformer extends AbstractTransformer return $result; } - /** - * @param string $amount - * - * @return string - */ private function getAmount(string $amount): string { return app('steam')->positive($amount); } - /** - * @param string|null $foreignAmount - * - * @return string|null - */ private function getForeignAmount(?string $foreignAmount): ?string { $result = null; - if (null !== $foreignAmount && '' !== $foreignAmount && bccomp('0', $foreignAmount) !== 0) { + if (null !== $foreignAmount && '' !== $foreignAmount && 0 !== bccomp('0', $foreignAmount)) { $result = app('steam')->positive($foreignAmount); } return $result; } - /** - * @param NullArrayObject $dates - * - * @return array - */ private function getDates(NullArrayObject $dates): array { $fields = [ @@ -571,11 +497,6 @@ class TransactionGroupTransformer extends AbstractTransformer return $return; } - /** - * @param TransactionCurrency|null $currency - * - * @return array - */ private function getForeignCurrency(?TransactionCurrency $currency): array { $array = [ @@ -595,11 +516,6 @@ class TransactionGroupTransformer extends AbstractTransformer return $array; } - /** - * @param Budget|null $budget - * - * @return array - */ private function getBudget(?Budget $budget): array { $array = [ @@ -615,11 +531,6 @@ class TransactionGroupTransformer extends AbstractTransformer return $array; } - /** - * @param Category|null $category - * - * @return array - */ private function getCategory(?Category $category): array { $array = [ @@ -635,11 +546,6 @@ class TransactionGroupTransformer extends AbstractTransformer return $array; } - /** - * @param Bill|null $bill - * - * @return array - */ private function getBill(?Bill $bill): array { $array = [ @@ -649,7 +555,7 @@ class TransactionGroupTransformer extends AbstractTransformer if (null === $bill) { return $array; } - $array['id'] = (string)$bill->id; + $array['id'] = (string) $bill->id; $array['name'] = $bill->name; return $array; diff --git a/app/Transformers/TransactionLinkTransformer.php b/app/Transformers/TransactionLinkTransformer.php index 82d717b5f7..ca34064533 100644 --- a/app/Transformers/TransactionLinkTransformer.php +++ b/app/Transformers/TransactionLinkTransformer.php @@ -27,7 +27,6 @@ use FireflyIII\Models\TransactionJournalLink; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; /** - * * Class TransactionLinkTransformer */ class TransactionLinkTransformer extends AbstractTransformer @@ -37,19 +36,12 @@ class TransactionLinkTransformer extends AbstractTransformer /** * Constructor. - * - */ public function __construct() { $this->repository = app(JournalRepositoryInterface::class); } - /** - * @param TransactionJournalLink $link - * - * @return array - */ public function transform(TransactionJournalLink $link): array { $notes = $this->repository->getLinkNoteText($link); @@ -65,7 +57,7 @@ class TransactionLinkTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/transaction_links/' . $link->id, + 'uri' => '/transaction_links/'.$link->id, ], ], ]; diff --git a/app/Transformers/UserTransformer.php b/app/Transformers/UserTransformer.php index 9b343cff54..de5ed25c65 100644 --- a/app/Transformers/UserTransformer.php +++ b/app/Transformers/UserTransformer.php @@ -32,15 +32,11 @@ use FireflyIII\User; */ class UserTransformer extends AbstractTransformer { - /** @var UserRepositoryInterface */ private UserRepositoryInterface $repository; /** * Transform user. * - * @param User $user - * - * @return array * @throws FireflyException */ public function transform(User $user): array @@ -58,7 +54,7 @@ class UserTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/users/' . $user->id, + 'uri' => '/users/'.$user->id, ], ], ]; diff --git a/app/Transformers/V2/AbstractTransformer.php b/app/Transformers/V2/AbstractTransformer.php index ef03237dd8..9803753688 100644 --- a/app/Transformers/V2/AbstractTransformer.php +++ b/app/Transformers/V2/AbstractTransformer.php @@ -37,25 +37,14 @@ abstract class AbstractTransformer extends TransformerAbstract /** * This method is called exactly ONCE from FireflyIII\Api\V2\Controllers\Controller::jsonApiList - * - * @param Collection $objects - * - * @return void */ abstract public function collectMetaData(Collection $objects): void; - - /** - * @return ParameterBag - */ final public function getParameters(): ParameterBag { return $this->parameters; } - /** - * @param ParameterBag $parameters - */ final public function setParameters(ParameterBag $parameters): void { $this->parameters = $parameters; diff --git a/app/Transformers/V2/AccountTransformer.php b/app/Transformers/V2/AccountTransformer.php index 262ea013c3..02cc34a747 100644 --- a/app/Transformers/V2/AccountTransformer.php +++ b/app/Transformers/V2/AccountTransformer.php @@ -46,7 +46,6 @@ class AccountTransformer extends AbstractTransformer private TransactionCurrency $default; /** - * @inheritDoc * @throws FireflyException */ public function collectMetaData(Collection $objects): void @@ -64,8 +63,9 @@ class AccountTransformer extends AbstractTransformer // get currencies: $accountIds = $objects->pluck('id')->toArray(); $meta = AccountMeta::whereIn('account_id', $accountIds) - ->where('name', 'currency_id') - ->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data']); + ->where('name', 'currency_id') + ->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data']) + ; $currencyIds = $meta->pluck('data')->toArray(); $currencies = $repository->getByIds($currencyIds); @@ -80,33 +80,18 @@ class AccountTransformer extends AbstractTransformer // get account types: // select accounts.id, account_types.type from account_types left join accounts on accounts.account_type_id = account_types.id; $accountTypes = AccountType::leftJoin('accounts', 'accounts.account_type_id', '=', 'account_types.id') - ->whereIn('accounts.id', $accountIds) - ->get(['accounts.id', 'account_types.type']); + ->whereIn('accounts.id', $accountIds) + ->get(['accounts.id', 'account_types.type']) + ; + /** @var AccountType $row */ foreach ($accountTypes as $row) { $this->accountTypes[$row->id] = (string)config(sprintf('firefly.shortNamesByFullName.%s', $row->type)); } } - /** - * @return Carbon - */ - private function getDate(): Carbon - { - $date = today(config('app.timezone')); - if (null !== $this->parameters->get('date')) { - $date = $this->parameters->get('date'); - } - - return $date; - } - /** * Transform the account. - * - * @param Account $account - * - * @return array */ public function transform(Account $account): array { @@ -178,9 +163,19 @@ class AccountTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/accounts/' . $account->id, + 'uri' => '/accounts/'.$account->id, ], ], ]; } + + private function getDate(): Carbon + { + $date = today(config('app.timezone')); + if (null !== $this->parameters->get('date')) { + $date = $this->parameters->get('date'); + } + + return $date; + } } diff --git a/app/Transformers/V2/BillTransformer.php b/app/Transformers/V2/BillTransformer.php index 382ed6863f..274feedba9 100644 --- a/app/Transformers/V2/BillTransformer.php +++ b/app/Transformers/V2/BillTransformer.php @@ -48,7 +48,9 @@ class BillTransformer extends AbstractTransformer private array $paidDates; /** - * @inheritDoc + * @throws \FireflyIII\Exceptions\FireflyException + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function collectMetaData(Collection $objects): void { @@ -58,8 +60,6 @@ class BillTransformer extends AbstractTransformer $this->groups = []; $this->paidDates = []; - - // start with currencies: /** @var Bill $object */ foreach ($objects as $object) { $id = $object->transaction_currency_id; @@ -67,9 +67,8 @@ class BillTransformer extends AbstractTransformer $currencies[$id] ??= TransactionCurrency::find($id); } $this->currencies = $currencies; + $notes = Note::whereNoteableType(Bill::class)->whereIn('noteable_id', array_keys($bills))->get(); - // continue with notes - $notes = Note::whereNoteableType(Bill::class)->whereIn('noteable_id', array_keys($bills))->get(); /** @var Note $note */ foreach ($notes as $note) { $id = $note->noteable_id; @@ -77,20 +76,21 @@ class BillTransformer extends AbstractTransformer } // grab object groups: $set = DB::table('object_groupables') - ->leftJoin('object_groups', 'object_groups.id', '=', 'object_groupables.object_group_id') - ->where('object_groupables.object_groupable_type', Bill::class) - ->get(['object_groupables.*', 'object_groups.title', 'object_groups.order']); + ->leftJoin('object_groups', 'object_groups.id', '=', 'object_groupables.object_group_id') + ->where('object_groupables.object_groupable_type', Bill::class) + ->get(['object_groupables.*', 'object_groups.title', 'object_groups.order']) + ; + /** @var ObjectGroup $entry */ foreach ($set as $entry) { - $billId = (int)$entry->object_groupable_id; - $id = (int)$entry->object_group_id; + $billId = (int) $entry->object_groupable_id; + $id = (int) $entry->object_group_id; $order = $entry->order; $this->groups[$billId] = [ 'object_group_id' => $id, 'object_group_title' => $entry->title, 'object_group_order' => $order, ]; - } $this->default = app('amount')->getDefaultCurrency(); $this->converter = new ExchangeRateConverter(); @@ -98,28 +98,31 @@ class BillTransformer extends AbstractTransformer // grab all paid dates: if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { $journals = TransactionJournal::whereIn('bill_id', $bills) - ->where('date', '>=', $this->parameters->get('start')) - ->where('date', '<=', $this->parameters->get('end')) - ->get(['transaction_journals.id', 'transaction_journals.transaction_group_id', 'transaction_journals.date', 'transaction_journals.bill_id']); + ->where('date', '>=', $this->parameters->get('start')) + ->where('date', '<=', $this->parameters->get('end')) + ->get(['transaction_journals.id', 'transaction_journals.transaction_group_id', 'transaction_journals.date', 'transaction_journals.bill_id']) + ; $journalIds = $journals->pluck('id')->toArray(); // grab transactions for amount: - $set = Transaction::whereIn('transaction_journal_id', $journalIds) - ->where('transactions.amount', '<', 0) - ->get(['transactions.id', 'transactions.transaction_journal_id', 'transactions.amount', 'transactions.foreign_amount', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']); - // convert to array for easy finding: + $set = Transaction::whereIn('transaction_journal_id', $journalIds) + ->where('transactions.amount', '<', 0) + ->get(['transactions.id', 'transactions.transaction_journal_id', 'transactions.amount', 'transactions.foreign_amount', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']) + ; $transactions = []; + /** @var Transaction $transaction */ foreach ($set as $transaction) { $journalId = $transaction->transaction_journal_id; $transactions[$journalId] = $transaction->toArray(); } + /** @var TransactionJournal $journal */ foreach ($journals as $journal) { app('log')->debug(sprintf('Processing journal #%d', $journal->id)); $transaction = $transactions[$journal->id] ?? []; - $billId = (int)$journal->bill_id; - $currencyId = (int)($transaction['transaction_currency_id'] ?? 0); + $billId = (int) $journal->bill_id; + $currencyId = (int) ($transaction['transaction_currency_id'] ?? 0); $currencies[$currencyId] ??= TransactionCurrency::find($currencyId); // foreign currency @@ -131,7 +134,7 @@ class BillTransformer extends AbstractTransformer app('log')->debug('Foreign currency is NULL'); if (null !== $transaction['foreign_currency_id']) { app('log')->debug(sprintf('Foreign currency is #%d', $transaction['foreign_currency_id'])); - $foreignCurrencyId = (int)$transaction['foreign_currency_id']; + $foreignCurrencyId = (int) $transaction['foreign_currency_id']; $currencies[$foreignCurrencyId] ??= TransactionCurrency::find($foreignCurrencyId); $foreignCurrencyCode = $currencies[$foreignCurrencyId]->code; $foreignCurrencyName = $currencies[$foreignCurrencyId]->name; @@ -140,8 +143,8 @@ class BillTransformer extends AbstractTransformer } $this->paidDates[$billId][] = [ - 'transaction_group_id' => (string)$journal->id, - 'transaction_journal_id' => (string)$journal->transaction_group_id, + 'transaction_group_id' => (string) $journal->id, + 'transaction_journal_id' => (string) $journal->transaction_group_id, 'date' => $journal->date->toAtomString(), 'currency_id' => $currencies[$currencyId]->id, 'currency_code' => $currencies[$currencyId]->code, @@ -160,7 +163,7 @@ class BillTransformer extends AbstractTransformer 'amount' => $transaction['amount'], 'foreign_amount' => $transaction['foreign_amount'], 'native_amount' => $this->converter->convert($currencies[$currencyId], $this->default, $journal->date, $transaction['amount']), - 'foreign_native_amount' => '' === (string)$transaction['foreign_amount'] ? null : $this->converter->convert( + 'foreign_native_amount' => '' === (string) $transaction['foreign_amount'] ? null : $this->converter->convert( $currencies[$foreignCurrencyId], $this->default, $journal->date, @@ -173,10 +176,6 @@ class BillTransformer extends AbstractTransformer /** * Transform the bill. - * - * @param Bill $bill - * - * @return array */ public function transform(Bill $bill): array { @@ -187,13 +186,15 @@ class BillTransformer extends AbstractTransformer $group = $this->groups[$bill->id] ?? null; // date for currency conversion - /** @var Carbon|null $startParam */ + /** @var null|Carbon $startParam */ $startParam = $this->parameters->get('start'); - /** @var Carbon|null $date */ + + /** @var null|Carbon $date */ $date = null === $startParam ? today() : clone $startParam; - $nextExpectedMatchDiff = $this->getNextExpectedMatchDiff($nextExpectedMatch, $payDates); + $this->converter->summarize(); + return [ 'id' => $bill->id, 'created_at' => $bill->created_at->toAtomString(), @@ -203,7 +204,7 @@ class BillTransformer extends AbstractTransformer 'amount_max' => app('steam')->bcround($bill->amount_max, $currency->decimal_places), 'native_amount_min' => $this->converter->convert($currency, $this->default, $date, $bill->amount_min), 'native_amount_max' => $this->converter->convert($currency, $this->default, $date, $bill->amount_max), - 'currency_id' => (string)$bill->transaction_currency_id, + 'currency_id' => (string) $bill->transaction_currency_id, 'currency_code' => $currency->code, 'currency_name' => $currency->name, 'currency_symbol' => $currency->symbol, @@ -239,11 +240,6 @@ class BillTransformer extends AbstractTransformer /** * Get the data the bill was paid and predict the next expected match. - * - * @param Bill $bill - * @param array $dates - * - * @return Carbon */ protected function nextExpectedMatch(Bill $bill, array $dates): Carbon { @@ -251,9 +247,10 @@ class BillTransformer extends AbstractTransformer // 2023-07-18 this particular date is used to search for the last paid date. // 2023-07-18 the cloned $searchDate is used to grab the correct transactions. - /** @var Carbon|null $startParam */ + /** @var null|Carbon $startParam */ $startParam = $this->parameters->get('start'); - /** @var Carbon|null $start */ + + /** @var null|Carbon $start */ $start = null === $startParam ? today() : clone $startParam; $start->subDay(); @@ -267,21 +264,15 @@ class BillTransformer extends AbstractTransformer $nextMatch = app('navigation')->addPeriod($nextMatch, $bill->repeat_freq, $bill->skip); } if ($nextMatch->isSameDay($lastPaidDate)) { - /* - * Add another period because it's the same day as the last paid date. - */ + // Add another period because it's the same day as the last paid date. $nextMatch = app('navigation')->addPeriod($nextMatch, $bill->repeat_freq, $bill->skip); } + return $nextMatch; } /** * Returns the latest date in the set, or start when set is empty. - * - * @param array $dates - * @param Carbon $default - * - * @return Carbon */ protected function lastPaidDate(array $dates, Carbon $default): Carbon { @@ -289,6 +280,7 @@ class BillTransformer extends AbstractTransformer return $default; } $latest = $dates[0]['date']; + /** @var array $row */ foreach ($dates as $row) { $carbon = new Carbon($row['date']); @@ -300,16 +292,11 @@ class BillTransformer extends AbstractTransformer return new Carbon($latest); } - /** - * @param Bill $bill - * - * @return array - */ protected function payDates(Bill $bill): array { - //app('log')->debug(sprintf('Now in payDates() for bill #%d', $bill->id)); + // app('log')->debug(sprintf('Now in payDates() for bill #%d', $bill->id)); if (null === $this->parameters->get('start') || null === $this->parameters->get('end')) { - //app('log')->debug('No start or end date, give empty array.'); + // app('log')->debug('No start or end date, give empty array.'); return []; } @@ -328,7 +315,7 @@ class BillTransformer extends AbstractTransformer $set->push(clone $nextExpectedMatch); $nextExpectedMatch->addDay(); $currentStart = clone $nextExpectedMatch; - $loop++; + ++$loop; if ($loop > 4) { break; } @@ -347,32 +334,21 @@ class BillTransformer extends AbstractTransformer * transaction. Whether or not it is there already, is not relevant. * * TODO this method is bad compared to the v1 one. - * - * @param Bill $bill - * @param Carbon $date - * - * @return Carbon */ protected function nextDateMatch(Bill $bill, Carbon $date): Carbon { - //app('log')->debug(sprintf('Now in nextDateMatch(%d, %s)', $bill->id, $date->format('Y-m-d'))); + // app('log')->debug(sprintf('Now in nextDateMatch(%d, %s)', $bill->id, $date->format('Y-m-d'))); $start = clone $bill->date; - //app('log')->debug(sprintf('Bill start date is %s', $start->format('Y-m-d'))); + // app('log')->debug(sprintf('Bill start date is %s', $start->format('Y-m-d'))); while ($start < $date) { $start = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip); } - //app('log')->debug(sprintf('End of loop, bill start date is now %s', $start->format('Y-m-d'))); + // app('log')->debug(sprintf('End of loop, bill start date is now %s', $start->format('Y-m-d'))); return $start; } - /** - * @param Carbon $next - * @param array $dates - * - * @return string - */ private function getNextExpectedMatchDiff(Carbon $next, array $dates): string { if ($next->isToday()) { @@ -383,8 +359,7 @@ class BillTransformer extends AbstractTransformer return trans('firefly.not_expected_period'); } $carbon = new Carbon($current); + return $carbon->diffForHumans(today(config('app.timezone')), CarbonInterface::DIFF_RELATIVE_TO_NOW); } - - } diff --git a/app/Transformers/V2/BudgetLimitTransformer.php b/app/Transformers/V2/BudgetLimitTransformer.php index 2c28e62c9c..ac37adba53 100644 --- a/app/Transformers/V2/BudgetLimitTransformer.php +++ b/app/Transformers/V2/BudgetLimitTransformer.php @@ -37,9 +37,6 @@ class BudgetLimitTransformer extends AbstractTransformer 'budget', ]; - /** - * @inheritDoc - */ public function collectMetaData(Collection $objects): void { // TODO: Implement collectMetaData() method. @@ -48,8 +45,6 @@ class BudgetLimitTransformer extends AbstractTransformer /** * Include Budget * - * @param BudgetLimit $limit - * * @return Item */ public function includeBudget(BudgetLimit $limit) @@ -59,10 +54,6 @@ class BudgetLimitTransformer extends AbstractTransformer /** * Transform the note. - * - * @param BudgetLimit $budgetLimit - * - * @return array */ public function transform(BudgetLimit $budgetLimit): array { @@ -102,7 +93,7 @@ class BudgetLimitTransformer extends AbstractTransformer 'currency_symbol' => $currencySymbol, 'amount' => $amount, 'period' => $budgetLimit->period, - //'spent' => $expenses[$currencyId]['sum'] ?? '0', + // 'spent' => $expenses[$currencyId]['sum'] ?? '0', 'links' => [ [ 'rel' => 'self', diff --git a/app/Transformers/V2/BudgetTransformer.php b/app/Transformers/V2/BudgetTransformer.php index 664e0f4f88..eb86c28a7b 100644 --- a/app/Transformers/V2/BudgetTransformer.php +++ b/app/Transformers/V2/BudgetTransformer.php @@ -32,24 +32,19 @@ use Symfony\Component\HttpFoundation\ParameterBag; */ class BudgetTransformer extends AbstractTransformer { - //private OperationsRepositoryInterface $opsRepository; - //private BudgetRepositoryInterface $repository; + // private OperationsRepositoryInterface $opsRepository; + // private BudgetRepositoryInterface $repository; /** * BudgetTransformer constructor. - * - */ public function __construct() { - //$this->opsRepository = app(OperationsRepositoryInterface::class); - //$this->repository = app(BudgetRepositoryInterface::class); + // $this->opsRepository = app(OperationsRepositoryInterface::class); + // $this->repository = app(BudgetRepositoryInterface::class); $this->parameters = new ParameterBag(); } - /** - * @inheritDoc - */ public function collectMetaData(Collection $objects): void { // TODO: Implement collectMetaData() method. @@ -57,17 +52,13 @@ class BudgetTransformer extends AbstractTransformer /** * Transform a budget. - * - * @param Budget $budget - * - * @return array */ public function transform(Budget $budget): array { - //$this->opsRepository->setUser($budget->user); - //$start = $this->parameters->get('start'); - //$end = $this->parameters->get('end'); - //$autoBudget = $this->repository->getAutoBudget($budget); + // $this->opsRepository->setUser($budget->user); + // $start = $this->parameters->get('start'); + // $end = $this->parameters->get('end'); + // $autoBudget = $this->repository->getAutoBudget($budget); // $spent = []; // if (null !== $start && null !== $end) { // $spent = $this->beautify($this->opsRepository->sumExpenses($start, $end, null, new Collection([$budget]))); @@ -115,5 +106,4 @@ class BudgetTransformer extends AbstractTransformer ], ]; } - } diff --git a/app/Transformers/V2/CurrencyTransformer.php b/app/Transformers/V2/CurrencyTransformer.php index 18b28588f9..9466b253cf 100644 --- a/app/Transformers/V2/CurrencyTransformer.php +++ b/app/Transformers/V2/CurrencyTransformer.php @@ -31,17 +31,10 @@ use Illuminate\Support\Collection; */ class CurrencyTransformer extends AbstractTransformer { - /** - * @inheritDoc - */ public function collectMetaData(Collection $objects): void {} /** * Transform the currency. - * - * @param TransactionCurrency $currency - * - * @return array */ public function transform(TransactionCurrency $currency): array { @@ -58,7 +51,7 @@ class CurrencyTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/currencies/' . $currency->id, + 'uri' => '/currencies/'.$currency->id, ], ], ]; diff --git a/app/Transformers/V2/PiggyBankTransformer.php b/app/Transformers/V2/PiggyBankTransformer.php index e00d3ce6ea..1fa7428b73 100644 --- a/app/Transformers/V2/PiggyBankTransformer.php +++ b/app/Transformers/V2/PiggyBankTransformer.php @@ -36,7 +36,6 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; -use JsonException; /** * Class PiggyBankTransformer @@ -56,8 +55,6 @@ class PiggyBankTransformer extends AbstractTransformer /** * PiggyBankTransformer constructor. - * - */ public function __construct() { @@ -71,9 +68,6 @@ class PiggyBankTransformer extends AbstractTransformer // $this->piggyRepos = app(PiggyBankRepositoryInterface::class); } - /** - * @inheritDoc - */ public function collectMetaData(Collection $objects): void { // TODO move to repository (does not exist yet) @@ -81,6 +75,7 @@ class PiggyBankTransformer extends AbstractTransformer $accountInfo = Account::whereIn('id', $objects->pluck('account_id')->toArray())->get(); $currencyPreferences = AccountMeta::where('name', '"currency_id"')->whereIn('account_id', $objects->pluck('account_id')->toArray())->get(); $currencies = []; + /** @var Account $account */ foreach ($accountInfo as $account) { $id = $account->id; @@ -88,6 +83,7 @@ class PiggyBankTransformer extends AbstractTransformer 'name' => $account->name, ]; } + /** @var AccountMeta $preference */ foreach ($currencyPreferences as $preference) { $currencyId = (int)$preference->data; @@ -98,9 +94,11 @@ class PiggyBankTransformer extends AbstractTransformer // grab object groups: $set = DB::table('object_groupables') - ->leftJoin('object_groups', 'object_groups.id', '=', 'object_groupables.object_group_id') - ->where('object_groupables.object_groupable_type', PiggyBank::class) - ->get(['object_groupables.*', 'object_groups.title', 'object_groups.order']); + ->leftJoin('object_groups', 'object_groups.id', '=', 'object_groupables.object_group_id') + ->where('object_groupables.object_groupable_type', PiggyBank::class) + ->get(['object_groupables.*', 'object_groups.title', 'object_groups.order']) + ; + /** @var ObjectGroup $entry */ foreach ($set as $entry) { $piggyBankId = (int)$entry->object_groupable_id; @@ -111,11 +109,11 @@ class PiggyBankTransformer extends AbstractTransformer 'object_group_title' => $entry->title, 'object_group_order' => $order, ]; - } // grab repetitions (for current amount): $repetitions = PiggyBankRepetition::whereIn('piggy_bank_id', $piggyBanks)->get(); + /** @var PiggyBankRepetition $repetition */ foreach ($repetitions as $repetition) { $this->repetitions[$repetition->piggy_bank_id] = [ @@ -126,6 +124,7 @@ class PiggyBankTransformer extends AbstractTransformer // grab notes // continue with notes $notes = Note::whereNoteableType(PiggyBank::class)->whereIn('noteable_id', array_keys($piggyBanks))->get(); + /** @var Note $note */ foreach ($notes as $note) { $id = $note->noteable_id; @@ -139,11 +138,7 @@ class PiggyBankTransformer extends AbstractTransformer /** * Transform the piggy bank. * - * @param PiggyBank $piggyBank - * - * @return array * @throws FireflyException - * @throws JsonException */ public function transform(PiggyBank $piggyBank): array { @@ -197,6 +192,7 @@ class PiggyBankTransformer extends AbstractTransformer $savePerMonth = $this->getSuggestedMonthlyAmount($currentAmount, $targetAmount, $piggyBank->startdate, $piggyBank->targetdate); $nativeSavePerMonth = $this->converter->convert($this->default, $currency, today(), $savePerMonth); } + $this->converter->summarize(); return [ 'id' => (string)$piggyBank->id, @@ -233,20 +229,12 @@ class PiggyBankTransformer extends AbstractTransformer 'links' => [ [ 'rel' => 'self', - 'uri' => '/piggy_banks/' . $piggyBank->id, + 'uri' => '/piggy_banks/'.$piggyBank->id, ], ], ]; } - /** - * @param string $currentAmount - * @param string $targetAmount - * @param Carbon|null $startDate - * @param Carbon|null $targetDate - * - * @return string - */ private function getSuggestedMonthlyAmount(string $currentAmount, string $targetAmount, ?Carbon $startDate, ?Carbon $targetDate): string { $savePerMonth = '0'; diff --git a/app/Transformers/V2/PreferenceTransformer.php b/app/Transformers/V2/PreferenceTransformer.php index 8d5c6548b9..5736daf853 100644 --- a/app/Transformers/V2/PreferenceTransformer.php +++ b/app/Transformers/V2/PreferenceTransformer.php @@ -31,9 +31,6 @@ use Illuminate\Support\Collection; */ class PreferenceTransformer extends AbstractTransformer { - /** - * @inheritDoc - */ public function collectMetaData(Collection $objects): void { // TODO: Implement collectMetaData() method. @@ -41,10 +38,6 @@ class PreferenceTransformer extends AbstractTransformer /** * Transform the preference - * - * @param Preference $preference - * - * @return array */ public function transform(Preference $preference): array { diff --git a/app/Transformers/V2/TransactionGroupTransformer.php b/app/Transformers/V2/TransactionGroupTransformer.php index 77be731803..a603a258c8 100644 --- a/app/Transformers/V2/TransactionGroupTransformer.php +++ b/app/Transformers/V2/TransactionGroupTransformer.php @@ -35,7 +35,6 @@ use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\Support\NullArrayObject; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; -use stdClass; /** * Class TransactionGroupTransformer @@ -49,23 +48,22 @@ class TransactionGroupTransformer extends AbstractTransformer private array $notes; private array $tags; - /** - * @inheritDoc - */ public function collectMetaData(Collection $objects): void { // start with currencies: $currencies = []; $journals = []; + /** @var array $object */ foreach ($objects as $object) { foreach ($object['sums'] as $sum) { - $id = (int)$sum['currency_id']; + $id = (int) $sum['currency_id']; $currencies[$id] ??= TransactionCurrency::find($sum['currency_id']); } + /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { - $id = (int)$transaction['transaction_journal_id']; + $id = (int) $transaction['transaction_journal_id']; $journals[$id] = []; } } @@ -74,6 +72,7 @@ class TransactionGroupTransformer extends AbstractTransformer // grab meta for all journals: $meta = TransactionJournalMeta::whereIn('transaction_journal_id', array_keys($journals))->get(); + /** @var TransactionJournalMeta $entry */ foreach ($meta as $entry) { $id = $entry->transaction_journal_id; @@ -82,6 +81,7 @@ class TransactionGroupTransformer extends AbstractTransformer // grab all notes for all journals: $notes = Note::whereNoteableType(TransactionJournal::class)->whereIn('noteable_id', array_keys($journals))->get(); + /** @var Note $note */ foreach ($notes as $note) { $id = $note->noteable_id; @@ -90,12 +90,14 @@ class TransactionGroupTransformer extends AbstractTransformer // grab all tags for all journals: $tags = DB::table('tag_transaction_journal') - ->leftJoin('tags', 'tags.id', 'tag_transaction_journal.tag_id') - ->whereIn('tag_transaction_journal.transaction_journal_id', array_keys($journals)) - ->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag']); - /** @var stdClass $tag */ + ->leftJoin('tags', 'tags.id', 'tag_transaction_journal.tag_id') + ->whereIn('tag_transaction_journal.transaction_journal_id', array_keys($journals)) + ->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag']) + ; + + /** @var \stdClass $tag */ foreach ($tags as $tag) { - $id = (int)$tag->transaction_journal_id; + $id = (int) $tag->transaction_journal_id; $this->tags[$id][] = $tag->tag; } @@ -103,20 +105,16 @@ class TransactionGroupTransformer extends AbstractTransformer $this->converter = new ExchangeRateConverter(); } - /** - * @param array $group - * - * @return array - */ public function transform(array $group): array { $first = reset($group['transactions']); + return [ - 'id' => (string)$group['id'], + 'id' => (string) $group['id'], 'created_at' => $first['created_at']->toAtomString(), 'updated_at' => $first['updated_at']->toAtomString(), - 'user' => (string)$first['user_id'], - 'user_group' => (string)$first['user_group_id'], + 'user' => (string) $first['user_id'], + 'user_group' => (string) $first['user_group_id'], 'group_title' => $group['title'] ?? null, 'transactions' => $this->transformTransactions($group['transactions'] ?? []), 'links' => [ @@ -128,52 +126,49 @@ class TransactionGroupTransformer extends AbstractTransformer ]; } - /** - * @param array $transactions - * - * @return array - */ private function transformTransactions(array $transactions): array { $return = []; + /** @var array $transaction */ foreach ($transactions as $transaction) { $return[] = $this->transformTransaction($transaction); } + return $return; } /** - * @param array $transaction - * - * @return array * @throws FireflyException + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ private function transformTransaction(array $transaction): array { $transaction = new NullArrayObject($transaction); $type = $this->stringFromArray($transaction, 'transaction_type_type', TransactionType::WITHDRAWAL); - $journalId = (int)$transaction['transaction_journal_id']; + $journalId = (int) $transaction['transaction_journal_id']; $meta = new NullArrayObject($this->meta[$journalId] ?? []); /** * Convert and use amount: */ - $amount = app('steam')->positive((string)($transaction['amount'] ?? '0')); - $currencyId = (int)$transaction['currency_id']; + $amount = app('steam')->positive((string) ($transaction['amount'] ?? '0')); + $currencyId = (int) $transaction['currency_id']; $nativeAmount = $this->converter->convert($this->default, $this->currencies[$currencyId], $transaction['date'], $amount); $foreignAmount = null; $nativeForeignAmount = null; if (null !== $transaction['foreign_amount']) { - $foreignCurrencyId = (int)$transaction['foreign_currency_id']; + $foreignCurrencyId = (int) $transaction['foreign_currency_id']; $foreignAmount = app('steam')->positive($transaction['foreign_amount']); $nativeForeignAmount = $this->converter->convert($this->default, $this->currencies[$foreignCurrencyId], $transaction['date'], $foreignAmount); } + $this->converter->summarize(); return [ - 'user' => (string)$transaction['user_id'], - 'user_group' => (string)$transaction['user_group_id'], - 'transaction_journal_id' => (string)$transaction['transaction_journal_id'], + 'user' => (string) $transaction['user_id'], + 'user_group' => (string) $transaction['user_group_id'], + 'transaction_journal_id' => (string) $transaction['transaction_journal_id'], 'type' => strtolower($type), 'date' => $transaction['date']->toAtomString(), 'order' => $transaction['order'], @@ -181,14 +176,14 @@ class TransactionGroupTransformer extends AbstractTransformer 'native_amount' => $nativeAmount, 'foreign_amount' => $foreignAmount, 'native_foreign_amount' => $nativeForeignAmount, - 'currency_id' => (string)$transaction['currency_id'], + 'currency_id' => (string) $transaction['currency_id'], 'currency_code' => $transaction['currency_code'], 'currency_name' => $transaction['currency_name'], 'currency_symbol' => $transaction['currency_symbol'], - 'currency_decimal_places' => (int)$transaction['currency_decimal_places'], + 'currency_decimal_places' => (int) $transaction['currency_decimal_places'], // converted to native currency - 'native_currency_id' => (string)$this->default->id, + 'native_currency_id' => (string) $this->default->id, 'native_currency_code' => $this->default->code, 'native_currency_name' => $this->default->name, 'native_currency_symbol' => $this->default->symbol, @@ -203,11 +198,11 @@ class TransactionGroupTransformer extends AbstractTransformer // foreign converted to native: 'description' => $transaction['description'], - 'source_id' => (string)$transaction['source_account_id'], + 'source_id' => (string) $transaction['source_account_id'], 'source_name' => $transaction['source_account_name'], 'source_iban' => $transaction['source_account_iban'], 'source_type' => $transaction['source_account_type'], - 'destination_id' => (string)$transaction['destination_account_id'], + 'destination_id' => (string) $transaction['destination_account_id'], 'destination_name' => $transaction['destination_account_name'], 'destination_iban' => $transaction['destination_account_iban'], 'destination_type' => $transaction['destination_account_type'], @@ -258,16 +253,10 @@ class TransactionGroupTransformer extends AbstractTransformer * * Used to extract a value from the given array, and fall back on a sensible default or NULL * if it can't be helped. - * - * @param NullArrayObject $array - * @param string $key - * @param string|null $default - * - * @return string|null */ private function stringFromArray(NullArrayObject $array, string $key, ?string $default): ?string { - //app('log')->debug(sprintf('%s: %s', $key, var_export($array[$key], true))); + // app('log')->debug(sprintf('%s: %s', $key, var_export($array[$key], true))); if (null === $array[$key] && null === $default) { return null; } @@ -278,7 +267,7 @@ class TransactionGroupTransformer extends AbstractTransformer return $default; } if (null !== $array[$key]) { - return (string)$array[$key]; + return (string) $array[$key]; } if (null !== $default) { @@ -288,11 +277,6 @@ class TransactionGroupTransformer extends AbstractTransformer return null; } - /** - * @param string|null $string - * - * @return Carbon|null - */ private function date(?string $string): ?Carbon { if (null === $string) { @@ -304,6 +288,7 @@ class TransactionGroupTransformer extends AbstractTransformer if (false === $res) { return null; } + return $res; } if (25 === strlen($string)) { @@ -314,6 +299,7 @@ class TransactionGroupTransformer extends AbstractTransformer if (false === $res) { return null; } + return $res; } @@ -322,6 +308,7 @@ class TransactionGroupTransformer extends AbstractTransformer if (false === $res) { return null; } + return $res; } } diff --git a/app/Transformers/V2/UserGroupTransformer.php b/app/Transformers/V2/UserGroupTransformer.php index f8fb013411..2c523a0281 100644 --- a/app/Transformers/V2/UserGroupTransformer.php +++ b/app/Transformers/V2/UserGroupTransformer.php @@ -1,6 +1,5 @@ memberships = []; } - /** - * @inheritDoc - */ public function collectMetaData(Collection $objects): void { if (auth()->check()) { // collect memberships so they can be listed in the group. /** @var User $user */ $user = auth()->user(); + /** @var UserGroup $userGroup */ foreach ($objects as $userGroup) { $userGroupId = $userGroup->id; $access = $user->hasRoleInGroupOrOwner($userGroup, UserRoleEnum::VIEW_MEMBERSHIPS) || $user->hasRole('owner'); if ($access) { $groupMemberships = $userGroup->groupMemberships()->get(); + /** @var GroupMembership $groupMembership */ foreach ($groupMemberships as $groupMembership) { $this->memberships[$userGroupId][] = [ @@ -76,14 +71,10 @@ class UserGroupTransformer extends AbstractTransformer /** * Transform the user group. - * - * @param UserGroup $userGroup - * - * @return array */ public function transform(UserGroup $userGroup): array { - $return = [ + return [ 'id' => $userGroup->id, 'created_at' => $userGroup->created_at->toAtomString(), 'updated_at' => $userGroup->updated_at->toAtomString(), @@ -91,7 +82,5 @@ class UserGroupTransformer extends AbstractTransformer 'members' => $this->memberships[$userGroup->id] ?? [], ]; // if the user has a specific role in this group, then collect the memberships. - - return $return; } } diff --git a/app/Transformers/WebhookAttemptTransformer.php b/app/Transformers/WebhookAttemptTransformer.php index 8576c3800f..6feafb88e1 100644 --- a/app/Transformers/WebhookAttemptTransformer.php +++ b/app/Transformers/WebhookAttemptTransformer.php @@ -32,10 +32,6 @@ class WebhookAttemptTransformer extends AbstractTransformer { /** * Transform the preference - * - * @param WebhookAttempt $attempt - * - * @return array */ public function transform(WebhookAttempt $attempt): array { diff --git a/app/Transformers/WebhookMessageTransformer.php b/app/Transformers/WebhookMessageTransformer.php index f4a1bea0b9..7cdee7adc7 100644 --- a/app/Transformers/WebhookMessageTransformer.php +++ b/app/Transformers/WebhookMessageTransformer.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Transformers; use FireflyIII\Models\WebhookMessage; -use JsonException; /** * Class WebhookMessageTransformer @@ -33,17 +32,14 @@ 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) { + } catch (\JsonException $e) { app('log')->error(sprintf('Could not encode webhook message #%d: %s', $message->id, $e->getMessage())); } diff --git a/app/Transformers/WebhookTransformer.php b/app/Transformers/WebhookTransformer.php index 5ed705f4de..d913b38f3d 100644 --- a/app/Transformers/WebhookTransformer.php +++ b/app/Transformers/WebhookTransformer.php @@ -41,10 +41,6 @@ class WebhookTransformer extends AbstractTransformer /** * Transform webhook. - * - * @param Webhook $webhook - * - * @return array */ public function transform(Webhook $webhook): array { @@ -68,12 +64,6 @@ class WebhookTransformer extends AbstractTransformer ]; } - /** - * @param string $type - * @param int $value - * - * @return string - */ private function getEnum(string $type, int $value): string { if ('trigger' === $type) { @@ -82,6 +72,7 @@ class WebhookTransformer extends AbstractTransformer if ('response' === $type) { return WebhookResponse::from($value)->name; } + return WebhookDelivery::from($value)->name; } } diff --git a/app/User.php b/app/User.php index 4ce095271c..e83688f07c 100644 --- a/app/User.php +++ b/app/User.php @@ -26,7 +26,6 @@ namespace FireflyIII; use Carbon\Carbon; use Eloquent; -use Exception; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Events\RequestedNewPassword; use FireflyIII\Exceptions\FireflyException; @@ -72,47 +71,47 @@ use Illuminate\Support\Str; use Laravel\Passport\Client; use Laravel\Passport\HasApiTokens; use Laravel\Passport\Token; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; -use Request; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class User. * - * @property int|string $id - * @property string $email - * @property bool $isAdmin - * @property bool $has2FA - * @property array $prefs - * @property string $password - * @property string $mfa_secret - * @property Collection $roles - * @property string $blocked_code - * @property bool $blocked - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property string|null $remember_token - * @property string|null $reset - * @property-read \Illuminate\Database\Eloquent\Collection|Account[] $accounts - * @property-read \Illuminate\Database\Eloquent\Collection|Attachment[] $attachments - * @property-read \Illuminate\Database\Eloquent\Collection|AvailableBudget[] $availableBudgets - * @property-read \Illuminate\Database\Eloquent\Collection|Bill[] $bills - * @property-read \Illuminate\Database\Eloquent\Collection|Budget[] $budgets - * @property-read \Illuminate\Database\Eloquent\Collection|Category[] $categories - * @property-read \Illuminate\Database\Eloquent\Collection|Client[] $clients - * @property-read \Illuminate\Database\Eloquent\Collection|CurrencyExchangeRate[] $currencyExchangeRates - * @property-read DatabaseNotificationCollection|DatabaseNotification[] $notifications - * @property-read \Illuminate\Database\Eloquent\Collection|PiggyBank[] $piggyBanks - * @property-read \Illuminate\Database\Eloquent\Collection|Preference[] $preferences - * @property-read \Illuminate\Database\Eloquent\Collection|Recurrence[] $recurrences - * @property-read \Illuminate\Database\Eloquent\Collection|RuleGroup[] $ruleGroups - * @property-read \Illuminate\Database\Eloquent\Collection|Rule[] $rules - * @property-read \Illuminate\Database\Eloquent\Collection|Tag[] $tags - * @property-read \Illuminate\Database\Eloquent\Collection|Token[] $tokens - * @property-read \Illuminate\Database\Eloquent\Collection|TransactionGroup[] $transactionGroups - * @property-read \Illuminate\Database\Eloquent\Collection|TransactionJournal[] $transactionJournals - * @property-read \Illuminate\Database\Eloquent\Collection|Transaction[] $transactions + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @property int|string $id + * @property string $email + * @property bool $isAdmin + * @property bool $has2FA + * @property array $prefs + * @property string $password + * @property string $mfa_secret + * @property Collection $roles + * @property string $blocked_code + * @property bool $blocked + * @property null|Carbon $created_at + * @property null|Carbon $updated_at + * @property null|string $remember_token + * @property null|string $reset + * @property Account[]|\Illuminate\Database\Eloquent\Collection $accounts + * @property Attachment[]|\Illuminate\Database\Eloquent\Collection $attachments + * @property AvailableBudget[]|\Illuminate\Database\Eloquent\Collection $availableBudgets + * @property Bill[]|\Illuminate\Database\Eloquent\Collection $bills + * @property Budget[]|\Illuminate\Database\Eloquent\Collection $budgets + * @property Category[]|\Illuminate\Database\Eloquent\Collection $categories + * @property Client[]|\Illuminate\Database\Eloquent\Collection $clients + * @property CurrencyExchangeRate[]|\Illuminate\Database\Eloquent\Collection $currencyExchangeRates + * @property DatabaseNotification[]|DatabaseNotificationCollection $notifications + * @property \Illuminate\Database\Eloquent\Collection|PiggyBank[] $piggyBanks + * @property \Illuminate\Database\Eloquent\Collection|Preference[] $preferences + * @property \Illuminate\Database\Eloquent\Collection|Recurrence[] $recurrences + * @property \Illuminate\Database\Eloquent\Collection|RuleGroup[] $ruleGroups + * @property \Illuminate\Database\Eloquent\Collection|Rule[] $rules + * @property \Illuminate\Database\Eloquent\Collection|Tag[] $tags + * @property \Illuminate\Database\Eloquent\Collection|Token[] $tokens + * @property \Illuminate\Database\Eloquent\Collection|TransactionGroup[] $transactionGroups + * @property \Illuminate\Database\Eloquent\Collection|TransactionJournal[] $transactionJournals + * @property \Illuminate\Database\Eloquent\Collection|Transaction[] $transactions + * * @method static Builder|User newModelQuery() * @method static Builder|User newQuery() * @method static Builder|User query() @@ -125,50 +124,60 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static Builder|User whereRememberToken($value) * @method static Builder|User whereReset($value) * @method static Builder|User whereUpdatedAt($value) - * @property string|null $objectguid - * @property-read int|null $accounts_count - * @property-read int|null $attachments_count - * @property-read int|null $available_budgets_count - * @property-read int|null $bills_count - * @property-read int|null $budgets_count - * @property-read int|null $categories_count - * @property-read int|null $clients_count - * @property-read int|null $currency_exchange_rates_count - * @property-read int|null $notifications_count - * @property-read int|null $piggy_banks_count - * @property-read int|null $preferences_count - * @property-read int|null $recurrences_count - * @property-read int|null $roles_count - * @property-read int|null $rule_groups_count - * @property-read int|null $rules_count - * @property-read int|null $tags_count - * @property-read int|null $tokens_count - * @property-read int|null $transaction_groups_count - * @property-read int|null $transaction_journals_count - * @property-read int|null $transactions_count + * + * @property null|string $objectguid + * @property null|int $accounts_count + * @property null|int $attachments_count + * @property null|int $available_budgets_count + * @property null|int $bills_count + * @property null|int $budgets_count + * @property null|int $categories_count + * @property null|int $clients_count + * @property null|int $currency_exchange_rates_count + * @property null|int $notifications_count + * @property null|int $piggy_banks_count + * @property null|int $preferences_count + * @property null|int $recurrences_count + * @property null|int $roles_count + * @property null|int $rule_groups_count + * @property null|int $rules_count + * @property null|int $tags_count + * @property null|int $tokens_count + * @property null|int $transaction_groups_count + * @property null|int $transaction_journals_count + * @property null|int $transactions_count + * * @method static Builder|User whereMfaSecret($value) * @method static Builder|User whereObjectguid($value) - * @property string|null $provider + * + * @property null|string $provider + * * @method static Builder|User whereProvider($value) - * @property-read \Illuminate\Database\Eloquent\Collection|ObjectGroup[] $objectGroups - * @property-read int|null $object_groups_count - * @property-read \Illuminate\Database\Eloquent\Collection|Webhook[] $webhooks - * @property-read int|null $webhooks_count - * @property string|null $two_factor_secret - * @property string|null $two_factor_recovery_codes - * @property string|null $guid - * @property string|null $domain + * + * @property \Illuminate\Database\Eloquent\Collection|ObjectGroup[] $objectGroups + * @property null|int $object_groups_count + * @property \Illuminate\Database\Eloquent\Collection|Webhook[] $webhooks + * @property null|int $webhooks_count + * @property null|string $two_factor_secret + * @property null|string $two_factor_recovery_codes + * @property null|string $guid + * @property null|string $domain + * * @method static Builder|User whereDomain($value) * @method static Builder|User whereGuid($value) * @method static Builder|User whereTwoFactorRecoveryCodes($value) * @method static Builder|User whereTwoFactorSecret($value) - * @property int|null $user_group_id - * @property-read \Illuminate\Database\Eloquent\Collection|GroupMembership[] $groupMemberships - * @property-read int|null $group_memberships_count - * @property-read UserGroup|null $userGroup + * + * @property null|int $user_group_id + * @property GroupMembership[]|\Illuminate\Database\Eloquent\Collection $groupMemberships + * @property null|int $group_memberships_count + * @property null|UserGroup $userGroup + * * @method static Builder|User whereUserGroupId($value) - * @property-read \Illuminate\Database\Eloquent\Collection $currencies - * @property-read int|null $currencies_count + * + * @property \Illuminate\Database\Eloquent\Collection $currencies + * @property null|int $currencies_count + * * @mixin Eloquent */ class User extends Authenticatable @@ -178,18 +187,15 @@ class User extends Authenticatable protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'blocked' => 'boolean', - ]; + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'blocked' => 'boolean', + ]; protected $fillable = ['email', 'password', 'blocked', 'blocked_code']; protected $hidden = ['password', 'remember_token']; protected $table = 'users'; /** - * @param string $value - * - * @return User * @throws NotFoundHttpException */ public static function routeBinder(string $value): self @@ -201,13 +207,12 @@ class User extends Authenticatable return $user; } } + throw new NotFoundHttpException(); } /** * Link to accounts. - * - * @return HasMany */ public function accounts(): HasMany { @@ -216,8 +221,6 @@ class User extends Authenticatable /** * Link to attachments - * - * @return HasMany */ public function attachments(): HasMany { @@ -226,8 +229,6 @@ class User extends Authenticatable /** * Link to available budgets - * - * @return HasMany */ public function availableBudgets(): HasMany { @@ -236,8 +237,6 @@ class User extends Authenticatable /** * Link to bills. - * - * @return HasMany */ public function bills(): HasMany { @@ -246,8 +245,6 @@ class User extends Authenticatable /** * Link to budgets. - * - * @return HasMany */ public function budgets(): HasMany { @@ -256,8 +253,6 @@ class User extends Authenticatable /** * Link to categories - * - * @return HasMany */ public function categories(): HasMany { @@ -266,8 +261,6 @@ class User extends Authenticatable /** * Link to currencies - * - * @return BelongsToMany */ public function currencies(): BelongsToMany { @@ -276,8 +269,6 @@ class User extends Authenticatable /** * Link to currency exchange rates - * - * @return HasMany */ public function currencyExchangeRates(): HasMany { @@ -287,8 +278,7 @@ class User extends Authenticatable /** * Generates access token. * - * @return string - * @throws Exception + * @throws \Exception */ public function generateAccessToken(): string { @@ -300,7 +290,6 @@ class User extends Authenticatable /** * A safe method that returns the user's current administration ID (group ID). * - * @return int * @throws FireflyException */ public function getAdministrationId(): int @@ -309,6 +298,7 @@ class User extends Authenticatable if (0 === $groupId) { throw new FireflyException('User has no administration ID.'); } + return $groupId; } @@ -316,8 +306,8 @@ class User extends Authenticatable * Get the models LDAP domain. * * @return string - * @deprecated * + * @deprecated */ public function getLdapDomain() { @@ -328,8 +318,8 @@ class User extends Authenticatable * Get the database column name of the domain. * * @return string - * @deprecated * + * @deprecated */ public function getLdapDomainColumn() { @@ -340,8 +330,8 @@ class User extends Authenticatable * Get the models LDAP GUID. * * @return string - * @deprecated * + * @deprecated */ public function getLdapGuid() { @@ -352,8 +342,8 @@ class User extends Authenticatable * Get the models LDAP GUID database column name. * * @return string - * @deprecated * + * @deprecated */ public function getLdapGuidColumn() { @@ -365,96 +355,22 @@ class User extends Authenticatable * * If $allowOverride is set to true, then the roles FULL or OWNER will also be checked, * which means that in most cases the user DOES have access, regardless of the original role submitted in $role. - * - * @param UserGroup $userGroup - * @param UserRoleEnum $role - * - * @return bool */ public function hasRoleInGroupOrOwner(UserGroup $userGroup, UserRoleEnum $role): bool { $roles = [$role->value, UserRoleEnum::OWNER->value, UserRoleEnum::FULL->value]; + return $this->hasAnyRoleInGroup($userGroup, $roles); } /** * Does the user have role X in group Y? - * - * @param UserGroup $userGroup - * @param UserRoleEnum $role - * - * @return bool */ public function hasSpecificRoleInGroup(UserGroup $userGroup, UserRoleEnum $role): bool { return $this->hasAnyRoleInGroup($userGroup, [$role]); } - /** - * Does the user have role X, Y or Z in group A? - * - * @param UserGroup $userGroup - * @param array $roles - * - * @return bool - */ - private function hasAnyRoleInGroup(UserGroup $userGroup, array $roles): bool - { - app('log')->debug(sprintf('in hasAnyRoleInGroup(%s)', implode(', ', $roles))); - /** @var Collection $dbRoles */ - $dbRoles = UserRole::whereIn('title', $roles)->get(); - if (0 === $dbRoles->count()) { - app('log')->error(sprintf('Could not find role(s): %s. Probably migration mishap.', implode(', ', $roles))); - return false; - } - $dbRolesIds = $dbRoles->pluck('id')->toArray(); - $dbRolesTitles = $dbRoles->pluck('title')->toArray(); - - /** @var Collection $groupMemberships */ - $groupMemberships = $this->groupMemberships() - ->whereIn('user_role_id', $dbRolesIds) - ->where('user_group_id', $userGroup->id)->get(); - if (0 === $groupMemberships->count()) { - app('log')->error(sprintf( - 'User #%d "%s" does not have roles %s in user group #%d "%s"', - $this->id, - $this->email, - implode(', ', $roles), - $userGroup->id, - $userGroup->title - )); - return false; - } - foreach ($groupMemberships as $membership) { - app('log')->debug(sprintf( - 'User #%d "%s" has role "%s" in user group #%d "%s"', - $this->id, - $this->email, - $membership->userRole->title, - $userGroup->id, - $userGroup->title - )); - if (in_array($membership->userRole->title, $dbRolesTitles, true)) { - app('log')->debug(sprintf('Return true, found role "%s"', $membership->userRole->title)); - return true; - } - } - app('log')->error(sprintf( - 'User #%d "%s" does not have roles %s in user group #%d "%s"', - $this->id, - $this->email, - implode(', ', $roles), - $userGroup->id, - $userGroup->title - )); - return false; - - } - - /** - * - * @return HasMany - */ public function groupMemberships(): HasMany { return $this->hasMany(GroupMembership::class)->with(['userGroup', 'userRole']); @@ -462,8 +378,6 @@ class User extends Authenticatable /** * Link to object groups. - * - * @return HasMany */ public function objectGroups(): HasMany { @@ -472,8 +386,6 @@ class User extends Authenticatable /** * Link to piggy banks. - * - * @return HasManyThrough */ public function piggyBanks(): HasManyThrough { @@ -482,8 +394,6 @@ class User extends Authenticatable /** * Link to preferences. - * - * @return HasMany */ public function preferences(): HasMany { @@ -492,8 +402,6 @@ class User extends Authenticatable /** * Link to recurring transactions. - * - * @return HasMany */ public function recurrences(): HasMany { @@ -504,13 +412,13 @@ class User extends Authenticatable * Get the notification routing information for the given driver. * * @param string $driver - * @param Notification|null $notification + * @param null|Notification $notification * * @return mixed */ public function routeNotificationFor($driver, $notification = null) { - $method = 'routeNotificationFor' . Str::studly($driver); + $method = 'routeNotificationFor'.Str::studly($driver); if (method_exists($this, $method)) { return $this->{$method}($notification); // @phpstan-ignore-line } @@ -534,20 +442,14 @@ class User extends Authenticatable /** * This method refers to the "global" role a user can have, outside of any group they may be part of. - * - * @param string $role - * - * @return bool */ public function hasRole(string $role): bool { - return $this->roles()->where('name', $role)->count() === 1; + return 1 === $this->roles()->where('name', $role)->count(); } /** * Link to roles. - * - * @return BelongsToMany */ public function roles(): BelongsToMany { @@ -556,12 +458,6 @@ class User extends Authenticatable /** * Route notifications for the Slack channel. - * - * @param Notification $notification - * - * @return string - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface */ public function routeNotificationForSlack(Notification $notification): string { @@ -587,13 +483,12 @@ class User extends Authenticatable if (is_array($pref)) { return ''; } + return (string)$pref; } /** * Link to rule groups. - * - * @return HasMany */ public function ruleGroups(): HasMany { @@ -602,8 +497,6 @@ class User extends Authenticatable /** * Link to rules. - * - * @return HasMany */ public function rules(): HasMany { @@ -619,7 +512,7 @@ class User extends Authenticatable */ public function sendPasswordResetNotification($token): void { - $ipAddress = Request::ip(); + $ipAddress = \Request::ip(); event(new RequestedNewPassword($this, $token, $ipAddress)); } @@ -629,11 +522,9 @@ class User extends Authenticatable * * @param string $domain * - * @return void * @deprecated - * */ - public function setLdapDomain($domain) + public function setLdapDomain($domain): void { $this->{$this->getLdapDomainColumn()} = $domain; } @@ -643,18 +534,15 @@ class User extends Authenticatable * * @param string $guid * - * @return void * @deprecated */ - public function setLdapGuid($guid) + public function setLdapGuid($guid): void { $this->{$this->getLdapGuidColumn()} = $guid; } /** * Link to tags. - * - * @return HasMany */ public function tags(): HasMany { @@ -663,8 +551,6 @@ class User extends Authenticatable /** * Link to transaction groups. - * - * @return HasMany */ public function transactionGroups(): HasMany { @@ -673,8 +559,6 @@ class User extends Authenticatable /** * Link to transaction journals. - * - * @return HasMany */ public function transactionJournals(): HasMany { @@ -683,30 +567,83 @@ class User extends Authenticatable /** * Link to transactions. - * - * @return HasManyThrough */ public function transactions(): HasManyThrough { return $this->hasManyThrough(Transaction::class, TransactionJournal::class); } - /** - * @return BelongsTo - */ public function userGroup(): BelongsTo { - return $this->belongsTo(UserGroup::class, ); + return $this->belongsTo(UserGroup::class); } /** - * * Link to webhooks - * - * @return HasMany */ public function webhooks(): HasMany { return $this->hasMany(Webhook::class); } + + /** + * Does the user have role X, Y or Z in group A? + */ + private function hasAnyRoleInGroup(UserGroup $userGroup, array $roles): bool + { + app('log')->debug(sprintf('in hasAnyRoleInGroup(%s)', implode(', ', $roles))); + + /** @var Collection $dbRoles */ + $dbRoles = UserRole::whereIn('title', $roles)->get(); + if (0 === $dbRoles->count()) { + app('log')->error(sprintf('Could not find role(s): %s. Probably migration mishap.', implode(', ', $roles))); + + return false; + } + $dbRolesIds = $dbRoles->pluck('id')->toArray(); + $dbRolesTitles = $dbRoles->pluck('title')->toArray(); + + /** @var Collection $groupMemberships */ + $groupMemberships = $this->groupMemberships() + ->whereIn('user_role_id', $dbRolesIds) + ->where('user_group_id', $userGroup->id)->get() + ; + if (0 === $groupMemberships->count()) { + app('log')->error(sprintf( + 'User #%d "%s" does not have roles %s in user group #%d "%s"', + $this->id, + $this->email, + implode(', ', $roles), + $userGroup->id, + $userGroup->title + )); + + return false; + } + foreach ($groupMemberships as $membership) { + app('log')->debug(sprintf( + 'User #%d "%s" has role "%s" in user group #%d "%s"', + $this->id, + $this->email, + $membership->userRole->title, + $userGroup->id, + $userGroup->title + )); + if (in_array($membership->userRole->title, $dbRolesTitles, true)) { + app('log')->debug(sprintf('Return true, found role "%s"', $membership->userRole->title)); + + return true; + } + } + app('log')->error(sprintf( + 'User #%d "%s" does not have roles %s in user group #%d "%s"', + $this->id, + $this->email, + implode(', ', $roles), + $userGroup->id, + $userGroup->title + )); + + return false; + } } diff --git a/app/Validation/Account/AccountValidatorProperties.php b/app/Validation/Account/AccountValidatorProperties.php index b77f592bdf..106c3660f5 100644 --- a/app/Validation/Account/AccountValidatorProperties.php +++ b/app/Validation/Account/AccountValidatorProperties.php @@ -29,6 +29,4 @@ namespace FireflyIII\Validation\Account; * * Trait AccountValidatorProperties */ -trait AccountValidatorProperties -{ -} +trait AccountValidatorProperties {} diff --git a/app/Validation/Account/DepositValidation.php b/app/Validation/Account/DepositValidation.php index fb1911c0a7..1b4166d2ae 100644 --- a/app/Validation/Account/DepositValidation.php +++ b/app/Validation/Account/DepositValidation.php @@ -31,11 +31,6 @@ use FireflyIII\Models\AccountType; */ trait DepositValidation { - /** - * @param array $array - * - * @return bool - */ protected function validateDepositDestination(array $array): bool { $result = null; @@ -50,7 +45,7 @@ trait DepositValidation if (null === $accountId && null === $accountName && null === $accountIban && false === $this->canCreateTypes($validTypes)) { // if both values are NULL we return false, // because the destination of a deposit can't be created. - $this->destError = (string)trans('validation.deposit_dest_need_data'); + $this->destError = (string) trans('validation.deposit_dest_need_data'); app('log')->error('Both values are NULL, cant create deposit destination.'); $result = false; } @@ -65,7 +60,7 @@ trait DepositValidation $search = $this->findExistingAccount($validTypes, $array); if (null === $search) { app('log')->debug('findExistingAccount() returned NULL, so the result is false.'); - $this->destError = (string)trans('validation.deposit_dest_bad_data', ['id' => $accountId, 'name' => $accountName]); + $this->destError = (string) trans('validation.deposit_dest_bad_data', ['id' => $accountId, 'name' => $accountName]); $result = false; } if (null !== $search) { @@ -79,25 +74,15 @@ trait DepositValidation return $result; } - /** - * @param array $accountTypes - * - * @return bool - */ abstract protected function canCreateTypes(array $accountTypes): bool; - /** - * @param array $validTypes - * @param array $data - * - * @return Account|null - */ abstract protected function findExistingAccount(array $validTypes, array $data): ?Account; /** - * @param array $array + * Pretty complex unfortunately. * - * @return bool + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function validateDepositSource(array $array): bool { @@ -113,15 +98,15 @@ trait DepositValidation // source can be any of the following types. $validTypes = array_keys($this->combinations[$this->transactionType]); - if (null === $accountId && - null === $accountName && - null === $accountIban && - null === $accountNumber && - false === $this->canCreateTypes($validTypes)) { + if (null === $accountId + && null === $accountName + && null === $accountIban + && null === $accountNumber + && false === $this->canCreateTypes($validTypes)) { // if both values are NULL return false, // because the source of a deposit can't be created. // (this never happens). - $this->sourceError = (string)trans('validation.deposit_source_need_data'); + $this->sourceError = (string) trans('validation.deposit_source_need_data'); $result = false; } @@ -130,7 +115,8 @@ trait DepositValidation app('log')->debug('Check if there is not already another account with this IBAN'); $existing = $this->findExistingAccount($validTypes, ['iban' => $accountIban], true); if (null !== $existing) { - $this->sourceError = (string)trans('validation.deposit_src_iban_exists'); + $this->sourceError = (string) trans('validation.deposit_src_iban_exists'); + return false; } } diff --git a/app/Validation/Account/LiabilityValidation.php b/app/Validation/Account/LiabilityValidation.php index f1920d13ee..09bf1622b2 100644 --- a/app/Validation/Account/LiabilityValidation.php +++ b/app/Validation/Account/LiabilityValidation.php @@ -32,11 +32,6 @@ use FireflyIII\Models\AccountType; */ trait LiabilityValidation { - /** - * @param array $array - * - * @return bool - */ protected function validateLCDestination(array $array): bool { app('log')->debug('Now in validateLCDestination', $array); @@ -50,30 +45,31 @@ trait LiabilityValidation if (null !== $accountId) { if (AccountType::LIABILITY_CREDIT !== $this->source?->accountType?->type) { app('log')->error('Source account is not a liability.'); + return false; } $result = $this->findExistingAccount($validTypes, $array); if (null === $result) { app('log')->error('Destination account is not a liability.'); + return false; } + return true; } if (null !== $accountName && '' !== $accountName) { app('log')->debug('Destination ID is null, now we can assume the destination is a (new) liability credit account.'); + return true; } app('log')->error('Destination ID is null, but destination name is also NULL.'); + return false; } /** * Source of a liability credit must be a liability or liability credit account. - * - * @param array $array - * - * @return bool */ protected function validateLCSource(array $array): bool { @@ -87,10 +83,12 @@ trait LiabilityValidation $result = $this->findExistingAccount(config('firefly.valid_liabilities'), $array); if (null === $result) { app('log')->error('Did not find a liability account, return false.'); + return false; } app('log')->debug(sprintf('Return true, found #%d ("%s")', $result->id, $result->name)); $this->setSource($result); + return true; } diff --git a/app/Validation/Account/OBValidation.php b/app/Validation/Account/OBValidation.php index f419d8058a..434db7595a 100644 --- a/app/Validation/Account/OBValidation.php +++ b/app/Validation/Account/OBValidation.php @@ -32,11 +32,6 @@ use FireflyIII\Models\AccountType; */ trait OBValidation { - /** - * @param array $array - * - * @return bool - */ protected function validateOBDestination(array $array): bool { $result = null; @@ -78,20 +73,11 @@ trait OBValidation return $result; } - /** - * @param array $accountTypes - * - * @return bool - */ abstract protected function canCreateTypes(array $accountTypes): bool; /** * Source of an opening balance can either be an asset account * or an "initial balance account". The latter can be created. - * - * @param array $array - * - * @return bool */ protected function validateOBSource(array $array): bool { @@ -138,6 +124,7 @@ trait OBValidation // set the source to be a (dummy) initial balance account. $account = new Account(); + /** @var AccountType $accountType */ $accountType = AccountType::whereType(AccountType::INITIAL_BALANCE)->first(); $account->accountType = $accountType; diff --git a/app/Validation/Account/ReconciliationValidation.php b/app/Validation/Account/ReconciliationValidation.php index f55f725eef..ee8465b0a6 100644 --- a/app/Validation/Account/ReconciliationValidation.php +++ b/app/Validation/Account/ReconciliationValidation.php @@ -34,11 +34,6 @@ trait ReconciliationValidation public ?Account $destination; public ?Account $source; - /** - * @param array $array - * - * @return bool - */ protected function validateReconciliationDestination(array $array): bool { $accountId = array_key_exists('id', $array) ? $array['id'] : null; @@ -71,10 +66,6 @@ trait ReconciliationValidation /** * Basically the same check - * - * @param array $array - * - * @return bool */ protected function validateReconciliationSource(array $array): bool { @@ -86,6 +77,7 @@ trait ReconciliationValidation if (null === $accountId && null === $accountName) { app('log')->debug('The source is valid because ID and name are NULL.'); $this->setSource(new Account()); + return true; } diff --git a/app/Validation/Account/TransferValidation.php b/app/Validation/Account/TransferValidation.php index 5c8146d899..fa4582b09a 100644 --- a/app/Validation/Account/TransferValidation.php +++ b/app/Validation/Account/TransferValidation.php @@ -30,11 +30,6 @@ use FireflyIII\Models\Account; */ trait TransferValidation { - /** - * @param array $array - * - * @return bool - */ protected function validateTransferDestination(array $array): bool { $accountId = array_key_exists('id', $array) ? $array['id'] : null; @@ -72,26 +67,10 @@ trait TransferValidation return true; } - /** - * @param array $accountTypes - * - * @return bool - */ abstract protected function canCreateTypes(array $accountTypes): bool; - /** - * @param array $validTypes - * @param array $data - * - * @return Account|null - */ abstract protected function findExistingAccount(array $validTypes, array $data): ?Account; - /** - * @param array $array - * - * @return bool - */ protected function validateTransferSource(array $array): bool { $accountId = array_key_exists('id', $array) ? $array['id'] : null; diff --git a/app/Validation/Account/WithdrawalValidation.php b/app/Validation/Account/WithdrawalValidation.php index 2683fa14e8..1ff0cb4ec7 100644 --- a/app/Validation/Account/WithdrawalValidation.php +++ b/app/Validation/Account/WithdrawalValidation.php @@ -31,11 +31,6 @@ use FireflyIII\Models\AccountType; */ trait WithdrawalValidation { - /** - * @param array $array - * - * @return bool - */ protected function validateGenericSource(array $array): bool { $accountId = array_key_exists('id', $array) ? $array['id'] : null; @@ -67,26 +62,10 @@ trait WithdrawalValidation return true; } - /** - * @param array $accountTypes - * - * @return bool - */ abstract protected function canCreateTypes(array $accountTypes): bool; - /** - * @param array $validTypes - * @param array $data - * - * @return Account|null - */ abstract protected function findExistingAccount(array $validTypes, array $data): ?Account; - /** - * @param array $array - * - * @return bool - */ protected function validateWithdrawalDestination(array $array): bool { $accountId = array_key_exists('id', $array) ? $array['id'] : null; @@ -112,6 +91,7 @@ trait WithdrawalValidation $type = $found->accountType->type; if (in_array($type, $validTypes, true)) { $this->setDestination($found); + return true; } // todo explain error in log message. @@ -128,6 +108,7 @@ trait WithdrawalValidation $existing = $this->findExistingAccount($validTypes, ['iban' => $accountIban], true); if (null !== $existing) { $this->destError = (string)trans('validation.withdrawal_dest_iban_exists'); + return false; } } @@ -136,11 +117,6 @@ trait WithdrawalValidation return true === $this->canCreateTypes($validTypes); } - /** - * @param array $array - * - * @return bool - */ protected function validateWithdrawalSource(array $array): bool { $accountId = array_key_exists('id', $array) ? $array['id'] : null; diff --git a/app/Validation/AccountValidator.php b/app/Validation/AccountValidator.php index d8fc17fb07..d2f4f61767 100644 --- a/app/Validation/AccountValidator.php +++ b/app/Validation/AccountValidator.php @@ -75,17 +75,11 @@ class AccountValidator $this->userGroupAccountRepository = app(UserGroupAccountRepositoryInterface::class); } - /** - * @return Account|null - */ public function getSource(): ?Account { return $this->source; } - /** - * @param Account|null $account - */ public function setSource(?Account $account): void { if (null === $account) { @@ -97,9 +91,6 @@ class AccountValidator $this->source = $account; } - /** - * @param Account|null $account - */ public function setDestination(?Account $account): void { if (null === $account) { @@ -111,40 +102,24 @@ class AccountValidator $this->destination = $account; } - /** - * @param string $transactionType - */ public function setTransactionType(string $transactionType): void { app('log')->debug(sprintf('Transaction type for validator is now "%s".', ucfirst($transactionType))); $this->transactionType = ucfirst($transactionType); } - /** - * @param User $user - */ public function setUser(User $user): void { $this->accountRepository->setUser($user); $this->useUserGroupRepository = false; } - /** - * @param UserGroup $userGroup - * - * @return void - */ public function setUserGroup(UserGroup $userGroup): void { $this->userGroupAccountRepository->setUserGroup($userGroup); $this->useUserGroupRepository = true; } - /** - * @param array $array - * - * @return bool - */ public function validateDestination(array $array): bool { app('log')->debug('Now in AccountValidator::validateDestination()', $array); @@ -154,83 +129,100 @@ class AccountValidator return false; } + switch ($this->transactionType) { default: $this->destError = sprintf('AccountValidator::validateDestination cannot handle "%s", so it will always return false.', $this->transactionType); app('log')->error(sprintf('AccountValidator::validateDestination cannot handle "%s", so it will always return false.', $this->transactionType)); $result = false; + break; case TransactionType::WITHDRAWAL: $result = $this->validateWithdrawalDestination($array); + break; + case TransactionType::DEPOSIT: $result = $this->validateDepositDestination($array); + break; + case TransactionType::TRANSFER: $result = $this->validateTransferDestination($array); + break; + case TransactionType::OPENING_BALANCE: $result = $this->validateOBDestination($array); + break; + case TransactionType::LIABILITY_CREDIT: $result = $this->validateLCDestination($array); + break; + case TransactionType::RECONCILIATION: $result = $this->validateReconciliationDestination($array); + break; } return $result; } - /** - * @param array $array - * - * @return bool - */ public function validateSource(array $array): bool { app('log')->debug('Now in AccountValidator::validateSource()', $array); + switch ($this->transactionType) { default: app('log')->error(sprintf('AccountValidator::validateSource cannot handle "%s", so it will do a generic check.', $this->transactionType)); $result = $this->validateGenericSource($array); + break; + case TransactionType::WITHDRAWAL: $result = $this->validateWithdrawalSource($array); + break; + case TransactionType::DEPOSIT: $result = $this->validateDepositSource($array); + break; + case TransactionType::TRANSFER: $result = $this->validateTransferSource($array); + break; + case TransactionType::OPENING_BALANCE: $result = $this->validateOBSource($array); + break; + case TransactionType::LIABILITY_CREDIT: $result = $this->validateLCSource($array); + break; case TransactionType::RECONCILIATION: app('log')->debug('Calling validateReconciliationSource'); $result = $this->validateReconciliationSource($array); + break; } return $result; } - /** - * @param array $accountTypes - * - * @return bool - */ protected function canCreateTypes(array $accountTypes): bool { app('log')->debug('Can we create any of these types?', $accountTypes); + /** @var string $accountType */ foreach ($accountTypes as $accountType) { if ($this->canCreateType($accountType)) { @@ -244,11 +236,6 @@ class AccountValidator return false; } - /** - * @param string $accountType - * - * @return bool - */ protected function canCreateType(string $accountType): bool { $canCreate = [AccountType::EXPENSE, AccountType::REVENUE, AccountType::INITIAL_BALANCE, AccountType::LIABILITY_CREDIT]; @@ -260,12 +247,11 @@ class AccountValidator } /** - * @param array $validTypes - * @param array $data - * @param bool $inverse + * It's a long and fairly complex method, but I don't mind. * - * @return Account|null + * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.BooleanArgumentFlag) + * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function findExistingAccount(array $validTypes, array $data, bool $inverse = false): ?Account { @@ -284,6 +270,7 @@ class AccountValidator $check = $inverse ? !$check : $check; // reverse the validation check if necessary. if ((null !== $first) && $check) { app('log')->debug(sprintf('ID: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban')); + return $first; } } @@ -296,6 +283,7 @@ class AccountValidator $check = $inverse ? !$check : $check; // reverse the validation check if necessary. if ((null !== $first) && $check) { app('log')->debug(sprintf('Iban: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban')); + return $first; } } @@ -308,6 +296,7 @@ class AccountValidator $check = $inverse ? !$check : $check; // reverse the validation check if necessary. if ((null !== $first) && $check) { app('log')->debug(sprintf('Number: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban')); + return $first; } } @@ -317,6 +306,7 @@ class AccountValidator $first = $this->getRepository()->findByName($accountName, $validTypes); if (null !== $first) { app('log')->debug(sprintf('Name: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban')); + return $first; } } @@ -325,10 +315,7 @@ class AccountValidator return null; } - /** - * @return AccountRepositoryInterface|UserGroupAccountRepositoryInterface - */ - private function getRepository(): AccountRepositoryInterface | UserGroupAccountRepositoryInterface + private function getRepository(): AccountRepositoryInterface|UserGroupAccountRepositoryInterface { if ($this->useUserGroupRepository) { return $this->userGroupAccountRepository; diff --git a/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php b/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php index 4e1637fca7..53db5422a9 100644 --- a/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php +++ b/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php @@ -26,18 +26,9 @@ namespace FireflyIII\Validation\Api\Data\Bulk; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use Illuminate\Validation\Validator; -use JsonException; -/** - * - */ trait ValidatesBulkTransactionQuery { - /** - * @param Validator $validator - * - * @throws JsonException - */ protected function validateTransactionQuery(Validator $validator): void { $data = $validator->getData(); @@ -73,8 +64,8 @@ trait ValidatesBulkTransactionQuery $sourceCurrency = $repository->getAccountCurrency($source); $destCurrency = $repository->getAccountCurrency($dest); if ( - $sourceCurrency !== null - && $destCurrency !== null + null !== $sourceCurrency + && null !== $destCurrency && $sourceCurrency->id !== $destCurrency->id ) { $validator->errors()->add('query', (string)trans('validation.invalid_query_currency')); diff --git a/app/Validation/AutoBudget/ValidatesAutoBudgetRequest.php b/app/Validation/AutoBudget/ValidatesAutoBudgetRequest.php index c7abe0fdbc..146f25cf1b 100644 --- a/app/Validation/AutoBudget/ValidatesAutoBudgetRequest.php +++ b/app/Validation/AutoBudget/ValidatesAutoBudgetRequest.php @@ -31,7 +31,7 @@ use Illuminate\Validation\Validator; trait ValidatesAutoBudgetRequest { /** - * @param Validator $validator + * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function validateAutoBudgetAmount(Validator $validator): void { @@ -39,33 +39,34 @@ trait ValidatesAutoBudgetRequest $type = $data['auto_budget_type'] ?? ''; $amount = array_key_exists('auto_budget_amount', $data) ? $data['auto_budget_amount'] : null; $period = array_key_exists('auto_budget_period', $data) ? $data['auto_budget_period'] : null; - $currencyId = array_key_exists('auto_budget_currency_id', $data) ? (int)$data['auto_budget_currency_id'] : null; + $currencyId = array_key_exists('auto_budget_currency_id', $data) ? (int) $data['auto_budget_currency_id'] : null; $currencyCode = array_key_exists('auto_budget_currency_code', $data) ? $data['auto_budget_currency_code'] : null; if (is_numeric($type)) { - $type = (int)$type; + $type = (int) $type; } if ('' === $type || 0 === $type) { return; } // basic float check: if (!is_numeric($amount)) { - $validator->errors()->add('auto_budget_amount', (string)trans('validation.amount_required_for_auto_budget')); + $validator->errors()->add('auto_budget_amount', (string) trans('validation.amount_required_for_auto_budget')); return; } - if (1 !== bccomp((string)$amount, '0')) { - $validator->errors()->add('auto_budget_amount', (string)trans('validation.auto_budget_amount_positive')); + if (1 !== bccomp((string) $amount, '0')) { + $validator->errors()->add('auto_budget_amount', (string) trans('validation.auto_budget_amount_positive')); } if ('' === $period) { - $validator->errors()->add('auto_budget_period', (string)trans('validation.auto_budget_period_mandatory')); + $validator->errors()->add('auto_budget_period', (string) trans('validation.auto_budget_period_mandatory')); } if (null !== $currencyId && null !== $currencyCode && '' === $currencyCode && 0 === $currencyId) { - $validator->errors()->add('auto_budget_amount', (string)trans('validation.require_currency_info')); + $validator->errors()->add('auto_budget_amount', (string) trans('validation.require_currency_info')); } // too big amount - if ((int)$amount > 268435456) { - $validator->errors()->add('auto_budget_amount', (string)trans('validation.amount_required_for_auto_budget')); + if ((int) $amount > 268435456) { + $validator->errors()->add('auto_budget_amount', (string) trans('validation.amount_required_for_auto_budget')); + return; } } diff --git a/app/Validation/CurrencyValidation.php b/app/Validation/CurrencyValidation.php index 9571b1027a..5c02abbc64 100644 --- a/app/Validation/CurrencyValidation.php +++ b/app/Validation/CurrencyValidation.php @@ -37,8 +37,6 @@ trait CurrencyValidation /** * If the transactions contain foreign amounts, there must also be foreign currency information. - * - * @param Validator $validator */ protected function validateForeignCurrencyInformation(Validator $validator): void { @@ -62,7 +60,7 @@ trait CurrencyValidation && 0 !== bccomp('0', $transaction['foreign_amount']) ) { $validator->errors()->add( - 'transactions.' . $index . '.foreign_amount', + 'transactions.'.$index.'.foreign_amount', (string)trans('validation.require_currency_info') ); } @@ -73,17 +71,12 @@ trait CurrencyValidation $transaction )) { $validator->errors()->add( - 'transactions.' . $index . '.foreign_amount', + 'transactions.'.$index.'.foreign_amount', (string)trans('validation.require_currency_amount') ); } } } - /** - * @param Validator $validator - * - * @return array - */ abstract protected function getTransactionsArray(Validator $validator): array; } diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 396a1d32ce..4362849730 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -23,8 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Validation; -use Config; -use DB; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\AccountMeta; @@ -37,14 +35,11 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Services\Password\Verifier; use FireflyIII\Support\ParseDateString; -use FireflyIII\TransactionRules\Triggers\TriggerInterface; use FireflyIII\User; -use Google2FA; use Illuminate\Validation\Validator; use PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException; use PragmaRX\Google2FA\Exceptions\InvalidCharactersException; use PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException; -use ValueError; /** * Class FireflyValidator. @@ -56,10 +51,10 @@ class FireflyValidator extends Validator * @param mixed $attribute * @param mixed $value * - * @return bool * @throws IncompatibleWithGoogleAuthenticatorException * @throws InvalidCharactersException * @throws SecretKeyTooShortException + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validate2faCode($attribute, $value): bool @@ -70,6 +65,7 @@ class FireflyValidator extends Validator $user = auth()->user(); if (null === $user) { app('log')->error('No user during validate2faCode'); + return false; } $secretPreference = app('preferences')->get('temp-mfa-secret'); @@ -78,25 +74,24 @@ class FireflyValidator extends Validator $secret = ''; } - return (bool)Google2FA::verifyKey((string)$secret, $value); + return (bool) \Google2FA::verifyKey((string) $secret, $value); } /** * @param mixed $attribute * @param mixed $value * @param mixed $parameters - * @SuppressWarnings(PHPMD.UnusedFormalParameter) * - * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validateBelongsToUser($attribute, $value, $parameters): bool { $field = $parameters[1] ?? 'id'; - if (0 === (int)$value) { + if (0 === (int) $value) { return true; } - $count = DB::table($parameters[0])->where('user_id', auth()->user()->id)->where($field, $value)->count(); + $count = \DB::table($parameters[0])->where('user_id', auth()->user()->id)->where($field, $value)->count(); return 1 === $count; } @@ -104,18 +99,14 @@ class FireflyValidator extends Validator /** * @param mixed $attribute * @param mixed $value - * @SuppressWarnings(PHPMD.UnusedFormalParameter) * - * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validateBic($attribute, $value): bool { $regex = '/^[a-z]{6}[0-9a-z]{2}([0-9a-z]{3})?\z/i'; $result = preg_match($regex, $value); - if (false === $result) { - return false; - } - if (0 === $result) { + if (false === $result || 0 === $result) { return false; } @@ -123,11 +114,8 @@ class FireflyValidator extends Validator } /** - * @param mixed $attribute - * @param mixed $value * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * - * @return bool + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function validateIban(mixed $attribute, mixed $value): bool { @@ -190,49 +178,22 @@ class FireflyValidator extends Validator $value = strtoupper($value); // replace characters outside of ASCI range. - $value = (string)iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $value); + $value = (string) iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $value); $search = [' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; - $replace = [ - '', - '10', - '11', - '12', - '13', - '14', - '15', - '16', - '17', - '18', - '19', - '20', - '21', - '22', - '23', - '24', - '25', - '26', - '27', - '28', - '29', - '30', - '31', - '32', - '33', - '34', - '35', - ]; + $replace = ['', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35']; // take $first = substr($value, 0, 4); $last = substr($value, 4); - $iban = $last . $first; + $iban = $last.$first; $iban = trim(str_replace($search, $replace, $iban)); if ('' === $iban) { return false; } + try { $checksum = bcmod($iban, '97'); - } catch (ValueError $e) { // @phpstan-ignore-line + } catch (\ValueError $e) { // @phpstan-ignore-line $message = sprintf('Could not validate IBAN check value "%s" (IBAN "%s")', $iban, $value); app('log')->error($message); app('log')->error($e->getTraceAsString()); @@ -240,74 +201,64 @@ class FireflyValidator extends Validator return false; } - return 1 === (int)$checksum; + return 1 === (int) $checksum; } /** * @param mixed $attribute * @param mixed $value * @param mixed $parameters - * @SuppressWarnings(PHPMD.UnusedFormalParameter) * - * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validateLess($attribute, $value, $parameters): bool { /** @var mixed $compare */ $compare = $parameters[0] ?? '0'; - return bccomp((string)$value, (string)$compare) < 0; + return bccomp((string) $value, (string) $compare) < 0; } /** * @param mixed $attribute * @param mixed $value * @param mixed $parameters - * @SuppressWarnings(PHPMD.UnusedFormalParameter) * - * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validateMore($attribute, $value, $parameters): bool { /** @var mixed $compare */ $compare = $parameters[0] ?? '0'; - return bccomp((string)$value, (string)$compare) > 0; + return bccomp((string) $value, (string) $compare) > 0; } /** * @param mixed $attribute * @param mixed $value * @param mixed $parameters - * @SuppressWarnings(PHPMD.UnusedFormalParameter) * - * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validateMustExist($attribute, $value, $parameters): bool { $field = $parameters[1] ?? 'id'; - if (0 === (int)$value) { + if (0 === (int) $value) { return true; } - $count = DB::table($parameters[0])->where($field, $value)->count(); + $count = \DB::table($parameters[0])->where($field, $value)->count(); return 1 === $count; } - /** - * @param string $attribute - * - * @param string|null $value - * - * @return bool - */ public function validateRuleActionValue(string $attribute, string $value = null): bool { // first, get the index from this string: $value ??= ''; $parts = explode('.', $attribute); - $index = (int)($parts[1] ?? '0'); + $index = (int) ($parts[1] ?? '0'); // get the name of the trigger from the data array: $actionType = $this->data['actions'][$index]['type'] ?? 'invalid'; @@ -367,17 +318,12 @@ class FireflyValidator extends Validator /** * $attribute has the format triggers.%d.value. - * - * @param string $attribute - * @param string|null $value - * - * @return bool */ public function validateRuleTriggerValue(string $attribute, string $value = null): bool { // first, get the index from this string: $parts = explode('.', $attribute); - $index = (int)($parts[1] ?? '0'); + $index = (int) ($parts[1] ?? '0'); // get the name of the trigger from the data array: $triggerType = $this->data['triggers'][$index]['type'] ?? 'invalid'; @@ -422,7 +368,7 @@ class FireflyValidator extends Validator // check if it's an existing account. if (in_array($triggerType, ['destination_account_id', 'source_account_id'], true)) { - return is_numeric($value) && (int)$value > 0; + return is_numeric($value) && (int) $value > 0; } // check transaction type. @@ -436,6 +382,7 @@ class FireflyValidator extends Validator if (in_array($triggerType, ['date_is', 'created_on', 'updated_on', 'date_before', 'date_after'], true)) { /** @var ParseDateString $parser */ $parser = app(ParseDateString::class); + try { $parser->parseDate($value); } catch (FireflyException $e) { @@ -451,15 +398,14 @@ class FireflyValidator extends Validator /** * @param mixed $attribute * @param mixed $value - * @SuppressWarnings(PHPMD.UnusedFormalParameter) * - * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validateSecurePassword($attribute, $value): bool { $verify = false; if (array_key_exists('verify_password', $this->data)) { - $verify = 1 === (int)$this->data['verify_password']; + $verify = 1 === (int) $this->data['verify_password']; } if ($verify) { /** @var Verifier $service */ @@ -475,180 +421,70 @@ class FireflyValidator extends Validator * @param mixed $attribute * @param mixed $value * @param mixed $parameters - * @SuppressWarnings(PHPMD.UnusedFormalParameter) * - * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validateUniqueAccountForUser($attribute, $value, $parameters): bool { // because a user does not have to be logged in (tests and what-not). if (!auth()->check()) { app('log')->debug('validateUniqueAccountForUser::anon'); + return $this->validateAccountAnonymously(); } if (array_key_exists('objectType', $this->data)) { app('log')->debug('validateUniqueAccountForUser::typeString'); + return $this->validateByAccountTypeString($value, $parameters, $this->data['objectType']); } if (array_key_exists('type', $this->data)) { app('log')->debug('validateUniqueAccountForUser::typeString'); - return $this->validateByAccountTypeString($value, $parameters, (string)$this->data['type']); + + return $this->validateByAccountTypeString($value, $parameters, (string) $this->data['type']); } if (array_key_exists('account_type_id', $this->data)) { app('log')->debug('validateUniqueAccountForUser::typeId'); + return $this->validateByAccountTypeId($value, $parameters); } $parameterId = $parameters[0] ?? null; if (null !== $parameterId) { app('log')->debug('validateUniqueAccountForUser::paramId'); - return $this->validateByParameterId((int)$parameterId, $value); + + return $this->validateByParameterId((int) $parameterId, $value); } if (array_key_exists('id', $this->data)) { app('log')->debug('validateUniqueAccountForUser::accountId'); + return $this->validateByAccountId($value); } // without type, just try to validate the name. app('log')->debug('validateUniqueAccountForUser::accountName'); + return $this->validateByAccountName($value); } - /** - * @return bool - */ - private function validateAccountAnonymously(): bool - { - if (!array_key_exists('user_id', $this->data)) { - return false; - } - - /** @var User $user */ - $user = User::find($this->data['user_id']); - $type = AccountType::find($this->data['account_type_id'])->first(); - $value = $this->data['name']; - - /** @var Account|null $result */ - $result = $user->accounts()->where('account_type_id', $type->id)->where('name', $value)->first(); - - return null === $result; - } - - /** - * @param string $value - * @param array $parameters - * @param string $type - * - * @return bool - */ - private function validateByAccountTypeString(string $value, array $parameters, string $type): bool - { - /** @var array|null $search */ - $search = Config::get('firefly.accountTypeByIdentifier.' . $type); - - if (null === $search) { - return false; - } - - $accountTypes = AccountType::whereIn('type', $search)->get(); - $ignore = (int)($parameters[0] ?? 0.0); - $accountTypeIds = $accountTypes->pluck('id')->toArray(); - /** @var Account|null $result */ - $result = auth()->user()->accounts()->whereIn('account_type_id', $accountTypeIds)->where('id', '!=', $ignore) - ->where('name', $value) - ->first(); - return null === $result; - } - - /** - * @param mixed $value - * @param mixed $parameters - * - * @return bool - */ - private function validateByAccountTypeId($value, $parameters): bool - { - $type = AccountType::find($this->data['account_type_id'])->first(); - $ignore = (int)($parameters[0] ?? 0.0); - - /** @var Account|null $result */ - $result = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore) - ->where('name', $value) - ->first(); - - return null === $result; - } - - /** - * @param int $accountId - * @param mixed $value - * - * @return bool - */ - private function validateByParameterId(int $accountId, $value): bool - { - /** @var Account $existingAccount */ - $existingAccount = Account::find($accountId); - - $type = $existingAccount->accountType; - $ignore = $existingAccount->id; - - $entry = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore) - ->where('name', $value) - ->first(); - - return null === $entry; - } - - /** - * @param mixed $value - * - * @return bool - */ - private function validateByAccountId($value): bool - { - /** @var Account $existingAccount */ - $existingAccount = Account::find($this->data['id']); - - $type = $existingAccount->accountType; - $ignore = $existingAccount->id; - - $entry = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore) - ->where('name', $value) - ->first(); - - return null === $entry; - } - - /** - * @param string $value - * - * @return bool - */ - private function validateByAccountName(string $value): bool - { - return auth()->user()->accounts()->where('name', $value)->count() === 0; - } - /** * @param mixed $attribute * @param mixed $value * @param mixed $parameters - * @SuppressWarnings(PHPMD.UnusedFormalParameter) * - * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validateUniqueAccountNumberForUser($attribute, $value, $parameters): bool { - $accountId = (int)($this->data['id'] ?? 0.0); + $accountId = (int) ($this->data['id'] ?? 0.0); if (0 === $accountId) { - $accountId = (int)($parameters[0] ?? 0.0); + $accountId = (int) ($parameters[0] ?? 0.0); } $query = AccountMeta::leftJoin('accounts', 'accounts.id', '=', 'account_meta.account_id') - ->whereNull('accounts.deleted_at') - ->where('accounts.user_id', auth()->user()->id) - ->where('account_meta.name', 'account_number') - ->where('account_meta.data', json_encode($value)); + ->whereNull('accounts.deleted_at') + ->where('accounts.user_id', auth()->user()->id) + ->where('account_meta.name', 'account_number') + ->where('account_meta.data', json_encode($value)) + ; if ($accountId > 0) { // exclude current account from check. @@ -666,81 +502,63 @@ class FireflyValidator extends Validator $type = $this->data['objectType'] ?? 'unknown'; if ('expense' !== $type && 'revenue' !== $type) { app('log')->warning(sprintf('Account number "%s" is not unique and account type "%s" cannot share its account number.', $value, $type)); + return false; } app('log')->debug(sprintf('Account number "%s" is not unique but account type "%s" may share its account number.', $value, $type)); + // one other account with this account number. /** @var AccountMeta $entry */ foreach ($set as $entry) { $otherAccount = $entry->account; - $otherType = (string)config(sprintf('firefly.shortNamesByFullName.%s', $otherAccount->accountType->type)); + $otherType = (string) config(sprintf('firefly.shortNamesByFullName.%s', $otherAccount->accountType->type)); if (('expense' === $otherType || 'revenue' === $otherType) && $otherType !== $type) { app('log')->debug(sprintf('The other account with this account number is a "%s" so return true.', $otherType)); + return true; } app('log')->debug(sprintf('The other account with this account number is a "%s" so return false.', $otherType)); } + return false; } /** - * @param string|null $attribute - * @param string|null $value * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * - * @return bool */ - public function validateUniqueCurrencyCode(string | null $attribute, string | null $value): bool + public function validateUniqueCurrencyCode(null|string $attribute, null|string $value): bool { - return $this->validateUniqueCurrency('code', (string)$attribute, (string)$value); + return $this->validateUniqueCurrency('code', (string) $attribute, (string) $value); } /** - * @param string $field - * @param string $attribute - * @param string $value * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * - * @return bool */ public function validateUniqueCurrency(string $field, string $attribute, string $value): bool { - return 0 === DB::table('transaction_currencies')->where($field, $value)->whereNull('deleted_at')->count(); + return 0 === \DB::table('transaction_currencies')->where($field, $value)->whereNull('deleted_at')->count(); } - /** - * @param string|null $attribute - * @param string|null $value - * - * @return bool - */ - public function validateUniqueCurrencyName(string | null $attribute, string | null $value): bool + public function validateUniqueCurrencyName(null|string $attribute, null|string $value): bool { - return $this->validateUniqueCurrency('name', (string)$attribute, (string)$value); + return $this->validateUniqueCurrency('name', (string) $attribute, (string) $value); } - /** - * @param string|null $attribute - * @param string|null $value - * - * @return bool - */ - public function validateUniqueCurrencySymbol(string | null $attribute, string | null $value): bool + public function validateUniqueCurrencySymbol(null|string $attribute, null|string $value): bool { - return $this->validateUniqueCurrency('symbol', (string)$attribute, (string)$value); + return $this->validateUniqueCurrency('symbol', (string) $attribute, (string) $value); } /** * @param mixed $value * @param mixed $parameters * @param mixed $something - * @SuppressWarnings(PHPMD.UnusedFormalParameter) * - * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validateUniqueExistingWebhook($value, $parameters, $something): bool { - $existingId = (int)($something[0] ?? 0); + $existingId = (int) ($something[0] ?? 0); $trigger = 0; $response = 0; $delivery = 0; @@ -750,7 +568,7 @@ class FireflyValidator extends Validator if (auth()->check()) { // get existing webhook value: if (0 !== $existingId) { - /** @var Webhook|null $webhook */ + /** @var null|Webhook $webhook */ $webhook = auth()->user()->webhooks()->find($existingId); if (null === $webhook) { return false; @@ -769,18 +587,18 @@ class FireflyValidator extends Validator $userId = auth()->user()->id; return 0 === Webhook::whereUserId($userId) - ->where('trigger', $trigger) - ->where('response', $response) - ->where('delivery', $delivery) - ->where('id', '!=', $existingId) - ->where('url', $url)->count(); + ->where('trigger', $trigger) + ->where('response', $response) + ->where('delivery', $delivery) + ->where('id', '!=', $existingId) + ->where('url', $url)->count() + ; } return false; } /** - * * Validate an object and its uniqueness. Checks for encryption / encrypted values as well. * * parameter 0: the table @@ -790,31 +608,32 @@ class FireflyValidator extends Validator * @param mixed $attribute * @param mixed $value * @param mixed $parameters - * @SuppressWarnings(PHPMD.UnusedFormalParameter) * - * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validateUniqueObjectForUser($attribute, $value, $parameters): bool { [$table, $field] = $parameters; - $exclude = (int)($parameters[2] ?? 0.0); + $exclude = (int) ($parameters[2] ?? 0.0); /* * If other data (in $this->getData()) contains * ID field, set that field to be the $exclude. */ $data = $this->getData(); - if (!array_key_exists(2, $parameters) && array_key_exists('id', $data) && (int)$data['id'] > 0) { - $exclude = (int)$data['id']; + if (!array_key_exists(2, $parameters) && array_key_exists('id', $data) && (int) $data['id'] > 0) { + $exclude = (int) $data['id']; } // get entries from table - $result = DB::table($table)->where('user_id', auth()->user()->id)->whereNull('deleted_at') - ->where('id', '!=', $exclude) - ->where($field, $value) - ->first([$field]); + $result = \DB::table($table)->where('user_id', auth()->user()->id)->whereNull('deleted_at') + ->where('id', '!=', $exclude) + ->where($field, $value) + ->first([$field]) + ; if (null === $result) { return true; // not found, so true. } + // found, so not unique. return false; } @@ -823,19 +642,19 @@ class FireflyValidator extends Validator * @param mixed $attribute * @param mixed $value * @param mixed $parameters - * @SuppressWarnings(PHPMD.UnusedFormalParameter) * - * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validateUniqueObjectGroup($attribute, $value, $parameters): bool { $exclude = $parameters[0] ?? null; - $query = DB::table('object_groups') - ->whereNull('object_groups.deleted_at') - ->where('object_groups.user_id', auth()->user()->id) - ->where('object_groups.title', $value); + $query = \DB::table('object_groups') + ->whereNull('object_groups.deleted_at') + ->where('object_groups.user_id', auth()->user()->id) + ->where('object_groups.title', $value) + ; if (null !== $exclude) { - $query->where('object_groups.id', '!=', (int)$exclude); + $query->where('object_groups.id', '!=', (int) $exclude); } return 0 === $query->count(); @@ -845,17 +664,17 @@ class FireflyValidator extends Validator * @param mixed $attribute * @param mixed $value * @param mixed $parameters - * @SuppressWarnings(PHPMD.UnusedFormalParameter) * - * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validateUniquePiggyBankForUser($attribute, $value, $parameters): bool { $exclude = $parameters[0] ?? null; - $query = DB::table('piggy_banks')->whereNull('piggy_banks.deleted_at') - ->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')->where('accounts.user_id', auth()->user()->id); + $query = \DB::table('piggy_banks')->whereNull('piggy_banks.deleted_at') + ->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')->where('accounts.user_id', auth()->user()->id) + ; if (null !== $exclude) { - $query->where('piggy_banks.id', '!=', (int)$exclude); + $query->where('piggy_banks.id', '!=', (int) $exclude); } $query->where('piggy_banks.name', $value); @@ -865,9 +684,8 @@ class FireflyValidator extends Validator /** * @param mixed $value * @param mixed $parameters - * @SuppressWarnings(PHPMD.UnusedFormalParameter) * - * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validateUniqueWebhook($value, $parameters): bool { @@ -884,12 +702,113 @@ class FireflyValidator extends Validator $userId = auth()->user()->id; return 0 === Webhook::whereUserId($userId) - ->where('trigger', $trigger) - ->where('response', $response) - ->where('delivery', $delivery) - ->where('url', $url)->count(); + ->where('trigger', $trigger) + ->where('response', $response) + ->where('delivery', $delivery) + ->where('url', $url)->count() + ; } return false; } + + private function validateAccountAnonymously(): bool + { + if (!array_key_exists('user_id', $this->data)) { + return false; + } + + /** @var User $user */ + $user = User::find($this->data['user_id']); + $type = AccountType::find($this->data['account_type_id'])->first(); + $value = $this->data['name']; + + /** @var null|Account $result */ + $result = $user->accounts()->where('account_type_id', $type->id)->where('name', $value)->first(); + + return null === $result; + } + + private function validateByAccountTypeString(string $value, array $parameters, string $type): bool + { + /** @var null|array $search */ + $search = \Config::get('firefly.accountTypeByIdentifier.'.$type); + + if (null === $search) { + return false; + } + + $accountTypes = AccountType::whereIn('type', $search)->get(); + $ignore = (int) ($parameters[0] ?? 0.0); + $accountTypeIds = $accountTypes->pluck('id')->toArray(); + + /** @var null|Account $result */ + $result = auth()->user()->accounts()->whereIn('account_type_id', $accountTypeIds)->where('id', '!=', $ignore) + ->where('name', $value) + ->first() + ; + + return null === $result; + } + + /** + * @param mixed $value + * @param mixed $parameters + */ + private function validateByAccountTypeId($value, $parameters): bool + { + $type = AccountType::find($this->data['account_type_id'])->first(); + $ignore = (int) ($parameters[0] ?? 0.0); + + /** @var null|Account $result */ + $result = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore) + ->where('name', $value) + ->first() + ; + + return null === $result; + } + + /** + * @param mixed $value + */ + private function validateByParameterId(int $accountId, $value): bool + { + /** @var Account $existingAccount */ + $existingAccount = Account::find($accountId); + + $type = $existingAccount->accountType; + $ignore = $existingAccount->id; + + $entry = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore) + ->where('name', $value) + ->first() + ; + + return null === $entry; + } + + /** + * @param mixed $value + */ + private function validateByAccountId($value): bool + { + /** @var Account $existingAccount */ + $existingAccount = Account::find($this->data['id']); + + $type = $existingAccount->accountType; + $ignore = $existingAccount->id; + + $entry = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore) + ->where('name', $value) + ->first() + ; + + return null === $entry; + } + + private function validateByAccountName(string $value): bool + { + return 0 === auth()->user()->accounts()->where('name', $value)->count(); + } } diff --git a/app/Validation/GroupValidation.php b/app/Validation/GroupValidation.php index 4b5a2b2138..289064a791 100644 --- a/app/Validation/GroupValidation.php +++ b/app/Validation/GroupValidation.php @@ -42,8 +42,6 @@ trait GroupValidation * TODO This should prevent errors down the road but I'm not yet sure what I'm validating here * TODO so I disabled this on 2023-10-22 to see if it causes any issues. * - * @param Validator $validator - * * @throws FireflyException */ protected function preventNoAccountInfo(Validator $validator): void @@ -59,7 +57,8 @@ trait GroupValidation 'source_number', 'destination_number', ]; - /** @var array|null $transaction */ + + /** @var null|array $transaction */ foreach ($transactions as $index => $transaction) { if (!is_array($transaction)) { throw new FireflyException('Invalid data submitted: transaction is not array.'); @@ -86,35 +85,26 @@ trait GroupValidation // only an issue if there is no transaction_journal_id } - /** - * @param Validator $validator - * - * @return array - */ abstract protected function getTransactionsArray(Validator $validator): array; - /** - * @param Validator $validator - * @param TransactionGroup $transactionGroup - * - * @return void - */ protected function preventUpdateReconciled(Validator $validator, TransactionGroup $transactionGroup): void { app('log')->debug(sprintf('Now in %s', __METHOD__)); $count = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id') - ->leftJoin('transaction_groups', 'transaction_groups.id', 'transaction_journals.transaction_group_id') - ->where('transaction_journals.transaction_group_id', $transactionGroup->id) - ->where('transactions.reconciled', 1)->where('transactions.amount', '<', 0)->count('transactions.id'); + ->leftJoin('transaction_groups', 'transaction_groups.id', 'transaction_journals.transaction_group_id') + ->where('transaction_journals.transaction_group_id', $transactionGroup->id) + ->where('transactions.reconciled', 1)->where('transactions.amount', '<', 0)->count('transactions.id') + ; if (0 === $count) { app('log')->debug(sprintf('Transaction is not reconciled, done with %s', __METHOD__)); + return; } $data = $validator->getData(); $forbidden = ['amount', 'foreign_amount', 'currency_code', 'currency_id', 'foreign_currency_code', 'foreign_currency_id', - 'source_id', 'source_name', 'source_number', 'source_iban', - 'destination_id', 'destination_name', 'destination_number', 'destination_iban', + 'source_id', 'source_name', 'source_number', 'source_iban', + 'destination_id', 'destination_name', 'destination_number', 'destination_iban', ]; foreach ($data['transactions'] as $index => $row) { foreach ($forbidden as $key) { @@ -133,8 +123,6 @@ trait GroupValidation /** * Adds an error to the "description" field when the user has submitted no descriptions and no * journal description. - * - * @param Validator $validator */ protected function validateDescriptions(Validator $validator): void { @@ -146,7 +134,7 @@ trait GroupValidation $validDescriptions = 0; foreach ($transactions as $transaction) { if ('' !== (string)($transaction['description'] ?? null)) { - $validDescriptions++; + ++$validDescriptions; } } @@ -159,9 +147,6 @@ trait GroupValidation } } - /** - * @param Validator $validator - */ protected function validateGroupDescription(Validator $validator): void { if ($validator->errors()->count() > 0) { @@ -181,9 +166,6 @@ trait GroupValidation * This method validates if the user has submitted transaction journal ID's for each array they submit, if they've * submitted more than 1 transaction journal. This check is necessary because Firefly III isn't able to distinguish * between journals without the ID. - * - * @param Validator $validator - * @param TransactionGroup $transactionGroup */ protected function validateJournalIds(Validator $validator, TransactionGroup $transactionGroup): void { @@ -196,6 +178,7 @@ trait GroupValidation return; } + // check each array: /** * @var int $index @@ -208,12 +191,6 @@ trait GroupValidation /** * Do the validation required by validateJournalIds. - * - * @param Validator $validator - * @param int $index - * @param array $transaction - * @param TransactionGroup $transactionGroup - * */ private function validateJournalId(Validator $validator, int $index, array $transaction, TransactionGroup $transactionGroup): void { diff --git a/app/Validation/RecurrenceValidation.php b/app/Validation/RecurrenceValidation.php index f318050e5a..c353dc55de 100644 --- a/app/Validation/RecurrenceValidation.php +++ b/app/Validation/RecurrenceValidation.php @@ -27,13 +27,11 @@ use Carbon\Carbon; use FireflyIII\Models\Recurrence; use FireflyIII\Models\RecurrenceTransaction; use Illuminate\Validation\Validator; -use InvalidArgumentException; /** * Trait RecurrenceValidation * * Contains advanced validation rules used in validation of new and existing recurrences. - * */ trait RecurrenceValidation { @@ -41,8 +39,6 @@ trait RecurrenceValidation * Validate account information input for recurrences which are being updated. * * TODO Must always trigger when the type of the recurrence changes. - * - * @param Validator $validator */ public function valUpdateAccountInfo(Validator $validator): void { @@ -53,12 +49,14 @@ trait RecurrenceValidation // grab model from parameter and try to set the transaction type from it if ('invalid' === $transactionType) { app('log')->debug('Type is invalid but we will search for it.'); - /** @var Recurrence|null $recurrence */ + + /** @var null|Recurrence $recurrence */ $recurrence = $this->route()?->parameter('recurrence'); if (null !== $recurrence) { app('log')->debug('There is a recurrence in the route.'); + // ok so we have a recurrence should be able to extract type somehow. - /** @var RecurrenceTransaction|null $first */ + /** @var null|RecurrenceTransaction $first */ $first = $recurrence->recurrenceTransactions()->first(); if (null !== $first) { $transactionType = null !== $first->transactionType ? $first->transactionType->type : 'withdrawal'; @@ -90,7 +88,7 @@ trait RecurrenceValidation continue; } // validate source account. - $sourceId = array_key_exists('source_id', $transaction) ? (int)$transaction['source_id'] : null; + $sourceId = array_key_exists('source_id', $transaction) ? (int) $transaction['source_id'] : null; $sourceName = $transaction['source_name'] ?? null; $validSource = $accountValidator->validateSource(['id' => $sourceId, 'name' => $sourceName]); @@ -102,9 +100,9 @@ trait RecurrenceValidation return; } // validate destination account - $destinationId = array_key_exists('destination_id', $transaction) ? (int)$transaction['destination_id'] : null; + $destinationId = array_key_exists('destination_id', $transaction) ? (int) $transaction['destination_id'] : null; $destinationName = $transaction['destination_name'] ?? null; - $validDestination = $accountValidator->validateDestination(['id' => $destinationId, 'name' => $destinationName,]); + $validDestination = $accountValidator->validateDestination(['id' => $destinationId, 'name' => $destinationName]); // do something with result: if (false === $validDestination) { $validator->errors()->add(sprintf('transactions.%d.destination_id', $index), $accountValidator->destError); @@ -117,8 +115,6 @@ trait RecurrenceValidation /** * Adds an error to the validator when there are no repetitions in the array of data. - * - * @param Validator $validator */ public function validateOneRepetition(Validator $validator): void { @@ -126,14 +122,12 @@ trait RecurrenceValidation $repetitions = $data['repetitions'] ?? []; // need at least one transaction if (!is_countable($repetitions) || 0 === count($repetitions)) { - $validator->errors()->add('repetitions', (string)trans('validation.at_least_one_repetition')); + $validator->errors()->add('repetitions', (string) trans('validation.at_least_one_repetition')); } } /** * Adds an error to the validator when there are no repetitions in the array of data. - * - * @param Validator $validator */ public function validateOneRepetitionUpdate(Validator $validator): void { @@ -144,15 +138,13 @@ trait RecurrenceValidation } // need at least one transaction if (0 === count($repetitions)) { - $validator->errors()->add('repetitions', (string)trans('validation.at_least_one_repetition')); + $validator->errors()->add('repetitions', (string) trans('validation.at_least_one_repetition')); } } /** * Validates that the recurrence has valid repetition information. It either doesn't stop, * or stops after X times or at X date. Not both of them., - * - * @param Validator $validator */ public function validateRecurrenceRepetition(Validator $validator): void { @@ -161,20 +153,15 @@ trait RecurrenceValidation $repeatUntil = $data['repeat_until'] ?? null; if (null !== $repetitions && null !== $repeatUntil) { // expect a date OR count: - $validator->errors()->add('repeat_until', (string)trans('validation.require_repeat_until')); - $validator->errors()->add('nr_of_repetitions', (string)trans('validation.require_repeat_until')); + $validator->errors()->add('repeat_until', (string) trans('validation.require_repeat_until')); + $validator->errors()->add('nr_of_repetitions', (string) trans('validation.require_repeat_until')); } } - /** - * @param Validator $validator - * - * @return void - */ - public function validateRecurringConfig(Validator $validator) + public function validateRecurringConfig(Validator $validator): void { $data = $validator->getData(); - $reps = array_key_exists('nr_of_repetitions', $data) ? (int)$data['nr_of_repetitions'] : null; + $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) { @@ -189,18 +176,16 @@ trait RecurrenceValidation } } - /** - * @param Validator $validator - */ public function validateRepetitionMoment(Validator $validator): void { $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')); + $validator->errors()->add(sprintf('repetitions.%d.type', 0), (string) trans('validation.valid_recurrence_rep_type')); return; } + /** * @var int $index * @var array $repetition @@ -215,23 +200,33 @@ trait RecurrenceValidation switch ($repetition['type'] ?? 'empty') { default: - $validator->errors()->add(sprintf('repetitions.%d.type', $index), (string)trans('validation.valid_recurrence_rep_type')); + $validator->errors()->add(sprintf('repetitions.%d.type', $index), (string) trans('validation.valid_recurrence_rep_type')); return; + case 'daily': - $this->validateDaily($validator, $index, (string)$repetition['moment']); + $this->validateDaily($validator, $index, (string) $repetition['moment']); + break; + case 'monthly': - $this->validateMonthly($validator, $index, (int)$repetition['moment']); + $this->validateMonthly($validator, $index, (int) $repetition['moment']); + break; + case 'ndom': - $this->validateNdom($validator, $index, (string)$repetition['moment']); + $this->validateNdom($validator, $index, (string) $repetition['moment']); + break; + case 'weekly': - $this->validateWeekly($validator, $index, (int)$repetition['moment']); + $this->validateWeekly($validator, $index, (int) $repetition['moment']); + break; + case 'yearly': - $this->validateYearly($validator, $index, (string)$repetition['moment']); + $this->validateYearly($validator, $index, (string) $repetition['moment']); + break; } } @@ -239,96 +234,73 @@ trait RecurrenceValidation /** * If the repetition type is daily, the moment should be empty. - * - * @param Validator $validator - * @param int $index - * @param string $moment */ protected function validateDaily(Validator $validator, int $index, string $moment): void { if ('' !== $moment) { - $validator->errors()->add(sprintf('repetitions.%d.moment', $index), (string)trans('validation.valid_recurrence_rep_moment')); + $validator->errors()->add(sprintf('repetitions.%d.moment', $index), (string) trans('validation.valid_recurrence_rep_moment')); } } /** * If the repetition type is monthly, the moment should be a day between 1-31 (inclusive). - * - * @param Validator $validator - * @param int $index - * @param int $dayOfMonth */ protected function validateMonthly(Validator $validator, int $index, int $dayOfMonth): void { if ($dayOfMonth < 1 || $dayOfMonth > 31) { - $validator->errors()->add(sprintf('repetitions.%d.moment', $index), (string)trans('validation.valid_recurrence_rep_moment')); + $validator->errors()->add(sprintf('repetitions.%d.moment', $index), (string) trans('validation.valid_recurrence_rep_moment')); } } /** * If the repetition type is "ndom", the first part must be between 1-5 (inclusive), for the week in the month, * and the second one must be between 1-7 (inclusive) for the day of the week. - * - * @param Validator $validator - * @param int $index - * @param string $moment */ protected function validateNdom(Validator $validator, int $index, string $moment): void { $parameters = explode(',', $moment); if (2 !== count($parameters)) { - $validator->errors()->add(sprintf('repetitions.%d.moment', $index), (string)trans('validation.valid_recurrence_rep_moment')); + $validator->errors()->add(sprintf('repetitions.%d.moment', $index), (string) trans('validation.valid_recurrence_rep_moment')); return; } - $nthDay = (int)($parameters[0] ?? 0.0); - $dayOfWeek = (int)($parameters[1] ?? 0.0); + $nthDay = (int) ($parameters[0] ?? 0.0); + $dayOfWeek = (int) ($parameters[1] ?? 0.0); if ($nthDay < 1 || $nthDay > 5) { - $validator->errors()->add(sprintf('repetitions.%d.moment', $index), (string)trans('validation.valid_recurrence_rep_moment')); + $validator->errors()->add(sprintf('repetitions.%d.moment', $index), (string) trans('validation.valid_recurrence_rep_moment')); return; } if ($dayOfWeek < 1 || $dayOfWeek > 7) { - $validator->errors()->add(sprintf('repetitions.%d.moment', $index), (string)trans('validation.valid_recurrence_rep_moment')); + $validator->errors()->add(sprintf('repetitions.%d.moment', $index), (string) trans('validation.valid_recurrence_rep_moment')); } } /** * If the repetition type is weekly, the moment should be a day between 1-7 (inclusive). - * - * @param Validator $validator - * @param int $index - * @param int $dayOfWeek */ protected function validateWeekly(Validator $validator, int $index, int $dayOfWeek): void { if ($dayOfWeek < 1 || $dayOfWeek > 7) { - $validator->errors()->add(sprintf('repetitions.%d.moment', $index), (string)trans('validation.valid_recurrence_rep_moment')); + $validator->errors()->add(sprintf('repetitions.%d.moment', $index), (string) trans('validation.valid_recurrence_rep_moment')); } } /** * If the repetition type is yearly, the moment should be a valid date. - * - * @param Validator $validator - * @param int $index - * @param string $moment */ protected function validateYearly(Validator $validator, int $index, string $moment): void { try { Carbon::createFromFormat('Y-m-d', $moment); - } catch (InvalidArgumentException $e) { // @phpstan-ignore-line + } catch (\InvalidArgumentException $e) { // @phpstan-ignore-line app('log')->debug(sprintf('Invalid argument for Carbon: %s', $e->getMessage())); - $validator->errors()->add(sprintf('repetitions.%d.moment', $index), (string)trans('validation.valid_recurrence_rep_moment')); + $validator->errors()->add(sprintf('repetitions.%d.moment', $index), (string) trans('validation.valid_recurrence_rep_moment')); } } /** - * @param Recurrence $recurrence - * @param Validator $validator - * - * @return void + * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function validateTransactionId(Recurrence $recurrence, Validator $validator): void { @@ -338,7 +310,8 @@ trait RecurrenceValidation if (0 === $submittedTrCount) { app('log')->warning('[b] User submitted no transactions.'); - $validator->errors()->add('transactions', (string)trans('validation.at_least_one_transaction')); + $validator->errors()->add('transactions', (string) trans('validation.at_least_one_transaction')); + return; } $originalTrCount = $recurrence->recurrenceTransactions()->count(); @@ -346,20 +319,23 @@ trait RecurrenceValidation $first = $transactions[0]; // can safely assume index 0. if (!array_key_exists('id', $first)) { app('log')->debug('Single count and no ID, done.'); + return; // home safe! } $id = $first['id']; - if ('' === (string)$id) { + if ('' === (string) $id) { app('log')->debug('Single count and empty ID, done.'); + return; // home safe! } - $integer = (int)$id; + $integer = (int) $id; $secondCount = $recurrence->recurrenceTransactions()->where('recurrences_transactions.id', $integer)->count(); app('log')->debug(sprintf('Result of ID count: %d', $secondCount)); if (0 === $secondCount) { - $validator->errors()->add('transactions.0.id', (string)trans('validation.id_does_not_match', ['id' => $integer])); + $validator->errors()->add('transactions.0.id', (string) trans('validation.id_does_not_match', ['id' => $integer])); } app('log')->debug('Single ID validation done.'); + return; } @@ -369,6 +345,7 @@ trait RecurrenceValidation app('log')->debug(sprintf('User submits %d transaction, recurrence has %d transactions. All entries must have ID.', $submittedTrCount, $originalTrCount)); $idsMandatory = true; } + /** * Loop all transactions submitted by the user. * If the user has submitted fewer transactions than the original recurrence has, all submitted entries must have an ID. @@ -386,25 +363,27 @@ trait RecurrenceValidation app('log')->debug(sprintf('Now at %d/%d', $index + 1, $submittedTrCount)); if (!is_array($transaction)) { app('log')->warning('Not an array. Give error.'); - $validator->errors()->add(sprintf('transactions.%d.id', $index), (string)trans('validation.at_least_one_transaction')); + $validator->errors()->add(sprintf('transactions.%d.id', $index), (string) trans('validation.at_least_one_transaction')); + return; } if (!array_key_exists('id', $transaction) && $idsMandatory) { app('log')->warning('ID is mandatory but array has no ID.'); - $validator->errors()->add(sprintf('transactions.%d.id', $index), (string)trans('validation.need_id_to_match')); + $validator->errors()->add(sprintf('transactions.%d.id', $index), (string) trans('validation.need_id_to_match')); + return; } if (array_key_exists('id', $transaction)) { // don't matter if $idsMandatory app('log')->debug('Array has ID.'); - $idCount = $recurrence->recurrenceTransactions()->where('recurrences_transactions.id', (int)$transaction['id'])->count(); + $idCount = $recurrence->recurrenceTransactions()->where('recurrences_transactions.id', (int) $transaction['id'])->count(); if (0 === $idCount) { app('log')->debug('ID does not exist or no match. Count another unmatched ID.'); - $unmatchedIds++; + ++$unmatchedIds; } } if (!array_key_exists('id', $transaction) && !$idsMandatory) { app('log')->debug('Array has no ID but was not mandatory at this point.'); - $unmatchedIds++; + ++$unmatchedIds; } } // if too many don't match, but you haven't submitted more than already present: @@ -412,7 +391,8 @@ trait RecurrenceValidation app('log')->debug(sprintf('Submitted: %d. Original: %d. User can submit %d unmatched transactions.', $submittedTrCount, $originalTrCount, $maxUnmatched)); if ($unmatchedIds > $maxUnmatched) { app('log')->warning(sprintf('Too many unmatched transactions (%d).', $unmatchedIds)); - $validator->errors()->add('transactions.0.id', (string)trans('validation.too_many_unmatched')); + $validator->errors()->add('transactions.0.id', (string) trans('validation.too_many_unmatched')); + return; } app('log')->debug('Done with ID validation.'); diff --git a/app/Validation/TransactionValidation.php b/app/Validation/TransactionValidation.php index 871b33816f..724f9dadd3 100644 --- a/app/Validation/TransactionValidation.php +++ b/app/Validation/TransactionValidation.php @@ -44,10 +44,6 @@ trait TransactionValidation * Validates the given account information. Switches on given transaction type. * * Inclusion of user and/or group is optional. - * - * @param Validator $validator - * @param User|null $user - * @param UserGroup|null $userGroup */ public function validateAccountInformation(Validator $validator, User $user = null, UserGroup $userGroup = null): void { @@ -60,8 +56,9 @@ trait TransactionValidation $transactionType = $data['type'] ?? 'invalid'; app('log')->debug(sprintf('Going to loop %d transaction(s)', count($transactions))); + /** - * @var int|null $index + * @var null|int $index * @var array $transaction */ foreach ($transactions as $index => $transaction) { @@ -75,10 +72,135 @@ trait TransactionValidation } /** - * @param Validator $validator + * Validates the given account information. Switches on given transaction type. * - * @return array + * @throws FireflyException */ + public function validateAccountInformationUpdate(Validator $validator, TransactionGroup $transactionGroup): void + { + app('log')->debug('Now in validateAccountInformationUpdate()'); + if ($validator->errors()->count() > 0) { + app('log')->debug('Validator already has errors, so return.'); + + return; + } + + $transactions = $this->getTransactionsArray($validator); + + /** + * @var null|int $index + * @var array $transaction + */ + foreach ($transactions as $index => $transaction) { + if (!is_int($index)) { + throw new FireflyException('Invalid data submitted: transaction is not array.'); + } + $this->validateSingleUpdate($validator, $index, $transaction, $transactionGroup); + } + } + + /** + * Adds an error to the validator when there are no transactions in the array of data. + */ + public function validateOneRecurrenceTransaction(Validator $validator): void + { + app('log')->debug('Now in validateOneRecurrenceTransaction()'); + $transactions = $this->getTransactionsArray($validator); + + // need at least one transaction + if (0 === count($transactions)) { + $validator->errors()->add('transactions', (string) trans('validation.at_least_one_transaction')); + } + } + + /** + * Adds an error to the validator when there are no transactions in the array of data. + */ + public function validateOneTransaction(Validator $validator): void + { + app('log')->debug('Now in validateOneTransaction'); + if ($validator->errors()->count() > 0) { + app('log')->debug('Validator already has errors, so return.'); + + return; + } + $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')); + app('log')->debug('Added error: at_least_one_transaction.'); + + return; + } + app('log')->debug('Added NO errors.'); + } + + public function validateTransactionArray(Validator $validator): void + { + if ($validator->errors()->count() > 0) { + return; + } + $transactions = $this->getTransactionsArray($validator); + foreach (array_keys($transactions) as $key) { + if (!is_int($key)) { + $validator->errors()->add('transactions.0.description', (string) trans('validation.at_least_one_transaction')); + app('log')->debug('Added error: at_least_one_transaction.'); + + return; + } + } + } + + /** + * All types of splits must be equal. + */ + public function validateTransactionTypes(Validator $validator): void + { + if ($validator->errors()->count() > 0) { + return; + } + app('log')->debug('Now in validateTransactionTypes()'); + $transactions = $this->getTransactionsArray($validator); + + $types = []; + foreach ($transactions as $transaction) { + $types[] = $transaction['type'] ?? 'invalid'; + } + $unique = array_unique($types); + if (count($unique) > 1) { + $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')); + } + } + + /** + * All types of splits must be equal. + */ + public function validateTransactionTypesForUpdate(Validator $validator): void + { + app('log')->debug('Now in validateTransactionTypesForUpdate()'); + $transactions = $this->getTransactionsArray($validator); + $types = []; + foreach ($transactions as $transaction) { + $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) { + app('log')->warning('Add error for mismatch transaction types.'); + $validator->errors()->add('transactions.0.type', (string) trans('validation.transaction_types_equal')); + + return; + } + app('log')->debug('No errors in validateTransactionTypesForUpdate()'); + } + protected function getTransactionsArray(Validator $validator): array { app('log')->debug('Now in getTransactionsArray'); @@ -96,14 +218,12 @@ trait TransactionValidation } /** - * @param Validator $validator - * @param int $index - * @param string $transactionType - * @param array $transaction + * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function validateSingleAccount(Validator $validator, int $index, string $transactionType, array $transaction): void { app('log')->debug(sprintf('Now in validateSingleAccount(%d)', $index)); + /** @var AccountValidator $accountValidator */ $accountValidator = app(AccountValidator::class); @@ -118,10 +238,10 @@ trait TransactionValidation $accountValidator->setTransactionType($transactionType); // validate source account. - $sourceId = array_key_exists('source_id', $transaction) ? (int)$transaction['source_id'] : null; - $sourceName = array_key_exists('source_name', $transaction) ? (string)$transaction['source_name'] : null; - $sourceIban = array_key_exists('source_iban', $transaction) ? (string)$transaction['source_iban'] : null; - $sourceNumber = array_key_exists('source_number', $transaction) ? (string)$transaction['source_number'] : null; + $sourceId = array_key_exists('source_id', $transaction) ? (int) $transaction['source_id'] : null; + $sourceName = array_key_exists('source_name', $transaction) ? (string) $transaction['source_name'] : null; + $sourceIban = array_key_exists('source_iban', $transaction) ? (string) $transaction['source_iban'] : null; + $sourceNumber = array_key_exists('source_number', $transaction) ? (string) $transaction['source_number'] : null; $source = [ 'id' => $sourceId, 'name' => $sourceName, @@ -138,10 +258,10 @@ trait TransactionValidation return; } // validate destination account - $destinationId = array_key_exists('destination_id', $transaction) ? (int)$transaction['destination_id'] : null; - $destinationName = array_key_exists('destination_name', $transaction) ? (string)$transaction['destination_name'] : null; - $destinationIban = array_key_exists('destination_iban', $transaction) ? (string)$transaction['destination_iban'] : null; - $destinationNumber = array_key_exists('destination_number', $transaction) ? (string)$transaction['destination_number'] : null; + $destinationId = array_key_exists('destination_id', $transaction) ? (int) $transaction['destination_id'] : null; + $destinationName = array_key_exists('destination_name', $transaction) ? (string) $transaction['destination_name'] : null; + $destinationIban = array_key_exists('destination_iban', $transaction) ? (string) $transaction['destination_iban'] : null; + $destinationNumber = array_key_exists('destination_number', $transaction) ? (string) $transaction['destination_number'] : null; $destination = [ 'id' => $destinationId, 'name' => $destinationName, @@ -162,20 +282,13 @@ trait TransactionValidation } /** - * @param Validator $validator - * @param string $transactionType - * @param int $index - * @param array $source - * @param array $destination - * - * @return void * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ protected function sanityCheckReconciliation(Validator $validator, string $transactionType, int $index, array $source, array $destination): void { app('log')->debug('Now in sanityCheckReconciliation'); - if (TransactionType::RECONCILIATION === ucfirst($transactionType) && - null === $source['id'] && null === $source['name'] && null === $destination['id'] && null === $destination['name'] + if (TransactionType::RECONCILIATION === ucfirst($transactionType) + && null === $source['id'] && null === $source['name'] && null === $destination['id'] && null === $destination['name'] ) { app('log')->debug('Both are NULL, error!'); $validator->errors()->add(sprintf('transactions.%d.source_id', $index), trans('validation.reconciliation_either_account')); @@ -184,9 +297,9 @@ trait TransactionValidation $validator->errors()->add(sprintf('transactions.%d.destination_name', $index), trans('validation.reconciliation_either_account')); } - if (TransactionType::RECONCILIATION === $transactionType && - (null !== $source['id'] || null !== $source['name']) && - (null !== $destination['id'] || null !== $destination['name'])) { + if (TransactionType::RECONCILIATION === $transactionType + && (null !== $source['id'] || null !== $source['name']) + && (null !== $destination['id'] || null !== $destination['name'])) { app('log')->debug('Both are not NULL, error!'); $validator->errors()->add(sprintf('transactions.%d.source_id', $index), trans('validation.reconciliation_either_account')); $validator->errors()->add(sprintf('transactions.%d.source_name', $index), trans('validation.reconciliation_either_account')); @@ -195,208 +308,6 @@ trait TransactionValidation } } - /** - * TODO describe this method. - * - * @param Validator $validator - * @param AccountValidator $accountValidator - * @param array $transaction - * @param string $transactionType - * @param int $index - * - * @return void - * @SuppressWarnings(PHPMD.ExcessiveParameterList) - */ - private function sanityCheckForeignCurrency( - Validator $validator, - AccountValidator $accountValidator, - array $transaction, - string $transactionType, - int $index - ): void - { - app('log')->debug('Now in sanityCheckForeignCurrency()'); - if (0 !== $validator->errors()->count()) { - app('log')->debug('Already have errors, return'); - return; - } - if (null === $accountValidator->source) { - app('log')->debug('No source, return'); - return; - } - if (null === $accountValidator->destination) { - app('log')->debug('No destination, return'); - return; - } - $source = $accountValidator->source; - $destination = $accountValidator->destination; - - app('log')->debug(sprintf('Source: #%d "%s (%s)"', $source->id, $source->name, $source->accountType->type)); - app('log')->debug(sprintf('Destination: #%d "%s" (%s)', $destination->id, $destination->name, $source->accountType->type)); - - if (!$this->isLiabilityOrAsset($source) || !$this->isLiabilityOrAsset($destination)) { - app('log')->debug('Any account must be liability or asset account to continue.'); - return; - } - - - /** @var AccountRepositoryInterface $accountRepository */ - $accountRepository = app(AccountRepositoryInterface::class); - $defaultCurrency = app('amount')->getDefaultCurrency(); - $sourceCurrency = $accountRepository->getAccountCurrency($source) ?? $defaultCurrency; - $destinationCurrency = $accountRepository->getAccountCurrency($destination) ?? $defaultCurrency; - // if both accounts have the same currency, continue. - if ($sourceCurrency->code === $destinationCurrency->code) { - app('log')->debug('Both accounts have the same currency, continue.'); - return; - } - app('log')->debug(sprintf('Source account expects %s', $sourceCurrency->code)); - app('log')->debug(sprintf('Destination account expects %s', $destinationCurrency->code)); - - app('log')->debug(sprintf('Amount is %s', $transaction['amount'])); - - if (TransactionType::DEPOSIT === ucfirst($transactionType)) { - app('log')->debug(sprintf('Processing as a "%s"', $transactionType)); - // use case: deposit from liability account to an asset account - // the foreign amount must be in the currency of the source - // the amount must be in the currency of the destination - - // no foreign currency information is present: - if (!$this->hasForeignCurrencyInfo($transaction)) { - $validator->errors()->add(sprintf('transactions.%d.foreign_amount', $index), (string)trans('validation.require_foreign_currency')); - return; - } - - // wrong currency information is present - $foreignCurrencyCode = $transaction['foreign_currency_code'] ?? false; - $foreignCurrencyId = (int)($transaction['foreign_currency_id'] ?? 0); - app('log')->debug(sprintf('Foreign currency code seems to be #%d "%s"', $foreignCurrencyId, $foreignCurrencyCode), $transaction); - if ($foreignCurrencyCode !== $sourceCurrency->code && $foreignCurrencyId !== $sourceCurrency->id) { - $validator->errors()->add(sprintf('transactions.%d.foreign_currency_code', $index), (string)trans('validation.require_foreign_src')); - return; - } - } - if (TransactionType::TRANSFER === ucfirst($transactionType) || TransactionType::WITHDRAWAL === ucfirst($transactionType)) { - app('log')->debug(sprintf('Processing as a "%s"', $transactionType)); - // use case: withdrawal from asset account to a liability account. - // the foreign amount must be in the currency of the destination - // the amount must be in the currency of the source - - // use case: transfer between accounts with different currencies. - // the foreign amount must be in the currency of the destination - // the amount must be in the currency of the source - - // no foreign currency information is present: - if (!$this->hasForeignCurrencyInfo($transaction)) { - $validator->errors()->add(sprintf('transactions.%d.foreign_amount', $index), (string)trans('validation.require_foreign_currency')); - return; - } - - // wrong currency information is present - $foreignCurrencyCode = $transaction['foreign_currency_code'] ?? false; - $foreignCurrencyId = (int)($transaction['foreign_currency_id'] ?? 0); - app('log')->debug(sprintf('Foreign currency code seems to be #%d "%s"', $foreignCurrencyId, $foreignCurrencyCode), $transaction); - if ($foreignCurrencyCode !== $destinationCurrency->code && $foreignCurrencyId !== $destinationCurrency->id) { - app('log')->debug(sprintf('No match on code, "%s" vs "%s"', $foreignCurrencyCode, $destinationCurrency->code)); - app('log')->debug(sprintf('No match on ID, #%d vs #%d', $foreignCurrencyId, $destinationCurrency->id)); - $validator->errors()->add(sprintf('transactions.%d.foreign_amount', $index), (string)trans('validation.require_foreign_dest')); - } - } - } - - /** - * @param Account $account - * - * @return bool - */ - private function isLiabilityOrAsset(Account $account): bool - { - return $this->isLiability($account) || $this->isAsset($account); - } - - /** - * @param Account $account - * - * @return bool - */ - private function isLiability(Account $account): bool - { - $type = $account->accountType->type; - if (in_array($type, config('firefly.valid_liabilities'), true)) { - return true; - } - return false; - } - - /** - * @param Account $account - * - * @return bool - */ - private function isAsset(Account $account): bool - { - $type = $account->accountType->type; - return $type === AccountType::ASSET; - } - - /** - * @param array $transaction - * - * @return bool - */ - private function hasForeignCurrencyInfo(array $transaction): bool - { - if (!array_key_exists('foreign_currency_code', $transaction) && !array_key_exists('foreign_currency_id', $transaction)) { - return false; - } - if (!array_key_exists('foreign_amount', $transaction)) { - return false; - } - if ('' === $transaction['foreign_amount']) { - return false; - } - if (bccomp('0', $transaction['foreign_amount']) === 0) { - return false; - } - return true; - } - - /** - * Validates the given account information. Switches on given transaction type. - * - * @param Validator $validator - * @param TransactionGroup $transactionGroup - * - * @throws FireflyException - */ - public function validateAccountInformationUpdate(Validator $validator, TransactionGroup $transactionGroup): void - { - app('log')->debug('Now in validateAccountInformationUpdate()'); - if ($validator->errors()->count() > 0) { - app('log')->debug('Validator already has errors, so return.'); - return; - } - - $transactions = $this->getTransactionsArray($validator); - - /** - * @var int|null $index - * @var array $transaction - */ - foreach ($transactions as $index => $transaction) { - if (!is_int($index)) { - throw new FireflyException('Invalid data submitted: transaction is not array.'); - } - $this->validateSingleUpdate($validator, $index, $transaction, $transactionGroup); - } - } - - /** - * @param Validator $validator - * @param int $index - * @param array $transaction - * @param TransactionGroup $transactionGroup - */ protected function validateSingleUpdate(Validator $validator, int $index, array $transaction, TransactionGroup $transactionGroup): void { app('log')->debug('Now validating single account update in validateSingleUpdate()'); @@ -411,6 +322,7 @@ trait TransactionValidation return; } + // create validator: /** @var AccountValidator $accountValidator */ $accountValidator = app(AccountValidator::class); @@ -420,13 +332,13 @@ trait TransactionValidation // validate if the submitted source ID/name/iban/number are valid if ( - array_key_exists('source_id', $transaction) || - array_key_exists('source_name', $transaction) || - array_key_exists('source_iban', $transaction) || - array_key_exists('source_number', $transaction) + array_key_exists('source_id', $transaction) + || array_key_exists('source_name', $transaction) + || array_key_exists('source_iban', $transaction) + || array_key_exists('source_number', $transaction) ) { app('log')->debug('Will try to validate source account information.'); - $sourceId = (int)($transaction['source_id'] ?? 0); + $sourceId = (int) ($transaction['source_id'] ?? 0); $sourceName = $transaction['source_name'] ?? null; $sourceIban = $transaction['source_iban'] ?? null; $sourceNumber = $transaction['source_number'] ?? null; @@ -448,11 +360,10 @@ trait TransactionValidation } if ( - array_key_exists('destination_id', $transaction) || - array_key_exists('destination_name', $transaction) || - array_key_exists('destination_iban', $transaction) || - array_key_exists('destination_number', $transaction) - + array_key_exists('destination_id', $transaction) + || array_key_exists('destination_name', $transaction) + || array_key_exists('destination_iban', $transaction) + || array_key_exists('destination_number', $transaction) ) { app('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. @@ -466,7 +377,7 @@ trait TransactionValidation $accountValidator->source = $source; } } - $destinationId = (int)($transaction['destination_id'] ?? 0); + $destinationId = (int) ($transaction['destination_id'] ?? 0); $destinationName = $transaction['destination_name'] ?? null; $destinationIban = $transaction['destination_iban'] ?? null; $destinationNumber = $transaction['destination_number'] ?? null; @@ -484,22 +395,159 @@ trait TransactionValidation } /** - * @param TransactionGroup $group - * @param array $transactions + * TODO describe this method. * - * @return string + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * @SuppressWarnings(PHPMD.NPathComplexity) */ - private function getTransactionType(TransactionGroup $group, array $transactions): string - { - return $transactions[0]['type'] ?? strtolower((string)$group->transactionJournals()->first()?->transactionType->type); + private function sanityCheckForeignCurrency( + Validator $validator, + AccountValidator $accountValidator, + array $transaction, + string $transactionType, + int $index + ): void { + app('log')->debug('Now in sanityCheckForeignCurrency()'); + if (0 !== $validator->errors()->count()) { + app('log')->debug('Already have errors, return'); + + return; + } + if (null === $accountValidator->source) { + app('log')->debug('No source, return'); + + return; + } + if (null === $accountValidator->destination) { + app('log')->debug('No destination, return'); + + return; + } + $source = $accountValidator->source; + $destination = $accountValidator->destination; + + app('log')->debug(sprintf('Source: #%d "%s (%s)"', $source->id, $source->name, $source->accountType->type)); + app('log')->debug(sprintf('Destination: #%d "%s" (%s)', $destination->id, $destination->name, $source->accountType->type)); + + if (!$this->isLiabilityOrAsset($source) || !$this->isLiabilityOrAsset($destination)) { + app('log')->debug('Any account must be liability or asset account to continue.'); + + return; + } + + /** @var AccountRepositoryInterface $accountRepository */ + $accountRepository = app(AccountRepositoryInterface::class); + $defaultCurrency = app('amount')->getDefaultCurrency(); + $sourceCurrency = $accountRepository->getAccountCurrency($source) ?? $defaultCurrency; + $destinationCurrency = $accountRepository->getAccountCurrency($destination) ?? $defaultCurrency; + // if both accounts have the same currency, continue. + if ($sourceCurrency->code === $destinationCurrency->code) { + app('log')->debug('Both accounts have the same currency, continue.'); + + return; + } + app('log')->debug(sprintf('Source account expects %s', $sourceCurrency->code)); + app('log')->debug(sprintf('Destination account expects %s', $destinationCurrency->code)); + + app('log')->debug(sprintf('Amount is %s', $transaction['amount'])); + + if (TransactionType::DEPOSIT === ucfirst($transactionType)) { + app('log')->debug(sprintf('Processing as a "%s"', $transactionType)); + // use case: deposit from liability account to an asset account + // the foreign amount must be in the currency of the source + // the amount must be in the currency of the destination + + // no foreign currency information is present: + if (!$this->hasForeignCurrencyInfo($transaction)) { + $validator->errors()->add(sprintf('transactions.%d.foreign_amount', $index), (string) trans('validation.require_foreign_currency')); + + return; + } + + // wrong currency information is present + $foreignCurrencyCode = $transaction['foreign_currency_code'] ?? false; + $foreignCurrencyId = (int) ($transaction['foreign_currency_id'] ?? 0); + app('log')->debug(sprintf('Foreign currency code seems to be #%d "%s"', $foreignCurrencyId, $foreignCurrencyCode), $transaction); + if ($foreignCurrencyCode !== $sourceCurrency->code && $foreignCurrencyId !== $sourceCurrency->id) { + $validator->errors()->add(sprintf('transactions.%d.foreign_currency_code', $index), (string) trans('validation.require_foreign_src')); + + return; + } + } + if (TransactionType::TRANSFER === ucfirst($transactionType) || TransactionType::WITHDRAWAL === ucfirst($transactionType)) { + app('log')->debug(sprintf('Processing as a "%s"', $transactionType)); + // use case: withdrawal from asset account to a liability account. + // the foreign amount must be in the currency of the destination + // the amount must be in the currency of the source + + // use case: transfer between accounts with different currencies. + // the foreign amount must be in the currency of the destination + // the amount must be in the currency of the source + + // no foreign currency information is present: + if (!$this->hasForeignCurrencyInfo($transaction)) { + $validator->errors()->add(sprintf('transactions.%d.foreign_amount', $index), (string) trans('validation.require_foreign_currency')); + + return; + } + + // wrong currency information is present + $foreignCurrencyCode = $transaction['foreign_currency_code'] ?? false; + $foreignCurrencyId = (int) ($transaction['foreign_currency_id'] ?? 0); + app('log')->debug(sprintf('Foreign currency code seems to be #%d "%s"', $foreignCurrencyId, $foreignCurrencyCode), $transaction); + if ($foreignCurrencyCode !== $destinationCurrency->code && $foreignCurrencyId !== $destinationCurrency->id) { + app('log')->debug(sprintf('No match on code, "%s" vs "%s"', $foreignCurrencyCode, $destinationCurrency->code)); + app('log')->debug(sprintf('No match on ID, #%d vs #%d', $foreignCurrencyId, $destinationCurrency->id)); + $validator->errors()->add(sprintf('transactions.%d.foreign_amount', $index), (string) trans('validation.require_foreign_dest')); + } + } + } + + private function isLiabilityOrAsset(Account $account): bool + { + return $this->isLiability($account) || $this->isAsset($account); + } + + private function isLiability(Account $account): bool + { + $type = $account->accountType->type; + if (in_array($type, config('firefly.valid_liabilities'), true)) { + return true; + } + + return false; + } + + private function isAsset(Account $account): bool + { + $type = $account->accountType->type; + + return AccountType::ASSET === $type; + } + + private function hasForeignCurrencyInfo(array $transaction): bool + { + if (!array_key_exists('foreign_currency_code', $transaction) && !array_key_exists('foreign_currency_id', $transaction)) { + return false; + } + if (!array_key_exists('foreign_amount', $transaction)) { + return false; + } + if ('' === $transaction['foreign_amount']) { + return false; + } + if (0 === bccomp('0', $transaction['foreign_amount'])) { + return false; + } + + return true; + } + + private function getTransactionType(TransactionGroup $group, array $transactions): string + { + return $transactions[0]['type'] ?? strtolower((string) $group->transactionJournals()->first()?->transactionType->type); } - /** - * @param array $transaction - * @param TransactionGroup $transactionGroup - * - * @return Account|null - */ private function getOriginalSource(array $transaction, TransactionGroup $transactionGroup): ?Account { if (1 === $transactionGroup->transactionJournals->count()) { @@ -507,9 +555,10 @@ trait TransactionValidation return $journal?->transactions()->where('amount', '<', 0)->first()?->account; } + /** @var TransactionJournal $journal */ foreach ($transactionGroup->transactionJournals as $journal) { - $journalId = (int)($transaction['transaction_journal_id'] ?? 0); + $journalId = (int) ($transaction['transaction_journal_id'] ?? 0); if ($journal->id === $journalId) { return $journal->transactions()->where('amount', '<', 0)->first()?->account; } @@ -518,129 +567,13 @@ trait TransactionValidation return null; } - /** - * Adds an error to the validator when there are no transactions in the array of data. - * - * @param Validator $validator - */ - public function validateOneRecurrenceTransaction(Validator $validator): void - { - app('log')->debug('Now in validateOneRecurrenceTransaction()'); - $transactions = $this->getTransactionsArray($validator); - - // need at least one transaction - if (0 === count($transactions)) { - $validator->errors()->add('transactions', (string)trans('validation.at_least_one_transaction')); - } - } - - /** - * Adds an error to the validator when there are no transactions in the array of data. - * - * @param Validator $validator - */ - public function validateOneTransaction(Validator $validator): void - { - app('log')->debug('Now in validateOneTransaction'); - if ($validator->errors()->count() > 0) { - app('log')->debug('Validator already has errors, so return.'); - return; - } - $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')); - app('log')->debug('Added error: at_least_one_transaction.'); - - return; - } - app('log')->debug('Added NO errors.'); - } - - /** - * @param Validator $validator - */ - public function validateTransactionArray(Validator $validator): void - { - if ($validator->errors()->count() > 0) { - return; - } - $transactions = $this->getTransactionsArray($validator); - foreach (array_keys($transactions) as $key) { - if (!is_int($key)) { - $validator->errors()->add('transactions.0.description', (string)trans('validation.at_least_one_transaction')); - app('log')->debug('Added error: at_least_one_transaction.'); - - return; - } - } - } - - /** - * All types of splits must be equal. - * - * @param Validator $validator - */ - public function validateTransactionTypes(Validator $validator): void - { - if ($validator->errors()->count() > 0) { - return; - } - app('log')->debug('Now in validateTransactionTypes()'); - $transactions = $this->getTransactionsArray($validator); - - $types = []; - foreach ($transactions as $transaction) { - $types[] = $transaction['type'] ?? 'invalid'; - } - $unique = array_unique($types); - if (count($unique) > 1) { - $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')); - } - } - - /** - * All types of splits must be equal. - * - * @param Validator $validator - */ - public function validateTransactionTypesForUpdate(Validator $validator): void - { - app('log')->debug('Now in validateTransactionTypesForUpdate()'); - $transactions = $this->getTransactionsArray($validator); - $types = []; - foreach ($transactions as $transaction) { - $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) { - app('log')->warning('Add error for mismatch transaction types.'); - $validator->errors()->add('transactions.0.type', (string)trans('validation.transaction_types_equal')); - - return; - } - app('log')->debug('No errors in validateTransactionTypesForUpdate()'); - } - - /** - * @param int $journalId - * - * @return string - */ private function getOriginalType(int $journalId): string { if (0 === $journalId) { return 'invalid'; } - /** @var TransactionJournal|null $journal */ + + /** @var null|TransactionJournal $journal */ $journal = TransactionJournal::with(['transactionType'])->find($journalId); if (null !== $journal) { return strtolower($journal->transactionType->type); @@ -649,9 +582,6 @@ trait TransactionValidation return 'invalid'; } - /** - * @param Validator $validator - */ private function validateEqualAccounts(Validator $validator): void { if ($validator->errors()->count() > 0) { @@ -673,35 +603,38 @@ trait TransactionValidation } $sources = array_unique($sources); $dests = array_unique($dests); + switch ($type) { 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; } } - /** - * @param Validator $validator - * @param TransactionGroup $transactionGroup - */ private function validateEqualAccountsForUpdate(Validator $validator, TransactionGroup $transactionGroup): void { if ($validator->errors()->count() > 0) { app('log')->debug('Validator already has errors, so return.'); + return; } @@ -724,14 +657,14 @@ 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')); } app('log')->warning('Add error about equal accounts.'); @@ -740,21 +673,17 @@ trait TransactionValidation app('log')->debug('No errors found in validateEqualAccountsForUpdate'); } - /** - * @param array $transactions - * - * @return array - */ private function collectComparisonData(array $transactions): array { $fields = ['source_id', 'destination_id', 'source_name', 'destination_name']; $comparison = []; foreach ($fields as $field) { $comparison[$field] = []; + /** @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]; @@ -764,11 +693,6 @@ trait TransactionValidation return $comparison; } - /** - * @param int $journalId - * - * @return array - */ private function getOriginalData(int $journalId): array { $return = [ @@ -780,13 +704,15 @@ trait TransactionValidation if (0 === $journalId) { return $return; } - /** @var Transaction|null $source */ + + /** @var null|Transaction $source */ $source = Transaction::where('transaction_journal_id', $journalId)->where('amount', '<', 0)->with(['account'])->first(); if (null !== $source) { $return['source_id'] = $source->account_id; $return['source_name'] = $source->account->name; } - /** @var Transaction|null $destination */ + + /** @var null|Transaction $destination */ $destination = Transaction::where('transaction_journal_id', $journalId)->where('amount', '>', 0)->with(['account'])->first(); if (null !== $destination) { $return['destination_id'] = $destination->account_id; @@ -796,12 +722,6 @@ trait TransactionValidation return $return; } - /** - * @param string $type - * @param array $comparison - * - * @return bool - */ private function compareAccountData(string $type, array $comparison): bool { return match ($type) { @@ -811,11 +731,6 @@ trait TransactionValidation }; } - /** - * @param array $comparison - * - * @return bool - */ private function compareAccountDataWithdrawal(array $comparison): bool { if ($this->arrayEqual($comparison['source_id'])) { @@ -830,21 +745,11 @@ trait TransactionValidation return false; } - /** - * @param array $array - * - * @return bool - */ private function arrayEqual(array $array): bool { return 1 === count(array_unique($array)); } - /** - * @param array $comparison - * - * @return bool - */ private function compareAccountDataDeposit(array $comparison): bool { if ($this->arrayEqual($comparison['destination_id'])) { @@ -859,11 +764,6 @@ trait TransactionValidation return false; } - /** - * @param array $comparison - * - * @return bool - */ private function compareAccountDataTransfer(array $comparison): bool { if ($this->arrayEqual($comparison['source_id'])) { diff --git a/changelog.md b/changelog.md index 0b67245b91..db9a17a72e 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,35 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## 6.1.1 - 2023-12-27 + +### Changed + +- Rule overview is lower in height. + +### Removed + +- Removed fixed sidebar again + +### Fixed + +- Nullpointer in rule trigger render code +- [Issue 8272](https://github.com/firefly-iii/firefly-iii/issues/8272) The sum for expected bills in a group includes unexpected bills as well +- [Issue 8273](https://github.com/firefly-iii/firefly-iii/issues/8273) Frontpage preferences indicate all accounts are shown on the frontpage, even when not true +- [Issue 8274](https://github.com/firefly-iii/firefly-iii/issues/8274) Semi specific dates do not work correctly with the "Transaction date is.." rule trigger +- [Issue 8277](https://github.com/firefly-iii/firefly-iii/issues/8277) Expected bill next month, but shown as not expected +- [Issue 8278](https://github.com/firefly-iii/firefly-iii/issues/8278) Net worth is empty in the dashboard due to division by zero +- [Issue 8281](https://github.com/firefly-iii/firefly-iii/issues/8281) Database CPU utilization after v6.1.0 upgrade +- [Issue 8291](https://github.com/firefly-iii/firefly-iii/issues/8291) Multiple "Any tag is" (negated or not) rule triggers don't all apply in strict mode + +### Security + +- HTML Injection Vulnerability in webhooks code, discovered by @stefan-schiller-sonarsource from Sonar. Thanks! + +### API + +- [Issue 8282](https://github.com/firefly-iii/firefly-iii/issues/8282) Update transaction via API does not update the "updated_at" parameter + ## 6.1.0 - 2023-12-17 > ⚠️⚠️ This release required **PHP 8.3.0** and will not work on earlier releases of PHP ⚠️⚠️ diff --git a/composer.lock b/composer.lock index 05a55fa130..0546e1a051 100644 --- a/composer.lock +++ b/composer.lock @@ -2014,16 +2014,16 @@ }, { "name": "laravel/framework", - "version": "v10.37.3", + "version": "v10.38.2", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "996375dd61f8c6e4ac262b57ed485655d71fcbdc" + "reference": "43da808391da3540d44a8dfeb4e46da4ad8f5723" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/996375dd61f8c6e4ac262b57ed485655d71fcbdc", - "reference": "996375dd61f8c6e4ac262b57ed485655d71fcbdc", + "url": "https://api.github.com/repos/laravel/framework/zipball/43da808391da3540d44a8dfeb4e46da4ad8f5723", + "reference": "43da808391da3540d44a8dfeb4e46da4ad8f5723", "shasum": "" }, "require": { @@ -2069,6 +2069,8 @@ "voku/portable-ascii": "^2.0" }, "conflict": { + "carbonphp/carbon-doctrine-types": ">=3.0", + "doctrine/dbal": ">=4.0", "tightenco/collect": "<5.5.33" }, "provide": { @@ -2180,6 +2182,7 @@ "files": [ "src/Illuminate/Collections/helpers.php", "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", "src/Illuminate/Foundation/helpers.php", "src/Illuminate/Support/helpers.php" ], @@ -2212,7 +2215,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-12-13T20:10:58+00:00" + "time": "2023-12-22T14:39:10+00:00" }, { "name": "laravel/passport", @@ -2542,16 +2545,16 @@ }, { "name": "laravel/ui", - "version": "v4.2.3", + "version": "v4.3.0", "source": { "type": "git", "url": "https://github.com/laravel/ui.git", - "reference": "eb532ea096ca1c0298c87c19233daf011fda743a" + "reference": "d166e09cdcb2e23836f694774cba77a32edb6007" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/ui/zipball/eb532ea096ca1c0298c87c19233daf011fda743a", - "reference": "eb532ea096ca1c0298c87c19233daf011fda743a", + "url": "https://api.github.com/repos/laravel/ui/zipball/d166e09cdcb2e23836f694774cba77a32edb6007", + "reference": "d166e09cdcb2e23836f694774cba77a32edb6007", "shasum": "" }, "require": { @@ -2598,9 +2601,9 @@ "ui" ], "support": { - "source": "https://github.com/laravel/ui/tree/v4.2.3" + "source": "https://github.com/laravel/ui/tree/v4.3.0" }, - "time": "2023-11-23T14:44:22+00:00" + "time": "2023-12-19T14:46:09+00:00" }, { "name": "lcobucci/clock", @@ -5879,16 +5882,16 @@ }, { "name": "spatie/laravel-ignition", - "version": "2.3.2", + "version": "2.3.3", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ignition.git", - "reference": "4800661a195e15783477d99f7f8f669a49793996" + "reference": "66499cd3c858642ded56dafb8fa0352057ca20dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/4800661a195e15783477d99f7f8f669a49793996", - "reference": "4800661a195e15783477d99f7f8f669a49793996", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/66499cd3c858642ded56dafb8fa0352057ca20dd", + "reference": "66499cd3c858642ded56dafb8fa0352057ca20dd", "shasum": "" }, "require": { @@ -5967,7 +5970,7 @@ "type": "github" } ], - "time": "2023-12-15T13:44:49+00:00" + "time": "2023-12-21T09:43:05+00:00" }, { "name": "spatie/period", @@ -10354,23 +10357,23 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.10", + "version": "10.1.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "599109c8ca6bae97b23482d557d2874c25a65e59" + "reference": "78c3b7625965c2513ee96569a4dbb62601784145" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/599109c8ca6bae97b23482d557d2874c25a65e59", - "reference": "599109c8ca6bae97b23482d557d2874c25a65e59", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/78c3b7625965c2513ee96569a4dbb62601784145", + "reference": "78c3b7625965c2513ee96569a4dbb62601784145", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1", "phpunit/php-file-iterator": "^4.0", "phpunit/php-text-template": "^3.0", @@ -10420,7 +10423,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.10" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.11" }, "funding": [ { @@ -10428,7 +10431,7 @@ "type": "github" } ], - "time": "2023-12-11T06:28:43+00:00" + "time": "2023-12-21T15:38:30+00:00" }, { "name": "phpunit/php-file-iterator", @@ -11020,20 +11023,20 @@ }, { "name": "sebastian/complexity", - "version": "3.1.0", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68cfb347a44871f01e33ab0ef8215966432f6957" + "reference": "68ff824baeae169ec9f2137158ee529584553799" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957", - "reference": "68cfb347a44871f01e33ab0ef8215966432f6957", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", "shasum": "" }, "require": { - "nikic/php-parser": "^4.10", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1" }, "require-dev": { @@ -11042,7 +11045,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.1-dev" + "dev-main": "3.2-dev" } }, "autoload": { @@ -11066,7 +11069,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0" + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" }, "funding": [ { @@ -11074,20 +11077,20 @@ "type": "github" } ], - "time": "2023-09-28T11:50:59+00:00" + "time": "2023-12-21T08:37:17+00:00" }, { "name": "sebastian/diff", - "version": "5.0.3", + "version": "5.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b" + "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b", - "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/fbf413a49e54f6b9b17e12d900ac7f6101591b7f", + "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f", "shasum": "" }, "require": { @@ -11100,7 +11103,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -11133,7 +11136,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.0" }, "funding": [ { @@ -11141,7 +11144,7 @@ "type": "github" } ], - "time": "2023-05-01T07:48:21+00:00" + "time": "2023-12-22T10:55:06+00:00" }, { "name": "sebastian/environment", @@ -11349,20 +11352,20 @@ }, { "name": "sebastian/lines-of-code", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d" + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d", - "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", "shasum": "" }, "require": { - "nikic/php-parser": "^4.10", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1" }, "require-dev": { @@ -11395,7 +11398,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" }, "funding": [ { @@ -11403,7 +11406,7 @@ "type": "github" } ], - "time": "2023-08-31T09:25:50+00:00" + "time": "2023-12-21T08:38:20+00:00" }, { "name": "sebastian/object-enumerator", diff --git a/config/app.php b/config/app.php index a7c44efb4e..9cfcb5b34a 100644 --- a/config/app.php +++ b/config/app.php @@ -43,10 +43,7 @@ return [ 'key' => env('APP_KEY'), 'cipher' => 'AES-256-CBC', 'providers' => [ - - /* - * Laravel Framework Service Providers... - */ + // Laravel Framework Service Providers... Illuminate\Auth\AuthServiceProvider::class, Illuminate\Broadcasting\BroadcastServiceProvider::class, Illuminate\Bus\BusServiceProvider::class, @@ -70,13 +67,9 @@ return [ Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, - /* - * Package Service Providers... - */ + // Package Service Providers... - /* - * Application Service Providers... - */ + // Application Service Providers... FireflyIII\Providers\AppServiceProvider::class, FireflyIII\Providers\AuthServiceProvider::class, // FireflyIII\Providers\BroadcastServiceProvider::class, @@ -87,9 +80,7 @@ return [ PragmaRX\Google2FALaravel\ServiceProvider::class, TwigBridge\ServiceProvider::class, - /* - * More service providers. - */ + // More service providers. FireflyIII\Providers\AccountServiceProvider::class, FireflyIII\Providers\AttachmentServiceProvider::class, FireflyIII\Providers\BillServiceProvider::class, @@ -173,5 +164,4 @@ return [ */ 'faker_locale' => 'en_US', - ]; diff --git a/config/auth.php b/config/auth.php index c645307e39..87bb7ce6c5 100644 --- a/config/auth.php +++ b/config/auth.php @@ -22,7 +22,7 @@ declare(strict_types=1); if ('ldap' === strtolower((string)env('AUTHENTICATION_GUARD'))) { - die('LDAP is no longer supported by Firefly III v5.7+. Sorry about that. You will have to switch to "remote_user_guard", and use tools like Authelia or Keycloak to use LDAP together with Firefly III.'); + exit('LDAP is no longer supported by Firefly III v5.7+. Sorry about that. You will have to switch to "remote_user_guard", and use tools like Authelia or Keycloak to use LDAP together with Firefly III.'); } return [ @@ -139,5 +139,4 @@ return [ */ 'password_timeout' => 10800, - ]; diff --git a/config/breadcrumbs.php b/config/breadcrumbs.php index 0cfb2b1185..c1c3dbcb2b 100644 --- a/config/breadcrumbs.php +++ b/config/breadcrumbs.php @@ -23,7 +23,6 @@ declare(strict_types=1); return [ - /* |-------------------------------------------------------------------------- | View Name @@ -95,5 +94,4 @@ return [ // Generator 'generator-class' => Diglactic\Breadcrumbs\Generator::class, - ]; diff --git a/config/broadcasting.php b/config/broadcasting.php index abc48f2e99..1996f5af12 100644 --- a/config/broadcasting.php +++ b/config/broadcasting.php @@ -49,7 +49,6 @@ return [ */ 'connections' => [ - 'pusher' => [ 'driver' => 'pusher', 'key' => env('PUSHER_APP_KEY'), @@ -57,11 +56,11 @@ return [ 'app_id' => env('PUSHER_APP_ID'), 'options' => [ 'cluster' => env('PUSHER_APP_CLUSTER'), - 'host' => null !== env('PUSHER_HOST') ? env('PUSHER_HOST') : 'api-' . env('PUSHER_APP_CLUSTER', 'mt1') . '.pusher.com', + 'host' => null !== env('PUSHER_HOST') ? env('PUSHER_HOST') : 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com', 'port' => env('PUSHER_PORT', 443), 'scheme' => env('PUSHER_SCHEME', 'https'), 'encrypted' => true, - 'useTLS' => env('PUSHER_SCHEME', 'https') === 'https', + 'useTLS' => 'https' === env('PUSHER_SCHEME', 'https'), ], 'client_options' => [ // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html @@ -85,7 +84,5 @@ return [ 'null' => [ 'driver' => 'null', ], - ], - ]; diff --git a/config/cache.php b/config/cache.php index d28d9d6882..bac320509d 100644 --- a/config/cache.php +++ b/config/cache.php @@ -49,7 +49,6 @@ return [ */ 'stores' => [ - 'apc' => [ 'driver' => 'apc', ], diff --git a/config/cer.php b/config/cer.php index 7445ea37bd..30b0ebb70a 100644 --- a/config/cer.php +++ b/config/cer.php @@ -23,7 +23,6 @@ declare(strict_types=1); return [ - 'url' => 'https://ff3exchangerates.z6.web.core.windows.net', 'enabled' => true, 'download_enabled' => env('ENABLE_EXTERNAL_RATES', false), @@ -35,7 +34,6 @@ return [ // all rates are from EUR to $currency: 'rates' => [ - // europa 'EUR' => 1, 'HUF' => 387.9629, diff --git a/config/cors.php b/config/cors.php index 69da8e5802..cbfc4d044e 100644 --- a/config/cors.php +++ b/config/cors.php @@ -1,6 +1,5 @@ 0, 'supports_credentials' => false, - ]; diff --git a/config/database.php b/config/database.php index 3a36cb9e72..34755ed3a8 100644 --- a/config/database.php +++ b/config/database.php @@ -39,9 +39,7 @@ if (false !== $databaseUrl) { $database = substr($options['path'] ?? '/firefly', 1); } -/* - * Get SSL parameters from .env file. - */ +// Get SSL parameters from .env file. $mysql_ssl_ca_dir = envNonEmpty('MYSQL_SSL_CAPATH', null); $mysql_ssl_ca_file = envNonEmpty('MYSQL_SSL_CA', null); $mysql_ssl_cert = envNonEmpty('MYSQL_SSL_CERT', null); @@ -121,7 +119,6 @@ return [ 'charset' => 'utf8', 'prefix' => '', ], - ], 'migrations' => 'migrations', /* @@ -138,7 +135,7 @@ return [ 'client' => env('REDIS_CLIENT', 'predis'), 'options' => [ 'cluster' => env('REDIS_CLUSTER', 'predis'), - //'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_database_'), + // 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_database_'), ], 'default' => [ 'scheme' => envNonEmpty('REDIS_SCHEME', 'tcp'), @@ -161,5 +158,4 @@ return [ 'database' => env('REDIS_CACHE_DB', '1'), ], ], - ]; diff --git a/config/debugbar.php b/config/debugbar.php index 7441f1af5d..e2cf2bf6b1 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -163,9 +163,9 @@ return [ 'backtrace' => true, // Use a backtrace to find the origin of the query in your files. 'timeline' => false, // Add the queries to the timeline 'explain' => [ // Show EXPLAIN output on queries - 'enabled' => false, - 'types' => ['SELECT'], - // // workaround ['SELECT'] only. https://github.com/barryvdh/laravel-debugbar/issues/888 ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+ + 'enabled' => false, + 'types' => ['SELECT'], + // // workaround ['SELECT'] only. https://github.com/barryvdh/laravel-debugbar/issues/888 ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+ ], 'hints' => true, // Show hints for common mistakes ], @@ -173,7 +173,7 @@ return [ 'full_log' => false, ], 'views' => [ - 'data' => true, //Note: Can slow down the application, because the data can be quite large.. + 'data' => true, // Note: Can slow down the application, because the data can be quite large.. ], 'route' => [ 'label' => true, // show complete route on bar diff --git a/config/filesystems.php b/config/filesystems.php index 8deff7ba8f..a15413d68b 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -88,10 +88,9 @@ return [ 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), - 'url' => env('APP_URL') . '/storage', + 'url' => env('APP_URL').'/storage', 'visibility' => 'public', ], - ], /* diff --git a/config/firefly.php b/config/firefly.php index 8e72089804..6edc877339 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -114,7 +114,7 @@ return [ 'handle_debts' => true, // see cer.php for exchange rates feature flag. ], - 'version' => '6.1.0', + 'version' => '6.1.1', 'api_version' => '2.0.12', 'db_version' => 22, @@ -191,7 +191,7 @@ return [ // 'si_LK' => ['name_locale' => 'සිංහල', 'name_english' => 'Sinhala (Sri Lanka)'], 'sk_SK' => ['name_locale' => 'Slovenčina', 'name_english' => 'Slovak'], 'sl_SI' => ['name_locale' => 'Slovenian', 'name_english' => 'Slovenian'], - //// 'sr_CS' => ['name_locale' => 'Serbian (Latin)', 'name_english' => 'Serbian (Latin)'], + // // 'sr_CS' => ['name_locale' => 'Serbian (Latin)', 'name_english' => 'Serbian (Latin)'], 'sv_SE' => ['name_locale' => 'Svenska', 'name_english' => 'Swedish'], // // 'tlh_AA' => ['name_locale' => 'tlhIngan Hol', 'name_english' => 'Klingon'], 'tr_TR' => ['name_locale' => 'Türkçe', 'name_english' => 'Turkish'], @@ -252,41 +252,41 @@ return [ ], 'available_dark_modes' => ['light', 'dark', 'browser'], 'bill_reminder_periods' => [90, 30, 14, 7, 0], - 'valid_view_ranges' => ['1D', '1W', '1M', '3M', '6M', '1Y',], + 'valid_view_ranges' => ['1D', '1W', '1M', '3M', '6M', '1Y'], 'valid_url_protocols' => envNonEmpty('VALID_URL_PROTOCOLS', 'http,https,ftp,ftps,mailto'), 'allowedMimes' => [ - /* plain files */ + // plain files 'text/plain', - /* images */ + // images 'image/jpeg', 'image/svg+xml', 'image/png', 'image/heic', 'image/heic-sequence', - /* PDF */ + // PDF 'application/pdf', - /* Generic upload */ + // Generic upload 'application/octet-stream', - /* MS word */ + // MS word 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', - /* MS excel */ + // MS excel 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', - /* MS powerpoint */ + // MS powerpoint 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.openxmlformats-officedocument.presentationml.template', 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', - /* iWork */ + // iWork 'application/x-iwork-pages-sffpages', - /* open office */ + // open office 'application/vnd.sun.xml.writer', 'application/vnd.sun.xml.writer.template', 'application/vnd.sun.xml.writer.global', @@ -318,15 +318,15 @@ return [ 'application/vnd.oasis.opendocument.database', 'application/vnd.oasis.opendocument.image', - /* EML */ + // EML 'message/rfc822', - /* JSON */ + // JSON 'application/json', ], 'accountRoles' => ['defaultAsset', 'sharedAsset', 'savingAsset', 'ccAsset', 'cashWalletAsset'], 'valid_liabilities' => [AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE], - 'ccTypes' => ['monthlyFull' => 'Full payment every month',], + 'ccTypes' => ['monthlyFull' => 'Full payment every month'], 'credit_card_types' => ['monthlyFull'], // "period must be in this list" values @@ -487,8 +487,6 @@ return [ 'userGroupAccount' => UserGroupAccount::class, 'userGroupBill' => UserGroupBill::class, 'userGroup' => UserGroup::class, - - ], 'rule-actions' => [ 'set_category' => SetCategory::class, @@ -545,7 +543,6 @@ return [ 'range' => 200, ], - // expected source types for each transaction type, in order of preference. 'expected_source_types' => [ 'source' => [ @@ -637,7 +634,6 @@ return [ AccountType::RECONCILIATION => [AccountType::ASSET], AccountType::REVENUE => [AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE], AccountType::LIABILITY_CREDIT => [AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE], - ], 'destination' => [ AccountType::ASSET => [ @@ -679,7 +675,7 @@ return [ ], AccountType::RECONCILIATION => [AccountType::ASSET], AccountType::REVENUE => [], // is not allowed as a destination - AccountType::LIABILITY_CREDIT => [],// is not allowed as a destination + AccountType::LIABILITY_CREDIT => [], // is not allowed as a destination ], ], // depending on the account type, return the allowed transaction types: @@ -749,7 +745,6 @@ return [ AccountType::RECONCILIATION => [TransactionTypeModel::RECONCILIATION], AccountType::LIABILITY_CREDIT => [], // is not allowed as a destination ], - ], // having the source + dest will tell you the transaction type. @@ -914,5 +909,5 @@ return [ 'allowed_sort_parameters' => ['order', 'name', 'iban'], // preselected account lists possibilities: - 'preselected_accounts' => ['all','assets','liabilities'], + 'preselected_accounts' => ['all', 'assets', 'liabilities'], ]; diff --git a/config/google2fa.php b/config/google2fa.php index 553c4a6014..bcf0b19822 100644 --- a/config/google2fa.php +++ b/config/google2fa.php @@ -24,9 +24,7 @@ declare(strict_types=1); use PragmaRX\Google2FALaravel\Support\Constants; return [ - /* - * Auth container binding - */ + // Auth container binding 'enabled' => true, @@ -37,63 +35,42 @@ return [ 'lifetime' => 0, // 0 = eternal - /* - * Renew lifetime at every new request. - */ + // Renew lifetime at every new request. 'keep_alive' => true, - /* - * Auth container binding - */ + // Auth container binding 'auth' => 'auth', - /* - * 2FA verified session var - */ + // 2FA verified session var 'session_var' => 'google2fa', - /* - * One Time Password request input name - */ + // One Time Password request input name 'otp_input' => 'one_time_password', - /* - * One Time Password Window - */ + // One Time Password Window 'window' => 1, - /* - * Forbid user to reuse One Time Passwords. - */ + // Forbid user to reuse One Time Passwords. 'forbid_old_passwords' => false, - /* - * User's table column for google2fa secret - */ + // User's table column for google2fa secret 'otp_secret_column' => 'mfa_secret', - /* - * One Time Password View - */ + // One Time Password View 'view' => 'auth.mfa', - /* - * One Time Password error message - */ + // One Time Password error message 'error_messages' => [ 'wrong_otp' => "The 'One Time Password' typed was wrong.", ], - /* - * Throw exceptions or just fire events? - */ + // Throw exceptions or just fire events? 'throw_exceptions' => true, 'store_in_cookie' => true, 'qrcode_image_backend' => Constants::QRCODE_IMAGE_BACKEND_SVG, - ]; diff --git a/config/hashing.php b/config/hashing.php index a82faad299..5a928e2e5e 100644 --- a/config/hashing.php +++ b/config/hashing.php @@ -1,6 +1,5 @@ 1, 'time' => 4, ], - ]; diff --git a/config/ide-helper.php b/config/ide-helper.php index 95598ccdcf..2ead082aff 100644 --- a/config/ide-helper.php +++ b/config/ide-helper.php @@ -86,7 +86,7 @@ return [ 'include_helpers' => false, 'helper_files' => [ - base_path() . '/vendor/laravel/framework/src/Illuminate/Support/helpers.php', + base_path().'/vendor/laravel/framework/src/Illuminate/Support/helpers.php', ], /* @@ -141,7 +141,6 @@ return [ */ 'interfaces' => [ - ], /* @@ -171,7 +170,6 @@ return [ | */ 'custom_db_types' => [ - ], /* @@ -223,5 +221,4 @@ return [ | */ 'include_class_docblocks' => false, - ]; diff --git a/config/intro.php b/config/intro.php index b364b9f7b5..fcfbd0aa01 100644 --- a/config/intro.php +++ b/config/intro.php @@ -21,9 +21,7 @@ declare(strict_types=1); -/* - * Always make sure intro is the first element (if any) and outro is the last one. - */ +// Always make sure intro is the first element (if any) and outro is the last one. return [ // index @@ -73,7 +71,6 @@ return [ 'new_budget' => ['element' => '#createBudgetBox'], 'list_of_budgets' => ['element' => '#budgetList'], 'outro' => [], - ], // reports: index, default report, audit, budget, cat, tag 'reports_index' => [ @@ -94,7 +91,6 @@ return [ 'intro' => [], 'pieCharts' => ['element' => '#pieCharts'], 'incomeAndExpensesChart' => ['element' => '#incomeAndExpensesChart', 'position' => 'top'], - ], 'reports_report_tag' => [ 'intro' => [], @@ -116,7 +112,6 @@ return [ 'piggy-banks_create' => [ 'name' => ['element' => '#ffInput_name'], 'date' => ['element' => '#ffInput_targetdate'], - ], 'piggy-banks_show' => [ 'piggyChart' => ['element' => '#piggyChart'], @@ -133,7 +128,7 @@ return [ 'bills_create' => [ 'intro' => [], 'name' => ['element' => '#name_holder'], - //'match' => ['element' => '#match_holder'], + // 'match' => ['element' => '#match_holder'], 'amount_min_holder' => ['element' => '#amount_min_holder'], 'repeat_freq_holder' => ['element' => '#repeat_freq_holder'], 'skip_holder' => ['element' => '#skip_holder'], @@ -142,7 +137,6 @@ return [ 'billInfo' => ['element' => '#billInfo'], 'billButtons' => ['element' => '#billButtons'], 'billChart' => ['element' => '#billChart', 'position' => 'top'], - ], // rules: index, create-rule, edit-rule 'rules_index' => [ diff --git a/config/logging.php b/config/logging.php index 57d2601851..667ec95e05 100644 --- a/config/logging.php +++ b/config/logging.php @@ -28,7 +28,6 @@ use Monolog\Handler\SyslogUdpHandler; $defaultChannels = ['daily', 'stdout']; $auditChannels = ['audit_daily', 'audit_stdout']; - // validChannels is missing 'stack' because we already check for that one. $validChannels = ['single', 'papertrail', 'stdout', 'daily', 'syslog', 'errorlog']; $validAuditChannels = ['audit_papertrail', 'audit_stdout', 'audit_stdout', 'audit_daily', 'audit_syslog', 'audit_errorlog']; @@ -89,9 +88,7 @@ return [ 'driver' => 'stack', 'channels' => $auditChannels, ], - /* - * There are 6 valid destinations for the normal logs, listed below: - */ + // There are 6 valid destinations for the normal logs, listed below: 'single' => [ 'driver' => 'single', 'path' => storage_path('logs/laravel.log'), @@ -113,7 +110,7 @@ return [ ], 'daily' => [ 'driver' => 'daily', - 'path' => storage_path('logs/ff3-' . PHP_SAPI . '.log'), + 'path' => storage_path('logs/ff3-'.PHP_SAPI.'.log'), 'level' => envNonEmpty('APP_LOG_LEVEL', 'info'), 'days' => 7, ], @@ -163,8 +160,5 @@ return [ 'tap' => [AuditLogger::class], 'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'), ], - - ], - ]; diff --git a/config/mail.php b/config/mail.php index baea55bdc0..59ea401c04 100644 --- a/config/mail.php +++ b/config/mail.php @@ -22,7 +22,6 @@ declare(strict_types=1); return [ - /* |-------------------------------------------------------------------------- | Default Mailer @@ -91,5 +90,4 @@ return [ resource_path('views/vendor/mail'), ], ], - ]; diff --git a/config/passport.php b/config/passport.php index 6ea53eb703..76579537d9 100644 --- a/config/passport.php +++ b/config/passport.php @@ -1,6 +1,5 @@ env('PASSPORT_PERSONAL_ACCESS_CLIENT_ID'), 'secret' => env('PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET'), ], - ]; diff --git a/config/queue.php b/config/queue.php index 0950149af1..559e245a95 100644 --- a/config/queue.php +++ b/config/queue.php @@ -23,7 +23,6 @@ declare(strict_types=1); return [ - /* |-------------------------------------------------------------------------- | Default Queue Connection Name @@ -51,7 +50,6 @@ return [ */ 'connections' => [ - 'sync' => [ 'driver' => 'sync', ], @@ -88,7 +86,6 @@ return [ 'retry_after' => 90, 'block_for' => null, ], - ], /* @@ -107,5 +104,4 @@ return [ 'database' => env('DB_CONNECTION', 'mysql'), 'table' => 'failed_jobs', ], - ]; diff --git a/config/sanctum.php b/config/sanctum.php index d0b57a048f..03ee208a26 100644 --- a/config/sanctum.php +++ b/config/sanctum.php @@ -26,7 +26,6 @@ use FireflyIII\Http\Middleware\EncryptCookies; use FireflyIII\Http\Middleware\VerifyCsrfToken; return [ - /* |-------------------------------------------------------------------------- | Stateful Domains @@ -82,5 +81,4 @@ return [ 'verify_csrf_token' => VerifyCsrfToken::class, 'encrypt_cookies' => EncryptCookies::class, ], - ]; diff --git a/config/search.php b/config/search.php index ec80652cff..c8dcb7cc6f 100644 --- a/config/search.php +++ b/config/search.php @@ -24,200 +24,200 @@ declare(strict_types=1); return [ 'operators' => [ - 'user_action' => ['alias' => false, 'needs_context' => true,], - 'account_id' => ['alias' => false, 'needs_context' => true,], - 'reconciled' => ['alias' => false, 'needs_context' => false,], - 'source_account_id' => ['alias' => false, 'needs_context' => true,], - 'destination_account_id' => ['alias' => false, 'needs_context' => true,], - 'transaction_type' => ['alias' => false, 'needs_context' => true,], - 'type' => ['alias' => true, 'alias_for' => 'transaction_type', 'needs_context' => true,], - 'tag_is' => ['alias' => false, 'needs_context' => true,], - 'tag_is_not' => ['alias' => false, 'needs_context' => true,], - 'tag' => ['alias' => true, 'alias_for' => 'tag_is', 'needs_context' => true,], - 'tag_contains' => ['alias' => false, 'needs_context' => true,], - 'tag_ends' => ['alias' => false, 'needs_context' => true,], - 'tag_starts' => ['alias' => false, 'needs_context' => true,], - 'description_is' => ['alias' => false, 'needs_context' => true,], - 'description' => ['alias' => true, 'alias_for' => 'description_is', 'needs_context' => true,], - 'description_contains' => ['alias' => false, 'needs_context' => true,], - 'description_ends' => ['alias' => false, 'needs_context' => true,], - 'description_starts' => ['alias' => false, 'needs_context' => true,], - 'notes_is' => ['alias' => false, 'needs_context' => true,], - 'notes_are' => ['alias' => true, 'alias_for' => 'notes_is', 'needs_context' => true,], - 'notes_contains' => ['alias' => false, 'needs_context' => true,], - 'notes_contain' => ['alias' => true, 'alias_for' => 'notes_contains', 'needs_context' => true,], - 'notes' => ['alias' => true, 'alias_for' => 'notes_contains', 'needs_context' => true,], - 'notes_ends' => ['alias' => false, 'needs_context' => true,], - 'notes_end' => ['alias' => true, 'alias_for' => 'notes_ends', 'needs_context' => true,], - 'notes_starts' => ['alias' => false, 'needs_context' => true,], - 'notes_start' => ['alias' => true, 'alias_for' => 'notes_starts', 'needs_context' => true,], - 'source_account_is' => ['alias' => false, 'needs_context' => true,], - 'from_account_is' => ['alias' => true, 'alias_for' => 'source_account_is', 'needs_context' => true,], - 'source_account_contains' => ['alias' => false, 'needs_context' => true,], - 'source' => ['alias' => true, 'alias_for' => 'source_account_contains', 'needs_context' => true,], - 'from' => ['alias' => true, 'alias_for' => 'source_account_contains', 'needs_context' => true,], - 'from_account_contains' => ['alias' => true, 'alias_for' => 'source_account_contains', 'needs_context' => true,], - 'source_account_ends' => ['alias' => false, 'needs_context' => true,], - 'from_account_ends' => ['alias' => true, 'alias_for' => 'source_account_ends', 'needs_context' => true,], - 'source_account_starts' => ['alias' => false, 'needs_context' => true,], - 'from_account_starts' => ['alias' => true, 'alias_for' => 'source_account_starts', 'needs_context' => true,], - 'source_account_nr_is' => ['alias' => false, 'needs_context' => true,], - 'from_account_nr_is' => ['alias' => true, 'alias_for' => 'source_account_nr_is', 'needs_context' => true,], - 'source_account_nr_contains' => ['alias' => false, 'needs_context' => true,], - 'from_account_nr_contains' => ['alias' => true, 'alias_for' => 'source_account_nr_contains', 'needs_context' => true,], - 'source_account_nr_ends' => ['alias' => false, 'needs_context' => true,], - 'from_account_nr_ends' => ['alias' => true, 'alias_for' => 'source_account_nr_ends', 'needs_context' => true,], - 'source_account_nr_starts' => ['alias' => false, 'needs_context' => true,], - 'from_account_nr_starts' => ['alias' => true, 'alias_for' => 'source_account_nr_starts', 'needs_context' => true,], - 'destination_account_is' => ['alias' => false, 'needs_context' => true,], - 'to_account_is' => ['alias' => true, 'alias_for' => 'destination_account_is', 'needs_context' => true,], - 'destination_account_contains' => ['alias' => false, 'needs_context' => true,], - 'destination' => ['alias' => true, 'alias_for' => 'destination_account_contains', 'needs_context' => true,], - 'to' => ['alias' => true, 'alias_for' => 'destination_account_contains', 'needs_context' => true,], - 'to_account_contains' => ['alias' => true, 'alias_for' => 'destination_account_contains', 'needs_context' => true,], - 'destination_account_ends' => ['alias' => false, 'needs_context' => true,], - 'to_account_ends' => ['alias' => true, 'alias_for' => 'destination_account_ends', 'needs_context' => true,], - 'destination_account_starts' => ['alias' => false, 'needs_context' => true,], - 'to_account_starts' => ['alias' => true, 'alias_for' => 'destination_account_starts', 'needs_context' => true,], - 'destination_account_nr_is' => ['alias' => false, 'needs_context' => true,], - 'to_account_nr_is' => ['alias' => true, 'alias_for' => 'destination_account_nr_is', 'needs_context' => true,], - 'destination_account_nr_contains' => ['alias' => false, 'needs_context' => true,], - 'to_account_nr_contains' => ['alias' => true, 'alias_for' => 'destination_account_nr_contains', 'needs_context' => true,], - 'destination_account_nr_ends' => ['alias' => false, 'needs_context' => true,], - 'to_account_nr_ends' => ['alias' => true, 'alias_for' => 'destination_account_nr_ends', 'needs_context' => true,], - 'destination_account_nr_starts' => ['alias' => false, 'needs_context' => true,], - 'to_account_nr_starts' => ['alias' => true, 'alias_for' => 'destination_account_nr_starts', 'needs_context' => true,], - 'account_is' => ['alias' => false, 'needs_context' => true,], - 'account_contains' => ['alias' => false, 'needs_context' => true,], - 'account_ends' => ['alias' => false, 'needs_context' => true,], - 'account_starts' => ['alias' => false, 'needs_context' => true,], - 'account_nr_is' => ['alias' => false, 'needs_context' => true,], - 'account_nr_contains' => ['alias' => false, 'needs_context' => true,], - 'account_nr_ends' => ['alias' => false, 'needs_context' => true,], - 'account_nr_starts' => ['alias' => false, 'needs_context' => true,], - 'category_is' => ['alias' => false, 'needs_context' => true,], - 'category_contains' => ['alias' => false, 'needs_context' => true,], - 'category' => ['alias' => true, 'alias_for' => 'category_contains', 'needs_context' => true,], - 'category_ends' => ['alias' => false, 'needs_context' => true,], - 'category_starts' => ['alias' => false, 'needs_context' => true,], - 'budget_is' => ['alias' => false, 'needs_context' => true,], - 'budget_contains' => ['alias' => false, 'needs_context' => true,], - 'budget' => ['alias' => true, 'alias_for' => 'budget_contains', 'needs_context' => true,], - 'budget_ends' => ['alias' => false, 'needs_context' => true,], - 'budget_starts' => ['alias' => false, 'needs_context' => true,], - 'bill_is' => ['alias' => false, 'needs_context' => true,], - 'bill_contains' => ['alias' => false, 'needs_context' => true,], - 'bill' => ['alias' => true, 'alias_for' => 'bill_contains', 'needs_context' => true,], - 'bill_ends' => ['alias' => false, 'needs_context' => true,], - 'bill_starts' => ['alias' => false, 'needs_context' => true,], - 'external_id_is' => ['alias' => false, 'needs_context' => true,], - 'external_id_contains' => ['alias' => false, 'needs_context' => true,], - 'external_id' => ['alias' => true, 'alias_for' => 'external_id_contains', 'needs_context' => true,], - 'external_id_ends' => ['alias' => false, 'needs_context' => true,], - 'external_id_starts' => ['alias' => false, 'needs_context' => true,], - 'internal_reference_is' => ['alias' => false, 'needs_context' => true,], - 'internal_reference_contains' => ['alias' => false, 'needs_context' => true,], - 'internal_reference' => ['alias' => true, 'alias_for' => 'internal_reference_contains', 'needs_context' => true,], - 'internal_reference_ends' => ['alias' => false, 'needs_context' => true,], - 'internal_reference_starts' => ['alias' => false, 'needs_context' => true,], - 'external_url_is' => ['alias' => false, 'needs_context' => true,], - 'external_url_contains' => ['alias' => false, 'needs_context' => true,], - 'external_url' => ['alias' => true, 'alias_for' => 'external_url_contains', 'needs_context' => true,], - 'external_url_ends' => ['alias' => false, 'needs_context' => true,], - 'external_url_starts' => ['alias' => false, 'needs_context' => true,], - 'has_attachments' => ['alias' => false, 'needs_context' => false,], - 'has_any_category' => ['alias' => false, 'needs_context' => false,], - 'has_any_budget' => ['alias' => false, 'needs_context' => false,], - 'has_any_bill' => ['alias' => false, 'needs_context' => false,], - 'has_any_tag' => ['alias' => false, 'needs_context' => false,], - 'any_notes' => ['alias' => false, 'needs_context' => false,], - 'has_any_notes' => ['alias' => true, 'alias_for' => 'any_notes', 'needs_context' => false,], - 'any_external_url' => ['alias' => false, 'needs_context' => false,], - 'has_any_external_url' => ['alias' => true, 'alias_for' => 'any_external_url', 'needs_context' => false,], - 'has_no_attachments' => ['alias' => false, 'needs_context' => false,], - 'has_no_category' => ['alias' => false, 'needs_context' => false,], - 'has_no_budget' => ['alias' => false, 'needs_context' => false,], - 'has_no_bill' => ['alias' => false, 'needs_context' => false,], - 'has_no_tag' => ['alias' => false, 'needs_context' => false,], - 'no_notes' => ['alias' => false, 'needs_context' => false,], - 'no_external_url' => ['alias' => false, 'needs_context' => false,], - 'source_is_cash' => ['alias' => false, 'needs_context' => false,], - 'destination_is_cash' => ['alias' => false, 'needs_context' => false,], - 'account_is_cash' => ['alias' => false, 'needs_context' => false,], - 'currency_is' => ['alias' => false, 'needs_context' => true,], - 'foreign_currency_is' => ['alias' => false, 'needs_context' => true,], - 'id' => ['alias' => false, 'trigger_class' => '', 'needs_context' => true,], - 'journal_id' => ['alias' => false, 'trigger_class' => '', 'needs_context' => true,], - 'recurrence_id' => ['alias' => false, 'trigger_class' => '', 'needs_context' => true,], - 'date_on' => ['alias' => false, 'needs_context' => true,], - 'date' => ['alias' => true, 'alias_for' => 'date_on', 'needs_context' => true,], - 'date_is' => ['alias' => true, 'alias_for' => 'date_on', 'needs_context' => true,], - 'on' => ['alias' => true, 'alias_for' => 'date_on', 'needs_context' => true,], - 'date_before' => ['alias' => false, 'needs_context' => true,], - 'before' => ['alias' => true, 'alias_for' => 'date_before', 'needs_context' => true,], - 'date_after' => ['alias' => false, 'needs_context' => true,], - 'after' => ['alias' => true, 'alias_for' => 'date_after', 'needs_context' => true,], - 'interest_date_on' => ['alias' => false, 'needs_context' => true,], - 'interest_date' => ['alias' => true, 'alias_for' => 'interest_date_on', 'needs_context' => true,], - 'interest_date_is' => ['alias' => true, 'alias_for' => 'interest_date_on', 'needs_context' => true,], - 'interest_date_before' => ['alias' => false, 'needs_context' => true,], - 'interest_date_after' => ['alias' => false, 'needs_context' => true,], - 'book_date_on' => ['alias' => false, 'needs_context' => true,], - 'book_date' => ['alias' => true, 'alias_for' => 'book_date_on', 'needs_context' => true,], - 'book_date_is' => ['alias' => true, 'alias_for' => 'book_date_on', 'needs_context' => true,], - 'book_date_before' => ['alias' => false, 'needs_context' => true,], - 'book_date_after' => ['alias' => false, 'needs_context' => true,], - 'process_date_on' => ['alias' => false, 'needs_context' => true,], - 'process_date' => ['alias' => true, 'alias_for' => 'process_date_on', 'needs_context' => true,], - 'process_date_is' => ['alias' => true, 'alias_for' => 'process_date_on', 'needs_context' => true,], - 'process_date_before' => ['alias' => false, 'needs_context' => true,], - 'process_date_after' => ['alias' => false, 'needs_context' => true,], - 'due_date_on' => ['alias' => false, 'needs_context' => true,], - 'due_date' => ['alias' => true, 'alias_for' => 'due_date_on', 'needs_context' => true,], - 'due_date_is' => ['alias' => true, 'alias_for' => 'due_date_on', 'needs_context' => true,], - 'due_date_before' => ['alias' => false, 'needs_context' => true,], - 'due_date_after' => ['alias' => false, 'needs_context' => true,], - 'payment_date_on' => ['alias' => false, 'needs_context' => true,], - 'payment_date' => ['alias' => true, 'alias_for' => 'payment_date_on', 'needs_context' => true,], - 'payment_date_is' => ['alias' => true, 'alias_for' => 'payment_date_on', 'needs_context' => true,], - 'payment_date_before' => ['alias' => false, 'needs_context' => true,], - 'payment_date_after' => ['alias' => false, 'needs_context' => true,], - 'invoice_date_on' => ['alias' => false, 'needs_context' => true,], - 'invoice_date' => ['alias' => true, 'alias_for' => 'invoice_date_on', 'needs_context' => true,], - 'invoice_date_is' => ['alias' => true, 'alias_for' => 'invoice_date_on', 'needs_context' => true,], - 'invoice_date_before' => ['alias' => false, 'needs_context' => true,], - 'invoice_date_after' => ['alias' => false, 'needs_context' => true,], - 'created_at_on' => ['alias' => false, 'needs_context' => true,], - 'created_at' => ['alias' => true, 'alias_for' => 'created_at_on', 'needs_context' => true,], - 'created_at_is' => ['alias' => true, 'alias_for' => 'created_at_on', 'needs_context' => true,], - 'created_at_before' => ['alias' => false, 'needs_context' => true,], - 'created_at_after' => ['alias' => false, 'needs_context' => true,], - 'updated_at_on' => ['alias' => false, 'needs_context' => true,], - 'updated_at' => ['alias' => true, 'alias_for' => 'updated_at_on', 'needs_context' => true,], - 'updated_at_is' => ['alias' => true, 'alias_for' => 'updated_at_on', 'needs_context' => true,], - 'updated_at_before' => ['alias' => false, 'needs_context' => true,], - 'updated_at_after' => ['alias' => false, 'needs_context' => true,], - 'created_on_on' => ['alias' => true, 'alias_for' => 'created_at_on', 'needs_context' => true,], - 'created_on' => ['alias' => true, 'alias_for' => 'created_at', 'needs_context' => true,], - 'created_on_before' => ['alias' => true, 'alias_for' => 'created_at_before', 'needs_context' => true,], - 'created_on_after' => ['alias' => true, 'alias_for' => 'created_at_after', 'needs_context' => true,], - 'updated_on_on' => ['alias' => true, 'alias_for' => 'updated_at_on', 'needs_context' => true,], - 'updated_on' => ['alias' => true, 'alias_for' => 'updated_at', 'needs_context' => true,], - 'updated_on_before' => ['alias' => true, 'alias_for' => 'updated_at_before', 'needs_context' => true,], - 'updated_on_after' => ['alias' => true, 'alias_for' => 'updated_at_after', 'needs_context' => true,], - 'amount_is' => ['alias' => false, 'needs_context' => true,], - 'amount' => ['alias' => true, 'alias_for' => 'amount_is', 'needs_context' => true,], - 'amount_exactly' => ['alias' => true, 'alias_for' => 'amount_is', 'needs_context' => true,], - 'amount_less' => ['alias' => false, 'needs_context' => true,], - 'amount_max' => ['alias' => true, 'alias_for' => 'amount_less', 'needs_context' => true,], - 'amount_more' => ['alias' => false, 'needs_context' => true,], - 'amount_min' => ['alias' => true, 'alias_for' => 'amount_more', 'needs_context' => true,], - 'foreign_amount_is' => ['alias' => false, 'needs_context' => true,], - 'foreign_amount' => ['alias' => true, 'alias_for' => 'foreign_amount_is', 'needs_context' => true,], - 'foreign_amount_less' => ['alias' => false, 'needs_context' => true,], - 'foreign_amount_max' => ['alias' => true, 'alias_for' => 'foreign_amount_less', 'needs_context' => true,], - 'foreign_amount_more' => ['alias' => false, 'needs_context' => true,], - 'foreign_amount_min' => ['alias' => true, 'alias_for' => 'foreign_amount_more', 'needs_context' => true,], + 'user_action' => ['alias' => false, 'needs_context' => true], + 'account_id' => ['alias' => false, 'needs_context' => true], + 'reconciled' => ['alias' => false, 'needs_context' => false], + 'source_account_id' => ['alias' => false, 'needs_context' => true], + 'destination_account_id' => ['alias' => false, 'needs_context' => true], + 'transaction_type' => ['alias' => false, 'needs_context' => true], + 'type' => ['alias' => true, 'alias_for' => 'transaction_type', 'needs_context' => true], + 'tag_is' => ['alias' => false, 'needs_context' => true], + 'tag_is_not' => ['alias' => false, 'needs_context' => true], + 'tag' => ['alias' => true, 'alias_for' => 'tag_is', 'needs_context' => true], + 'tag_contains' => ['alias' => false, 'needs_context' => true], + 'tag_ends' => ['alias' => false, 'needs_context' => true], + 'tag_starts' => ['alias' => false, 'needs_context' => true], + 'description_is' => ['alias' => false, 'needs_context' => true], + 'description' => ['alias' => true, 'alias_for' => 'description_is', 'needs_context' => true], + 'description_contains' => ['alias' => false, 'needs_context' => true], + 'description_ends' => ['alias' => false, 'needs_context' => true], + 'description_starts' => ['alias' => false, 'needs_context' => true], + 'notes_is' => ['alias' => false, 'needs_context' => true], + 'notes_are' => ['alias' => true, 'alias_for' => 'notes_is', 'needs_context' => true], + 'notes_contains' => ['alias' => false, 'needs_context' => true], + 'notes_contain' => ['alias' => true, 'alias_for' => 'notes_contains', 'needs_context' => true], + 'notes' => ['alias' => true, 'alias_for' => 'notes_contains', 'needs_context' => true], + 'notes_ends' => ['alias' => false, 'needs_context' => true], + 'notes_end' => ['alias' => true, 'alias_for' => 'notes_ends', 'needs_context' => true], + 'notes_starts' => ['alias' => false, 'needs_context' => true], + 'notes_start' => ['alias' => true, 'alias_for' => 'notes_starts', 'needs_context' => true], + 'source_account_is' => ['alias' => false, 'needs_context' => true], + 'from_account_is' => ['alias' => true, 'alias_for' => 'source_account_is', 'needs_context' => true], + 'source_account_contains' => ['alias' => false, 'needs_context' => true], + 'source' => ['alias' => true, 'alias_for' => 'source_account_contains', 'needs_context' => true], + 'from' => ['alias' => true, 'alias_for' => 'source_account_contains', 'needs_context' => true], + 'from_account_contains' => ['alias' => true, 'alias_for' => 'source_account_contains', 'needs_context' => true], + 'source_account_ends' => ['alias' => false, 'needs_context' => true], + 'from_account_ends' => ['alias' => true, 'alias_for' => 'source_account_ends', 'needs_context' => true], + 'source_account_starts' => ['alias' => false, 'needs_context' => true], + 'from_account_starts' => ['alias' => true, 'alias_for' => 'source_account_starts', 'needs_context' => true], + 'source_account_nr_is' => ['alias' => false, 'needs_context' => true], + 'from_account_nr_is' => ['alias' => true, 'alias_for' => 'source_account_nr_is', 'needs_context' => true], + 'source_account_nr_contains' => ['alias' => false, 'needs_context' => true], + 'from_account_nr_contains' => ['alias' => true, 'alias_for' => 'source_account_nr_contains', 'needs_context' => true], + 'source_account_nr_ends' => ['alias' => false, 'needs_context' => true], + 'from_account_nr_ends' => ['alias' => true, 'alias_for' => 'source_account_nr_ends', 'needs_context' => true], + 'source_account_nr_starts' => ['alias' => false, 'needs_context' => true], + 'from_account_nr_starts' => ['alias' => true, 'alias_for' => 'source_account_nr_starts', 'needs_context' => true], + 'destination_account_is' => ['alias' => false, 'needs_context' => true], + 'to_account_is' => ['alias' => true, 'alias_for' => 'destination_account_is', 'needs_context' => true], + 'destination_account_contains' => ['alias' => false, 'needs_context' => true], + 'destination' => ['alias' => true, 'alias_for' => 'destination_account_contains', 'needs_context' => true], + 'to' => ['alias' => true, 'alias_for' => 'destination_account_contains', 'needs_context' => true], + 'to_account_contains' => ['alias' => true, 'alias_for' => 'destination_account_contains', 'needs_context' => true], + 'destination_account_ends' => ['alias' => false, 'needs_context' => true], + 'to_account_ends' => ['alias' => true, 'alias_for' => 'destination_account_ends', 'needs_context' => true], + 'destination_account_starts' => ['alias' => false, 'needs_context' => true], + 'to_account_starts' => ['alias' => true, 'alias_for' => 'destination_account_starts', 'needs_context' => true], + 'destination_account_nr_is' => ['alias' => false, 'needs_context' => true], + 'to_account_nr_is' => ['alias' => true, 'alias_for' => 'destination_account_nr_is', 'needs_context' => true], + 'destination_account_nr_contains' => ['alias' => false, 'needs_context' => true], + 'to_account_nr_contains' => ['alias' => true, 'alias_for' => 'destination_account_nr_contains', 'needs_context' => true], + 'destination_account_nr_ends' => ['alias' => false, 'needs_context' => true], + 'to_account_nr_ends' => ['alias' => true, 'alias_for' => 'destination_account_nr_ends', 'needs_context' => true], + 'destination_account_nr_starts' => ['alias' => false, 'needs_context' => true], + 'to_account_nr_starts' => ['alias' => true, 'alias_for' => 'destination_account_nr_starts', 'needs_context' => true], + 'account_is' => ['alias' => false, 'needs_context' => true], + 'account_contains' => ['alias' => false, 'needs_context' => true], + 'account_ends' => ['alias' => false, 'needs_context' => true], + 'account_starts' => ['alias' => false, 'needs_context' => true], + 'account_nr_is' => ['alias' => false, 'needs_context' => true], + 'account_nr_contains' => ['alias' => false, 'needs_context' => true], + 'account_nr_ends' => ['alias' => false, 'needs_context' => true], + 'account_nr_starts' => ['alias' => false, 'needs_context' => true], + 'category_is' => ['alias' => false, 'needs_context' => true], + 'category_contains' => ['alias' => false, 'needs_context' => true], + 'category' => ['alias' => true, 'alias_for' => 'category_contains', 'needs_context' => true], + 'category_ends' => ['alias' => false, 'needs_context' => true], + 'category_starts' => ['alias' => false, 'needs_context' => true], + 'budget_is' => ['alias' => false, 'needs_context' => true], + 'budget_contains' => ['alias' => false, 'needs_context' => true], + 'budget' => ['alias' => true, 'alias_for' => 'budget_contains', 'needs_context' => true], + 'budget_ends' => ['alias' => false, 'needs_context' => true], + 'budget_starts' => ['alias' => false, 'needs_context' => true], + 'bill_is' => ['alias' => false, 'needs_context' => true], + 'bill_contains' => ['alias' => false, 'needs_context' => true], + 'bill' => ['alias' => true, 'alias_for' => 'bill_contains', 'needs_context' => true], + 'bill_ends' => ['alias' => false, 'needs_context' => true], + 'bill_starts' => ['alias' => false, 'needs_context' => true], + 'external_id_is' => ['alias' => false, 'needs_context' => true], + 'external_id_contains' => ['alias' => false, 'needs_context' => true], + 'external_id' => ['alias' => true, 'alias_for' => 'external_id_contains', 'needs_context' => true], + 'external_id_ends' => ['alias' => false, 'needs_context' => true], + 'external_id_starts' => ['alias' => false, 'needs_context' => true], + 'internal_reference_is' => ['alias' => false, 'needs_context' => true], + 'internal_reference_contains' => ['alias' => false, 'needs_context' => true], + 'internal_reference' => ['alias' => true, 'alias_for' => 'internal_reference_contains', 'needs_context' => true], + 'internal_reference_ends' => ['alias' => false, 'needs_context' => true], + 'internal_reference_starts' => ['alias' => false, 'needs_context' => true], + 'external_url_is' => ['alias' => false, 'needs_context' => true], + 'external_url_contains' => ['alias' => false, 'needs_context' => true], + 'external_url' => ['alias' => true, 'alias_for' => 'external_url_contains', 'needs_context' => true], + 'external_url_ends' => ['alias' => false, 'needs_context' => true], + 'external_url_starts' => ['alias' => false, 'needs_context' => true], + 'has_attachments' => ['alias' => false, 'needs_context' => false], + 'has_any_category' => ['alias' => false, 'needs_context' => false], + 'has_any_budget' => ['alias' => false, 'needs_context' => false], + 'has_any_bill' => ['alias' => false, 'needs_context' => false], + 'has_any_tag' => ['alias' => false, 'needs_context' => false], + 'any_notes' => ['alias' => false, 'needs_context' => false], + 'has_any_notes' => ['alias' => true, 'alias_for' => 'any_notes', 'needs_context' => false], + 'any_external_url' => ['alias' => false, 'needs_context' => false], + 'has_any_external_url' => ['alias' => true, 'alias_for' => 'any_external_url', 'needs_context' => false], + 'has_no_attachments' => ['alias' => false, 'needs_context' => false], + 'has_no_category' => ['alias' => false, 'needs_context' => false], + 'has_no_budget' => ['alias' => false, 'needs_context' => false], + 'has_no_bill' => ['alias' => false, 'needs_context' => false], + 'has_no_tag' => ['alias' => false, 'needs_context' => false], + 'no_notes' => ['alias' => false, 'needs_context' => false], + 'no_external_url' => ['alias' => false, 'needs_context' => false], + 'source_is_cash' => ['alias' => false, 'needs_context' => false], + 'destination_is_cash' => ['alias' => false, 'needs_context' => false], + 'account_is_cash' => ['alias' => false, 'needs_context' => false], + 'currency_is' => ['alias' => false, 'needs_context' => true], + 'foreign_currency_is' => ['alias' => false, 'needs_context' => true], + 'id' => ['alias' => false, 'trigger_class' => '', 'needs_context' => true], + 'journal_id' => ['alias' => false, 'trigger_class' => '', 'needs_context' => true], + 'recurrence_id' => ['alias' => false, 'trigger_class' => '', 'needs_context' => true], + 'date_on' => ['alias' => false, 'needs_context' => true], + 'date' => ['alias' => true, 'alias_for' => 'date_on', 'needs_context' => true], + 'date_is' => ['alias' => true, 'alias_for' => 'date_on', 'needs_context' => true], + 'on' => ['alias' => true, 'alias_for' => 'date_on', 'needs_context' => true], + 'date_before' => ['alias' => false, 'needs_context' => true], + 'before' => ['alias' => true, 'alias_for' => 'date_before', 'needs_context' => true], + 'date_after' => ['alias' => false, 'needs_context' => true], + 'after' => ['alias' => true, 'alias_for' => 'date_after', 'needs_context' => true], + 'interest_date_on' => ['alias' => false, 'needs_context' => true], + 'interest_date' => ['alias' => true, 'alias_for' => 'interest_date_on', 'needs_context' => true], + 'interest_date_is' => ['alias' => true, 'alias_for' => 'interest_date_on', 'needs_context' => true], + 'interest_date_before' => ['alias' => false, 'needs_context' => true], + 'interest_date_after' => ['alias' => false, 'needs_context' => true], + 'book_date_on' => ['alias' => false, 'needs_context' => true], + 'book_date' => ['alias' => true, 'alias_for' => 'book_date_on', 'needs_context' => true], + 'book_date_is' => ['alias' => true, 'alias_for' => 'book_date_on', 'needs_context' => true], + 'book_date_before' => ['alias' => false, 'needs_context' => true], + 'book_date_after' => ['alias' => false, 'needs_context' => true], + 'process_date_on' => ['alias' => false, 'needs_context' => true], + 'process_date' => ['alias' => true, 'alias_for' => 'process_date_on', 'needs_context' => true], + 'process_date_is' => ['alias' => true, 'alias_for' => 'process_date_on', 'needs_context' => true], + 'process_date_before' => ['alias' => false, 'needs_context' => true], + 'process_date_after' => ['alias' => false, 'needs_context' => true], + 'due_date_on' => ['alias' => false, 'needs_context' => true], + 'due_date' => ['alias' => true, 'alias_for' => 'due_date_on', 'needs_context' => true], + 'due_date_is' => ['alias' => true, 'alias_for' => 'due_date_on', 'needs_context' => true], + 'due_date_before' => ['alias' => false, 'needs_context' => true], + 'due_date_after' => ['alias' => false, 'needs_context' => true], + 'payment_date_on' => ['alias' => false, 'needs_context' => true], + 'payment_date' => ['alias' => true, 'alias_for' => 'payment_date_on', 'needs_context' => true], + 'payment_date_is' => ['alias' => true, 'alias_for' => 'payment_date_on', 'needs_context' => true], + 'payment_date_before' => ['alias' => false, 'needs_context' => true], + 'payment_date_after' => ['alias' => false, 'needs_context' => true], + 'invoice_date_on' => ['alias' => false, 'needs_context' => true], + 'invoice_date' => ['alias' => true, 'alias_for' => 'invoice_date_on', 'needs_context' => true], + 'invoice_date_is' => ['alias' => true, 'alias_for' => 'invoice_date_on', 'needs_context' => true], + 'invoice_date_before' => ['alias' => false, 'needs_context' => true], + 'invoice_date_after' => ['alias' => false, 'needs_context' => true], + 'created_at_on' => ['alias' => false, 'needs_context' => true], + 'created_at' => ['alias' => true, 'alias_for' => 'created_at_on', 'needs_context' => true], + 'created_at_is' => ['alias' => true, 'alias_for' => 'created_at_on', 'needs_context' => true], + 'created_at_before' => ['alias' => false, 'needs_context' => true], + 'created_at_after' => ['alias' => false, 'needs_context' => true], + 'updated_at_on' => ['alias' => false, 'needs_context' => true], + 'updated_at' => ['alias' => true, 'alias_for' => 'updated_at_on', 'needs_context' => true], + 'updated_at_is' => ['alias' => true, 'alias_for' => 'updated_at_on', 'needs_context' => true], + 'updated_at_before' => ['alias' => false, 'needs_context' => true], + 'updated_at_after' => ['alias' => false, 'needs_context' => true], + 'created_on_on' => ['alias' => true, 'alias_for' => 'created_at_on', 'needs_context' => true], + 'created_on' => ['alias' => true, 'alias_for' => 'created_at', 'needs_context' => true], + 'created_on_before' => ['alias' => true, 'alias_for' => 'created_at_before', 'needs_context' => true], + 'created_on_after' => ['alias' => true, 'alias_for' => 'created_at_after', 'needs_context' => true], + 'updated_on_on' => ['alias' => true, 'alias_for' => 'updated_at_on', 'needs_context' => true], + 'updated_on' => ['alias' => true, 'alias_for' => 'updated_at', 'needs_context' => true], + 'updated_on_before' => ['alias' => true, 'alias_for' => 'updated_at_before', 'needs_context' => true], + 'updated_on_after' => ['alias' => true, 'alias_for' => 'updated_at_after', 'needs_context' => true], + 'amount_is' => ['alias' => false, 'needs_context' => true], + 'amount' => ['alias' => true, 'alias_for' => 'amount_is', 'needs_context' => true], + 'amount_exactly' => ['alias' => true, 'alias_for' => 'amount_is', 'needs_context' => true], + 'amount_less' => ['alias' => false, 'needs_context' => true], + 'amount_max' => ['alias' => true, 'alias_for' => 'amount_less', 'needs_context' => true], + 'amount_more' => ['alias' => false, 'needs_context' => true], + 'amount_min' => ['alias' => true, 'alias_for' => 'amount_more', 'needs_context' => true], + 'foreign_amount_is' => ['alias' => false, 'needs_context' => true], + 'foreign_amount' => ['alias' => true, 'alias_for' => 'foreign_amount_is', 'needs_context' => true], + 'foreign_amount_less' => ['alias' => false, 'needs_context' => true], + 'foreign_amount_max' => ['alias' => true, 'alias_for' => 'foreign_amount_less', 'needs_context' => true], + 'foreign_amount_more' => ['alias' => false, 'needs_context' => true], + 'foreign_amount_min' => ['alias' => true, 'alias_for' => 'foreign_amount_more', 'needs_context' => true], 'attachment_name_is' => ['alias' => false, 'needs_context' => true], 'attachment' => ['alias' => true, 'alias_for' => 'attachment_name_is', 'needs_context' => true], 'attachment_is' => ['alias' => true, 'alias_for' => 'attachment_name_is', 'needs_context' => true], @@ -233,10 +233,9 @@ return [ 'attachment_notes_start' => ['alias' => true, 'alias_for' => 'attachment_notes_starts', 'needs_context' => true], 'attachment_notes_ends' => ['alias' => false, 'needs_context' => true], 'attachment_notes_end' => ['alias' => true, 'alias_for' => 'attachment_notes_ends', 'needs_context' => true], - 'exists' => ['alias' => false, 'needs_context' => false,], + 'exists' => ['alias' => false, 'needs_context' => false], 'sepa_ct_is' => ['alias' => false, 'needs_context' => true], 'no_external_id' => ['alias' => false, 'needs_context' => false], 'any_external_id' => ['alias' => false, 'needs_context' => false], - ], ]; diff --git a/config/twigbridge.php b/config/twigbridge.php index 70ecf8d088..5f3dec4e86 100644 --- a/config/twigbridge.php +++ b/config/twigbridge.php @@ -22,8 +22,7 @@ declare(strict_types=1); - -/** +/* * This file is part of the TwigBridge package. * * @copyright Robert Crowe @@ -53,11 +52,8 @@ use TwigBridge\Extension\Loader\Filters; use TwigBridge\Extension\Loader\Functions; use TwigBridge\Extension\Loader\Globals; -/** - * Configuration options for Twig. - */ +// Configuration options for Twig. return [ - 'twig' => [ 'extension' => 'twig', 'environment' => [ @@ -95,7 +91,6 @@ return [ ], 'extensions' => [ - /* |-------------------------------------------------------------------------- | Extensions @@ -193,7 +188,6 @@ return [ 'amountNoCurrency', 'percentage', 'objectGroup', - ], ], 'AccountForm' => [ diff --git a/config/user_roles.php b/config/user_roles.php index e7c057a2b4..6fac33621c 100644 --- a/config/user_roles.php +++ b/config/user_roles.php @@ -22,13 +22,12 @@ declare(strict_types=1); - use FireflyIII\Enums\UserRoleEnum; $result = []; - foreach (UserRoleEnum::cases() as $role) { $result[$role->value] = []; } + return $result; diff --git a/config/view.php b/config/view.php index 20ff1589d5..d240484f07 100644 --- a/config/view.php +++ b/config/view.php @@ -28,7 +28,6 @@ if ('v2' === env('FIREFLY_III_LAYOUT')) { realpath(base_path('resources/views'))]; } - return [ /* |-------------------------------------------------------------------------- @@ -55,5 +54,4 @@ return [ */ 'compiled' => realpath(storage_path('framework/views')), - ]; 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 37faf813de..d1580d5f4d 100644 --- a/database/migrations/2016_06_16_000000_create_support_tables.php +++ b/database/migrations/2016_06_16_000000_create_support_tables.php @@ -54,6 +54,7 @@ class CreateSupportTables extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void @@ -70,16 +71,13 @@ class CreateSupportTables extends Migration $this->createConfigurationTable(); } - /** - * @return void - */ private function createAccountTypeTable(): void { if (!Schema::hasTable('account_types')) { try { Schema::create( 'account_types', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->string('type', 50); @@ -95,16 +93,13 @@ class CreateSupportTables extends Migration } } - /** - * @return void - */ private function createCurrencyTable(): void { if (!Schema::hasTable('transaction_currencies')) { try { Schema::create( 'transaction_currencies', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -123,16 +118,13 @@ class CreateSupportTables extends Migration } } - /** - * @return void - */ private function createTransactionTypeTable(): void { if (!Schema::hasTable('transaction_types')) { try { Schema::create( 'transaction_types', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -149,16 +141,13 @@ class CreateSupportTables extends Migration } } - /** - * @return void - */ private function createJobsTable(): void { if (!Schema::hasTable('jobs')) { try { Schema::create( 'jobs', - static function (Blueprint $table) { + static function (Blueprint $table): void { // straight from Laravel $table->bigIncrements('id'); $table->string('queue'); @@ -178,16 +167,13 @@ class CreateSupportTables extends Migration } } - /** - * @return void - */ private function createPasswordTable(): void { if (!Schema::hasTable('password_resets')) { try { Schema::create( 'password_resets', - static function (Blueprint $table) { + static function (Blueprint $table): void { // straight from laravel $table->string('email')->index(); $table->string('token')->index(); @@ -201,16 +187,13 @@ class CreateSupportTables extends Migration } } - /** - * @return void - */ private function createPermissionsTable(): void { if (!Schema::hasTable('permissions')) { try { Schema::create( 'permissions', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->string('name')->unique(); @@ -225,16 +208,13 @@ class CreateSupportTables extends Migration } } - /** - * @return void - */ private function createRolesTable(): void { if (!Schema::hasTable('roles')) { try { Schema::create( 'roles', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->string('name')->unique(); @@ -249,16 +229,13 @@ class CreateSupportTables extends Migration } } - /** - * @return void - */ private function createPermissionRoleTable(): void { if (!Schema::hasTable('permission_role')) { try { Schema::create( 'permission_role', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->integer('permission_id')->unsigned(); $table->integer('role_id')->unsigned(); @@ -275,16 +252,13 @@ class CreateSupportTables extends Migration } } - /** - * @return void - */ private function createSessionsTable(): void { if (!Schema::hasTable('sessions')) { try { Schema::create( 'sessions', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->string('id')->unique(); $table->integer('user_id')->nullable(); $table->string('ip_address', 45)->nullable(); @@ -300,16 +274,13 @@ class CreateSupportTables extends Migration } } - /** - * @return void - */ private function createConfigurationTable(): void { if (!Schema::hasTable('configuration')) { try { Schema::create( 'configuration', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); 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 2b7f8c9682..5002e26302 100644 --- a/database/migrations/2016_06_16_000001_create_users_table.php +++ b/database/migrations/2016_06_16_000001_create_users_table.php @@ -45,8 +45,8 @@ class CreateUsersTable extends Migration /** * Run the migrations. - * @SuppressWarnings(PHPMD.ShortMethodName) * + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { @@ -54,7 +54,7 @@ class CreateUsersTable extends Migration try { Schema::create( 'users', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->string('email', 255); 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 6c85d671c0..71b842a467 100644 --- a/database/migrations/2016_06_16_000002_create_main_tables.php +++ b/database/migrations/2016_06_16_000002_create_main_tables.php @@ -72,8 +72,8 @@ class CreateMainTables extends Migration /** * Run the migrations. - * @SuppressWarnings(PHPMD.ShortMethodName) * + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { @@ -96,7 +96,7 @@ class CreateMainTables extends Migration try { Schema::create( 'accounts', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -121,7 +121,7 @@ class CreateMainTables extends Migration try { Schema::create( 'account_meta', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->integer('account_id', false, true); @@ -143,7 +143,7 @@ class CreateMainTables extends Migration try { Schema::create( 'piggy_banks', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -168,7 +168,7 @@ class CreateMainTables extends Migration try { Schema::create( 'piggy_bank_repetitions', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->integer('piggy_bank_id', false, true); @@ -191,7 +191,7 @@ class CreateMainTables extends Migration try { Schema::create( 'attachments', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -224,7 +224,7 @@ class CreateMainTables extends Migration try { Schema::create( 'bills', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -252,15 +252,13 @@ class CreateMainTables extends Migration } } - /** - */ private function createBudgetTables(): void { if (!Schema::hasTable('budgets')) { try { Schema::create( 'budgets', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -280,7 +278,7 @@ class CreateMainTables extends Migration try { Schema::create( 'budget_limits', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->integer('budget_id', false, true); @@ -300,7 +298,7 @@ class CreateMainTables extends Migration try { Schema::create( 'limit_repetitions', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->integer('budget_limit_id', false, true); @@ -317,16 +315,13 @@ class CreateMainTables extends Migration } } - /** - * @return void - */ private function createCategoriesTable(): void { if (!Schema::hasTable('categories')) { try { Schema::create( 'categories', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -351,7 +346,7 @@ class CreateMainTables extends Migration try { Schema::create( 'preferences', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->integer('user_id', false, true); @@ -368,16 +363,13 @@ class CreateMainTables extends Migration } } - /** - * @return void - */ private function createRoleTable(): void { if (!Schema::hasTable('role_user')) { try { Schema::create( 'role_user', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->integer('user_id', false, true); $table->integer('role_id', false, true); @@ -394,13 +386,16 @@ class CreateMainTables extends Migration } } + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ private function createRuleTables(): void { if (!Schema::hasTable('rule_groups')) { try { Schema::create( 'rule_groups', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -423,7 +418,7 @@ class CreateMainTables extends Migration try { Schema::create( 'rules', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -451,7 +446,7 @@ class CreateMainTables extends Migration try { Schema::create( 'rule_actions', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->integer('rule_id', false, true); @@ -476,7 +471,7 @@ class CreateMainTables extends Migration try { Schema::create( 'rule_triggers', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->integer('rule_id', false, true); @@ -499,16 +494,13 @@ class CreateMainTables extends Migration } } - /** - * @return void - */ private function createTagsTable(): void { if (!Schema::hasTable('tags')) { try { Schema::create( 'tags', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -534,7 +526,8 @@ class CreateMainTables extends Migration } /** - * @return void + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.NPathComplexity) */ private function createTransactionTables(): void { @@ -542,7 +535,7 @@ class CreateMainTables extends Migration try { Schema::create( 'transaction_journals', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -575,7 +568,7 @@ class CreateMainTables extends Migration try { Schema::create( 'journal_meta', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->integer('transaction_journal_id', false, true); @@ -595,7 +588,7 @@ class CreateMainTables extends Migration try { Schema::create( 'tag_transaction_journal', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->integer('tag_id', false, true); $table->integer('transaction_journal_id', false, true); @@ -616,7 +609,7 @@ class CreateMainTables extends Migration try { Schema::create( 'budget_transaction_journal', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->integer('budget_id', false, true); $table->integer('transaction_journal_id', false, true); @@ -634,7 +627,7 @@ class CreateMainTables extends Migration try { Schema::create( 'category_transaction_journal', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->integer('category_id', false, true); $table->integer('transaction_journal_id', false, true); @@ -652,7 +645,7 @@ class CreateMainTables extends Migration try { Schema::create( 'piggy_bank_events', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->integer('piggy_bank_id', false, true); @@ -674,7 +667,7 @@ class CreateMainTables extends Migration try { Schema::create( 'transactions', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -697,7 +690,7 @@ class CreateMainTables extends Migration try { Schema::create( 'budget_transaction', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->integer('budget_id', false, true); $table->integer('transaction_id', false, true); @@ -716,7 +709,7 @@ class CreateMainTables extends Migration try { Schema::create( 'category_transaction', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->integer('category_id', false, true); $table->integer('transaction_id', false, true); 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 f1aa6f3e42..2743c4fad5 100644 --- a/database/migrations/2016_08_25_091522_changes_for_3101.php +++ b/database/migrations/2016_08_25_091522_changes_for_3101.php @@ -37,8 +37,8 @@ class ChangesFor3101 extends Migration /** * Run the migrations. - * @SuppressWarnings(PHPMD.ShortMethodName) * + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void {} } diff --git a/database/migrations/2016_09_12_121359_fix_nullables.php b/database/migrations/2016_09_12_121359_fix_nullables.php index 0394d5b810..3f8d858915 100644 --- a/database/migrations/2016_09_12_121359_fix_nullables.php +++ b/database/migrations/2016_09_12_121359_fix_nullables.php @@ -42,8 +42,8 @@ class FixNullables extends Migration /** * Run the migrations. - * @SuppressWarnings(PHPMD.ShortMethodName) * + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { @@ -51,7 +51,7 @@ class FixNullables extends Migration try { Schema::table( 'rule_groups', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->text('description')->nullable()->change(); } ); @@ -65,7 +65,7 @@ class FixNullables extends Migration try { Schema::table( 'rules', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->text('description')->nullable()->change(); } ); 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 d89351ab75..29a356f96d 100644 --- a/database/migrations/2016_10_09_150037_expand_transactions_table.php +++ b/database/migrations/2016_10_09_150037_expand_transactions_table.php @@ -42,11 +42,11 @@ class ExpandTransactionsTable extends Migration try { Schema::table( 'transactions', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('identifier'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not drop column "identifier": %s', $e->getMessage())); app('log')->error('If the column does not exist, this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -55,8 +55,8 @@ class ExpandTransactionsTable extends Migration /** * Run the migrations. - * @SuppressWarnings(PHPMD.ShortMethodName) * + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { @@ -64,7 +64,7 @@ class ExpandTransactionsTable extends Migration try { Schema::table( 'transactions', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->smallInteger('identifier', false, true)->default(0); } ); 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 54f9d108e2..417bc70afe 100644 --- a/database/migrations/2016_10_22_075804_changes_for_v410.php +++ b/database/migrations/2016_10_22_075804_changes_for_v410.php @@ -42,6 +42,7 @@ class ChangesForV410 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void @@ -50,7 +51,7 @@ class ChangesForV410 extends Migration try { Schema::create( 'notes', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); 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 a2e9afe412..54a8c898c3 100644 --- a/database/migrations/2016_11_24_210552_changes_for_v420.php +++ b/database/migrations/2016_11_24_210552_changes_for_v420.php @@ -41,7 +41,7 @@ class ChangesForV420 extends Migration try { Schema::table( 'journal_meta', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropSoftDeletes(); } ); @@ -54,8 +54,8 @@ class ChangesForV420 extends Migration /** * Run the migrations. - * @SuppressWarnings(PHPMD.ShortMethodName) * + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { @@ -63,7 +63,7 @@ class ChangesForV420 extends Migration try { Schema::table( 'journal_meta', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->softDeletes(); } ); 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 b265485128..dce36a973d 100644 --- a/database/migrations/2016_12_22_150431_changes_for_v430.php +++ b/database/migrations/2016_12_22_150431_changes_for_v430.php @@ -42,6 +42,7 @@ class ChangesForV430 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void @@ -50,7 +51,7 @@ class ChangesForV430 extends Migration try { Schema::create( 'available_budgets', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); 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 c177e7317e..8c20187715 100644 --- a/database/migrations/2016_12_28_203205_changes_for_v431.php +++ b/database/migrations/2016_12_28_203205_changes_for_v431.php @@ -43,7 +43,7 @@ class ChangesForV431 extends Migration try { Schema::table( 'budget_limits', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->string('repeat_freq', 30)->nullable(); } ); @@ -56,7 +56,7 @@ class ChangesForV431 extends Migration try { Schema::table( 'budget_limits', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->boolean('repeats')->default(0); } ); @@ -70,11 +70,11 @@ class ChangesForV431 extends Migration try { Schema::table( 'budget_limits', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->renameColumn('start_date', 'startdate'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -85,11 +85,11 @@ class ChangesForV431 extends Migration try { Schema::table( 'budget_limits', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('end_date'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -99,11 +99,11 @@ class ChangesForV431 extends Migration try { Schema::table( 'transaction_currencies', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('decimal_places'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -112,6 +112,7 @@ class ChangesForV431 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void @@ -121,7 +122,7 @@ class ChangesForV431 extends Migration try { Schema::table( 'transaction_currencies', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->smallInteger('decimal_places', false, true)->default(2); } ); @@ -136,11 +137,11 @@ class ChangesForV431 extends Migration try { Schema::table( 'budget_limits', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->renameColumn('startdate', 'start_date'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -151,7 +152,7 @@ class ChangesForV431 extends Migration try { Schema::table( 'budget_limits', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->date('end_date')->nullable()->after('start_date'); } ); @@ -166,11 +167,11 @@ class ChangesForV431 extends Migration try { Schema::table( 'budget_limits', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('repeats'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -179,11 +180,11 @@ class ChangesForV431 extends Migration try { Schema::table( 'budget_limits', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('repeat_freq'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } 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 5252a8042a..075c03181c 100644 --- a/database/migrations/2017_04_13_163623_changes_for_v440.php +++ b/database/migrations/2017_04_13_163623_changes_for_v440.php @@ -39,10 +39,11 @@ class ChangesForV440 extends Migration public function down(): void { Schema::dropIfExists('currency_exchange_rates'); + try { Schema::table( 'transactions', - static function (Blueprint $table) { + static function (Blueprint $table): void { if (Schema::hasColumn('transactions', 'transaction_currency_id')) { // cannot drop foreign keys in SQLite: if ('sqlite' !== config('database.default')) { @@ -52,7 +53,7 @@ class ChangesForV440 extends Migration } } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -60,6 +61,7 @@ class ChangesForV440 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void @@ -68,7 +70,7 @@ class ChangesForV440 extends Migration try { Schema::create( 'currency_exchange_rates', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -93,7 +95,7 @@ class ChangesForV440 extends Migration try { Schema::table( 'transactions', - static function (Blueprint $table) { + static function (Blueprint $table): void { if (!Schema::hasColumn('transactions', 'transaction_currency_id')) { $table->integer('transaction_currency_id', false, true)->after('description')->nullable(); $table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('set null'); 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 e0db079ec9..b1e5267036 100644 --- a/database/migrations/2017_06_02_105232_changes_for_v450.php +++ b/database/migrations/2017_06_02_105232_changes_for_v450.php @@ -43,11 +43,11 @@ class ChangesForV450 extends Migration try { Schema::table( 'transactions', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('foreign_amount'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -56,7 +56,7 @@ class ChangesForV450 extends Migration try { Schema::table( 'transactions', - static function (Blueprint $table) { + static function (Blueprint $table): void { // cannot drop foreign keys in SQLite: if ('sqlite' !== config('database.default')) { $table->dropForeign('transactions_foreign_currency_id_foreign'); @@ -71,11 +71,11 @@ class ChangesForV450 extends Migration try { Schema::table( 'transactions', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('foreign_currency_id'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -84,6 +84,7 @@ class ChangesForV450 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void @@ -93,7 +94,7 @@ class ChangesForV450 extends Migration try { Schema::table( 'transactions', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->decimal('foreign_amount', 32, 12)->nullable()->after('amount'); } ); @@ -108,7 +109,7 @@ class ChangesForV450 extends Migration try { Schema::table( 'transactions', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->integer('foreign_currency_id', false, true)->default(null)->after('foreign_amount')->nullable(); $table->foreign('foreign_currency_id')->references('id')->on('transaction_currencies')->onDelete('set null'); } 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 4ab5e7d0c1..0c3794cda4 100644 --- a/database/migrations/2017_08_20_062014_changes_for_v470.php +++ b/database/migrations/2017_08_20_062014_changes_for_v470.php @@ -44,6 +44,7 @@ class ChangesForV470 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void @@ -52,7 +53,7 @@ class ChangesForV470 extends Migration try { Schema::create( 'link_types', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -74,7 +75,7 @@ class ChangesForV470 extends Migration try { Schema::create( 'journal_links', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->integer('link_type_id', false, true); 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 e9ffe8c980..01e7460537 100644 --- a/database/migrations/2017_11_04_170844_changes_for_v470a.php +++ b/database/migrations/2017_11_04_170844_changes_for_v470a.php @@ -43,11 +43,11 @@ class ChangesForV470a extends Migration try { Schema::table( 'transactions', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('reconciled'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -56,6 +56,7 @@ class ChangesForV470a extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void @@ -64,7 +65,7 @@ class ChangesForV470a extends Migration try { Schema::table( 'transactions', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->boolean('reconciled')->after('deleted_at')->default(0); } ); 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 e04833531e..cb70131bf2 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 @@ -44,6 +44,7 @@ class CreateOauthAuthCodesTable extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void @@ -52,7 +53,7 @@ class CreateOauthAuthCodesTable extends Migration try { Schema::create( 'oauth_auth_codes', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->string('id', 100)->primary(); $table->integer('user_id'); $table->integer('client_id'); 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 bb9f1cdd93..cc41b105bd 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 @@ -44,6 +44,7 @@ class CreateOauthAccessTokensTable extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void @@ -52,7 +53,7 @@ class CreateOauthAccessTokensTable extends Migration try { Schema::create( 'oauth_access_tokens', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->string('id', 100)->primary(); $table->integer('user_id')->index()->nullable(); $table->integer('client_id'); 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 324f0e213d..3b91d74abc 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 @@ -44,6 +44,7 @@ class CreateOauthRefreshTokensTable extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void @@ -52,7 +53,7 @@ class CreateOauthRefreshTokensTable extends Migration try { Schema::create( 'oauth_refresh_tokens', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->string('id', 100)->primary(); $table->string('access_token_id', 100)->index(); $table->boolean('revoked'); 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 16ef069c02..7c75715cb0 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 @@ -44,6 +44,7 @@ class CreateOauthClientsTable extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void @@ -52,7 +53,7 @@ class CreateOauthClientsTable extends Migration try { Schema::create( 'oauth_clients', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->integer('user_id')->index()->nullable(); $table->string('name'); 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 4dd78aea8c..2bdfa66075 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 @@ -44,6 +44,7 @@ class CreateOauthPersonalAccessClientsTable extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void @@ -52,7 +53,7 @@ class CreateOauthPersonalAccessClientsTable extends Migration try { Schema::create( 'oauth_personal_access_clients', - static function (Blueprint $table) { + static function (Blueprint $table): void { $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 3ee040ca42..b6919563bc 100644 --- a/database/migrations/2018_03_19_141348_changes_for_v472.php +++ b/database/migrations/2018_03_19_141348_changes_for_v472.php @@ -36,8 +36,6 @@ class ChangesForV472 extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -45,7 +43,7 @@ class ChangesForV472 extends Migration try { Schema::table( 'attachments', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->text('notes')->nullable(); } ); @@ -59,11 +57,11 @@ class ChangesForV472 extends Migration try { Schema::table( 'budgets', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('order'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -73,7 +71,6 @@ class ChangesForV472 extends Migration /** * Run the migrations. * - * @return void * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void @@ -82,11 +79,11 @@ class ChangesForV472 extends Migration try { Schema::table( 'attachments', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('notes'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -96,7 +93,7 @@ class ChangesForV472 extends Migration try { Schema::table( 'budgets', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->mediumInteger('order', false, true)->default(0); } ); 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 e52f0ef5a2..b9ba91e78c 100644 --- a/database/migrations/2018_04_07_210913_changes_for_v473.php +++ b/database/migrations/2018_04_07_210913_changes_for_v473.php @@ -37,8 +37,6 @@ class ChangesForV473 extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -46,7 +44,7 @@ class ChangesForV473 extends Migration try { Schema::table( 'bills', - static function (Blueprint $table) { + static function (Blueprint $table): void { // cannot drop foreign keys in SQLite: if ('sqlite' !== config('database.default')) { $table->dropForeign('bills_transaction_currency_id_foreign'); @@ -54,7 +52,7 @@ class ChangesForV473 extends Migration $table->dropColumn('transaction_currency_id'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -64,11 +62,11 @@ class ChangesForV473 extends Migration try { Schema::table( 'rules', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('strict'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -77,8 +75,8 @@ class ChangesForV473 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { @@ -86,7 +84,7 @@ class ChangesForV473 extends Migration try { Schema::table( 'bills', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->integer('transaction_currency_id', false, true)->nullable()->after('user_id'); $table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('set null'); } @@ -100,7 +98,7 @@ class ChangesForV473 extends Migration try { Schema::table( 'rules', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->boolean('strict')->default(true); } ); 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 719f01d4f8..d78013e8ec 100644 --- a/database/migrations/2018_04_29_174524_changes_for_v474.php +++ b/database/migrations/2018_04_29_174524_changes_for_v474.php @@ -33,15 +33,12 @@ class ChangesForV474 extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void {} /** * Run the migrations. * - * @return void * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void {} 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 2426126da4..7a73769960 100644 --- a/database/migrations/2018_06_08_200526_changes_for_v475.php +++ b/database/migrations/2018_06_08_200526_changes_for_v475.php @@ -35,8 +35,6 @@ class ChangesForV475 extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -49,8 +47,9 @@ class ChangesForV475 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function up(): void { @@ -58,7 +57,7 @@ class ChangesForV475 extends Migration try { Schema::create( 'recurrences', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -89,7 +88,7 @@ class ChangesForV475 extends Migration try { Schema::create( 'recurrences_transactions', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -120,7 +119,7 @@ class ChangesForV475 extends Migration try { Schema::create( 'recurrences_repetitions', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -143,7 +142,7 @@ class ChangesForV475 extends Migration try { Schema::create( 'recurrences_meta', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -165,7 +164,7 @@ class ChangesForV475 extends Migration try { Schema::create( 'rt_meta', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); 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 e93b73cea1..b4c1e8c9fb 100644 --- a/database/migrations/2018_09_05_195147_changes_for_v477.php +++ b/database/migrations/2018_09_05_195147_changes_for_v477.php @@ -36,8 +36,6 @@ class ChangesForV477 extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -45,7 +43,7 @@ class ChangesForV477 extends Migration try { Schema::table( 'budget_limits', - static function (Blueprint $table) { + static function (Blueprint $table): void { // cannot drop foreign keys in SQLite: if ('sqlite' !== config('database.default')) { $table->dropForeign('budget_limits_transaction_currency_id_foreign'); @@ -54,7 +52,7 @@ class ChangesForV477 extends Migration $table->dropColumn(['transaction_currency_id']); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -63,8 +61,8 @@ class ChangesForV477 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { @@ -72,7 +70,7 @@ class ChangesForV477 extends Migration try { Schema::table( 'budget_limits', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->integer('transaction_currency_id', false, true)->nullable()->after('budget_id'); $table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('set null'); } 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 31269943b2..f391bc5431 100644 --- a/database/migrations/2018_11_06_172532_changes_for_v479.php +++ b/database/migrations/2018_11_06_172532_changes_for_v479.php @@ -36,8 +36,6 @@ class ChangesForV479 extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -45,11 +43,11 @@ class ChangesForV479 extends Migration try { Schema::table( 'transaction_currencies', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn(['enabled']); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -58,8 +56,8 @@ class ChangesForV479 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { @@ -67,7 +65,7 @@ class ChangesForV479 extends Migration try { Schema::table( 'transaction_currencies', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->boolean('enabled')->default(0)->after('deleted_at'); } ); 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 118b7848e4..26d1423964 100644 --- a/database/migrations/2019_01_28_193833_changes_for_v4710.php +++ b/database/migrations/2019_01_28_193833_changes_for_v4710.php @@ -36,8 +36,6 @@ class ChangesForV4710 extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -47,8 +45,8 @@ class ChangesForV4710 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { @@ -56,7 +54,7 @@ class ChangesForV4710 extends Migration try { Schema::create( 'transaction_groups', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -76,7 +74,7 @@ class ChangesForV4710 extends Migration try { Schema::create( 'group_journals', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->integer('transaction_group_id', false, true); $table->integer('transaction_journal_id', false, true); 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 bbdb40e8c7..b9b4d8a7de 100644 --- a/database/migrations/2019_02_05_055516_changes_for_v4711.php +++ b/database/migrations/2019_02_05_055516_changes_for_v4711.php @@ -36,22 +36,17 @@ class ChangesForV4711 extends Migration { /** * Reverse the migrations. - * - * @return void */ - public function down(): void - { - // - } + public function down(): void {} /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { - /** + /* * In 4.7.11, I changed the date field to a "datetimetz" field. This wreaks havoc * because apparently MySQL is not actually capable of handling multiple time zones, * only having a server wide time zone setting. Actual database schemes like Postgres @@ -62,7 +57,7 @@ class ChangesForV4711 extends Migration try { Schema::table( 'transaction_journals', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dateTime('date')->change(); } ); @@ -74,7 +69,7 @@ class ChangesForV4711 extends Migration try { Schema::table( 'preferences', - static function (Blueprint $table) { + static function (Blueprint $table): void { $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 c93ba77ff3..118d6c63c7 100644 --- a/database/migrations/2019_02_11_170529_changes_for_v4712.php +++ b/database/migrations/2019_02_11_170529_changes_for_v4712.php @@ -35,22 +35,17 @@ class ChangesForV4712 extends Migration { /** * Reverse the migrations. - * - * @return void */ - public function down(): void - { - // - } + public function down(): void {} /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { - /** + /* * In 4.7.11, I changed the date field to a "datetimetz" field. This wreaks havoc * because apparently MySQL is not actually capable of handling multiple time zones, * only having a server wide time zone setting. Actual database schemes like Postgres @@ -61,7 +56,7 @@ class ChangesForV4712 extends Migration try { Schema::table( 'transaction_journals', - static function (Blueprint $table) { + static function (Blueprint $table): void { $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 1a896d7f1a..c31cb12910 100644 --- a/database/migrations/2019_03_11_223700_fix_ldap_configuration.php +++ b/database/migrations/2019_03_11_223700_fix_ldap_configuration.php @@ -36,8 +36,6 @@ class FixLdapConfiguration extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -45,11 +43,11 @@ class FixLdapConfiguration extends Migration try { Schema::table( 'users', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn(['objectguid']); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -58,12 +56,12 @@ class FixLdapConfiguration extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { - /** + /* * ADLdap2 appears to require the ability to store an objectguid for LDAP users * now. To support this, we add the column. */ @@ -71,7 +69,7 @@ class FixLdapConfiguration extends Migration try { Schema::table( 'users', - static function (Blueprint $table) { + static function (Blueprint $table): void { $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 de803caf48..bdd332e7d4 100644 --- a/database/migrations/2019_03_22_183214_changes_for_v480.php +++ b/database/migrations/2019_03_22_183214_changes_for_v480.php @@ -35,8 +35,6 @@ class ChangesForV480 extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -45,7 +43,7 @@ class ChangesForV480 extends Migration try { Schema::table( 'transaction_journals', - static function (Blueprint $table) { + static function (Blueprint $table): void { // drop transaction_group_id + foreign key. // cannot drop foreign keys in SQLite: if ('sqlite' !== config('database.default')) { @@ -56,9 +54,10 @@ class ChangesForV480 extends Migration app('log')->error('If the foreign ID does not exist (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } } + try { $table->dropColumn('transaction_group_id'); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not drop column: %s', $e->getMessage())); app('log')->error('If the column does not exist, this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -75,10 +74,10 @@ class ChangesForV480 extends Migration try { Schema::table( 'rule_groups', - static function (Blueprint $table) { + static function (Blueprint $table): void { try { $table->dropColumn('stop_processing'); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not drop column: %s', $e->getMessage())); app('log')->error('If the column does not exist, this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -95,10 +94,10 @@ class ChangesForV480 extends Migration try { Schema::table( 'users', - static function (Blueprint $table) { + static function (Blueprint $table): void { try { $table->dropColumn('mfa_secret'); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not drop column: %s', $e->getMessage())); app('log')->error('If the column does not exist, this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -113,8 +112,8 @@ class ChangesForV480 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { @@ -123,12 +122,13 @@ class ChangesForV480 extends Migration try { Schema::table( 'transaction_journals', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->integer('transaction_currency_id', false, true)->nullable()->change(); // add column "group_id" after "transaction_type_id" $table->integer('transaction_group_id', false, true) - ->nullable()->default(null)->after('transaction_type_id'); + ->nullable()->default(null)->after('transaction_type_id') + ; // add foreign key for "transaction_group_id" try { @@ -152,7 +152,7 @@ class ChangesForV480 extends Migration try { Schema::table( 'rule_groups', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->boolean('stop_processing')->default(false); } ); @@ -167,7 +167,7 @@ class ChangesForV480 extends Migration try { Schema::table( 'users', - static function (Blueprint $table) { + static function (Blueprint $table): void { $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 7b3443b06b..b9125a2de6 100644 --- a/database/migrations/2019_12_28_191351_make_locations_table.php +++ b/database/migrations/2019_12_28_191351_make_locations_table.php @@ -36,8 +36,6 @@ class MakeLocationsTable extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -46,8 +44,8 @@ class MakeLocationsTable extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { @@ -55,7 +53,7 @@ class MakeLocationsTable extends Migration try { Schema::create( 'locations', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->bigIncrements('id'); $table->timestamps(); $table->softDeletes(); 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 18d254418c..7bbc6f1fba 100644 --- a/database/migrations/2020_03_13_201950_changes_for_v520.php +++ b/database/migrations/2020_03_13_201950_changes_for_v520.php @@ -34,8 +34,6 @@ class ChangesForV520 extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -45,8 +43,8 @@ class ChangesForV520 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { @@ -54,7 +52,7 @@ class ChangesForV520 extends Migration try { Schema::create( 'auto_budgets', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); 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 b5615f6b24..42c3d53903 100644 --- a/database/migrations/2020_06_07_063612_changes_for_v530.php +++ b/database/migrations/2020_06_07_063612_changes_for_v530.php @@ -35,8 +35,6 @@ class ChangesForV530 extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -46,8 +44,8 @@ class ChangesForV530 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { @@ -55,7 +53,7 @@ class ChangesForV530 extends Migration try { Schema::create( 'object_groups', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->integer('user_id', false, true); $table->timestamps(); @@ -75,7 +73,7 @@ class ChangesForV530 extends Migration try { Schema::create( 'object_groupables', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->integer('object_group_id'); $table->integer('object_groupable_id', false, true); $table->string('object_groupable_type', 255); 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 2dd5834058..b9b6f0925f 100644 --- a/database/migrations/2020_06_30_202620_changes_for_v530a.php +++ b/database/migrations/2020_06_30_202620_changes_for_v530a.php @@ -37,8 +37,6 @@ class ChangesForV530a extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -46,11 +44,11 @@ class ChangesForV530a extends Migration try { Schema::table( 'bills', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('order'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -59,8 +57,8 @@ class ChangesForV530a extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { @@ -68,7 +66,7 @@ class ChangesForV530a extends Migration try { Schema::table( 'bills', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->integer('order', false, true)->default(0); } ); 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 a3acb2f8de..7fe7afd7a5 100644 --- a/database/migrations/2020_07_24_162820_changes_for_v540.php +++ b/database/migrations/2020_07_24_162820_changes_for_v540.php @@ -37,8 +37,6 @@ class ChangesForV540 extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -46,11 +44,11 @@ class ChangesForV540 extends Migration try { Schema::table( 'oauth_clients', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('provider'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -60,11 +58,11 @@ class ChangesForV540 extends Migration try { Schema::table( 'accounts', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('order'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -74,11 +72,11 @@ class ChangesForV540 extends Migration try { Schema::table( 'bills', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('end_date'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -87,11 +85,11 @@ class ChangesForV540 extends Migration try { Schema::table( 'bills', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('extension_date'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -100,8 +98,8 @@ class ChangesForV540 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { @@ -109,7 +107,7 @@ class ChangesForV540 extends Migration try { Schema::table( 'accounts', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->integer('order', false, true)->default(0); } ); @@ -123,7 +121,7 @@ class ChangesForV540 extends Migration try { Schema::table( 'oauth_clients', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->string('provider')->nullable(); } ); @@ -137,7 +135,7 @@ class ChangesForV540 extends Migration try { Schema::table( 'bills', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->date('end_date')->nullable()->after('date'); $table->date('extension_date')->nullable()->after('end_date'); } @@ -152,7 +150,7 @@ class ChangesForV540 extends Migration try { Schema::table( 'oauth_clients', - static function (Blueprint $table) { + static function (Blueprint $table): void { $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 a08a2c9987..6b6a8ed0ec 100644 --- a/database/migrations/2020_11_12_070604_changes_for_v550.php +++ b/database/migrations/2020_11_12_070604_changes_for_v550.php @@ -35,8 +35,6 @@ class ChangesForV550 extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -47,7 +45,7 @@ class ChangesForV550 extends Migration try { Schema::create( 'jobs', - static function (Blueprint $table) { + static function (Blueprint $table): void { // straight from Laravel (this is the OLD table) $table->bigIncrements('id'); $table->string('queue'); @@ -71,14 +69,14 @@ class ChangesForV550 extends Migration try { Schema::table( 'budget_transaction_journal', - static function (Blueprint $table) { + static function (Blueprint $table): void { if ('sqlite' !== config('database.default')) { $table->dropForeign('budget_id_foreign'); } $table->dropColumn('budget_limit_id'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -93,11 +91,11 @@ class ChangesForV550 extends Migration try { Schema::table( 'budget_limits', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('period'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -106,11 +104,11 @@ class ChangesForV550 extends Migration try { Schema::table( 'budget_limits', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn('generated'); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -124,8 +122,10 @@ class ChangesForV550 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.NPathComplexity) */ public function up(): void { @@ -136,7 +136,7 @@ class ChangesForV550 extends Migration try { Schema::create( 'jobs', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->bigIncrements('id'); $table->string('queue')->index(); $table->longText('payload'); @@ -159,7 +159,7 @@ class ChangesForV550 extends Migration try { Schema::create( 'failed_jobs', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->bigIncrements('id'); $table->string('uuid')->unique(); $table->text('connection'); @@ -180,7 +180,7 @@ class ChangesForV550 extends Migration try { Schema::table( 'budget_transaction_journal', - static function (Blueprint $table) { + static function (Blueprint $table): void { if (!Schema::hasColumn('budget_transaction_journal', 'budget_limit_id')) { $table->integer('budget_limit_id', false, true)->nullable()->default(null)->after('budget_id'); $table->foreign('budget_limit_id', 'budget_id_foreign')->references('id')->on('budget_limits')->onDelete('set null'); @@ -199,7 +199,7 @@ class ChangesForV550 extends Migration try { Schema::table( 'budget_limits', - static function (Blueprint $table) { + static function (Blueprint $table): void { if (!Schema::hasColumn('budget_limits', 'period')) { $table->string('period', 12)->nullable(); } @@ -218,7 +218,7 @@ class ChangesForV550 extends Migration try { Schema::create( 'webhooks', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -245,7 +245,7 @@ class ChangesForV550 extends Migration try { Schema::create( 'webhook_messages', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); @@ -269,7 +269,7 @@ class ChangesForV550 extends Migration try { Schema::create( 'webhook_attempts', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->increments('id'); $table->timestamps(); $table->softDeletes(); diff --git a/database/migrations/2021_03_12_061213_changes_for_v550b2.php b/database/migrations/2021_03_12_061213_changes_for_v550b2.php index ac009e6309..5e1f30eda4 100644 --- a/database/migrations/2021_03_12_061213_changes_for_v550b2.php +++ b/database/migrations/2021_03_12_061213_changes_for_v550b2.php @@ -35,8 +35,6 @@ class ChangesForV550b2 extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -44,7 +42,7 @@ class ChangesForV550b2 extends Migration try { Schema::table( 'recurrences_transactions', - static function (Blueprint $table) { + static function (Blueprint $table): void { if ('sqlite' !== config('database.default')) { $table->dropForeign('type_foreign'); } @@ -53,7 +51,7 @@ class ChangesForV550b2 extends Migration } } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -62,8 +60,8 @@ class ChangesForV550b2 extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { @@ -72,7 +70,7 @@ class ChangesForV550b2 extends Migration try { Schema::table( 'recurrences_transactions', - static function (Blueprint $table) { + static function (Blueprint $table): void { if (!Schema::hasColumn('recurrences_transactions', 'transaction_type_id')) { $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/migrations/2021_05_09_064644_add_ldap_columns_to_users_table.php b/database/migrations/2021_05_09_064644_add_ldap_columns_to_users_table.php index e02c16a61c..8d96ab23f4 100644 --- a/database/migrations/2021_05_09_064644_add_ldap_columns_to_users_table.php +++ b/database/migrations/2021_05_09_064644_add_ldap_columns_to_users_table.php @@ -39,11 +39,11 @@ class AddLdapColumnsToUsersTable extends Migration try { Schema::table( 'users', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->dropColumn(['domain']); } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -52,6 +52,7 @@ class AddLdapColumnsToUsersTable extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void @@ -60,7 +61,7 @@ class AddLdapColumnsToUsersTable extends Migration try { Schema::table( 'users', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->string('domain')->nullable(); } ); diff --git a/database/migrations/2021_05_13_053836_extend_currency_info.php b/database/migrations/2021_05_13_053836_extend_currency_info.php index 32ac1506c4..888bbab2fa 100644 --- a/database/migrations/2021_05_13_053836_extend_currency_info.php +++ b/database/migrations/2021_05_13_053836_extend_currency_info.php @@ -34,25 +34,20 @@ class ExtendCurrencyInfo extends Migration { /** * Reverse the migrations. - * - * @return void */ - public function down(): void - { - // - } + public function down(): void {} /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { try { Schema::table( 'transaction_currencies', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->string('code', 51)->change(); $table->string('symbol', 51)->change(); } diff --git a/database/migrations/2021_07_05_193044_drop_tele_table.php b/database/migrations/2021_07_05_193044_drop_tele_table.php index 746c40211d..105cf474d6 100644 --- a/database/migrations/2021_07_05_193044_drop_tele_table.php +++ b/database/migrations/2021_07_05_193044_drop_tele_table.php @@ -32,8 +32,6 @@ class DropTeleTable extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -42,8 +40,8 @@ class DropTeleTable extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { diff --git a/database/migrations/2021_08_28_073733_user_groups.php b/database/migrations/2021_08_28_073733_user_groups.php index 76de049742..9ec60e4178 100644 --- a/database/migrations/2021_08_28_073733_user_groups.php +++ b/database/migrations/2021_08_28_073733_user_groups.php @@ -53,8 +53,6 @@ class UserGroups extends Migration /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -65,7 +63,7 @@ class UserGroups extends Migration try { Schema::table( $tableName, - static function (Blueprint $table) use ($tableName) { + static function (Blueprint $table) use ($tableName): void { if ('sqlite' !== config('database.default')) { $table->dropForeign(sprintf('%s_to_ugi', $tableName)); } @@ -74,7 +72,7 @@ class UserGroups extends Migration } } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -85,7 +83,7 @@ class UserGroups extends Migration try { Schema::table( 'users', - static function (Blueprint $table) { + static function (Blueprint $table): void { if ('sqlite' !== config('database.default')) { $table->dropForeign('type_user_group_id'); } @@ -94,7 +92,7 @@ class UserGroups extends Migration } } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } @@ -107,8 +105,9 @@ class UserGroups extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function up(): void { @@ -120,7 +119,7 @@ class UserGroups extends Migration try { Schema::create( 'user_groups', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->bigIncrements('id'); $table->timestamps(); $table->softDeletes(); @@ -138,7 +137,7 @@ class UserGroups extends Migration try { Schema::create( 'user_roles', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->bigIncrements('id'); $table->timestamps(); $table->softDeletes(); @@ -156,7 +155,7 @@ class UserGroups extends Migration try { Schema::create( 'group_memberships', - static function (Blueprint $table) { + static function (Blueprint $table): void { $table->bigIncrements('id'); $table->timestamps(); $table->softDeletes(); @@ -175,10 +174,11 @@ class UserGroups extends Migration app('log')->error('If this table exists already (see the error message), this is not a problem. Other errors? Please open a discussion on GitHub.'); } } + try { Schema::table( 'users', - static function (Blueprint $table) { + static function (Blueprint $table): void { if (!Schema::hasColumn('users', 'user_group_id')) { $table->bigInteger('user_group_id', false, true)->nullable(); $table->foreign('user_group_id', 'type_user_group_id')->references('id')->on('user_groups')->onDelete('set null')->onUpdate( @@ -191,13 +191,14 @@ class UserGroups extends Migration app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } + // ADD columns to tables /** @var string $tableName */ foreach ($this->tables as $tableName) { try { Schema::table( $tableName, - static function (Blueprint $table) use ($tableName) { + static function (Blueprint $table) use ($tableName): void { if (!Schema::hasColumn($tableName, 'user_group_id')) { $table->bigInteger('user_group_id', false, true)->nullable()->after('user_id'); $table->foreign('user_group_id', sprintf('%s_to_ugi', $tableName))->references('id')->on('user_groups')->onDelete( diff --git a/database/migrations/2021_12_27_000001_create_local_personal_access_tokens_table.php b/database/migrations/2021_12_27_000001_create_local_personal_access_tokens_table.php index 7e438f922e..c551a9fe43 100644 --- a/database/migrations/2021_12_27_000001_create_local_personal_access_tokens_table.php +++ b/database/migrations/2021_12_27_000001_create_local_personal_access_tokens_table.php @@ -34,8 +34,6 @@ class CreateLocalPersonalAccessTokensTable extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { @@ -44,14 +42,14 @@ class CreateLocalPersonalAccessTokensTable extends Migration /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { if (!Schema::hasTable('personal_access_tokens')) { try { - Schema::create('personal_access_tokens', static function (Blueprint $table) { + Schema::create('personal_access_tokens', static function (Blueprint $table): void { $table->bigIncrements('id'); $table->morphs('tokenable'); $table->string('name'); diff --git a/database/migrations/2022_08_21_104626_add_user_groups.php b/database/migrations/2022_08_21_104626_add_user_groups.php index 222467d847..552c66bcc1 100644 --- a/database/migrations/2022_08_21_104626_add_user_groups.php +++ b/database/migrations/2022_08_21_104626_add_user_groups.php @@ -28,21 +28,18 @@ use Illuminate\Database\QueryException; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -/** - * - */ -return new class () extends Migration { +return new class() extends Migration { /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { try { Schema::table( 'currency_exchange_rates', - static function (Blueprint $table) { + static function (Blueprint $table): void { if (!Schema::hasColumn('currency_exchange_rates', 'user_group_id')) { $table->bigInteger('user_group_id', false, true)->nullable()->after('user_id'); $table->foreign('user_group_id', 'cer_to_ugi')->references('id')->on('user_groups')->onDelete('set null')->onUpdate('cascade'); @@ -57,15 +54,13 @@ return new class () extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { try { Schema::table( 'currency_exchange_rates', - static function (Blueprint $table) { + static function (Blueprint $table): void { if ('sqlite' !== config('database.default')) { $table->dropForeign('cer_to_ugi'); } @@ -74,7 +69,7 @@ return new class () extends Migration { } } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } diff --git a/database/migrations/2022_09_18_123911_create_notifications_table.php b/database/migrations/2022_09_18_123911_create_notifications_table.php index 09e4963fa9..034f4625fe 100644 --- a/database/migrations/2022_09_18_123911_create_notifications_table.php +++ b/database/migrations/2022_09_18_123911_create_notifications_table.php @@ -27,17 +27,17 @@ use Illuminate\Database\QueryException; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class () extends Migration { +return new class() extends Migration { /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { if (!Schema::hasTable('notifications')) { try { - Schema::create('notifications', static function (Blueprint $table) { + Schema::create('notifications', static function (Blueprint $table): void { $table->uuid('id')->primary(); $table->string('type'); $table->morphs('notifiable'); @@ -54,8 +54,6 @@ return new class () extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { diff --git a/database/migrations/2022_10_01_074908_invited_users.php b/database/migrations/2022_10_01_074908_invited_users.php index c2c0de8cc8..f802f72179 100644 --- a/database/migrations/2022_10_01_074908_invited_users.php +++ b/database/migrations/2022_10_01_074908_invited_users.php @@ -27,17 +27,17 @@ use Illuminate\Database\QueryException; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class () extends Migration { +return new class() extends Migration { /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { if (!Schema::hasTable('invited_users')) { try { - Schema::create('invited_users', static function (Blueprint $table) { + Schema::create('invited_users', static function (Blueprint $table): void { $table->id(); $table->timestamps(); $table->integer('user_id', false, true); @@ -56,8 +56,6 @@ return new class () extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { diff --git a/database/migrations/2022_10_01_210238_audit_log_entries.php b/database/migrations/2022_10_01_210238_audit_log_entries.php index fd64d2915c..bf6cf14f60 100644 --- a/database/migrations/2022_10_01_210238_audit_log_entries.php +++ b/database/migrations/2022_10_01_210238_audit_log_entries.php @@ -27,17 +27,17 @@ use Illuminate\Database\QueryException; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class () extends Migration { +return new class() extends Migration { /** * Run the migrations. + * * @SuppressWarnings(PHPMD.ShortMethodName) - * @return void */ public function up(): void { if (!Schema::hasTable('audit_log_entries')) { try { - Schema::create('audit_log_entries', static function (Blueprint $table) { + Schema::create('audit_log_entries', static function (Blueprint $table): void { $table->id(); $table->timestamps(); $table->softDeletes(); @@ -61,8 +61,6 @@ return new class () extends Migration { /** * Reverse the migrations. - * - * @return void */ public function down(): void { diff --git a/database/migrations/2023_08_11_192521_upgrade_og_table.php b/database/migrations/2023_08_11_192521_upgrade_og_table.php index 1a01ed8f10..aea11c646f 100644 --- a/database/migrations/2023_08_11_192521_upgrade_og_table.php +++ b/database/migrations/2023_08_11_192521_upgrade_og_table.php @@ -1,6 +1,5 @@ bigInteger('user_group_id', false, true)->nullable()->after('user_id'); $table->foreign('user_group_id', sprintf('%s_to_ugi', 'object_groups'))->references('id')->on('user_groups')->onDelete( @@ -65,7 +62,7 @@ return new class () extends Migration { try { Schema::table( 'object_groups', - static function (Blueprint $table) { + static function (Blueprint $table): void { if ('sqlite' !== config('database.default')) { $table->dropForeign(sprintf('%s_to_ugi', 'object_groups')); } @@ -74,7 +71,7 @@ return new class () extends Migration { } } ); - } catch (QueryException | ColumnDoesNotExist $e) { + } catch (ColumnDoesNotExist|QueryException $e) { app('log')->error(sprintf('Could not execute query: %s', $e->getMessage())); app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.'); } diff --git a/database/migrations/2023_10_21_113213_add_currency_pivot_tables.php b/database/migrations/2023_10_21_113213_add_currency_pivot_tables.php index 6845c3f0f4..ee53d2c3e5 100644 --- a/database/migrations/2023_10_21_113213_add_currency_pivot_tables.php +++ b/database/migrations/2023_10_21_113213_add_currency_pivot_tables.php @@ -1,6 +1,5 @@ id(); $table->timestamps(); $table->integer('user_id', false, true); @@ -57,7 +56,7 @@ return new class () extends Migration { // transaction_currency_user_group if (!Schema::hasTable('transaction_currency_user_group')) { try { - Schema::create('transaction_currency_user_group', static function (Blueprint $table) { + Schema::create('transaction_currency_user_group', static function (Blueprint $table): void { $table->id(); $table->timestamps(); $table->bigInteger('user_group_id', false, true); diff --git a/database/seeders/AccountTypeSeeder.php b/database/seeders/AccountTypeSeeder.php index 57ebb11570..7f968ba642 100644 --- a/database/seeders/AccountTypeSeeder.php +++ b/database/seeders/AccountTypeSeeder.php @@ -25,7 +25,6 @@ namespace Database\Seeders; use FireflyIII\Models\AccountType; use Illuminate\Database\Seeder; -use PDOException; /** * Class AccountTypeSeeder. @@ -52,7 +51,7 @@ class AccountTypeSeeder extends Seeder foreach ($types as $type) { try { AccountType::create(['type' => $type]); - } catch (PDOException $e) { + } catch (\PDOException $e) { // @ignoreException } } diff --git a/database/seeders/ExchangeRateSeeder.php b/database/seeders/ExchangeRateSeeder.php index 164473461e..e687beb754 100644 --- a/database/seeders/ExchangeRateSeeder.php +++ b/database/seeders/ExchangeRateSeeder.php @@ -34,14 +34,12 @@ use Illuminate\Database\Seeder; */ class ExchangeRateSeeder extends Seeder { - /** - * @return void - */ public function run(): void { $count = User::count(); if (0 === $count) { app('log')->debug('Will not seed exchange rates yet.'); + return; } $users = User::get(); @@ -73,41 +71,22 @@ class ExchangeRateSeeder extends Seeder } } - /** - * @param string $code - * - * @return TransactionCurrency|null - */ private function getCurrency(string $code): ?TransactionCurrency { return TransactionCurrency::whereNull('deleted_at')->where('code', $code)->first(); } - /** - * @param User $user - * @param TransactionCurrency $from - * @param TransactionCurrency $to - * @param string $date - * - * @return bool - */ private function hasRate(User $user, TransactionCurrency $from, TransactionCurrency $to, string $date): bool { return $user->currencyExchangeRates() - ->where('from_currency_id', $from->id) - ->where('to_currency_id', $to->id) - ->where('date', $date) - ->count() > 0; + ->where('from_currency_id', $from->id) + ->where('to_currency_id', $to->id) + ->where('date', $date) + ->count() > 0 + ; } /** - * @param User $user - * @param TransactionCurrency $from - * @param TransactionCurrency $to - * @param string $date - * @param float $rate - * - * @return void * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ private function addRate(User $user, TransactionCurrency $from, TransactionCurrency $to, string $date, float $rate): void diff --git a/database/seeders/LinkTypeSeeder.php b/database/seeders/LinkTypeSeeder.php index e115bb2ad8..1dca2fd2a0 100644 --- a/database/seeders/LinkTypeSeeder.php +++ b/database/seeders/LinkTypeSeeder.php @@ -25,16 +25,12 @@ namespace Database\Seeders; use FireflyIII\Models\LinkType; use Illuminate\Database\Seeder; -use PDOException; /** * Class LinkTypeSeeder. */ class LinkTypeSeeder extends Seeder { - /** - * @return void - */ public function run(): void { $types = [ @@ -66,7 +62,7 @@ class LinkTypeSeeder extends Seeder foreach ($types as $type) { try { LinkType::create($type); - } catch (PDOException $e) { + } catch (\PDOException $e) { // @ignoreException } } diff --git a/database/seeders/PermissionSeeder.php b/database/seeders/PermissionSeeder.php index 29ff77d72f..76e5106459 100644 --- a/database/seeders/PermissionSeeder.php +++ b/database/seeders/PermissionSeeder.php @@ -25,7 +25,6 @@ namespace Database\Seeders; use FireflyIII\Models\Role; use Illuminate\Database\Seeder; -use PDOException; /** * Class PermissionSeeder. @@ -49,7 +48,7 @@ class PermissionSeeder extends Seeder foreach ($roles as $role) { try { Role::create($role); - } catch (PDOException $e) { + } catch (\PDOException $e) { // @ignoreException } } diff --git a/database/seeders/TransactionCurrencySeeder.php b/database/seeders/TransactionCurrencySeeder.php index b13752fad8..2c70b4af14 100644 --- a/database/seeders/TransactionCurrencySeeder.php +++ b/database/seeders/TransactionCurrencySeeder.php @@ -25,15 +25,12 @@ namespace Database\Seeders; use FireflyIII\Models\TransactionCurrency; use Illuminate\Database\Seeder; -use PDOException; /** * Class TransactionCurrencySeeder. */ class TransactionCurrencySeeder extends Seeder { - /** - */ public function run(): void { $currencies = []; @@ -76,7 +73,7 @@ class TransactionCurrencySeeder extends Seeder foreach ($currencies as $currency) { try { TransactionCurrency::create($currency); - } catch (PDOException $e) { + } catch (\PDOException $e) { // @ignoreException } } diff --git a/database/seeders/TransactionTypeSeeder.php b/database/seeders/TransactionTypeSeeder.php index da0db936fd..1793b3e991 100644 --- a/database/seeders/TransactionTypeSeeder.php +++ b/database/seeders/TransactionTypeSeeder.php @@ -25,7 +25,6 @@ namespace Database\Seeders; use FireflyIII\Models\TransactionType; use Illuminate\Database\Seeder; -use PDOException; /** * Class TransactionTypeSeeder. @@ -47,7 +46,7 @@ class TransactionTypeSeeder extends Seeder foreach ($types as $type) { try { TransactionType::create(['type' => $type]); - } catch (PDOException $e) { + } catch (\PDOException $e) { // @ignoreException } } diff --git a/database/seeders/UserRoleSeeder.php b/database/seeders/UserRoleSeeder.php index 2eb8dc6059..76d1c2084f 100644 --- a/database/seeders/UserRoleSeeder.php +++ b/database/seeders/UserRoleSeeder.php @@ -27,7 +27,6 @@ namespace Database\Seeders; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\UserRole; use Illuminate\Database\Seeder; -use PDOException; /** * Class UserRoleSeeder @@ -36,10 +35,8 @@ class UserRoleSeeder extends Seeder { /** * Run the database seeds. - * - * @return void */ - public function run() + public function run(): void { $roles = []; foreach (UserRoleEnum::cases() as $role) { @@ -50,7 +47,7 @@ class UserRoleSeeder extends Seeder foreach ($roles as $role) { try { UserRole::create(['title' => $role]); - } catch (PDOException $e) { + } catch (\PDOException $e) { // @ignoreException } } diff --git a/package-lock.json b/package-lock.json index c2d91e4457..c5bcc5259b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "chart.js": "^4.4.0", "chartjs-adapter-date-fns": "^3.0.0", "chartjs-chart-sankey": "^0.12.0", - "date-fns": "^2.30.0", + "date-fns": "^3.0.6", "i18n-js": "^4.3.2", "store": "^2.0.12" }, @@ -25,17 +25,6 @@ "vite-plugin-manifest-sri": "^0.1.0" } }, - "node_modules/@babel/runtime": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.6.tgz", - "integrity": "sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@esbuild/android-arm": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", @@ -582,18 +571,12 @@ } }, "node_modules/date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dependencies": { - "@babel/runtime": "^7.21.0" - }, - "engines": { - "node": ">=0.11" - }, + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.0.6.tgz", + "integrity": "sha512-W+G99rycpKMMF2/YD064b2lE7jJGUe+EjOES7Q8BIGY8sbNdbgcs9XFTZwvzc9Jx1f3k7LB7gZaZa7f8Agzljg==", "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" } }, "node_modules/delayed-stream": { @@ -910,11 +893,6 @@ "node": ">=8.10.0" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, "node_modules/rollup": { "version": "3.29.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", diff --git a/package.json b/package.json index 5a098f9429..5c7331a2da 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "chart.js": "^4.4.0", "chartjs-adapter-date-fns": "^3.0.0", "chartjs-chart-sankey": "^0.12.0", - "date-fns": "^2.30.0", + "date-fns": "^3.0.6", "i18n-js": "^4.3.2", "store": "^2.0.12" } diff --git a/public/build/assets/create-46a6e026.js b/public/build/assets/create-46a6e026.js deleted file mode 100644 index 8935fd736f..0000000000 --- a/public/build/assets/create-46a6e026.js +++ /dev/null @@ -1,3 +0,0 @@ -var q=Object.defineProperty;var H=(n,e,t)=>e in n?q(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var C=(n,e,t)=>(H(n,typeof e!="symbol"?e+"":e,t),t);import{x as P,w as M,J as j,z as R,I as z,A as $,y as V}from"./load-translations-85b093de.js";function T(){return{id:"",name:"",alpine_name:""}}function B(){let e=P(new Date,"yyyy-MM-dd HH:mm");return{description:"",amount:"",currency_code:"EUR",source_account:T(),destination_account:T(),date:e}}function W(n,e){let t=[];for(let s in n)if(n.hasOwnProperty(s)){const i=n[s];let r={};r.description=i.description,r.source_name=i.source_account.name,r.destination_name=i.destination_account.name,r.amount=i.amount,r.date=i.date,r.currency_code=i.currency_code,typeof i.source_account.id<"u"&&i.source_account.id.toString()!==""&&(r.source_id=i.source_account.id),typeof i.destination_account.id<"u"&&i.destination_account.id.toString()!==""&&(r.destination_id=i.destination_account.id),r.type=e,t.push(r)}return t}const x={showAllSuggestions:!1,suggestionsThreshold:1,maximumItems:0,autoselectFirst:!0,ignoreEnter:!1,updateOnSelect:!1,highlightTyped:!1,highlightClass:"",fullWidth:!1,fixed:!1,fuzzy:!1,startsWith:!1,fillIn:!1,preventBrowserAutocomplete:!1,itemClass:"",activeClasses:["bg-primary","text-white"],labelField:"label",valueField:"value",searchFields:["label"],queryParam:"query",items:[],source:null,hiddenInput:!1,hiddenValue:"",clearControl:"",datalist:"",server:"",serverMethod:"GET",serverParams:{},serverDataKey:"data",fetchOptions:{},liveServer:!1,noCache:!0,debounceTime:300,notFoundMessage:"",onRenderItem:(n,e,t)=>e,onSelectItem:(n,e)=>{},onServerResponse:(n,e)=>n.json(),onChange:(n,e)=>{},onBeforeFetch:n=>{},onAfterFetch:n=>{}},A="is-loading",k="is-active",p="show",_="next",y="prev",g=new WeakMap;let L=0,v=0;function U(n,e=300){let t;return(...s)=>{clearTimeout(t),t=setTimeout(()=>{n.apply(this,s)},e)}}function G(n){return n.normalize("NFD").replace(/[\u0300-\u036f]/g,"")}function b(n){return n?G(n.toString()).toLowerCase():""}function K(n,e){if(n.indexOf(e)>=0)return!0;let t=0;for(let s=0;se+"‍").join("")}function Y(n,e="window"){return n.split(".").reduce((t,s)=>t[s],e)}class S{constructor(e,t={}){C(this,"handleEvent",e=>{["scroll","resize"].includes(e.type)?(this._timer&&window.cancelAnimationFrame(this._timer),this._timer=window.requestAnimationFrame(()=>{this[`on${e.type}`](e)})):this[`on${e.type}`](e)});if(!(e instanceof HTMLElement)){console.error("Invalid element",e);return}g.set(e,this),L++,v++,this._searchInput=e,this._configure(t),this._preventInput=!1,this._keyboardNavigation=!1,this._searchFunc=U(()=>{this._loadFromServer(!0)},this._config.debounceTime),this._configureSearchInput(),this._configureDropElement(),this._config.fixed&&(document.addEventListener("scroll",this,!0),window.addEventListener("resize",this));const s=this._getClearControl();s&&s.addEventListener("click",this),["focus","change","blur","input","keydown"].forEach(i=>{this._searchInput.addEventListener(i,this)}),["mousemove","mouseleave"].forEach(i=>{this._dropElement.addEventListener(i,this)}),this._fetchData()}static init(e="input.autocomplete",t={}){document.querySelectorAll(e).forEach(i=>{this.getOrCreateInstance(i,t)})}static getInstance(e){return g.has(e)?g.get(e):null}static getOrCreateInstance(e,t={}){return this.getInstance(e)||new this(e,t)}dispose(){v--,["focus","change","blur","input","keydown"].forEach(t=>{this._searchInput.removeEventListener(t,this)}),["mousemove","mouseleave"].forEach(t=>{this._dropElement.removeEventListener(t,this)});const e=this._getClearControl();e&&e.removeEventListener("click",this),this._config.fixed&&v<=0&&(document.removeEventListener("scroll",this,!0),window.removeEventListener("resize",this)),this._dropElement.parentElement.removeChild(this._dropElement),g.delete(this._searchInput)}_getClearControl(){if(this._config.clearControl)return document.querySelector(this._config.clearControl)}_configure(e={}){this._config=Object.assign({},x);const t={...e,...this._searchInput.dataset},s=i=>["true","false","1","0",!0,!1].includes(i)&&!!JSON.parse(i);for(const[i,r]of Object.entries(x)){if(t[i]===void 0)continue;const o=t[i];switch(typeof r){case"number":this._config[i]=parseInt(o);break;case"boolean":this._config[i]=s(o);break;case"string":this._config[i]=o.toString();break;case"object":if(Array.isArray(r))if(typeof o=="string"){const a=o.includes("|")?"|":",";this._config[i]=o.split(a)}else this._config[i]=o;else this._config[i]=typeof o=="string"?JSON.parse(o):o;break;case"function":this._config[i]=typeof o=="string"?window[o]:o;break;default:this._config[i]=o;break}}}_configureSearchInput(){if(this._searchInput.autocomplete="off",this._searchInput.spellcheck=!1,w(this._searchInput,{"aria-autocomplete":"list","aria-haspopup":"menu","aria-expanded":"false",role:"combobox"}),this._searchInput.id&&this._config.preventBrowserAutocomplete){const e=document.querySelector(`[for="${this._searchInput.id}"]`);e&&X(e)}this._hiddenInput=null,this._config.hiddenInput&&(this._hiddenInput=document.createElement("input"),this._hiddenInput.type="hidden",this._hiddenInput.value=this._config.hiddenValue,this._hiddenInput.name=this._searchInput.name,this._searchInput.name="_"+this._searchInput.name,O(this._searchInput,this._hiddenInput))}_configureDropElement(){this._dropElement=document.createElement("ul"),this._dropElement.id="ac-menu-"+L,this._dropElement.classList.add("dropdown-menu","autocomplete-menu","p-0"),this._dropElement.style.maxHeight="280px",this._config.fullWidth||(this._dropElement.style.maxWidth="360px"),this._config.fixed&&(this._dropElement.style.position="fixed"),this._dropElement.style.overflowY="auto",this._dropElement.style.overscrollBehavior="contain",this._dropElement.style.textAlign="unset",O(this._searchInput,this._dropElement),this._searchInput.setAttribute("aria-controls",this._dropElement.id)}onclick(e){e.target.matches(this._config.clearControl)&&this.clear()}oninput(e){this._preventInput||(this._hiddenInput&&(this._hiddenInput.value=null),this.showOrSearch())}onchange(e){const t=this._searchInput.value,s=Object.values(this._items).find(i=>i.label===t);this._config.onChange(s,this)}onblur(e){if(e.relatedTarget&&e.relatedTarget.classList.contains("modal")){this._searchInput.focus();return}setTimeout(()=>{this.hideSuggestions()},100)}onfocus(e){this.showOrSearch()}onkeydown(e){switch(e.keyCode||e.key){case 13:case"Enter":if(this.isDropdownVisible()){const s=this.getSelection();s&&s.click(),(s||!this._config.ignoreEnter)&&e.preventDefault()}break;case 38:case"ArrowUp":e.preventDefault(),this._keyboardNavigation=!0,this._moveSelection(y);break;case 40:case"ArrowDown":e.preventDefault(),this._keyboardNavigation=!0,this.isDropdownVisible()?this._moveSelection(_):this.showOrSearch(!1);break;case 27:case"Escape":this.isDropdownVisible()&&(this._searchInput.focus(),this.hideSuggestions());break}}onmousemove(e){this._keyboardNavigation=!1}onmouseleave(e){this.removeSelection()}onscroll(e){this._positionMenu()}onresize(e){this._positionMenu()}getConfig(e=null){return e!==null?this._config[e]:this._config}setConfig(e,t){this._config[e]=t}setData(e){this._items={},this._addItems(e)}enable(){this._searchInput.setAttribute("disabled","")}disable(){this._searchInput.removeAttribute("disabled")}isDisabled(){return this._searchInput.hasAttribute("disabled")||this._searchInput.disabled||this._searchInput.hasAttribute("readonly")}isDropdownVisible(){return this._dropElement.classList.contains(p)}clear(){this._searchInput.value="",this._hiddenInput&&(this._hiddenInput.value="")}getSelection(){return this._dropElement.querySelector("a."+k)}removeSelection(){const e=this.getSelection();e&&e.classList.remove(...this._activeClasses())}_activeClasses(){return[...this._config.activeClasses,k]}_isItemEnabled(e){if(e.style.display==="none")return!1;const t=e.firstElementChild;return t.tagName==="A"&&!t.classList.contains("disabled")}_moveSelection(e=_,t=null){const s=this.getSelection();if(s){const i=e===_?"nextSibling":"previousSibling";t=s.parentNode;do t=t[i];while(t&&!this._isItemEnabled(t));t?(s.classList.remove(...this._activeClasses()),e===y?t.parentNode.scrollTop=t.offsetTop-t.parentNode.offsetTop:t.offsetTop>t.parentNode.offsetHeight-t.offsetHeight&&(t.parentNode.scrollTop+=t.offsetHeight)):s&&(t=s.parentElement)}else{if(e===y)return t;if(!t)for(t=this._dropElement.firstChild;t&&!this._isItemEnabled(t);)t=t.nextSibling}if(t){const i=t.querySelector("a");i.classList.add(...this._activeClasses()),this._searchInput.setAttribute("aria-activedescendant",i.id),this._config.updateOnSelect&&(this._searchInput.value=i.dataset.label)}else this._searchInput.setAttribute("aria-activedescendant","");return t}_shouldShow(){return this.isDisabled()?!1:this._searchInput.value.length>=this._config.suggestionsThreshold}showOrSearch(e=!0){if(e&&!this._shouldShow()){this.hideSuggestions();return}this._config.liveServer?this._searchFunc():this._config.source?this._config.source(this._searchInput.value,t=>{this.setData(t),this._showSuggestions()}):this._showSuggestions()}_createGroup(e){const t=this._createLi(),s=document.createElement("span");return t.append(s),s.classList.add("dropdown-header","text-truncate"),s.innerHTML=e,t}_createItem(e,t){let s=t.label;if(this._config.highlightTyped){const o=b(s).indexOf(e);o!==-1&&(s=s.substring(0,o)+`${s.substring(o,o+e.length)}`+s.substring(o+e.length,s.length))}s=this._config.onRenderItem(t,s,this);const i=this._createLi(),r=document.createElement("a");if(i.append(r),r.id=this._dropElement.id+"-"+this._dropElement.children.length,r.classList.add("dropdown-item","text-truncate"),this._config.itemClass&&r.classList.add(...this._config.itemClass.split(" ")),r.setAttribute("data-value",t.value),r.setAttribute("data-label",t.label),r.setAttribute("tabindex","-1"),r.setAttribute("role","menuitem"),r.setAttribute("href","#"),r.innerHTML=s,t.data)for(const[o,a]of Object.entries(t.data))r.dataset[o]=a;if(this._config.fillIn){const o=document.createElement("button");o.type="button",o.classList.add("btn","btn-link","border-0"),o.innerHTML=` - - `,i.append(o),i.classList.add("d-flex","justify-content-between"),o.addEventListener("click",a=>{this._searchInput.value=t.label,this._searchInput.focus()})}return r.addEventListener("mouseenter",o=>{this._keyboardNavigation||(this.removeSelection(),i.querySelector("a").classList.add(...this._activeClasses()))}),r.addEventListener("mousedown",o=>{o.preventDefault()}),r.addEventListener("click",o=>{o.preventDefault(),this._preventInput=!0,this._searchInput.value=J(t.label),this._hiddenInput&&(this._hiddenInput.value=t.value),this._config.onSelectItem(t,this),this.hideSuggestions(),this._preventInput=!1}),i}_showSuggestions(){if(document.activeElement!=this._searchInput)return;const e=b(this._searchInput.value);this._dropElement.innerHTML="";const t=Object.keys(this._items);let s=0,i=null;const r=[];for(let o=0;o0&&this._config.searchFields.forEach(d=>{const f=b(c[d]);let m=!1;if(this._config.fuzzy)m=K(f,e);else{const E=f.indexOf(e);m=this._config.startsWith?E===0:E>=0}m&&(l=!0)});const N=l||e.length===0;if(h||l){if(s++,c.group&&!r.includes(c.group)){const f=this._createGroup(c.group);this._dropElement.appendChild(f),r.push(c.group)}const d=this._createItem(e,c);if(!i&&N&&(i=d),this._dropElement.appendChild(d),this._config.maximumItems>0&&s>=this._config.maximumItems)break}}if(i&&this._config.autoselectFirst&&(this.removeSelection(),this._moveSelection(_,i)),s===0)if(this._config.notFoundMessage){const o=this._createLi();o.innerHTML=`${this._config.notFoundMessage}`,this._dropElement.appendChild(o),this._showDropdown()}else this.hideSuggestions();else this._showDropdown()}_createLi(){const e=document.createElement("li");return e.setAttribute("role","presentation"),e}_showDropdown(){this._dropElement.classList.add(p),this._dropElement.setAttribute("role","menu"),w(this._searchInput,{"aria-expanded":"true"}),this._positionMenu()}toggleSuggestions(e=!0){this._dropElement.classList.contains(p)?this.hideSuggestions():this.showOrSearch(e)}hideSuggestions(){this._dropElement.classList.remove(p),w(this._searchInput,{"aria-expanded":"false"}),this.removeSelection()}getInput(){return this._searchInput}getDropMenu(){return this._dropElement}_positionMenu(){const e=window.getComputedStyle(this._searchInput),t=this._searchInput.getBoundingClientRect(),s=e.direction==="rtl",i=this._config.fullWidth,r=this._config.fixed;let o=null,a=null;r&&(o=t.x,a=t.y+t.height,s&&!i&&(o-=this._dropElement.offsetWidth-t.width)),this._dropElement.style.transform="unset",i&&(this._dropElement.style.width=this._searchInput.offsetWidth+"px"),o!==null&&(this._dropElement.style.left=o+"px"),a!==null&&(this._dropElement.style.top=a+"px");const c=this._dropElement.getBoundingClientRect(),h=window.innerHeight;if(c.y+c.height>h){const l=i?t.height+4:t.height;this._dropElement.style.transform="translateY(calc(-100.1% - "+l+"px))"}}_fetchData(){this._items={},this._addItems(this._config.items);const e=this._config.datalist;if(e){const t=document.querySelector(`#${e}`);if(t){const s=Array.from(t.children).map(i=>{const r=i.getAttribute("value")??i.innerHTML.toLowerCase(),o=i.innerHTML;return{value:r,label:o}});this._addItems(s)}else console.error(`Datalist not found ${e}`)}this._setHiddenVal(),this._config.server&&!this._config.liveServer&&this._loadFromServer()}_setHiddenVal(){if(this._config.hiddenInput&&!this._config.hiddenValue)for(const[e,t]of Object.entries(this._items))t.label==this._searchInput.value&&(this._hiddenInput.value=e)}_addItems(e){const t=Object.keys(e);for(let s=0;sc.group=r.group),this._addItems(r.items);continue}const o=typeof r=="string"?r:r.label,a=typeof r!="object"?{}:r;a.label=r[this._config.labelField]??o,a.value=r[this._config.valueField]??i,a.label&&(this._items[a.value]=a)}}_loadFromServer(e=!1){this._abortController&&this._abortController.abort(),this._abortController=new AbortController;let t=this._searchInput.dataset.serverParams||{};typeof t=="string"&&(t=JSON.parse(t));const s=Object.assign({},this._config.serverParams,t);if(s[this._config.queryParam]=this._searchInput.value,this._config.noCache&&(s.t=Date.now()),s.related){const a=document.getElementById(s.related);if(a){s.related=a.value;const c=a.getAttribute("name");c&&(s[c]=a.value)}}const i=new URLSearchParams(s);let r=this._config.server,o=Object.assign(this._config.fetchOptions,{method:this._config.serverMethod||"GET",signal:this._abortController.signal});o.method==="POST"?o.body=i:r+="?"+i.toString(),this._searchInput.classList.add(A),this._config.onBeforeFetch(this),fetch(r,o).then(a=>this._config.onServerResponse(a,this)).then(a=>{const c=Y(this._config.serverDataKey,a)||a;this.setData(c),this._setHiddenVal(),this._abortController=null,e&&this._showSuggestions()}).catch(a=>{a.name==="AbortError"||this._abortController.signal.aborted||console.error(a)}).finally(a=>{this._searchInput.classList.remove(A),this._config.onAfterFetch(this)})}}class Q{post(e){let t="/api/v2/transactions";return M.post(t,e)}}class Z{list(e){return M.get("/api/v2/currencies",{params:e})}}let u;const I={description:"/api/v2/autocomplete/transaction-descriptions",account:"/api/v2/autocomplete/accounts"};let ee=function(){return{count:0,totalAmount:0,transactionType:"unknown",showSuccessMessage:!1,showErrorMessage:!1,entries:[],loadingCurrencies:!0,defaultCurrency:{},enabledCurrencies:[],nativeCurrencies:[],foreignCurrencies:[],filters:{source:[],destination:[]},errorMessageText:"",detectTransactionType(){const n=this.entries[0].source_account.type??"unknown",e=this.entries[0].destination_account.type??"unknown";if(n==="unknown"&&e==="unknown"){this.transactionType="unknown",console.warn("Cannot infer transaction type from two unknown accounts.");return}if(n===e&&["Asset account","Loan","Debt","Mortgage"].includes(n)){this.transactionType="transfer",console.log('Transaction type is detected to be "'+this.transactionType+'".'),console.log("filter down currencies for transfer.");return}if(n==="Asset account"&&["Expense account","Debt","Loan","Mortgage"].includes(e)){this.transactionType="withdrawal",console.log('[a] Transaction type is detected to be "'+this.transactionType+'".'),this.filterNativeCurrencies(this.entries[0].source_account.currency_code);return}if(n==="Asset account"&&e==="unknown"){this.transactionType="withdrawal",console.log('[b] Transaction type is detected to be "'+this.transactionType+'".'),console.log(this.entries[0].source_account),this.filterNativeCurrencies(this.entries[0].source_account.currency_code);return}if(["Debt","Loan","Mortgage"].includes(n)&&e==="Expense account"){this.transactionType="withdrawal",console.log('[c] Transaction type is detected to be "'+this.transactionType+'".'),this.filterNativeCurrencies(this.entries[0].source_account.currency_code);return}if(n==="Revenue account"&&["Asset account","Debt","Loan","Mortgage"].includes(e)){this.transactionType="deposit",console.log('Transaction type is detected to be "'+this.transactionType+'".');return}if(["Debt","Loan","Mortgage"].includes(n)&&e==="Asset account"){this.transactionType="deposit",console.log('Transaction type is detected to be "'+this.transactionType+'".');return}console.warn('Unknown account combination between "'+n+'" and "'+e+'".')},selectSourceAccount(n,e){const t=parseInt(e._searchInput.attributes["data-index"].value);document.querySelector("#form")._x_dataStack[0].$data.entries[t].source_account={id:n.id,name:n.name,alpine_name:n.name,type:n.type,currency_code:n.currency_code},console.log("Changed source account into a known "+n.type.toLowerCase()),document.querySelector("#form")._x_dataStack[0].detectTransactionType()},filterNativeCurrencies(n){console.log('filterNativeCurrencies("'+n+'")');let e=[],t;for(let s in this.enabledCurrencies)if(this.enabledCurrencies.hasOwnProperty(s)){let i=this.enabledCurrencies[s];i.code===n&&(t=i)}e.push(t),this.nativeCurrencies=e;for(let s in this.entries)this.entries.hasOwnProperty(s)&&(this.entries[s].currency_code=n)},changedAmount(n){const e=parseInt(n.target.dataset.index);this.entries[e].amount=parseFloat(n.target.value),this.totalAmount=0;for(let t in this.entries)this.entries.hasOwnProperty(t)&&(this.totalAmount=this.totalAmount+parseFloat(this.entries[t].amount));console.log("Changed amount to "+this.totalAmount)},selectDestAccount(n,e){const t=parseInt(e._searchInput.attributes["data-index"].value);document.querySelector("#form")._x_dataStack[0].$data.entries[t].destination_account={id:n.id,name:n.name,alpine_name:n.name,type:n.type,currency_code:n.currency_code},console.log("Changed destination account into a known "+n.type.toLowerCase()),document.querySelector("#form")._x_dataStack[0].detectTransactionType()},loadCurrencies(){console.log("Loading user currencies."),new Z().list({}).then(e=>{for(let t in e.data.data)if(e.data.data.hasOwnProperty(t)){let s=e.data.data[t];if(s.attributes.enabled){let i={id:s.id,name:s.attributes.name,code:s.attributes.code,default:s.attributes.default,symbol:s.attributes.symbol,decimal_places:s.attributes.decimal_places};i.default&&(this.defaultCurrency=i),this.enabledCurrencies.push(i),this.nativeCurrencies.push(i)}}this.loadingCurrencies=!1,console.log(this.enabledCurrencies)})},changeSourceAccount(n,e){if(console.log("changeSourceAccount"),typeof n>"u"){const t=parseInt(e._searchInput.attributes["data-index"].value);if(document.querySelector("#form")._x_dataStack[0].$data.entries[t].source_account.name===e._searchInput.value){console.warn('Ignore hallucinated source account name change to "'+e._searchInput.value+'"'),document.querySelector("#form")._x_dataStack[0].detectTransactionType();return}document.querySelector("#form")._x_dataStack[0].$data.entries[t].source_account={name:e._searchInput.value,alpine_name:e._searchInput.value},console.log('Changed source account into a unknown account called "'+e._searchInput.value+'"'),document.querySelector("#form")._x_dataStack[0].detectTransactionType()}},changeDestAccount(n,e){if(document.querySelector("#form")._x_dataStack[0].$data.entries[0].destination_account,typeof n>"u"){const t=parseInt(e._searchInput.attributes["data-index"].value);if(document.querySelector("#form")._x_dataStack[0].$data.entries[t].destination_account.name===e._searchInput.value){console.warn('Ignore hallucinated destination account name change to "'+e._searchInput.value+'"'),document.querySelector("#form")._x_dataStack[0].detectTransactionType();return}document.querySelector("#form")._x_dataStack[0].$data.entries[t].destination_account={name:e._searchInput.value,alpine_name:e._searchInput.value},console.log('Changed destination account into a unknown account called "'+e._searchInput.value+'"'),document.querySelector("#form")._x_dataStack[0].detectTransactionType()}},showError:!1,showSuccess:!1,addedSplit(){console.log("addedSplit"),S.init("input.ac-source",{server:I.account,serverParams:{types:this.filters.source},fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}},hiddenInput:!0,preventBrowserAutocomplete:!0,highlightTyped:!0,liveServer:!0,onChange:this.changeSourceAccount,onSelectItem:this.selectSourceAccount,onRenderItem:function(n,e,t){return n.name_with_balance+'
'+u.t("firefly.account_type_"+n.type)+""}}),S.init("input.ac-dest",{server:I.account,serverParams:{types:this.filters.destination},fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}},hiddenInput:!0,preventBrowserAutocomplete:!0,liveServer:!0,highlightTyped:!0,onSelectItem:this.selectDestAccount,onChange:this.changeDestAccount,onRenderItem:function(n,e,t){return n.name_with_balance+'
'+u.t("firefly.account_type_"+n.type)+""}}),this.filters.destination=[],S.init("input.ac-description",{server:I.description,fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}},valueField:"id",labelField:"description",highlightTyped:!0,onSelectItem:console.log})},init(){Promise.all([R("language","en_US")]).then(n=>{u=new z;const e=n[0].replace("-","_");u.locale=e,$(u,e).then(()=>{this.addSplit()})}),this.loadCurrencies(),this.filters.source=["Asset account","Loan","Debt","Mortgage","Revenue account"],this.filters.destination=["Expense account","Loan","Debt","Mortgage","Asset account"]},submitTransaction(){this.detectTransactionType();let n=W(this.entries,this.transactionType),e={group_title:null,fire_webhooks:!1,apply_rules:!1,transactions:n};n.length>1&&(e.group_title=n[0].description);let t=new Q;console.log(e),t.post(e).then(s=>{this.showSuccessMessage=!0,console.log(s),window.location="transactions/show/"+s.data.data.id+"?transaction_group_id="+s.data.data.id+"&message=created"}).catch(s=>{this.showErrorMessage=!0,this.errorMessageText=s.response.data.message})},addSplit(){this.entries.push(B())},removeSplit(n){this.entries.splice(n,1),document.querySelector("#split-0-tab").click()},formattedTotalAmount(){return V(this.totalAmount,"EUR")}}},D={transactions:ee,dates:j};function F(){Object.keys(D).forEach(n=>{console.log(`Loading page component "${n}"`);let e=D[n]();Alpine.data(n,()=>e)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{console.log("Loaded through event listener."),F()});window.bootstrapped&&(console.log("Loaded through window variable."),F()); diff --git a/public/build/assets/create-8d2b7efc.js b/public/build/assets/create-8d2b7efc.js new file mode 100644 index 0000000000..f0f9bdb95d --- /dev/null +++ b/public/build/assets/create-8d2b7efc.js @@ -0,0 +1,3 @@ +var q=Object.defineProperty;var H=(n,e,t)=>e in n?q(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var C=(n,e,t)=>(H(n,typeof e!="symbol"?e+"":e,t),t);import{x as B,w as D,J as P,z as j,I as R,A as z,y as $}from"./load-translations-36a0ce82.js";function T(){return{id:"",name:"",alpine_name:""}}function V(){let e=B(new Date,"yyyy-MM-dd HH:mm");return{description:"",amount:"",currency_code:"EUR",source_account:T(),destination_account:T(),date:e,errors:{amount:[]}}}function W(n,e){let t=[];for(let s in n)if(n.hasOwnProperty(s)){const i=n[s];let r={};r.description=i.description,r.source_name=i.source_account.name,r.destination_name=i.destination_account.name,r.amount=i.amount,r.date=i.date,r.currency_code=i.currency_code,typeof i.source_account.id<"u"&&i.source_account.id.toString()!==""&&(r.source_id=i.source_account.id),typeof i.destination_account.id<"u"&&i.destination_account.id.toString()!==""&&(r.destination_id=i.destination_account.id),r.type=e,t.push(r)}return t}const k={showAllSuggestions:!1,suggestionsThreshold:1,maximumItems:0,autoselectFirst:!0,ignoreEnter:!1,updateOnSelect:!1,highlightTyped:!1,highlightClass:"",fullWidth:!1,fixed:!1,fuzzy:!1,startsWith:!1,fillIn:!1,preventBrowserAutocomplete:!1,itemClass:"",activeClasses:["bg-primary","text-white"],labelField:"label",valueField:"value",searchFields:["label"],queryParam:"query",items:[],source:null,hiddenInput:!1,hiddenValue:"",clearControl:"",datalist:"",server:"",serverMethod:"GET",serverParams:{},serverDataKey:"data",fetchOptions:{},liveServer:!1,noCache:!0,debounceTime:300,notFoundMessage:"",onRenderItem:(n,e,t)=>e,onSelectItem:(n,e)=>{},onServerResponse:(n,e)=>n.json(),onChange:(n,e)=>{},onBeforeFetch:n=>{},onAfterFetch:n=>{}},x="is-loading",A="is-active",p="show",_="next",y="prev",g=new WeakMap;let L=0,b=0;function U(n,e=300){let t;return(...s)=>{clearTimeout(t),t=setTimeout(()=>{n.apply(this,s)},e)}}function G(n){return n.normalize("NFD").replace(/[\u0300-\u036f]/g,"")}function v(n){return n?G(n.toString()).toLowerCase():""}function K(n,e){if(n.indexOf(e)>=0)return!0;let t=0;for(let s=0;se+"‍").join("")}function Y(n,e="window"){return n.split(".").reduce((t,s)=>t[s],e)}class S{constructor(e,t={}){C(this,"handleEvent",e=>{["scroll","resize"].includes(e.type)?(this._timer&&window.cancelAnimationFrame(this._timer),this._timer=window.requestAnimationFrame(()=>{this[`on${e.type}`](e)})):this[`on${e.type}`](e)});if(!(e instanceof HTMLElement)){console.error("Invalid element",e);return}g.set(e,this),L++,b++,this._searchInput=e,this._configure(t),this._preventInput=!1,this._keyboardNavigation=!1,this._searchFunc=U(()=>{this._loadFromServer(!0)},this._config.debounceTime),this._configureSearchInput(),this._configureDropElement(),this._config.fixed&&(document.addEventListener("scroll",this,!0),window.addEventListener("resize",this));const s=this._getClearControl();s&&s.addEventListener("click",this),["focus","change","blur","input","keydown"].forEach(i=>{this._searchInput.addEventListener(i,this)}),["mousemove","mouseleave"].forEach(i=>{this._dropElement.addEventListener(i,this)}),this._fetchData()}static init(e="input.autocomplete",t={}){document.querySelectorAll(e).forEach(i=>{this.getOrCreateInstance(i,t)})}static getInstance(e){return g.has(e)?g.get(e):null}static getOrCreateInstance(e,t={}){return this.getInstance(e)||new this(e,t)}dispose(){b--,["focus","change","blur","input","keydown"].forEach(t=>{this._searchInput.removeEventListener(t,this)}),["mousemove","mouseleave"].forEach(t=>{this._dropElement.removeEventListener(t,this)});const e=this._getClearControl();e&&e.removeEventListener("click",this),this._config.fixed&&b<=0&&(document.removeEventListener("scroll",this,!0),window.removeEventListener("resize",this)),this._dropElement.parentElement.removeChild(this._dropElement),g.delete(this._searchInput)}_getClearControl(){if(this._config.clearControl)return document.querySelector(this._config.clearControl)}_configure(e={}){this._config=Object.assign({},k);const t={...e,...this._searchInput.dataset},s=i=>["true","false","1","0",!0,!1].includes(i)&&!!JSON.parse(i);for(const[i,r]of Object.entries(k)){if(t[i]===void 0)continue;const o=t[i];switch(typeof r){case"number":this._config[i]=parseInt(o);break;case"boolean":this._config[i]=s(o);break;case"string":this._config[i]=o.toString();break;case"object":if(Array.isArray(r))if(typeof o=="string"){const a=o.includes("|")?"|":",";this._config[i]=o.split(a)}else this._config[i]=o;else this._config[i]=typeof o=="string"?JSON.parse(o):o;break;case"function":this._config[i]=typeof o=="string"?window[o]:o;break;default:this._config[i]=o;break}}}_configureSearchInput(){if(this._searchInput.autocomplete="off",this._searchInput.spellcheck=!1,w(this._searchInput,{"aria-autocomplete":"list","aria-haspopup":"menu","aria-expanded":"false",role:"combobox"}),this._searchInput.id&&this._config.preventBrowserAutocomplete){const e=document.querySelector(`[for="${this._searchInput.id}"]`);e&&X(e)}this._hiddenInput=null,this._config.hiddenInput&&(this._hiddenInput=document.createElement("input"),this._hiddenInput.type="hidden",this._hiddenInput.value=this._config.hiddenValue,this._hiddenInput.name=this._searchInput.name,this._searchInput.name="_"+this._searchInput.name,M(this._searchInput,this._hiddenInput))}_configureDropElement(){this._dropElement=document.createElement("ul"),this._dropElement.id="ac-menu-"+L,this._dropElement.classList.add("dropdown-menu","autocomplete-menu","p-0"),this._dropElement.style.maxHeight="280px",this._config.fullWidth||(this._dropElement.style.maxWidth="360px"),this._config.fixed&&(this._dropElement.style.position="fixed"),this._dropElement.style.overflowY="auto",this._dropElement.style.overscrollBehavior="contain",this._dropElement.style.textAlign="unset",M(this._searchInput,this._dropElement),this._searchInput.setAttribute("aria-controls",this._dropElement.id)}onclick(e){e.target.matches(this._config.clearControl)&&this.clear()}oninput(e){this._preventInput||(this._hiddenInput&&(this._hiddenInput.value=null),this.showOrSearch())}onchange(e){const t=this._searchInput.value,s=Object.values(this._items).find(i=>i.label===t);this._config.onChange(s,this)}onblur(e){if(e.relatedTarget&&e.relatedTarget.classList.contains("modal")){this._searchInput.focus();return}setTimeout(()=>{this.hideSuggestions()},100)}onfocus(e){this.showOrSearch()}onkeydown(e){switch(e.keyCode||e.key){case 13:case"Enter":if(this.isDropdownVisible()){const s=this.getSelection();s&&s.click(),(s||!this._config.ignoreEnter)&&e.preventDefault()}break;case 38:case"ArrowUp":e.preventDefault(),this._keyboardNavigation=!0,this._moveSelection(y);break;case 40:case"ArrowDown":e.preventDefault(),this._keyboardNavigation=!0,this.isDropdownVisible()?this._moveSelection(_):this.showOrSearch(!1);break;case 27:case"Escape":this.isDropdownVisible()&&(this._searchInput.focus(),this.hideSuggestions());break}}onmousemove(e){this._keyboardNavigation=!1}onmouseleave(e){this.removeSelection()}onscroll(e){this._positionMenu()}onresize(e){this._positionMenu()}getConfig(e=null){return e!==null?this._config[e]:this._config}setConfig(e,t){this._config[e]=t}setData(e){this._items={},this._addItems(e)}enable(){this._searchInput.setAttribute("disabled","")}disable(){this._searchInput.removeAttribute("disabled")}isDisabled(){return this._searchInput.hasAttribute("disabled")||this._searchInput.disabled||this._searchInput.hasAttribute("readonly")}isDropdownVisible(){return this._dropElement.classList.contains(p)}clear(){this._searchInput.value="",this._hiddenInput&&(this._hiddenInput.value="")}getSelection(){return this._dropElement.querySelector("a."+A)}removeSelection(){const e=this.getSelection();e&&e.classList.remove(...this._activeClasses())}_activeClasses(){return[...this._config.activeClasses,A]}_isItemEnabled(e){if(e.style.display==="none")return!1;const t=e.firstElementChild;return t.tagName==="A"&&!t.classList.contains("disabled")}_moveSelection(e=_,t=null){const s=this.getSelection();if(s){const i=e===_?"nextSibling":"previousSibling";t=s.parentNode;do t=t[i];while(t&&!this._isItemEnabled(t));t?(s.classList.remove(...this._activeClasses()),e===y?t.parentNode.scrollTop=t.offsetTop-t.parentNode.offsetTop:t.offsetTop>t.parentNode.offsetHeight-t.offsetHeight&&(t.parentNode.scrollTop+=t.offsetHeight)):s&&(t=s.parentElement)}else{if(e===y)return t;if(!t)for(t=this._dropElement.firstChild;t&&!this._isItemEnabled(t);)t=t.nextSibling}if(t){const i=t.querySelector("a");i.classList.add(...this._activeClasses()),this._searchInput.setAttribute("aria-activedescendant",i.id),this._config.updateOnSelect&&(this._searchInput.value=i.dataset.label)}else this._searchInput.setAttribute("aria-activedescendant","");return t}_shouldShow(){return this.isDisabled()?!1:this._searchInput.value.length>=this._config.suggestionsThreshold}showOrSearch(e=!0){if(e&&!this._shouldShow()){this.hideSuggestions();return}this._config.liveServer?this._searchFunc():this._config.source?this._config.source(this._searchInput.value,t=>{this.setData(t),this._showSuggestions()}):this._showSuggestions()}_createGroup(e){const t=this._createLi(),s=document.createElement("span");return t.append(s),s.classList.add("dropdown-header","text-truncate"),s.innerHTML=e,t}_createItem(e,t){let s=t.label;if(this._config.highlightTyped){const o=v(s).indexOf(e);o!==-1&&(s=s.substring(0,o)+`${s.substring(o,o+e.length)}`+s.substring(o+e.length,s.length))}s=this._config.onRenderItem(t,s,this);const i=this._createLi(),r=document.createElement("a");if(i.append(r),r.id=this._dropElement.id+"-"+this._dropElement.children.length,r.classList.add("dropdown-item","text-truncate"),this._config.itemClass&&r.classList.add(...this._config.itemClass.split(" ")),r.setAttribute("data-value",t.value),r.setAttribute("data-label",t.label),r.setAttribute("tabindex","-1"),r.setAttribute("role","menuitem"),r.setAttribute("href","#"),r.innerHTML=s,t.data)for(const[o,a]of Object.entries(t.data))r.dataset[o]=a;if(this._config.fillIn){const o=document.createElement("button");o.type="button",o.classList.add("btn","btn-link","border-0"),o.innerHTML=` + + `,i.append(o),i.classList.add("d-flex","justify-content-between"),o.addEventListener("click",a=>{this._searchInput.value=t.label,this._searchInput.focus()})}return r.addEventListener("mouseenter",o=>{this._keyboardNavigation||(this.removeSelection(),i.querySelector("a").classList.add(...this._activeClasses()))}),r.addEventListener("mousedown",o=>{o.preventDefault()}),r.addEventListener("click",o=>{o.preventDefault(),this._preventInput=!0,this._searchInput.value=J(t.label),this._hiddenInput&&(this._hiddenInput.value=t.value),this._config.onSelectItem(t,this),this.hideSuggestions(),this._preventInput=!1}),i}_showSuggestions(){if(document.activeElement!=this._searchInput)return;const e=v(this._searchInput.value);this._dropElement.innerHTML="";const t=Object.keys(this._items);let s=0,i=null;const r=[];for(let o=0;o0&&this._config.searchFields.forEach(d=>{const f=v(c[d]);let m=!1;if(this._config.fuzzy)m=K(f,e);else{const E=f.indexOf(e);m=this._config.startsWith?E===0:E>=0}m&&(u=!0)});const N=u||e.length===0;if(h||u){if(s++,c.group&&!r.includes(c.group)){const f=this._createGroup(c.group);this._dropElement.appendChild(f),r.push(c.group)}const d=this._createItem(e,c);if(!i&&N&&(i=d),this._dropElement.appendChild(d),this._config.maximumItems>0&&s>=this._config.maximumItems)break}}if(i&&this._config.autoselectFirst&&(this.removeSelection(),this._moveSelection(_,i)),s===0)if(this._config.notFoundMessage){const o=this._createLi();o.innerHTML=`${this._config.notFoundMessage}`,this._dropElement.appendChild(o),this._showDropdown()}else this.hideSuggestions();else this._showDropdown()}_createLi(){const e=document.createElement("li");return e.setAttribute("role","presentation"),e}_showDropdown(){this._dropElement.classList.add(p),this._dropElement.setAttribute("role","menu"),w(this._searchInput,{"aria-expanded":"true"}),this._positionMenu()}toggleSuggestions(e=!0){this._dropElement.classList.contains(p)?this.hideSuggestions():this.showOrSearch(e)}hideSuggestions(){this._dropElement.classList.remove(p),w(this._searchInput,{"aria-expanded":"false"}),this.removeSelection()}getInput(){return this._searchInput}getDropMenu(){return this._dropElement}_positionMenu(){const e=window.getComputedStyle(this._searchInput),t=this._searchInput.getBoundingClientRect(),s=e.direction==="rtl",i=this._config.fullWidth,r=this._config.fixed;let o=null,a=null;r&&(o=t.x,a=t.y+t.height,s&&!i&&(o-=this._dropElement.offsetWidth-t.width)),this._dropElement.style.transform="unset",i&&(this._dropElement.style.width=this._searchInput.offsetWidth+"px"),o!==null&&(this._dropElement.style.left=o+"px"),a!==null&&(this._dropElement.style.top=a+"px");const c=this._dropElement.getBoundingClientRect(),h=window.innerHeight;if(c.y+c.height>h){const u=i?t.height+4:t.height;this._dropElement.style.transform="translateY(calc(-100.1% - "+u+"px))"}}_fetchData(){this._items={},this._addItems(this._config.items);const e=this._config.datalist;if(e){const t=document.querySelector(`#${e}`);if(t){const s=Array.from(t.children).map(i=>{const r=i.getAttribute("value")??i.innerHTML.toLowerCase(),o=i.innerHTML;return{value:r,label:o}});this._addItems(s)}else console.error(`Datalist not found ${e}`)}this._setHiddenVal(),this._config.server&&!this._config.liveServer&&this._loadFromServer()}_setHiddenVal(){if(this._config.hiddenInput&&!this._config.hiddenValue)for(const[e,t]of Object.entries(this._items))t.label==this._searchInput.value&&(this._hiddenInput.value=e)}_addItems(e){const t=Object.keys(e);for(let s=0;sc.group=r.group),this._addItems(r.items);continue}const o=typeof r=="string"?r:r.label,a=typeof r!="object"?{}:r;a.label=r[this._config.labelField]??o,a.value=r[this._config.valueField]??i,a.label&&(this._items[a.value]=a)}}_loadFromServer(e=!1){this._abortController&&this._abortController.abort(),this._abortController=new AbortController;let t=this._searchInput.dataset.serverParams||{};typeof t=="string"&&(t=JSON.parse(t));const s=Object.assign({},this._config.serverParams,t);if(s[this._config.queryParam]=this._searchInput.value,this._config.noCache&&(s.t=Date.now()),s.related){const a=document.getElementById(s.related);if(a){s.related=a.value;const c=a.getAttribute("name");c&&(s[c]=a.value)}}const i=new URLSearchParams(s);let r=this._config.server,o=Object.assign(this._config.fetchOptions,{method:this._config.serverMethod||"GET",signal:this._abortController.signal});o.method==="POST"?o.body=i:r+="?"+i.toString(),this._searchInput.classList.add(x),this._config.onBeforeFetch(this),fetch(r,o).then(a=>this._config.onServerResponse(a,this)).then(a=>{const c=Y(this._config.serverDataKey,a)||a;this.setData(c),this._setHiddenVal(),this._abortController=null,e&&this._showSuggestions()}).catch(a=>{a.name==="AbortError"||this._abortController.signal.aborted||console.error(a)}).finally(a=>{this._searchInput.classList.remove(x),this._config.onAfterFetch(this)})}}class Q{post(e){let t="/api/v2/transactions";return D.post(t,e)}}class Z{list(e){return D.get("/api/v2/currencies",{params:e})}}let l;const I={description:"/api/v2/autocomplete/transaction-descriptions",account:"/api/v2/autocomplete/accounts"};let ee=function(){return{count:0,totalAmount:0,transactionType:"unknown",showSuccessMessage:!1,showErrorMessage:!1,entries:[],loadingCurrencies:!0,defaultCurrency:{},enabledCurrencies:[],nativeCurrencies:[],foreignCurrencies:[],filters:{source:[],destination:[]},errorMessageText:"",successMessageLink:"#",successMessageText:"",returnHereButton:!1,resetButton:!1,resetButtonEnabled:!1,rulesButton:!0,webhookButton:!0,submitting:!1,detectTransactionType(){const n=this.entries[0].source_account.type??"unknown",e=this.entries[0].destination_account.type??"unknown";if(n==="unknown"&&e==="unknown"){this.transactionType="unknown",console.warn("Cannot infer transaction type from two unknown accounts.");return}if(n===e&&["Asset account","Loan","Debt","Mortgage"].includes(n)){this.transactionType="transfer",console.log('Transaction type is detected to be "'+this.transactionType+'".'),console.log("filter down currencies for transfer.");return}if(n==="Asset account"&&["Expense account","Debt","Loan","Mortgage"].includes(e)){this.transactionType="withdrawal",console.log('[a] Transaction type is detected to be "'+this.transactionType+'".'),this.filterNativeCurrencies(this.entries[0].source_account.currency_code);return}if(n==="Asset account"&&e==="unknown"){this.transactionType="withdrawal",console.log('[b] Transaction type is detected to be "'+this.transactionType+'".'),console.log(this.entries[0].source_account),this.filterNativeCurrencies(this.entries[0].source_account.currency_code);return}if(["Debt","Loan","Mortgage"].includes(n)&&e==="Expense account"){this.transactionType="withdrawal",console.log('[c] Transaction type is detected to be "'+this.transactionType+'".'),this.filterNativeCurrencies(this.entries[0].source_account.currency_code);return}if(n==="Revenue account"&&["Asset account","Debt","Loan","Mortgage"].includes(e)){this.transactionType="deposit",console.log('Transaction type is detected to be "'+this.transactionType+'".');return}if(["Debt","Loan","Mortgage"].includes(n)&&e==="Asset account"){this.transactionType="deposit",console.log('Transaction type is detected to be "'+this.transactionType+'".');return}console.warn('Unknown account combination between "'+n+'" and "'+e+'".')},selectSourceAccount(n,e){const t=parseInt(e._searchInput.attributes["data-index"].value);document.querySelector("#form")._x_dataStack[0].$data.entries[t].source_account={id:n.id,name:n.name,alpine_name:n.name,type:n.type,currency_code:n.currency_code},console.log("Changed source account into a known "+n.type.toLowerCase()),document.querySelector("#form")._x_dataStack[0].detectTransactionType()},filterNativeCurrencies(n){console.log('filterNativeCurrencies("'+n+'")');let e=[],t;for(let s in this.enabledCurrencies)if(this.enabledCurrencies.hasOwnProperty(s)){let i=this.enabledCurrencies[s];i.code===n&&(t=i)}e.push(t),this.nativeCurrencies=e;for(let s in this.entries)this.entries.hasOwnProperty(s)&&(this.entries[s].currency_code=n)},changedAmount(n){const e=parseInt(n.target.dataset.index);this.entries[e].amount=parseFloat(n.target.value),this.totalAmount=0;for(let t in this.entries)this.entries.hasOwnProperty(t)&&(this.totalAmount=this.totalAmount+parseFloat(this.entries[t].amount));console.log("Changed amount to "+this.totalAmount)},selectDestAccount(n,e){const t=parseInt(e._searchInput.attributes["data-index"].value);document.querySelector("#form")._x_dataStack[0].$data.entries[t].destination_account={id:n.id,name:n.name,alpine_name:n.name,type:n.type,currency_code:n.currency_code},console.log("Changed destination account into a known "+n.type.toLowerCase()),document.querySelector("#form")._x_dataStack[0].detectTransactionType()},loadCurrencies(){console.log("Loading user currencies."),new Z().list({}).then(e=>{for(let t in e.data.data)if(e.data.data.hasOwnProperty(t)){let s=e.data.data[t];if(s.attributes.enabled){let i={id:s.id,name:s.attributes.name,code:s.attributes.code,default:s.attributes.default,symbol:s.attributes.symbol,decimal_places:s.attributes.decimal_places};i.default&&(this.defaultCurrency=i),this.enabledCurrencies.push(i),this.nativeCurrencies.push(i)}}this.loadingCurrencies=!1,console.log(this.enabledCurrencies)})},changeSourceAccount(n,e){if(console.log("changeSourceAccount"),typeof n>"u"){const t=parseInt(e._searchInput.attributes["data-index"].value);if(document.querySelector("#form")._x_dataStack[0].$data.entries[t].source_account.name===e._searchInput.value){console.warn('Ignore hallucinated source account name change to "'+e._searchInput.value+'"'),document.querySelector("#form")._x_dataStack[0].detectTransactionType();return}document.querySelector("#form")._x_dataStack[0].$data.entries[t].source_account={name:e._searchInput.value,alpine_name:e._searchInput.value},console.log('Changed source account into a unknown account called "'+e._searchInput.value+'"'),document.querySelector("#form")._x_dataStack[0].detectTransactionType()}},changeDestAccount(n,e){if(document.querySelector("#form")._x_dataStack[0].$data.entries[0].destination_account,typeof n>"u"){const t=parseInt(e._searchInput.attributes["data-index"].value);if(document.querySelector("#form")._x_dataStack[0].$data.entries[t].destination_account.name===e._searchInput.value){console.warn('Ignore hallucinated destination account name change to "'+e._searchInput.value+'"'),document.querySelector("#form")._x_dataStack[0].detectTransactionType();return}document.querySelector("#form")._x_dataStack[0].$data.entries[t].destination_account={name:e._searchInput.value,alpine_name:e._searchInput.value},console.log('Changed destination account into a unknown account called "'+e._searchInput.value+'"'),document.querySelector("#form")._x_dataStack[0].detectTransactionType()}},showError:!1,showSuccess:!1,addedSplit(){console.log("addedSplit"),S.init("input.ac-source",{server:I.account,serverParams:{types:this.filters.source},fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}},hiddenInput:!0,preventBrowserAutocomplete:!0,highlightTyped:!0,liveServer:!0,onChange:this.changeSourceAccount,onSelectItem:this.selectSourceAccount,onRenderItem:function(n,e,t){return n.name_with_balance+'
'+l.t("firefly.account_type_"+n.type)+""}}),S.init("input.ac-dest",{server:I.account,serverParams:{types:this.filters.destination},fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}},hiddenInput:!0,preventBrowserAutocomplete:!0,liveServer:!0,highlightTyped:!0,onSelectItem:this.selectDestAccount,onChange:this.changeDestAccount,onRenderItem:function(n,e,t){return n.name_with_balance+'
'+l.t("firefly.account_type_"+n.type)+""}}),this.filters.destination=[],S.init("input.ac-description",{server:I.description,fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}},valueField:"id",labelField:"description",highlightTyped:!0,onSelectItem:console.log})},init(){Promise.all([j("language","en_US")]).then(n=>{l=new R;const e=n[0].replace("-","_");l.locale=e,z(l,e).then(()=>{this.addSplit()})}),this.loadCurrencies(),this.filters.source=["Asset account","Loan","Debt","Mortgage","Revenue account"],this.filters.destination=["Expense account","Loan","Debt","Mortgage","Asset account"]},submitTransaction(){this.submitting=!0,this.showSuccessMessage=!1,this.showErrorMessage=!1,this.detectTransactionType();let n=W(this.entries,this.transactionType),e={group_title:null,fire_webhooks:!1,apply_rules:!1,transactions:n};n.length>1&&(e.group_title=n[0].description);let t=new Q;console.log(e),t.post(e).then(s=>{this.submitting=!1,console.log(s);const i=parseInt(s.data.data.id);this.returnHereButton&&(this.showSuccessMessage=!0,this.successMessageLink="transactions/show/"+i,this.successMessageText=l.t("firefly.stored_journal_js",{description:e.group_title??e.transactions[0].description}),this.resetButton&&(this.entries=[],this.addSplit(),this.totalAmount=0)),this.returnHereButton||(window.location="transactions/show/"+i+"?transaction_group_id="+i+"&message=created")}).catch(s=>{this.submitting=!1,this.parseErrors(s.response.data)})},parseErrors(n){this.setDefaultErrors(),this.showErrorMessage=!0,this.showSuccessMessage=!1,this.errorMessageText=l.t("firefly.errors_submission")+" "+n.message;let e,t;for(const s in n.errors)if(n.errors.hasOwnProperty(s)){if(s!=="group_title")switch(e=parseInt(s.split(".")[1]),t=s.split(".")[2],t){case"amount":case"date":case"budget_id":case"bill_id":case"description":case"tags":this.entries[e].errors[t]=n.errors[s];break;case"source_name":case"source_id":this.entries[e].errors.source_account=this.entries[e].errors.source_account.concat(n.errors[s]);break;case"destination_name":case"destination_id":this.entries[e].errors.destination_account=this.entries[e].errors.destination_account.concat(n.errors[s]);break;case"foreign_amount":case"foreign_currency_id":this.entries[e].errors.foreign_amount=this.entries[e].errors.foreign_amount.concat(n.errors[s]);break}typeof this.entries[e]<"u"&&(this.entries[e].errors.source_account=Array.from(new Set(this.entries[e].errors.source_account)),this.entries[e].errors.destination_account=Array.from(new Set(this.entries[e].errors.destination_account)))}console.log(this.entries[0].errors)},setDefaultErrors(){},addSplit(){this.entries.push(V())},removeSplit(n){this.entries.splice(n,1),document.querySelector("#split-0-tab").click()},formattedTotalAmount(){return $(this.totalAmount,"EUR")}}},O={transactions:ee,dates:P};function F(){Object.keys(O).forEach(n=>{console.log(`Loading page component "${n}"`);let e=O[n]();Alpine.data(n,()=>e)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{console.log("Loaded through event listener."),F()});window.bootstrapped&&(console.log("Loaded through window variable."),F()); diff --git a/public/build/assets/dashboard-8ab291b1.js b/public/build/assets/dashboard-8ab291b1.js deleted file mode 100644 index 9eba44b395..0000000000 --- a/public/build/assets/dashboard-8ab291b1.js +++ /dev/null @@ -1,30 +0,0 @@ -var Sa=Object.defineProperty;var Oa=(n,t,e)=>t in n?Sa(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var k=(n,t,e)=>(Oa(n,typeof t!="symbol"?t+"":t,e),e);import{r as z,a as ai,t as wt,s as Yi,g as ji,b as ls,c as br,d as j,e as yr,f as _r,_ as an,h as Aa,i as Ui,j as xr,k as La,l as Ra,m as vr,n as Fa,o as As,p as Ia,q as Ls,u as Ea,v as za,w as vt,x as J,y as V,z as bt,P as Ba,I as li,A as ci,B as Na,C as Wa,D as Ha,E as Va,F as Rs,G as Ya,H as ja,J as Ua}from"./load-translations-85b093de.js";var $a=36e5;function qa(n,t){z(2,arguments);var e=wt(t);return ai(n,e*$a)}var Xa=864e5;function Ka(n,t){z(2,arguments);var e=Yi(n),i=Yi(t),s=e.getTime()-ji(e),o=i.getTime()-ji(i);return Math.round((s-o)/Xa)}var Ga=6e4;function Qa(n,t){z(2,arguments);var e=wt(t);return ai(n,e*Ga)}function Za(n,t){z(2,arguments);var e=wt(t),i=e*3;return ls(n,i)}function Ja(n,t){z(2,arguments);var e=wt(t);return ai(n,e*1e3)}function tl(n,t){z(2,arguments);var e=wt(t),i=e*7;return br(n,i)}function el(n,t){z(2,arguments);var e=wt(t);return ls(n,e*12)}function tn(n,t){z(2,arguments);var e=j(n),i=j(t),s=e.getTime()-i.getTime();return s<0?-1:s>0?1:s}var ui=6e4,hi=36e5,nl=1e3;function il(n,t){z(2,arguments);var e=j(n),i=j(t),s=e.getFullYear()-i.getFullYear(),o=e.getMonth()-i.getMonth();return s*12+o}function sl(n,t){z(2,arguments);var e=j(n),i=j(t);return e.getFullYear()-i.getFullYear()}function Fs(n,t){var e=n.getFullYear()-t.getFullYear()||n.getMonth()-t.getMonth()||n.getDate()-t.getDate()||n.getHours()-t.getHours()||n.getMinutes()-t.getMinutes()||n.getSeconds()-t.getSeconds()||n.getMilliseconds()-t.getMilliseconds();return e<0?-1:e>0?1:e}function wr(n,t){z(2,arguments);var e=j(n),i=j(t),s=Fs(e,i),o=Math.abs(Ka(e,i));e.setDate(e.getDate()-s*o);var r=+(Fs(e,i)===-s),a=s*(o-r);return a===0?0:a}function di(n,t){return z(2,arguments),j(n).getTime()-j(t).getTime()}var Is={ceil:Math.ceil,round:Math.round,floor:Math.floor,trunc:function(t){return t<0?Math.ceil(t):Math.floor(t)}},ol="trunc";function bn(n){return n?Is[n]:Is[ol]}function rl(n,t,e){z(2,arguments);var i=di(n,t)/hi;return bn(e==null?void 0:e.roundingMethod)(i)}function al(n,t,e){z(2,arguments);var i=di(n,t)/ui;return bn(e==null?void 0:e.roundingMethod)(i)}function ll(n){z(1,arguments);var t=j(n);return yr(t).getTime()===_r(t).getTime()}function Mr(n,t){z(2,arguments);var e=j(n),i=j(t),s=tn(e,i),o=Math.abs(il(e,i)),r;if(o<1)r=0;else{e.getMonth()===1&&e.getDate()>27&&e.setDate(30),e.setMonth(e.getMonth()-s*o);var a=tn(e,i)===-s;ll(j(n))&&o===1&&tn(n,i)===1&&(a=!1),r=s*(o-Number(a))}return r===0?0:r}function cl(n,t,e){z(2,arguments);var i=Mr(n,t)/3;return bn(e==null?void 0:e.roundingMethod)(i)}function ul(n,t,e){z(2,arguments);var i=di(n,t)/1e3;return bn(e==null?void 0:e.roundingMethod)(i)}function hl(n,t,e){z(2,arguments);var i=wr(n,t)/7;return bn(e==null?void 0:e.roundingMethod)(i)}function dl(n,t){z(2,arguments);var e=j(n),i=j(t),s=tn(e,i),o=Math.abs(sl(e,i));e.setFullYear(1584),i.setFullYear(1584);var r=tn(e,i)===-s,a=s*(o-Number(r));return a===0?0:a}function fl(n){z(1,arguments);var t=j(n);return t.setSeconds(0,0),t}function gl(n){z(1,arguments);var t=j(n),e=t.getFullYear();return t.setFullYear(e+1,0,0),t.setHours(23,59,59,999),t}function pl(n){z(1,arguments);var t=j(n);return t.setMinutes(59,59,999),t}function ml(n){z(1,arguments);var t=j(n);return t.setSeconds(59,999),t}function bl(n){z(1,arguments);var t=j(n);return t.setMilliseconds(999),t}function yl(n,t){if(n==null)throw new TypeError("assign requires that input parameter not be null or undefined");for(var e in t)Object.prototype.hasOwnProperty.call(t,e)&&(n[e]=t[e]);return n}function Es(n,t){(t==null||t>n.length)&&(t=n.length);for(var e=0,i=new Array(t);e=n.length?{done:!0}:{done:!1,value:n[i++]}},e:function(c){throw c},f:s}}throw new TypeError(`Invalid attempt to iterate non-iterable instance. -In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}var o=!0,r=!1,a;return{s:function(){e=e.call(n)},n:function(){var c=e.next();return o=c.done,c},e:function(c){r=!0,a=c},f:function(){try{!o&&e.return!=null&&e.return()}finally{if(r)throw a}}}}function D(n){if(n===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return n}function $i(n,t){return $i=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(i,s){return i.__proto__=s,i},$i(n,t)}function B(n,t){if(typeof t!="function"&&t!==null)throw new TypeError("Super expression must either be null or a function");n.prototype=Object.create(t&&t.prototype,{constructor:{value:n,writable:!0,configurable:!0}}),Object.defineProperty(n,"prototype",{writable:!1}),t&&$i(n,t)}function Gn(n){return Gn=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},Gn(n)}function xl(){if(typeof Reflect>"u"||!Reflect.construct||Reflect.construct.sham)return!1;if(typeof Proxy=="function")return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch{return!1}}function vl(n,t){if(t&&(an(t)==="object"||typeof t=="function"))return t;if(t!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return D(n)}function N(n){var t=xl();return function(){var i=Gn(n),s;if(t){var o=Gn(this).constructor;s=Reflect.construct(i,arguments,o)}else s=i.apply(this,arguments);return vl(this,s)}}function I(n,t){if(!(n instanceof t))throw new TypeError("Cannot call a class as a function")}function wl(n,t){if(an(n)!="object"||!n)return n;var e=n[Symbol.toPrimitive];if(e!==void 0){var i=e.call(n,t||"default");if(an(i)!="object")return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(n)}function kr(n){var t=wl(n,"string");return an(t)=="symbol"?t:String(t)}function Bs(n,t){for(var e=0;e0,i=e?t:1-t,s;if(i<=50)s=n||100;else{var o=i+50,r=Math.floor(o/100)*100,a=n>=o%100;s=n+r-(a?100:0)}return e?s:1-s}function Tr(n){return n%400===0||n%4===0&&n%100!==0}var Dl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r0}},{key:"set",value:function(s,o,r){var a=s.getUTCFullYear();if(r.isTwoDigitYear){var l=Dr(r.year,a);return s.setUTCFullYear(l,0,1),s.setUTCHours(0,0,0,0),s}var c=!("era"in o)||o.era===1?r.year:1-r.year;return s.setUTCFullYear(c,0,1),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Tl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r0}},{key:"set",value:function(s,o,r,a){var l=Aa(s,a);if(r.isTwoDigitYear){var c=Dr(r.year,l);return s.setUTCFullYear(c,0,a.firstWeekContainsDate),s.setUTCHours(0,0,0,0),Ui(s,a)}var u=!("era"in o)||o.era===1?r.year:1-r.year;return s.setUTCFullYear(u,0,a.firstWeekContainsDate),s.setUTCHours(0,0,0,0),Ui(s,a)}}]),e}(Y),Sl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=4}},{key:"set",value:function(s,o,r){return s.setUTCMonth((r-1)*3,1),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Ll=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=4}},{key:"set",value:function(s,o,r){return s.setUTCMonth((r-1)*3,1),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Rl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=11}},{key:"set",value:function(s,o,r){return s.setUTCMonth(r,1),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Fl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=11}},{key:"set",value:function(s,o,r){return s.setUTCMonth(r,1),s.setUTCHours(0,0,0,0),s}}]),e}(Y);function Il(n,t,e){z(2,arguments);var i=j(n),s=wt(t),o=La(i,e)-s;return i.setUTCDate(i.getUTCDate()-o*7),i}var El=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=53}},{key:"set",value:function(s,o,r,a){return Ui(Il(s,r,a),a)}}]),e}(Y);function zl(n,t){z(2,arguments);var e=j(n),i=wt(t),s=Ra(e)-i;return e.setUTCDate(e.getUTCDate()-s*7),e}var Bl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=53}},{key:"set",value:function(s,o,r){return xr(zl(s,r))}}]),e}(Y),Nl=[31,28,31,30,31,30,31,31,30,31,30,31],Wl=[31,29,31,30,31,30,31,31,30,31,30,31],Hl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=Wl[l]:o>=1&&o<=Nl[l]}},{key:"set",value:function(s,o,r){return s.setUTCDate(r),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Vl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=366:o>=1&&o<=365}},{key:"set",value:function(s,o,r){return s.setUTCMonth(0,r),s.setUTCHours(0,0,0,0),s}}]),e}(Y);function us(n,t,e){var i,s,o,r,a,l,c,u;z(2,arguments);var h=vr(),d=wt((i=(s=(o=(r=e==null?void 0:e.weekStartsOn)!==null&&r!==void 0?r:e==null||(a=e.locale)===null||a===void 0||(l=a.options)===null||l===void 0?void 0:l.weekStartsOn)!==null&&o!==void 0?o:h.weekStartsOn)!==null&&s!==void 0?s:(c=h.locale)===null||c===void 0||(u=c.options)===null||u===void 0?void 0:u.weekStartsOn)!==null&&i!==void 0?i:0);if(!(d>=0&&d<=6))throw new RangeError("weekStartsOn must be between 0 and 6 inclusively");var f=j(n),g=wt(t),p=f.getUTCDay(),m=g%7,b=(m+7)%7,y=(b=0&&o<=6}},{key:"set",value:function(s,o,r,a){return s=us(s,r,a),s.setUTCHours(0,0,0,0),s}}]),e}(Y),jl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=6}},{key:"set",value:function(s,o,r,a){return s=us(s,r,a),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Ul=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=6}},{key:"set",value:function(s,o,r,a){return s=us(s,r,a),s.setUTCHours(0,0,0,0),s}}]),e}(Y);function $l(n,t){z(2,arguments);var e=wt(t);e%7===0&&(e=e-7);var i=1,s=j(n),o=s.getUTCDay(),r=e%7,a=(r+7)%7,l=(a=1&&o<=7}},{key:"set",value:function(s,o,r){return s=$l(s,r),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Xl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=12}},{key:"set",value:function(s,o,r){var a=s.getUTCHours()>=12;return a&&r<12?s.setUTCHours(r+12,0,0,0):!a&&r===12?s.setUTCHours(0,0,0,0):s.setUTCHours(r,0,0,0),s}}]),e}(Y),Zl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=23}},{key:"set",value:function(s,o,r){return s.setUTCHours(r,0,0,0),s}}]),e}(Y),Jl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=11}},{key:"set",value:function(s,o,r){var a=s.getUTCHours()>=12;return a&&r<12?s.setUTCHours(r+12,0,0,0):s.setUTCHours(r,0,0,0),s}}]),e}(Y),tc=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=24}},{key:"set",value:function(s,o,r){var a=r<=24?r%24:r;return s.setUTCHours(a,0,0,0),s}}]),e}(Y),ec=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=59}},{key:"set",value:function(s,o,r){return s.setUTCMinutes(r,0,0),s}}]),e}(Y),nc=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=59}},{key:"set",value:function(s,o,r){return s.setUTCSeconds(r,0),s}}]),e}(Y),ic=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&A<=7))throw new RangeError("firstWeekContainsDate must be between 1 and 7 inclusively");var O=wt((g=(p=(m=(b=i==null?void 0:i.weekStartsOn)!==null&&b!==void 0?b:i==null||(y=i.locale)===null||y===void 0||(x=y.options)===null||x===void 0?void 0:x.weekStartsOn)!==null&&m!==void 0?m:M.weekStartsOn)!==null&&p!==void 0?p:(v=M.locale)===null||v===void 0||(_=v.options)===null||_===void 0?void 0:_.weekStartsOn)!==null&&g!==void 0?g:0);if(!(O>=0&&O<=6))throw new RangeError("weekStartsOn must be between 0 and 6 inclusively");if(C==="")return w===""?j(e):new Date(NaN);var R={firstWeekContainsDate:A,weekStartsOn:O,locale:T},st=[new Cl],gt=C.match(uc).map(function(ct){var q=ct[0];if(q in As){var Ot=As[q];return Ot(ct,T.formatLong)}return ct}).join("").match(cc),W=[],U=zs(gt),Q;try{var Mt=function(){var q=Q.value;!(i!=null&&i.useAdditionalWeekYearTokens)&&Ia(q)&&Ls(q,C,n),!(i!=null&&i.useAdditionalDayOfYearTokens)&&Ea(q)&&Ls(q,C,n);var Ot=q[0],wn=lc[Ot];if(wn){var Ts=wn.incompatibleTokens;if(Array.isArray(Ts)){var Ss=W.find(function(Os){return Ts.includes(Os.token)||Os.token===Ot});if(Ss)throw new RangeError("The format string mustn't contain `".concat(Ss.fullToken,"` and `").concat(q,"` at the same time"))}else if(wn.incompatibleTokens==="*"&&W.length>0)throw new RangeError("The format string mustn't contain `".concat(q,"` and any other token at the same time"));W.push({token:Ot,fullToken:q});var xi=wn.run(w,q,T.match,R);if(!xi)return{v:new Date(NaN)};st.push(xi.setter),w=xi.rest}else{if(Ot.match(gc))throw new RangeError("Format string contains an unescaped latin alphabet character `"+Ot+"`");if(q==="''"?q="'":Ot==="'"&&(q=mc(q)),w.indexOf(q)===0)w=w.slice(q.length);else return{v:new Date(NaN)}}};for(U.s();!(Q=U.n()).done;){var lt=Mt();if(an(lt)==="object")return lt.v}}catch(ct){U.e(ct)}finally{U.f()}if(w.length>0&&fc.test(w))return new Date(NaN);var Gt=st.map(function(ct){return ct.priority}).sort(function(ct,q){return q-ct}).filter(function(ct,q,Ot){return Ot.indexOf(ct)===q}).map(function(ct){return st.filter(function(q){return q.priority===ct}).sort(function(q,Ot){return Ot.subPriority-q.subPriority})}).map(function(ct){return ct[0]}),Qt=j(e);if(isNaN(Qt.getTime()))return new Date(NaN);var Tt=za(Qt,ji(Qt)),Zt={},St=zs(Gt),Jt;try{for(St.s();!(Jt=St.n()).done;){var Ft=Jt.value;if(!Ft.validate(Tt,R))return new Date(NaN);var vn=Ft.set(Tt,Zt,R);Array.isArray(vn)?(Tt=vn[0],yl(Zt,vn[1])):Tt=vn}}catch(ct){St.e(ct)}finally{St.f()}return Tt}function mc(n){return n.match(hc)[1].replace(dc,"'")}function bc(n){z(1,arguments);var t=j(n);return t.setMinutes(0,0,0),t}function yc(n){z(1,arguments);var t=j(n);return t.setMilliseconds(0),t}function _c(n,t){var e;z(1,arguments);var i=wt((e=t==null?void 0:t.additionalDigits)!==null&&e!==void 0?e:2);if(i!==2&&i!==1&&i!==0)throw new RangeError("additionalDigits must be 0, 1 or 2");if(!(typeof n=="string"||Object.prototype.toString.call(n)==="[object String]"))return new Date(NaN);var s=Mc(n),o;if(s.date){var r=kc(s.date,i);o=Cc(r.restDateString,r.year)}if(!o||isNaN(o.getTime()))return new Date(NaN);var a=o.getTime(),l=0,c;if(s.time&&(l=Pc(s.time),isNaN(l)))return new Date(NaN);if(s.timezone){if(c=Dc(s.timezone),isNaN(c))return new Date(NaN)}else{var u=new Date(a+l),h=new Date(0);return h.setFullYear(u.getUTCFullYear(),u.getUTCMonth(),u.getUTCDate()),h.setHours(u.getUTCHours(),u.getUTCMinutes(),u.getUTCSeconds(),u.getUTCMilliseconds()),h}return new Date(a+l+c)}var Mn={dateTimeDelimiter:/[T ]/,timeZoneDelimiter:/[Z ]/i,timezone:/([Z+-].*)$/},xc=/^-?(?:(\d{3})|(\d{2})(?:-?(\d{2}))?|W(\d{2})(?:-?(\d{1}))?|)$/,vc=/^(\d{2}(?:[.,]\d*)?)(?::?(\d{2}(?:[.,]\d*)?))?(?::?(\d{2}(?:[.,]\d*)?))?$/,wc=/^([+-])(\d{2})(?::?(\d{2}))?$/;function Mc(n){var t={},e=n.split(Mn.dateTimeDelimiter),i;if(e.length>2)return t;if(/:/.test(e[0])?i=e[0]:(t.date=e[0],i=e[1],Mn.timeZoneDelimiter.test(t.date)&&(t.date=n.split(Mn.timeZoneDelimiter)[0],i=n.substr(t.date.length,n.length))),i){var s=Mn.timezone.exec(i);s?(t.time=i.replace(s[1],""),t.timezone=s[1]):t.time=i}return t}function kc(n,t){var e=new RegExp("^(?:(\\d{4}|[+-]\\d{"+(4+t)+"})|(\\d{2}|[+-]\\d{"+(2+t)+"})$)"),i=n.match(e);if(!i)return{year:NaN,restDateString:""};var s=i[1]?parseInt(i[1]):null,o=i[2]?parseInt(i[2]):null;return{year:o===null?s:o*100,restDateString:n.slice((i[1]||i[2]).length)}}function Cc(n,t){if(t===null)return new Date(NaN);var e=n.match(xc);if(!e)return new Date(NaN);var i=!!e[4],s=Be(e[1]),o=Be(e[2])-1,r=Be(e[3]),a=Be(e[4]),l=Be(e[5])-1;if(i)return Lc(t,a,l)?Tc(t,a,l):new Date(NaN);var c=new Date(0);return!Oc(t,o,r)||!Ac(t,s)?new Date(NaN):(c.setUTCFullYear(t,o,Math.max(s,r)),c)}function Be(n){return n?parseInt(n):1}function Pc(n){var t=n.match(vc);if(!t)return NaN;var e=vi(t[1]),i=vi(t[2]),s=vi(t[3]);return Rc(e,i,s)?e*hi+i*ui+s*1e3:NaN}function vi(n){return n&&parseFloat(n.replace(",","."))||0}function Dc(n){if(n==="Z")return 0;var t=n.match(wc);if(!t)return 0;var e=t[1]==="+"?-1:1,i=parseInt(t[2]),s=t[3]&&parseInt(t[3])||0;return Fc(i,s)?e*(i*hi+s*ui):NaN}function Tc(n,t,e){var i=new Date(0);i.setUTCFullYear(n,0,4);var s=i.getUTCDay()||7,o=(t-1)*7+e+1-s;return i.setUTCDate(i.getUTCDate()+o),i}var Sc=[31,null,31,30,31,30,31,31,30,31,30,31];function Sr(n){return n%400===0||n%4===0&&n%100!==0}function Oc(n,t,e){return t>=0&&t<=11&&e>=1&&e<=(Sc[t]||(Sr(n)?29:28))}function Ac(n,t){return t>=1&&t<=(Sr(n)?366:365)}function Lc(n,t,e){return t>=1&&t<=53&&e>=0&&e<=6}function Rc(n,t,e){return n===24?t===0&&e===0:e>=0&&e<60&&t>=0&&t<60&&n>=0&&n<25}function Fc(n,t){return t>=0&&t<=59}class Ic{get(t,e,i){return vt.get("/api/v2/summary/basic",{params:{start:t,end:e,code:i}})}}function Wt(n,t,e){const i=J(t,"y-MM-dd")+"_"+J(e,"y-MM-dd")+"_"+n;return console.log("getCacheKey: "+i),String(i)}let wi=!1;const Ec=()=>({balanceBox:{amounts:[],subtitles:[]},billBox:{paid:[],unpaid:[]},leftBox:{left:[],perDay:[]},netBox:{net:[]},autoConversion:!1,loading:!1,boxData:null,boxOptions:null,getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Wt("dashboard-boxes-data",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){this.boxData=s,this.generateOptions(this.boxData);return}new Ic().get(J(n,"yyyy-MM-dd"),J(t,"yyyy-MM-dd"),null).then(r=>{this.boxData=r.data,window.store.set(e,r.data),this.generateOptions(this.boxData)})},generateOptions(n){this.balanceBox={amounts:[],subtitles:[]},this.billBox={paid:[],unpaid:[]},this.leftBox={left:[],perDay:[]},this.netBox={net:[]};let t={};for(const e in n)if(n.hasOwnProperty(e)){const i=n[e];if(!i.hasOwnProperty("key"))continue;let s=i.key;if(this.autoConversion){if(s.startsWith("balance-in-native")){this.balanceBox.amounts.push(V(i.value,i.currency_code)),t.hasOwnProperty(i.currency_code)||(t[i.currency_code]="");continue}if(s.startsWith("spent-in-native")){t.hasOwnProperty(i.currency_code)||(t[i.currency_code]=""),t[i.currency_code]=t[i.currency_code]+V(i.value,i.currency_code);continue}if(s.startsWith("earned-in-native")){t.hasOwnProperty(i.currency_code)||(t[i.currency_code]=""),t[i.currency_code]=V(i.value,i.currency_code)+" + "+t[i.currency_code];continue}if(s.startsWith("bills-unpaid-in-native")){this.billBox.unpaid.push(V(i.value,i.currency_code));continue}if(s.startsWith("bills-paid-in-native")){this.billBox.paid.push(V(i.value,i.currency_code));continue}if(s.startsWith("left-to-spend-in-native")){this.leftBox.left.push(V(i.value,i.currency_code));continue}if(s.startsWith("left-per-day-to-spend-in-native")){this.leftBox.perDay.push(V(i.value,i.currency_code));continue}if(s.startsWith("net-worth-in-native")){this.netBox.net.push(V(i.value,i.currency_code));continue}}if(!this.autoConversion&&!s.endsWith("native")){if(s.startsWith("balance-in-")){this.balanceBox.amounts.push(V(i.value,i.currency_code));continue}if(s.startsWith("spent-in-")){t.hasOwnProperty(i.currency_code)||(t[i.currency_code]=""),t[i.currency_code]=t[i.currency_code]+V(i.value,i.currency_code);continue}if(s.startsWith("earned-in-")){t.hasOwnProperty(i.currency_code)||(t[i.currency_code]=""),t[i.currency_code]=V(i.value,i.currency_code)+" + "+t[i.currency_code];continue}if(s.startsWith("bills-unpaid-in-")){this.billBox.unpaid.push(V(i.value,i.currency_code));continue}if(s.startsWith("bills-paid-in-")){this.billBox.paid.push(V(i.value,i.currency_code));continue}if(s.startsWith("left-to-spend-in-")){this.leftBox.left.push(V(i.value,i.currency_code));continue}if(s.startsWith("left-per-day-to-spend-in-")){this.leftBox.perDay.push(V(i.value,i.currency_code));continue}s.startsWith("net-worth-in-")&&this.netBox.net.push(V(i.value,i.currency_code))}}for(let e in t)t.hasOwnProperty(e)&&this.balanceBox.subtitles.push(t[e]);this.loading=!1},loadBoxes(){if(this.loading!==!0){if(this.loading=!0,this.boxData===null){this.getFreshData();return}this.generateOptions(this.boxData),this.loading=!1}},init(){Promise.all([bt("viewRange"),bt("autoConversion",!1)]).then(n=>{wi=!0,this.autoConversion=n[1],this.loadBoxes()}),window.store.observe("end",()=>{wi&&(this.boxData=null,this.loadBoxes())}),window.store.observe("autoConversion",n=>{wi&&(this.autoConversion=n,this.loadBoxes())})}});class zc{put(t,e){let i="/api/v1/preferences/"+t;return vt.put(i,{data:e})}}function Bc(n,t=null){window.store.set(n,t),new zc().put(n,t).then(i=>{}).catch(()=>{new Ba().post(n,t).then(s=>{})})}let Nc=class{dashboard(t,e){let i=J(t,"y-MM-dd"),s=J(e,"y-MM-dd");return vt.get("/api/v2/chart/account/dashboard",{params:{start:i,end:s}})}expense(t,e){let i=J(t,"y-MM-dd"),s=J(e,"y-MM-dd");return vt.get("/api/v2/chart/account/expense-dashboard",{params:{start:i,end:s}})}},Ns=class{get(t,e){let i={date:J(e,"y-MM-dd").slice(0,10)};return e?vt.get("/api/v2/accounts/"+t,{params:i}):vt.get("/api/v2/accounts/"+t)}transactions(t,e){const i={page:e.page??1};return e.hasOwnProperty("start")&&(i.start=J(e.start,"y-MM-dd")),e.hasOwnProperty("end")&&(i.end=J(e.end,"y-MM-dd")),vt.get("/api/v2/accounts/"+t+"/transactions",{params:i})}};/*! - * @kurkle/color v0.3.2 - * https://github.com/kurkle/color#readme - * (c) 2023 Jukka Kurkela - * Released under the MIT License - */function yn(n){return n+.5|0}const ne=(n,t,e)=>Math.max(Math.min(n,e),t);function Ge(n){return ne(yn(n*2.55),0,255)}function le(n){return ne(yn(n*255),0,255)}function $t(n){return ne(yn(n/2.55)/100,0,1)}function Ws(n){return ne(yn(n*100),0,100)}const At={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},qi=[..."0123456789ABCDEF"],Wc=n=>qi[n&15],Hc=n=>qi[(n&240)>>4]+qi[n&15],kn=n=>(n&240)>>4===(n&15),Vc=n=>kn(n.r)&&kn(n.g)&&kn(n.b)&&kn(n.a);function Yc(n){var t=n.length,e;return n[0]==="#"&&(t===4||t===5?e={r:255&At[n[1]]*17,g:255&At[n[2]]*17,b:255&At[n[3]]*17,a:t===5?At[n[4]]*17:255}:(t===7||t===9)&&(e={r:At[n[1]]<<4|At[n[2]],g:At[n[3]]<<4|At[n[4]],b:At[n[5]]<<4|At[n[6]],a:t===9?At[n[7]]<<4|At[n[8]]:255})),e}const jc=(n,t)=>n<255?t(n):"";function Uc(n){var t=Vc(n)?Wc:Hc;return n?"#"+t(n.r)+t(n.g)+t(n.b)+jc(n.a,t):void 0}const $c=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function Or(n,t,e){const i=t*Math.min(e,1-e),s=(o,r=(o+n/30)%12)=>e-i*Math.max(Math.min(r-3,9-r,1),-1);return[s(0),s(8),s(4)]}function qc(n,t,e){const i=(s,o=(s+n/60)%6)=>e-e*t*Math.max(Math.min(o,4-o,1),0);return[i(5),i(3),i(1)]}function Xc(n,t,e){const i=Or(n,1,.5);let s;for(t+e>1&&(s=1/(t+e),t*=s,e*=s),s=0;s<3;s++)i[s]*=1-t-e,i[s]+=t;return i}function Kc(n,t,e,i,s){return n===s?(t-e)/i+(t.5?u/(2-o-r):u/(o+r),l=Kc(e,i,s,u,o),l=l*60+.5),[l|0,c||0,a]}function ds(n,t,e,i){return(Array.isArray(t)?n(t[0],t[1],t[2]):n(t,e,i)).map(le)}function fs(n,t,e){return ds(Or,n,t,e)}function Gc(n,t,e){return ds(Xc,n,t,e)}function Qc(n,t,e){return ds(qc,n,t,e)}function Ar(n){return(n%360+360)%360}function Zc(n){const t=$c.exec(n);let e=255,i;if(!t)return;t[5]!==i&&(e=t[6]?Ge(+t[5]):le(+t[5]));const s=Ar(+t[2]),o=+t[3]/100,r=+t[4]/100;return t[1]==="hwb"?i=Gc(s,o,r):t[1]==="hsv"?i=Qc(s,o,r):i=fs(s,o,r),{r:i[0],g:i[1],b:i[2],a:e}}function Jc(n,t){var e=hs(n);e[0]=Ar(e[0]+t),e=fs(e),n.r=e[0],n.g=e[1],n.b=e[2]}function tu(n){if(!n)return;const t=hs(n),e=t[0],i=Ws(t[1]),s=Ws(t[2]);return n.a<255?`hsla(${e}, ${i}%, ${s}%, ${$t(n.a)})`:`hsl(${e}, ${i}%, ${s}%)`}const Hs={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},Vs={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};function eu(){const n={},t=Object.keys(Vs),e=Object.keys(Hs);let i,s,o,r,a;for(i=0;i>16&255,o>>8&255,o&255]}return n}let Cn;function nu(n){Cn||(Cn=eu(),Cn.transparent=[0,0,0,0]);const t=Cn[n.toLowerCase()];return t&&{r:t[0],g:t[1],b:t[2],a:t.length===4?t[3]:255}}const iu=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;function su(n){const t=iu.exec(n);let e=255,i,s,o;if(t){if(t[7]!==i){const r=+t[7];e=t[8]?Ge(r):ne(r*255,0,255)}return i=+t[1],s=+t[3],o=+t[5],i=255&(t[2]?Ge(i):ne(i,0,255)),s=255&(t[4]?Ge(s):ne(s,0,255)),o=255&(t[6]?Ge(o):ne(o,0,255)),{r:i,g:s,b:o,a:e}}}function ou(n){return n&&(n.a<255?`rgba(${n.r}, ${n.g}, ${n.b}, ${$t(n.a)})`:`rgb(${n.r}, ${n.g}, ${n.b})`)}const Mi=n=>n<=.0031308?n*12.92:Math.pow(n,1/2.4)*1.055-.055,Ce=n=>n<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4);function ru(n,t,e){const i=Ce($t(n.r)),s=Ce($t(n.g)),o=Ce($t(n.b));return{r:le(Mi(i+e*(Ce($t(t.r))-i))),g:le(Mi(s+e*(Ce($t(t.g))-s))),b:le(Mi(o+e*(Ce($t(t.b))-o))),a:n.a+e*(t.a-n.a)}}function Pn(n,t,e){if(n){let i=hs(n);i[t]=Math.max(0,Math.min(i[t]+i[t]*e,t===0?360:1)),i=fs(i),n.r=i[0],n.g=i[1],n.b=i[2]}}function Lr(n,t){return n&&Object.assign(t||{},n)}function Ys(n){var t={r:0,g:0,b:0,a:255};return Array.isArray(n)?n.length>=3&&(t={r:n[0],g:n[1],b:n[2],a:255},n.length>3&&(t.a=le(n[3]))):(t=Lr(n,{r:0,g:0,b:0,a:1}),t.a=le(t.a)),t}function au(n){return n.charAt(0)==="r"?su(n):Zc(n)}class dt{constructor(t){if(t instanceof dt)return t;const e=typeof t;let i;e==="object"?i=Ys(t):e==="string"&&(i=Yc(t)||nu(t)||au(t)),this._rgb=i,this._valid=!!i}get valid(){return this._valid}get rgb(){var t=Lr(this._rgb);return t&&(t.a=$t(t.a)),t}set rgb(t){this._rgb=Ys(t)}rgbString(){return this._valid?ou(this._rgb):void 0}hexString(){return this._valid?Uc(this._rgb):void 0}hslString(){return this._valid?tu(this._rgb):void 0}mix(t,e){if(t){const i=this.rgb,s=t.rgb;let o;const r=e===o?.5:e,a=2*r-1,l=i.a-s.a,c=((a*l===-1?a:(a+l)/(1+a*l))+1)/2;o=1-c,i.r=255&c*i.r+o*s.r+.5,i.g=255&c*i.g+o*s.g+.5,i.b=255&c*i.b+o*s.b+.5,i.a=r*i.a+(1-r)*s.a,this.rgb=i}return this}interpolate(t,e){return t&&(this._rgb=ru(this._rgb,t._rgb,e)),this}clone(){return new dt(this.rgb)}alpha(t){return this._rgb.a=le(t),this}clearer(t){const e=this._rgb;return e.a*=1-t,this}greyscale(){const t=this._rgb,e=yn(t.r*.3+t.g*.59+t.b*.11);return t.r=t.g=t.b=e,this}opaquer(t){const e=this._rgb;return e.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return Pn(this._rgb,2,t),this}darken(t){return Pn(this._rgb,2,-t),this}saturate(t){return Pn(this._rgb,1,t),this}desaturate(t){return Pn(this._rgb,1,-t),this}rotate(t){return Jc(this._rgb,t),this}}/*! - * Chart.js v4.4.1 - * https://www.chartjs.org - * (c) 2023 Chart.js Contributors - * Released under the MIT License - */function Ht(){}const lu=(()=>{let n=0;return()=>n++})();function H(n){return n===null||typeof n>"u"}function K(n){if(Array.isArray&&Array.isArray(n))return!0;const t=Object.prototype.toString.call(n);return t.slice(0,7)==="[object"&&t.slice(-6)==="Array]"}function F(n){return n!==null&&Object.prototype.toString.call(n)==="[object Object]"}function et(n){return(typeof n=="number"||n instanceof Number)&&isFinite(+n)}function Pt(n,t){return et(n)?n:t}function S(n,t){return typeof n>"u"?t:n}const cu=(n,t)=>typeof n=="string"&&n.endsWith("%")?parseFloat(n)/100:+n/t,Rr=(n,t)=>typeof n=="string"&&n.endsWith("%")?parseFloat(n)/100*t:+n;function X(n,t,e){if(n&&typeof n.call=="function")return n.apply(e,t)}function $(n,t,e,i){let s,o,r;if(K(n))if(o=n.length,i)for(s=o-1;s>=0;s--)t.call(e,n[s],s);else for(s=0;sn,x:n=>n.x,y:n=>n.y};function du(n){const t=n.split("."),e=[];let i="";for(const s of t)i+=s,i.endsWith("\\")?i=i.slice(0,-1)+".":(e.push(i),i="");return e}function fu(n){const t=du(n);return e=>{for(const i of t){if(i==="")break;e=e&&e[i]}return e}}function ce(n,t){return(js[t]||(js[t]=fu(t)))(n)}function gs(n){return n.charAt(0).toUpperCase()+n.slice(1)}const cn=n=>typeof n<"u",ue=n=>typeof n=="function",Us=(n,t)=>{if(n.size!==t.size)return!1;for(const e of n)if(!t.has(e))return!1;return!0};function gu(n){return n.type==="mouseup"||n.type==="click"||n.type==="contextmenu"}const tt=Math.PI,G=2*tt,pu=G+tt,ti=Number.POSITIVE_INFINITY,mu=tt/180,at=tt/2,de=tt/4,$s=tt*2/3,ie=Math.log10,Nt=Math.sign;function nn(n,t,e){return Math.abs(n-t)s-o).pop(),t}function un(n){return!isNaN(parseFloat(n))&&isFinite(n)}function yu(n,t){const e=Math.round(n);return e-t<=n&&e+t>=n}function Ir(n,t,e){let i,s,o;for(i=0,s=n.length;il&&c=Math.min(t,e)-i&&n<=Math.max(t,e)+i}function ms(n,t,e){e=e||(r=>n[r]1;)o=s+i>>1,e(o)?s=o:i=o;return{lo:s,hi:i}}const xe=(n,t,e,i)=>ms(n,e,i?s=>{const o=n[s][t];return on[s][t]ms(n,e,i=>n[i][t]>=e);function wu(n,t,e){let i=0,s=n.length;for(;ii&&n[s-1]>e;)s--;return i>0||s{const i="_onData"+gs(e),s=n[e];Object.defineProperty(n,e,{configurable:!0,enumerable:!1,value(...o){const r=s.apply(this,o);return n._chartjs.listeners.forEach(a=>{typeof a[i]=="function"&&a[i](...o)}),r}})})}function Ks(n,t){const e=n._chartjs;if(!e)return;const i=e.listeners,s=i.indexOf(t);s!==-1&&i.splice(s,1),!(i.length>0)&&(zr.forEach(o=>{delete n[o]}),delete n._chartjs)}function Br(n){const t=new Set(n);return t.size===n.length?n:Array.from(t)}const Nr=function(){return typeof window>"u"?function(n){return n()}:window.requestAnimationFrame}();function Wr(n,t){let e=[],i=!1;return function(...s){e=s,i||(i=!0,Nr.call(window,()=>{i=!1,n.apply(t,e)}))}}function ku(n,t){let e;return function(...i){return t?(clearTimeout(e),e=setTimeout(n,t,i)):n.apply(this,i),t}}const Hr=n=>n==="start"?"left":n==="end"?"right":"center",kt=(n,t,e)=>n==="start"?t:n==="end"?e:(t+e)/2,Cu=(n,t,e,i)=>n===(i?"left":"right")?e:n==="center"?(t+e)/2:t;function Pu(n,t,e){const i=t.length;let s=0,o=i;if(n._sorted){const{iScale:r,_parsed:a}=n,l=r.axis,{min:c,max:u,minDefined:h,maxDefined:d}=r.getUserBounds();h&&(s=ft(Math.min(xe(a,l,c).lo,e?i:xe(t,l,r.getPixelForValue(c)).lo),0,i-1)),d?o=ft(Math.max(xe(a,r.axis,u,!0).hi+1,e?0:xe(t,l,r.getPixelForValue(u),!0).hi+1),s,i)-s:o=i-s}return{start:s,count:o}}function Du(n){const{xScale:t,yScale:e,_scaleRanges:i}=n,s={xmin:t.min,xmax:t.max,ymin:e.min,ymax:e.max};if(!i)return n._scaleRanges=s,!0;const o=i.xmin!==t.min||i.xmax!==t.max||i.ymin!==e.min||i.ymax!==e.max;return Object.assign(i,s),o}const Dn=n=>n===0||n===1,Gs=(n,t,e)=>-(Math.pow(2,10*(n-=1))*Math.sin((n-t)*G/e)),Qs=(n,t,e)=>Math.pow(2,-10*n)*Math.sin((n-t)*G/e)+1,sn={linear:n=>n,easeInQuad:n=>n*n,easeOutQuad:n=>-n*(n-2),easeInOutQuad:n=>(n/=.5)<1?.5*n*n:-.5*(--n*(n-2)-1),easeInCubic:n=>n*n*n,easeOutCubic:n=>(n-=1)*n*n+1,easeInOutCubic:n=>(n/=.5)<1?.5*n*n*n:.5*((n-=2)*n*n+2),easeInQuart:n=>n*n*n*n,easeOutQuart:n=>-((n-=1)*n*n*n-1),easeInOutQuart:n=>(n/=.5)<1?.5*n*n*n*n:-.5*((n-=2)*n*n*n-2),easeInQuint:n=>n*n*n*n*n,easeOutQuint:n=>(n-=1)*n*n*n*n+1,easeInOutQuint:n=>(n/=.5)<1?.5*n*n*n*n*n:.5*((n-=2)*n*n*n*n+2),easeInSine:n=>-Math.cos(n*at)+1,easeOutSine:n=>Math.sin(n*at),easeInOutSine:n=>-.5*(Math.cos(tt*n)-1),easeInExpo:n=>n===0?0:Math.pow(2,10*(n-1)),easeOutExpo:n=>n===1?1:-Math.pow(2,-10*n)+1,easeInOutExpo:n=>Dn(n)?n:n<.5?.5*Math.pow(2,10*(n*2-1)):.5*(-Math.pow(2,-10*(n*2-1))+2),easeInCirc:n=>n>=1?n:-(Math.sqrt(1-n*n)-1),easeOutCirc:n=>Math.sqrt(1-(n-=1)*n),easeInOutCirc:n=>(n/=.5)<1?-.5*(Math.sqrt(1-n*n)-1):.5*(Math.sqrt(1-(n-=2)*n)+1),easeInElastic:n=>Dn(n)?n:Gs(n,.075,.3),easeOutElastic:n=>Dn(n)?n:Qs(n,.075,.3),easeInOutElastic(n){return Dn(n)?n:n<.5?.5*Gs(n*2,.1125,.45):.5+.5*Qs(n*2-1,.1125,.45)},easeInBack(n){return n*n*((1.70158+1)*n-1.70158)},easeOutBack(n){return(n-=1)*n*((1.70158+1)*n+1.70158)+1},easeInOutBack(n){let t=1.70158;return(n/=.5)<1?.5*(n*n*(((t*=1.525)+1)*n-t)):.5*((n-=2)*n*(((t*=1.525)+1)*n+t)+2)},easeInBounce:n=>1-sn.easeOutBounce(1-n),easeOutBounce(n){return n<1/2.75?7.5625*n*n:n<2/2.75?7.5625*(n-=1.5/2.75)*n+.75:n<2.5/2.75?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375},easeInOutBounce:n=>n<.5?sn.easeInBounce(n*2)*.5:sn.easeOutBounce(n*2-1)*.5+.5};function bs(n){if(n&&typeof n=="object"){const t=n.toString();return t==="[object CanvasPattern]"||t==="[object CanvasGradient]"}return!1}function Ae(n){return bs(n)?n:new dt(n)}function on(n){return bs(n)?n:new dt(n).saturate(.5).darken(.1).hexString()}const Tu=["x","y","borderWidth","radius","tension"],Su=["color","borderColor","backgroundColor"];function Ou(n){n.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),n.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:t=>t!=="onProgress"&&t!=="onComplete"&&t!=="fn"}),n.set("animations",{colors:{type:"color",properties:Su},numbers:{type:"number",properties:Tu}}),n.describe("animations",{_fallback:"animation"}),n.set("transitions",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:"transparent"},visible:{type:"boolean",duration:0}}},hide:{animations:{colors:{to:"transparent"},visible:{type:"boolean",easing:"linear",fn:t=>t|0}}}})}function Au(n){n.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})}const Zs=new Map;function Lu(n,t){t=t||{};const e=n+JSON.stringify(t);let i=Zs.get(e);return i||(i=new Intl.NumberFormat(n,t),Zs.set(e,i)),i}function _n(n,t,e){return Lu(t,e).format(n)}const Vr={values(n){return K(n)?n:""+n},numeric(n,t,e){if(n===0)return"0";const i=this.chart.options.locale;let s,o=n;if(e.length>1){const c=Math.max(Math.abs(e[0].value),Math.abs(e[e.length-1].value));(c<1e-4||c>1e15)&&(s="scientific"),o=Ru(n,e)}const r=ie(Math.abs(o)),a=isNaN(r)?1:Math.max(Math.min(-1*Math.floor(r),20),0),l={notation:s,minimumFractionDigits:a,maximumFractionDigits:a};return Object.assign(l,this.options.ticks.format),_n(n,i,l)},logarithmic(n,t,e){if(n===0)return"0";const i=e[t].significand||n/Math.pow(10,Math.floor(ie(n)));return[1,2,3,5,10,15].includes(i)||t>.8*e.length?Vr.numeric.call(this,n,t,e):""}};function Ru(n,t){let e=t.length>3?t[2].value-t[1].value:t[1].value-t[0].value;return Math.abs(e)>=1&&n!==Math.floor(n)&&(e=n-Math.floor(n)),e}var fi={formatters:Vr};function Fu(n){n.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,e)=>e.lineWidth,tickColor:(t,e)=>e.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:fi.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),n.route("scale.ticks","color","","color"),n.route("scale.grid","color","","borderColor"),n.route("scale.border","color","","borderColor"),n.route("scale.title","color","","color"),n.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&t!=="callback"&&t!=="parser",_indexable:t=>t!=="borderDash"&&t!=="tickBorderDash"&&t!=="dash"}),n.describe("scales",{_fallback:"scale"}),n.describe("scale.ticks",{_scriptable:t=>t!=="backdropPadding"&&t!=="callback",_indexable:t=>t!=="backdropPadding"})}const Me=Object.create(null),Ki=Object.create(null);function rn(n,t){if(!t)return n;const e=t.split(".");for(let i=0,s=e.length;ii.chart.platform.getDevicePixelRatio(),this.elements={},this.events=["mousemove","mouseout","click","touchstart","touchmove"],this.font={family:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:12,style:"normal",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(i,s)=>on(s.backgroundColor),this.hoverBorderColor=(i,s)=>on(s.borderColor),this.hoverColor=(i,s)=>on(s.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(t),this.apply(e)}set(t,e){return ki(this,t,e)}get(t){return rn(this,t)}describe(t,e){return ki(Ki,t,e)}override(t,e){return ki(Me,t,e)}route(t,e,i,s){const o=rn(this,t),r=rn(this,i),a="_"+e;Object.defineProperties(o,{[a]:{value:o[e],writable:!0},[e]:{enumerable:!0,get(){const l=this[a],c=r[s];return F(l)?Object.assign({},c,l):S(l,c)},set(l){this[a]=l}}})}apply(t){t.forEach(e=>e(this))}}var nt=new Iu({_scriptable:n=>!n.startsWith("on"),_indexable:n=>n!=="events",hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[Ou,Au,Fu]);function Eu(n){return!n||H(n.size)||H(n.family)?null:(n.style?n.style+" ":"")+(n.weight?n.weight+" ":"")+n.size+"px "+n.family}function ei(n,t,e,i,s){let o=t[s];return o||(o=t[s]=n.measureText(s).width,e.push(s)),o>i&&(i=o),i}function zu(n,t,e,i){i=i||{};let s=i.data=i.data||{},o=i.garbageCollect=i.garbageCollect||[];i.font!==t&&(s=i.data={},o=i.garbageCollect=[],i.font=t),n.save(),n.font=t;let r=0;const a=e.length;let l,c,u,h,d;for(l=0;le.length){for(l=0;l0&&n.stroke()}}function Xt(n,t,e){return e=e||.5,!t||n&&n.x>t.left-e&&n.xt.top-e&&n.y0&&o.strokeColor!=="";let l,c;for(n.save(),n.font=s.string,Wu(n,o),l=0;l+n||0;function ys(n,t){const e={},i=F(t),s=i?Object.keys(t):t,o=F(n)?i?r=>S(n[r],n[t[r]]):r=>n[r]:()=>n;for(const r of s)e[r]=$u(o(r));return e}function jr(n){return ys(n,{top:"y",right:"x",bottom:"y",left:"x"})}function ve(n){return ys(n,["topLeft","topRight","bottomLeft","bottomRight"])}function yt(n){const t=jr(n);return t.width=t.left+t.right,t.height=t.top+t.bottom,t}function ut(n,t){n=n||{},t=t||nt.font;let e=S(n.size,t.size);typeof e=="string"&&(e=parseInt(e,10));let i=S(n.style,t.style);i&&!(""+i).match(ju)&&(console.warn('Invalid font style specified: "'+i+'"'),i=void 0);const s={family:S(n.family,t.family),lineHeight:Uu(S(n.lineHeight,t.lineHeight),e),size:e,style:i,weight:S(n.weight,t.weight),string:""};return s.string=Eu(s),s}function Tn(n,t,e,i){let s=!0,o,r,a;for(o=0,r=n.length;oe&&a===0?0:a+l;return{min:r(i,-Math.abs(o)),max:r(s,o)}}function he(n,t){return Object.assign(Object.create(n),t)}function _s(n,t=[""],e,i,s=()=>n[0]){const o=e||n;typeof i>"u"&&(i=Xr("_fallback",n));const r={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:n,_rootScopes:o,_fallback:i,_getTarget:s,override:a=>_s([a,...n],t,o,i)};return new Proxy(r,{deleteProperty(a,l){return delete a[l],delete a._keys,delete n[0][l],!0},get(a,l){return $r(a,l,()=>eh(l,t,n,a))},getOwnPropertyDescriptor(a,l){return Reflect.getOwnPropertyDescriptor(a._scopes[0],l)},getPrototypeOf(){return Reflect.getPrototypeOf(n[0])},has(a,l){return eo(a).includes(l)},ownKeys(a){return eo(a)},set(a,l,c){const u=a._storage||(a._storage=s());return a[l]=u[l]=c,delete a._keys,!0}})}function Ee(n,t,e,i){const s={_cacheable:!1,_proxy:n,_context:t,_subProxy:e,_stack:new Set,_descriptors:Ur(n,i),setContext:o=>Ee(n,o,e,i),override:o=>Ee(n.override(o),t,e,i)};return new Proxy(s,{deleteProperty(o,r){return delete o[r],delete n[r],!0},get(o,r,a){return $r(o,r,()=>Ku(o,r,a))},getOwnPropertyDescriptor(o,r){return o._descriptors.allKeys?Reflect.has(n,r)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(n,r)},getPrototypeOf(){return Reflect.getPrototypeOf(n)},has(o,r){return Reflect.has(n,r)},ownKeys(){return Reflect.ownKeys(n)},set(o,r,a){return n[r]=a,delete o[r],!0}})}function Ur(n,t={scriptable:!0,indexable:!0}){const{_scriptable:e=t.scriptable,_indexable:i=t.indexable,_allKeys:s=t.allKeys}=n;return{allKeys:s,scriptable:e,indexable:i,isScriptable:ue(e)?e:()=>e,isIndexable:ue(i)?i:()=>i}}const Xu=(n,t)=>n?n+gs(t):t,xs=(n,t)=>F(t)&&n!=="adapters"&&(Object.getPrototypeOf(t)===null||t.constructor===Object);function $r(n,t,e){if(Object.prototype.hasOwnProperty.call(n,t))return n[t];const i=e();return n[t]=i,i}function Ku(n,t,e){const{_proxy:i,_context:s,_subProxy:o,_descriptors:r}=n;let a=i[t];return ue(a)&&r.isScriptable(t)&&(a=Gu(t,a,n,e)),K(a)&&a.length&&(a=Qu(t,a,n,r.isIndexable)),xs(t,a)&&(a=Ee(a,s,o&&o[t],r)),a}function Gu(n,t,e,i){const{_proxy:s,_context:o,_subProxy:r,_stack:a}=e;if(a.has(n))throw new Error("Recursion detected: "+Array.from(a).join("->")+"->"+n);a.add(n);let l=t(o,r||i);return a.delete(n),xs(n,l)&&(l=vs(s._scopes,s,n,l)),l}function Qu(n,t,e,i){const{_proxy:s,_context:o,_subProxy:r,_descriptors:a}=e;if(typeof o.index<"u"&&i(n))return t[o.index%t.length];if(F(t[0])){const l=t,c=s._scopes.filter(u=>u!==l);t=[];for(const u of l){const h=vs(c,s,n,u);t.push(Ee(h,o,r&&r[n],a))}}return t}function qr(n,t,e){return ue(n)?n(t,e):n}const Zu=(n,t)=>n===!0?t:typeof n=="string"?ce(t,n):void 0;function Ju(n,t,e,i,s){for(const o of t){const r=Zu(e,o);if(r){n.add(r);const a=qr(r._fallback,e,s);if(typeof a<"u"&&a!==e&&a!==i)return a}else if(r===!1&&typeof i<"u"&&e!==i)return null}return!1}function vs(n,t,e,i){const s=t._rootScopes,o=qr(t._fallback,e,i),r=[...n,...s],a=new Set;a.add(i);let l=to(a,r,e,o||e,i);return l===null||typeof o<"u"&&o!==e&&(l=to(a,r,o,l,i),l===null)?!1:_s(Array.from(a),[""],s,o,()=>th(t,e,i))}function to(n,t,e,i,s){for(;e;)e=Ju(n,t,e,i,s);return e}function th(n,t,e){const i=n._getTarget();t in i||(i[t]={});const s=i[t];return K(s)&&F(e)?e:s||{}}function eh(n,t,e,i){let s;for(const o of t)if(s=Xr(Xu(o,n),e),typeof s<"u")return xs(n,s)?vs(e,i,n,s):s}function Xr(n,t){for(const e of t){if(!e)continue;const i=e[n];if(typeof i<"u")return i}}function eo(n){let t=n._keys;return t||(t=n._keys=nh(n._scopes)),t}function nh(n){const t=new Set;for(const e of n)for(const i of Object.keys(e).filter(s=>!s.startsWith("_")))t.add(i);return Array.from(t)}function ih(n,t,e,i){const{iScale:s}=n,{key:o="r"}=this._parsing,r=new Array(i);let a,l,c,u;for(a=0,l=i;atn==="x"?"y":"x";function oh(n,t,e,i){const s=n.skip?t:n,o=t,r=e.skip?t:e,a=Xi(o,s),l=Xi(r,o);let c=a/(a+l),u=l/(a+l);c=isNaN(c)?0:c,u=isNaN(u)?0:u;const h=i*c,d=i*u;return{previous:{x:o.x-h*(r.x-s.x),y:o.y-h*(r.y-s.y)},next:{x:o.x+d*(r.x-s.x),y:o.y+d*(r.y-s.y)}}}function rh(n,t,e){const i=n.length;let s,o,r,a,l,c=ze(n,0);for(let u=0;u!c.skip)),t.cubicInterpolationMode==="monotone")lh(n,s);else{let c=i?n[n.length-1]:n[0];for(o=0,r=n.length;on.ownerDocument.defaultView.getComputedStyle(n,null);function hh(n,t){return mi(n).getPropertyValue(t)}const dh=["top","right","bottom","left"];function we(n,t,e){const i={};e=e?"-"+e:"";for(let s=0;s<4;s++){const o=dh[s];i[o]=parseFloat(n[t+"-"+o+e])||0}return i.width=i.left+i.right,i.height=i.top+i.bottom,i}const fh=(n,t,e)=>(n>0||t>0)&&(!e||!e.shadowRoot);function gh(n,t){const e=n.touches,i=e&&e.length?e[0]:n,{offsetX:s,offsetY:o}=i;let r=!1,a,l;if(fh(s,o,n.target))a=s,l=o;else{const c=t.getBoundingClientRect();a=i.clientX-c.left,l=i.clientY-c.top,r=!0}return{x:a,y:l,box:r}}function be(n,t){if("native"in n)return n;const{canvas:e,currentDevicePixelRatio:i}=t,s=mi(e),o=s.boxSizing==="border-box",r=we(s,"padding"),a=we(s,"border","width"),{x:l,y:c,box:u}=gh(n,e),h=r.left+(u&&a.left),d=r.top+(u&&a.top);let{width:f,height:g}=t;return o&&(f-=r.width+a.width,g-=r.height+a.height),{x:Math.round((l-h)/f*e.width/i),y:Math.round((c-d)/g*e.height/i)}}function ph(n,t,e){let i,s;if(t===void 0||e===void 0){const o=Ms(n);if(!o)t=n.clientWidth,e=n.clientHeight;else{const r=o.getBoundingClientRect(),a=mi(o),l=we(a,"border","width"),c=we(a,"padding");t=r.width-c.width-l.width,e=r.height-c.height-l.height,i=ni(a.maxWidth,o,"clientWidth"),s=ni(a.maxHeight,o,"clientHeight")}}return{width:t,height:e,maxWidth:i||ti,maxHeight:s||ti}}const On=n=>Math.round(n*10)/10;function mh(n,t,e,i){const s=mi(n),o=we(s,"margin"),r=ni(s.maxWidth,n,"clientWidth")||ti,a=ni(s.maxHeight,n,"clientHeight")||ti,l=ph(n,t,e);let{width:c,height:u}=l;if(s.boxSizing==="content-box"){const d=we(s,"border","width"),f=we(s,"padding");c-=f.width+d.width,u-=f.height+d.height}return c=Math.max(0,c-o.width),u=Math.max(0,i?c/i:u-o.height),c=On(Math.min(c,r,l.maxWidth)),u=On(Math.min(u,a,l.maxHeight)),c&&!u&&(u=On(c/2)),(t!==void 0||e!==void 0)&&i&&l.height&&u>l.height&&(u=l.height,c=On(Math.floor(u*i))),{width:c,height:u}}function no(n,t,e){const i=t||1,s=Math.floor(n.height*i),o=Math.floor(n.width*i);n.height=Math.floor(n.height),n.width=Math.floor(n.width);const r=n.canvas;return r.style&&(e||!r.style.height&&!r.style.width)&&(r.style.height=`${n.height}px`,r.style.width=`${n.width}px`),n.currentDevicePixelRatio!==i||r.height!==s||r.width!==o?(n.currentDevicePixelRatio=i,r.height=s,r.width=o,n.ctx.setTransform(i,0,0,i,0,0),!0):!1}const bh=function(){let n=!1;try{const t={get passive(){return n=!0,!1}};ws()&&(window.addEventListener("test",null,t),window.removeEventListener("test",null,t))}catch{}return n}();function io(n,t){const e=hh(n,t),i=e&&e.match(/^(\d+)(\.\d+)?px$/);return i?+i[1]:void 0}function ye(n,t,e,i){return{x:n.x+e*(t.x-n.x),y:n.y+e*(t.y-n.y)}}function yh(n,t,e,i){return{x:n.x+e*(t.x-n.x),y:i==="middle"?e<.5?n.y:t.y:i==="after"?e<1?n.y:t.y:e>0?t.y:n.y}}function _h(n,t,e,i){const s={x:n.cp2x,y:n.cp2y},o={x:t.cp1x,y:t.cp1y},r=ye(n,s,e),a=ye(s,o,e),l=ye(o,t,e),c=ye(r,a,e),u=ye(a,l,e);return ye(c,u,e)}const xh=function(n,t){return{x(e){return n+n+t-e},setWidth(e){t=e},textAlign(e){return e==="center"?e:e==="right"?"left":"right"},xPlus(e,i){return e-i},leftForLtr(e,i){return e-i}}},vh=function(){return{x(n){return n},setWidth(n){},textAlign(n){return n},xPlus(n,t){return n+t},leftForLtr(n,t){return n}}};function Fe(n,t,e){return n?xh(t,e):vh()}function Gr(n,t){let e,i;(t==="ltr"||t==="rtl")&&(e=n.canvas.style,i=[e.getPropertyValue("direction"),e.getPropertyPriority("direction")],e.setProperty("direction",t,"important"),n.prevTextDirection=i)}function Qr(n,t){t!==void 0&&(delete n.prevTextDirection,n.canvas.style.setProperty("direction",t[0],t[1]))}function Zr(n){return n==="angle"?{between:hn,compare:_u,normalize:Dt}:{between:qt,compare:(t,e)=>t-e,normalize:t=>t}}function so({start:n,end:t,count:e,loop:i,style:s}){return{start:n%e,end:t%e,loop:i&&(t-n+1)%e===0,style:s}}function wh(n,t,e){const{property:i,start:s,end:o}=e,{between:r,normalize:a}=Zr(i),l=t.length;let{start:c,end:u,loop:h}=n,d,f;if(h){for(c+=l,u+=l,d=0,f=l;dl(s,x,b)&&a(s,x)!==0,_=()=>a(o,b)===0||l(o,x,b),w=()=>p||v(),C=()=>!p||_();for(let M=u,T=u;M<=h;++M)y=t[M%r],!y.skip&&(b=c(y[i]),b!==x&&(p=l(b,s,o),m===null&&w()&&(m=a(b,s)===0?M:T),m!==null&&C()&&(g.push(so({start:m,end:M,loop:d,count:r,style:f})),m=null),T=M,x=b));return m!==null&&g.push(so({start:m,end:h,loop:d,count:r,style:f})),g}function ta(n,t){const e=[],i=n.segments;for(let s=0;ss&&n[o%t].skip;)o--;return o%=t,{start:s,end:o}}function kh(n,t,e,i){const s=n.length,o=[];let r=t,a=n[t],l;for(l=t+1;l<=e;++l){const c=n[l%s];c.skip||c.stop?a.skip||(i=!1,o.push({start:t%s,end:(l-1)%s,loop:i}),t=r=c.stop?l:null):(r=l,a.skip&&(t=l)),a=c}return r!==null&&o.push({start:t%s,end:r%s,loop:i}),o}function Ch(n,t){const e=n.points,i=n.options.spanGaps,s=e.length;if(!s)return[];const o=!!n._loop,{start:r,end:a}=Mh(e,s,o,i);if(i===!0)return oo(n,[{start:r,end:a,loop:o}],e,t);const l=aa({chart:t,initial:e.initial,numSteps:r,currentStep:Math.min(i-e.start,r)}))}_refresh(){this._request||(this._running=!0,this._request=Nr.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(t=Date.now()){let e=0;this._charts.forEach((i,s)=>{if(!i.running||!i.items.length)return;const o=i.items;let r=o.length-1,a=!1,l;for(;r>=0;--r)l=o[r],l._active?(l._total>i.duration&&(i.duration=l._total),l.tick(t),a=!0):(o[r]=o[o.length-1],o.pop());a&&(s.draw(),this._notify(s,i,t,"progress")),o.length||(i.running=!1,this._notify(s,i,t,"complete"),i.initial=!1),e+=o.length}),this._lastDate=t,e===0&&(this._running=!1)}_getAnims(t){const e=this._charts;let i=e.get(t);return i||(i={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,i)),i}listen(t,e,i){this._getAnims(t).listeners[e].push(i)}add(t,e){!e||!e.length||this._getAnims(t).items.push(...e)}has(t){return this._getAnims(t).items.length>0}start(t){const e=this._charts.get(t);e&&(e.running=!0,e.start=Date.now(),e.duration=e.items.reduce((i,s)=>Math.max(i,s._duration),0),this._refresh())}running(t){if(!this._running)return!1;const e=this._charts.get(t);return!(!e||!e.running||!e.items.length)}stop(t){const e=this._charts.get(t);if(!e||!e.items.length)return;const i=e.items;let s=i.length-1;for(;s>=0;--s)i[s].cancel();e.items=[],this._notify(t,e,Date.now(),"complete")}remove(t){return this._charts.delete(t)}}var jt=new Th;const ao="transparent",Sh={boolean(n,t,e){return e>.5?t:n},color(n,t,e){const i=Ae(n||ao),s=i.valid&&Ae(t||ao);return s&&s.valid?s.mix(i,e).hexString():t},number(n,t,e){return n+(t-n)*e}};class Oh{constructor(t,e,i,s){const o=e[i];s=Tn([t.to,s,o,t.from]);const r=Tn([t.from,o,s]);this._active=!0,this._fn=t.fn||Sh[t.type||typeof r],this._easing=sn[t.easing]||sn.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=i,this._from=r,this._to=s,this._promises=void 0}active(){return this._active}update(t,e,i){if(this._active){this._notify(!1);const s=this._target[this._prop],o=i-this._start,r=this._duration-o;this._start=i,this._duration=Math.floor(Math.max(r,t.duration)),this._total+=o,this._loop=!!t.loop,this._to=Tn([t.to,e,s,t.from]),this._from=Tn([t.from,s,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,i=this._duration,s=this._prop,o=this._from,r=this._loop,a=this._to;let l;if(this._active=o!==a&&(r||e1?2-l:l,l=this._easing(Math.min(1,Math.max(0,l))),this._target[s]=this._fn(o,a,l)}wait(){const t=this._promises||(this._promises=[]);return new Promise((e,i)=>{t.push({res:e,rej:i})})}_notify(t){const e=t?"res":"rej",i=this._promises||[];for(let s=0;s{const o=t[s];if(!F(o))return;const r={};for(const a of e)r[a]=o[a];(K(o.properties)&&o.properties||[s]).forEach(a=>{(a===s||!i.has(a))&&i.set(a,r)})})}_animateOptions(t,e){const i=e.options,s=Lh(t,i);if(!s)return[];const o=this._createAnimations(s,i);return i.$shared&&Ah(t.options.$animations,i).then(()=>{t.options=i},()=>{}),o}_createAnimations(t,e){const i=this._properties,s=[],o=t.$animations||(t.$animations={}),r=Object.keys(e),a=Date.now();let l;for(l=r.length-1;l>=0;--l){const c=r[l];if(c.charAt(0)==="$")continue;if(c==="options"){s.push(...this._animateOptions(t,e));continue}const u=e[c];let h=o[c];const d=i.get(c);if(h)if(d&&h.active()){h.update(d,u,a);continue}else h.cancel();if(!d||!d.duration){t[c]=u;continue}o[c]=h=new Oh(d,t,c,u),s.push(h)}return s}update(t,e){if(this._properties.size===0){Object.assign(t,e);return}const i=this._createAnimations(t,e);if(i.length)return jt.add(this._chart,i),!0}}function Ah(n,t){const e=[],i=Object.keys(t);for(let s=0;s0||!e&&o<0)return s.index}return null}function fo(n,t){const{chart:e,_cachedMeta:i}=n,s=e._stacks||(e._stacks={}),{iScale:o,vScale:r,index:a}=i,l=o.axis,c=r.axis,u=Eh(o,r,i),h=t.length;let d;for(let f=0;fe[i].axis===t).shift()}function Nh(n,t){return he(n,{active:!1,dataset:void 0,datasetIndex:t,index:t,mode:"default",type:"dataset"})}function Wh(n,t,e){return he(n,{active:!1,dataIndex:t,parsed:void 0,raw:void 0,element:e,index:t,mode:"default",type:"data"})}function Ne(n,t){const e=n.controller.index,i=n.vScale&&n.vScale.axis;if(i){t=t||n._parsed;for(const s of t){const o=s._stacks;if(!o||o[i]===void 0||o[i][e]===void 0)return;delete o[i][e],o[i]._visualValues!==void 0&&o[i]._visualValues[e]!==void 0&&delete o[i]._visualValues[e]}}}const Pi=n=>n==="reset"||n==="none",go=(n,t)=>t?n:Object.assign({},n),Hh=(n,t,e)=>n&&!t.hidden&&t._stacked&&{keys:na(e,!0),values:null};class Kt{constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=uo(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled("filler")&&console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options")}updateIndex(t){this.index!==t&&Ne(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,i=this.getDataset(),s=(h,d,f,g)=>h==="x"?d:h==="r"?g:f,o=e.xAxisID=S(i.xAxisID,Ci(t,"x")),r=e.yAxisID=S(i.yAxisID,Ci(t,"y")),a=e.rAxisID=S(i.rAxisID,Ci(t,"r")),l=e.indexAxis,c=e.iAxisID=s(l,o,r,a),u=e.vAxisID=s(l,r,o,a);e.xScale=this.getScaleForId(o),e.yScale=this.getScaleForId(r),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(c),e.vScale=this.getScaleForId(u)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&Ks(this._data,this),t._stacked&&Ne(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),i=this._data;if(F(e))this._data=Ih(e);else if(i!==e){if(i){Ks(i,this);const s=this._cachedMeta;Ne(s),s._parsed=[]}e&&Object.isExtensible(e)&&Mu(e,this),this._syncList=[],this._data=e}}addElements(){const t=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(t.dataset=new this.datasetElementType)}buildOrUpdateElements(t){const e=this._cachedMeta,i=this.getDataset();let s=!1;this._dataCheck();const o=e._stacked;e._stacked=uo(e.vScale,e),e.stack!==i.stack&&(s=!0,Ne(e),e.stack=i.stack),this._resyncElements(t),(s||o!==e._stacked)&&fo(this,e._parsed)}configure(){const t=this.chart.config,e=t.datasetScopeKeys(this._type),i=t.getOptionScopes(this.getDataset(),e,!0);this.options=t.createResolver(i,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(t,e){const{_cachedMeta:i,_data:s}=this,{iScale:o,_stacked:r}=i,a=o.axis;let l=t===0&&e===s.length?!0:i._sorted,c=t>0&&i._parsed[t-1],u,h,d;if(this._parsing===!1)i._parsed=s,i._sorted=!0,d=s;else{K(s[t])?d=this.parseArrayData(i,s,t,e):F(s[t])?d=this.parseObjectData(i,s,t,e):d=this.parsePrimitiveData(i,s,t,e);const f=()=>h[a]===null||c&&h[a]p||h=0;--d)if(!g()){this.updateRangeFromParsed(c,t,f,l);break}}return c}getAllParsedValues(t){const e=this._cachedMeta._parsed,i=[];let s,o,r;for(s=0,o=e.length;s=0&&tthis.getContext(i,s,e),p=c.resolveNamedOptions(d,f,g,h);return p.$shared&&(p.$shared=l,o[r]=Object.freeze(go(p,l))),p}_resolveAnimations(t,e,i){const s=this.chart,o=this._cachedDataOpts,r=`animation-${e}`,a=o[r];if(a)return a;let l;if(s.options.animation!==!1){const u=this.chart.config,h=u.datasetAnimationScopeKeys(this._type,e),d=u.getOptionScopes(this.getDataset(),h);l=u.createResolver(d,this.getContext(t,i,e))}const c=new ea(s,l&&l.animations);return l&&l._cacheable&&(o[r]=Object.freeze(c)),c}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||Pi(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const i=this.resolveDataElementOptions(t,e),s=this._sharedOptions,o=this.getSharedOptions(i),r=this.includeOptions(e,o)||o!==s;return this.updateSharedOptions(o,e,i),{sharedOptions:o,includeOptions:r}}updateElement(t,e,i,s){Pi(s)?Object.assign(t,i):this._resolveAnimations(e,s).update(t,i)}updateSharedOptions(t,e,i){t&&!Pi(e)&&this._resolveAnimations(void 0,e).update(t,i)}_setStyle(t,e,i,s){t.active=s;const o=this.getStyle(e,s);this._resolveAnimations(e,i,s).update(t,{options:!s&&this.getSharedOptions(o)||o})}removeHoverStyle(t,e,i){this._setStyle(t,i,"active",!1)}setHoverStyle(t,e,i){this._setStyle(t,i,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const e=this._data,i=this._cachedMeta.data;for(const[a,l,c]of this._syncList)this[a](l,c);this._syncList=[];const s=i.length,o=e.length,r=Math.min(o,s);r&&this.parse(0,r),o>s?this._insertElements(s,o-s,t):o{for(c.length+=e,a=c.length-1;a>=r;a--)c[a]=c[a-e]};for(l(o),a=t;as-o))}return n._cache.$bar}function Yh(n){const t=n.iScale,e=Vh(t,n.type);let i=t._length,s,o,r,a;const l=()=>{r===32767||r===-32768||(cn(a)&&(i=Math.min(i,Math.abs(r-a)||i)),a=r)};for(s=0,o=e.length;s0?s[n-1]:null,a=nMath.abs(a)&&(l=a,c=r),t[e.axis]=c,t._custom={barStart:l,barEnd:c,start:s,end:o,min:r,max:a}}function ia(n,t,e,i){return K(n)?$h(n,t,e,i):t[e.axis]=e.parse(n,i),t}function po(n,t,e,i){const s=n.iScale,o=n.vScale,r=s.getLabels(),a=s===o,l=[];let c,u,h,d;for(c=e,u=e+i;c=e?1:-1)}function Xh(n){let t,e,i,s,o;return n.horizontal?(t=n.base>n.x,e="left",i="right"):(t=n.basel.controller.options.grouped),o=i.options.stacked,r=[],a=l=>{const c=l.controller.getParsed(e),u=c&&c[l.vScale.axis];if(H(u)||isNaN(u))return!0};for(const l of s)if(!(e!==void 0&&a(l))&&((o===!1||r.indexOf(l.stack)===-1||o===void 0&&l.stack===void 0)&&r.push(l.stack),l.index===t))break;return r.length||r.push(void 0),r}_getStackCount(t){return this._getStacks(void 0,t).length}_getStackIndex(t,e,i){const s=this._getStacks(t,i),o=e!==void 0?s.indexOf(e):-1;return o===-1?s.length-1:o}_getRuler(){const t=this.options,e=this._cachedMeta,i=e.iScale,s=[];let o,r;for(o=0,r=e.data.length;ohn(x,a,l,!0)?1:Math.max(v,v*e,_,_*e),g=(x,v,_)=>hn(x,a,l,!0)?-1:Math.min(v,v*e,_,_*e),p=f(0,c,h),m=f(at,u,d),b=g(tt,c,h),y=g(tt+at,u,d);i=(p-b)/2,s=(m-y)/2,o=-(p+b)/2,r=-(m+y)/2}return{ratioX:i,ratioY:s,offsetX:o,offsetY:r}}class Le extends Kt{constructor(t,e){super(t,e),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(t,e){const i=this.getDataset().data,s=this._cachedMeta;if(this._parsing===!1)s._parsed=i;else{let o=l=>+i[l];if(F(i[t])){const{key:l="value"}=this._parsing;o=c=>+ce(i[c],l)}let r,a;for(r=t,a=t+e;r0&&!isNaN(t)?G*(Math.abs(t)/e):0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],o=_n(e._parsed[t],i.options.locale);return{label:s[t]||"",value:o}}getMaxBorderWidth(t){let e=0;const i=this.chart;let s,o,r,a,l;if(!t){for(s=0,o=i.data.datasets.length;st!=="spacing",_indexable:t=>t!=="spacing"&&!t.startsWith("borderDash")&&!t.startsWith("hoverBorderDash")}),k(Le,"overrides",{aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i,color:s}}=t.legend.options;return e.labels.map((o,r)=>{const l=t.getDatasetMeta(0).controller.getStyle(r);return{text:o,fillStyle:l.backgroundColor,strokeStyle:l.borderColor,fontColor:s,lineWidth:l.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(r),index:r}})}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}}});class jn extends Kt{initialize(){this.enableOptionSharing=!0,this.supportsDecimation=!0,super.initialize()}update(t){const e=this._cachedMeta,{dataset:i,data:s=[],_dataset:o}=e,r=this.chart._animationsDisabled;let{start:a,count:l}=Pu(e,s,r);this._drawStart=a,this._drawCount=l,Du(e)&&(a=0,l=s.length),i._chart=this.chart,i._datasetIndex=this.index,i._decimated=!!o._decimated,i.points=s;const c=this.resolveDatasetElementOptions(t);this.options.showLine||(c.borderWidth=0),c.segment=this.options.segment,this.updateElement(i,void 0,{animated:!r,options:c},t),this.updateElements(s,a,l,t)}updateElements(t,e,i,s){const o=s==="reset",{iScale:r,vScale:a,_stacked:l,_dataset:c}=this._cachedMeta,{sharedOptions:u,includeOptions:h}=this._getSharedOptions(e,s),d=r.axis,f=a.axis,{spanGaps:g,segment:p}=this.options,m=un(g)?g:Number.POSITIVE_INFINITY,b=this.chart._animationsDisabled||o||s==="none",y=e+i,x=t.length;let v=e>0&&this.getParsed(e-1);for(let _=0;_=y){C.skip=!0;continue}const M=this.getParsed(_),T=H(M[f]),A=C[d]=r.getPixelForValue(M[d],_),O=C[f]=o||T?a.getBasePixel():a.getPixelForValue(l?this.applyStack(a,M,l):M[f],_);C.skip=isNaN(A)||isNaN(O)||T,C.stop=_>0&&Math.abs(M[d]-v[d])>m,p&&(C.parsed=M,C.raw=c.data[_]),h&&(C.options=u||this.resolveDataElementOptions(_,w.active?"active":s)),b||this.updateElement(w,_,C,s),v=M}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,i=e.options&&e.options.borderWidth||0,s=t.data||[];if(!s.length)return i;const o=s[0].size(this.resolveDataElementOptions(0)),r=s[s.length-1].size(this.resolveDataElementOptions(s.length-1));return Math.max(i,o,r)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}}k(jn,"id","line"),k(jn,"defaults",{datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1}),k(jn,"overrides",{scales:{_index_:{type:"category"},_value_:{type:"linear"}}});class Un extends Kt{constructor(t,e){super(t,e),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],o=_n(e._parsed[t].r,i.options.locale);return{label:s[t]||"",value:o}}parseObjectData(t,e,i,s){return ih.bind(this)(t,e,i,s)}update(t){const e=this._cachedMeta.data;this._updateRadius(),this.updateElements(e,0,e.length,t)}getMinMax(){const t=this._cachedMeta,e={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY};return t.data.forEach((i,s)=>{const o=this.getParsed(s).r;!isNaN(o)&&this.chart.getDataVisibility(s)&&(oe.max&&(e.max=o))}),e}_updateRadius(){const t=this.chart,e=t.chartArea,i=t.options,s=Math.min(e.right-e.left,e.bottom-e.top),o=Math.max(s/2,0),r=Math.max(i.cutoutPercentage?o/100*i.cutoutPercentage:1,0),a=(o-r)/t.getVisibleDatasetCount();this.outerRadius=o-a*this.index,this.innerRadius=this.outerRadius-a}updateElements(t,e,i,s){const o=s==="reset",r=this.chart,l=r.options.animation,c=this._cachedMeta.rScale,u=c.xCenter,h=c.yCenter,d=c.getIndexAngle(0)-.5*tt;let f=d,g;const p=360/this.countVisibleElements();for(g=0;g{!isNaN(this.getParsed(s).r)&&this.chart.getDataVisibility(s)&&e++}),e}_computeAngle(t,e,i){return this.chart.getDataVisibility(t)?Lt(this.resolveDataElementOptions(t,e).angle||i):0}}k(Un,"id","polarArea"),k(Un,"defaults",{dataElementType:"arc",animation:{animateRotate:!0,animateScale:!0},animations:{numbers:{type:"number",properties:["x","y","startAngle","endAngle","innerRadius","outerRadius"]}},indexAxis:"r",startAngle:0}),k(Un,"overrides",{aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i,color:s}}=t.legend.options;return e.labels.map((o,r)=>{const l=t.getDatasetMeta(0).controller.getStyle(r);return{text:o,fillStyle:l.backgroundColor,strokeStyle:l.borderColor,fontColor:s,lineWidth:l.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(r),index:r}})}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}},scales:{r:{type:"radialLinear",angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}});class Qi extends Le{}k(Qi,"id","pie"),k(Qi,"defaults",{cutout:0,rotation:0,circumference:360,radius:"100%"});function ge(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class ks{constructor(t){k(this,"options");this.options=t||{}}static override(t){Object.assign(ks.prototype,t)}init(){}formats(){return ge()}parse(){return ge()}format(){return ge()}add(){return ge()}diff(){return ge()}startOf(){return ge()}endOf(){return ge()}}var sa={_date:ks};function Jh(n,t,e,i){const{controller:s,data:o,_sorted:r}=n,a=s._cachedMeta.iScale;if(a&&t===a.axis&&t!=="r"&&r&&o.length){const l=a._reversePixels?vu:xe;if(i){if(s._sharedOptions){const c=o[0],u=typeof c.getRange=="function"&&c.getRange(t);if(u){const h=l(o,t,e-u),d=l(o,t,e+u);return{lo:h.lo,hi:d.hi}}}}else return l(o,t,e)}return{lo:0,hi:o.length-1}}function xn(n,t,e,i,s){const o=n.getSortedVisibleDatasetMetas(),r=e[t];for(let a=0,l=o.length;a{l[r](t[e],s)&&(o.push({element:l,datasetIndex:c,index:u}),a=a||l.inRange(t.x,t.y,s))}),i&&!a?[]:o}var id={evaluateInteractionItems:xn,modes:{index(n,t,e,i){const s=be(t,n),o=e.axis||"x",r=e.includeInvisible||!1,a=e.intersect?Ti(n,s,o,i,r):Si(n,s,o,!1,i,r),l=[];return a.length?(n.getSortedVisibleDatasetMetas().forEach(c=>{const u=a[0].index,h=c.data[u];h&&!h.skip&&l.push({element:h,datasetIndex:c.index,index:u})}),l):[]},dataset(n,t,e,i){const s=be(t,n),o=e.axis||"xy",r=e.includeInvisible||!1;let a=e.intersect?Ti(n,s,o,i,r):Si(n,s,o,!1,i,r);if(a.length>0){const l=a[0].datasetIndex,c=n.getDatasetMeta(l).data;a=[];for(let u=0;ue.pos===t)}function _o(n,t){return n.filter(e=>oa.indexOf(e.pos)===-1&&e.box.axis===t)}function He(n,t){return n.sort((e,i)=>{const s=t?i:e,o=t?e:i;return s.weight===o.weight?s.index-o.index:s.weight-o.weight})}function sd(n){const t=[];let e,i,s,o,r,a;for(e=0,i=(n||[]).length;ec.box.fullSize),!0),i=He(We(t,"left"),!0),s=He(We(t,"right")),o=He(We(t,"top"),!0),r=He(We(t,"bottom")),a=_o(t,"x"),l=_o(t,"y");return{fullSize:e,leftAndTop:i.concat(o),rightAndBottom:s.concat(l).concat(r).concat(a),chartArea:We(t,"chartArea"),vertical:i.concat(s).concat(l),horizontal:o.concat(r).concat(a)}}function xo(n,t,e,i){return Math.max(n[e],t[e])+Math.max(n[i],t[i])}function ra(n,t){n.top=Math.max(n.top,t.top),n.left=Math.max(n.left,t.left),n.bottom=Math.max(n.bottom,t.bottom),n.right=Math.max(n.right,t.right)}function ld(n,t,e,i){const{pos:s,box:o}=e,r=n.maxPadding;if(!F(s)){e.size&&(n[s]-=e.size);const h=i[e.stack]||{size:0,count:1};h.size=Math.max(h.size,e.horizontal?o.height:o.width),e.size=h.size/h.count,n[s]+=e.size}o.getPadding&&ra(r,o.getPadding());const a=Math.max(0,t.outerWidth-xo(r,n,"left","right")),l=Math.max(0,t.outerHeight-xo(r,n,"top","bottom")),c=a!==n.w,u=l!==n.h;return n.w=a,n.h=l,e.horizontal?{same:c,other:u}:{same:u,other:c}}function cd(n){const t=n.maxPadding;function e(i){const s=Math.max(t[i]-n[i],0);return n[i]+=s,s}n.y+=e("top"),n.x+=e("left"),e("right"),e("bottom")}function ud(n,t){const e=t.maxPadding;function i(s){const o={left:0,top:0,right:0,bottom:0};return s.forEach(r=>{o[r]=Math.max(t[r],e[r])}),o}return i(n?["left","right"]:["top","bottom"])}function Qe(n,t,e,i){const s=[];let o,r,a,l,c,u;for(o=0,r=n.length,c=0;o{typeof p.beforeLayout=="function"&&p.beforeLayout()});const u=l.reduce((p,m)=>m.box.options&&m.box.options.display===!1?p:p+1,0)||1,h=Object.freeze({outerWidth:t,outerHeight:e,padding:s,availableWidth:o,availableHeight:r,vBoxMaxWidth:o/2/u,hBoxMaxHeight:r/2}),d=Object.assign({},s);ra(d,yt(i));const f=Object.assign({maxPadding:d,w:o,h:r,x:s.left,y:s.top},s),g=rd(l.concat(c),h);Qe(a.fullSize,f,h,g),Qe(l,f,h,g),Qe(c,f,h,g)&&Qe(l,f,h,g),cd(f),vo(a.leftAndTop,f,h,g),f.x+=f.w,f.y+=f.h,vo(a.rightAndBottom,f,h,g),n.chartArea={left:f.left,top:f.top,right:f.left+f.w,bottom:f.top+f.h,height:f.h,width:f.w},$(a.chartArea,p=>{const m=p.box;Object.assign(m,n.chartArea),m.update(f.w,f.h,{left:0,top:0,right:0,bottom:0})})}};class aa{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,i){}removeEventListener(t,e,i){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,i,s){return e=Math.max(0,e||t.width),i=i||t.height,{width:e,height:Math.max(0,s?Math.floor(e/s):i)}}isAttached(t){return!0}updateConfig(t){}}class hd extends aa{acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}updateConfig(t){t.options.animation=!1}}const $n="$chartjs",dd={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},wo=n=>n===null||n==="";function fd(n,t){const e=n.style,i=n.getAttribute("height"),s=n.getAttribute("width");if(n[$n]={initial:{height:i,width:s,style:{display:e.display,height:e.height,width:e.width}}},e.display=e.display||"block",e.boxSizing=e.boxSizing||"border-box",wo(s)){const o=io(n,"width");o!==void 0&&(n.width=o)}if(wo(i))if(n.style.height==="")n.height=n.width/(t||2);else{const o=io(n,"height");o!==void 0&&(n.height=o)}return n}const la=bh?{passive:!0}:!1;function gd(n,t,e){n.addEventListener(t,e,la)}function pd(n,t,e){n.canvas.removeEventListener(t,e,la)}function md(n,t){const e=dd[n.type]||n.type,{x:i,y:s}=be(n,t);return{type:e,chart:t,native:n,x:i!==void 0?i:null,y:s!==void 0?s:null}}function ii(n,t){for(const e of n)if(e===t||e.contains(t))return!0}function bd(n,t,e){const i=n.canvas,s=new MutationObserver(o=>{let r=!1;for(const a of o)r=r||ii(a.addedNodes,i),r=r&&!ii(a.removedNodes,i);r&&e()});return s.observe(document,{childList:!0,subtree:!0}),s}function yd(n,t,e){const i=n.canvas,s=new MutationObserver(o=>{let r=!1;for(const a of o)r=r||ii(a.removedNodes,i),r=r&&!ii(a.addedNodes,i);r&&e()});return s.observe(document,{childList:!0,subtree:!0}),s}const fn=new Map;let Mo=0;function ca(){const n=window.devicePixelRatio;n!==Mo&&(Mo=n,fn.forEach((t,e)=>{e.currentDevicePixelRatio!==n&&t()}))}function _d(n,t){fn.size||window.addEventListener("resize",ca),fn.set(n,t)}function xd(n){fn.delete(n),fn.size||window.removeEventListener("resize",ca)}function vd(n,t,e){const i=n.canvas,s=i&&Ms(i);if(!s)return;const o=Wr((a,l)=>{const c=s.clientWidth;e(a,l),c{const l=a[0],c=l.contentRect.width,u=l.contentRect.height;c===0&&u===0||o(c,u)});return r.observe(s),_d(n,o),r}function Oi(n,t,e){e&&e.disconnect(),t==="resize"&&xd(n)}function wd(n,t,e){const i=n.canvas,s=Wr(o=>{n.ctx!==null&&e(md(o,n))},n);return gd(i,t,s),s}class Md extends aa{acquireContext(t,e){const i=t&&t.getContext&&t.getContext("2d");return i&&i.canvas===t?(fd(t,e),i):null}releaseContext(t){const e=t.canvas;if(!e[$n])return!1;const i=e[$n].initial;["height","width"].forEach(o=>{const r=i[o];H(r)?e.removeAttribute(o):e.setAttribute(o,r)});const s=i.style||{};return Object.keys(s).forEach(o=>{e.style[o]=s[o]}),e.width=e.width,delete e[$n],!0}addEventListener(t,e,i){this.removeEventListener(t,e);const s=t.$proxies||(t.$proxies={}),r={attach:bd,detach:yd,resize:vd}[e]||wd;s[e]=r(t,e,i)}removeEventListener(t,e){const i=t.$proxies||(t.$proxies={}),s=i[e];if(!s)return;({attach:Oi,detach:Oi,resize:Oi}[e]||pd)(t,e,s),i[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,i,s){return mh(t,e,i,s)}isAttached(t){const e=Ms(t);return!!(e&&e.isConnected)}}function kd(n){return!ws()||typeof OffscreenCanvas<"u"&&n instanceof OffscreenCanvas?hd:Md}class Rt{constructor(){k(this,"x");k(this,"y");k(this,"active",!1);k(this,"options");k(this,"$animations")}tooltipPosition(t){const{x:e,y:i}=this.getProps(["x","y"],t);return{x:e,y:i}}hasValue(){return un(this.x)&&un(this.y)}getProps(t,e){const i=this.$animations;if(!e||!i)return this;const s={};return t.forEach(o=>{s[o]=i[o]&&i[o].active()?i[o]._to:this[o]}),s}}k(Rt,"defaults",{}),k(Rt,"defaultRoutes");function Cd(n,t){const e=n.options.ticks,i=Pd(n),s=Math.min(e.maxTicksLimit||i,i),o=e.major.enabled?Td(t):[],r=o.length,a=o[0],l=o[r-1],c=[];if(r>s)return Sd(t,c,o,r/s),c;const u=Dd(o,t,s);if(r>0){let h,d;const f=r>1?Math.round((l-a)/(r-1)):null;for(Ln(t,c,u,H(f)?0:a-f,a),h=0,d=r-1;hs)return l}return Math.max(s,1)}function Td(n){const t=[];let e,i;for(e=0,i=n.length;en==="left"?"right":n==="right"?"left":n,ko=(n,t,e)=>t==="top"||t==="left"?n[t]+e:n[t]-e,Co=(n,t)=>Math.min(t||n,n);function Po(n,t){const e=[],i=n.length/t,s=n.length;let o=0;for(;or+a)))return l}function Rd(n,t){$(n,e=>{const i=e.gc,s=i.length/2;let o;if(s>t){for(o=0;oi?i:e,i=s&&e>i?e:i,{min:Pt(e,Pt(i,e)),max:Pt(i,Pt(e,i))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){X(this.options.beforeUpdate,[this])}update(t,e,i){const{beginAtZero:s,grace:o,ticks:r}=this.options,a=r.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=i=Object.assign({left:0,right:0,top:0,bottom:0},i),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+i.left+i.right:this.height+i.top+i.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=qu(this,o,s),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const l=a=o||i<=1||!this.isHorizontal()){this.labelRotation=s;return}const u=this._getLabelSizes(),h=u.widest.width,d=u.highest.height,f=ft(this.chart.width-h,0,this.maxWidth);a=t.offset?this.maxWidth/i:f/(i-1),h+6>a&&(a=f/(i-(t.offset?.5:1)),l=this.maxHeight-Ve(t.grid)-e.padding-Do(t.title,this.chart.options.font),c=Math.sqrt(h*h+d*d),r=ps(Math.min(Math.asin(ft((u.highest.height+6)/a,-1,1)),Math.asin(ft(l/c,-1,1))-Math.asin(ft(d/c,-1,1)))),r=Math.max(s,Math.min(o,r))),this.labelRotation=r}afterCalculateLabelRotation(){X(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){X(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:i,title:s,grid:o}}=this,r=this._isVisible(),a=this.isHorizontal();if(r){const l=Do(s,e.options.font);if(a?(t.width=this.maxWidth,t.height=Ve(o)+l):(t.height=this.maxHeight,t.width=Ve(o)+l),i.display&&this.ticks.length){const{first:c,last:u,widest:h,highest:d}=this._getLabelSizes(),f=i.padding*2,g=Lt(this.labelRotation),p=Math.cos(g),m=Math.sin(g);if(a){const b=i.mirror?0:m*h.width+p*d.height;t.height=Math.min(this.maxHeight,t.height+b+f)}else{const b=i.mirror?0:p*h.width+m*d.height;t.width=Math.min(this.maxWidth,t.width+b+f)}this._calculatePadding(c,u,m,p)}}this._handleMargins(),a?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,i,s){const{ticks:{align:o,padding:r},position:a}=this.options,l=this.labelRotation!==0,c=a!=="top"&&this.axis==="x";if(this.isHorizontal()){const u=this.getPixelForTick(0)-this.left,h=this.right-this.getPixelForTick(this.ticks.length-1);let d=0,f=0;l?c?(d=s*t.width,f=i*e.height):(d=i*t.height,f=s*e.width):o==="start"?f=e.width:o==="end"?d=t.width:o!=="inner"&&(d=t.width/2,f=e.width/2),this.paddingLeft=Math.max((d-u+r)*this.width/(this.width-u),0),this.paddingRight=Math.max((f-h+r)*this.width/(this.width-h),0)}else{let u=e.height/2,h=t.height/2;o==="start"?(u=0,h=t.height):o==="end"&&(u=e.height,h=0),this.paddingTop=u+r,this.paddingBottom=h+r}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){X(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return e==="top"||e==="bottom"||t==="x"}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){this.beforeTickToLabelConversion(),this.generateTickLabels(t);let e,i;for(e=0,i=t.length;e({width:r[T]||0,height:a[T]||0});return{first:M(0),last:M(e-1),widest:M(w),highest:M(C),widths:r,heights:a}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return xu(this._alignToPixels?fe(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&ta*s?a/i:l/s:l*s0}_computeGridLineItems(t){const e=this.axis,i=this.chart,s=this.options,{grid:o,position:r,border:a}=s,l=o.offset,c=this.isHorizontal(),h=this.ticks.length+(l?1:0),d=Ve(o),f=[],g=a.setContext(this.getContext()),p=g.display?g.width:0,m=p/2,b=function(U){return fe(i,U,p)};let y,x,v,_,w,C,M,T,A,O,R,st;if(r==="top")y=b(this.bottom),C=this.bottom-d,T=y-m,O=b(t.top)+m,st=t.bottom;else if(r==="bottom")y=b(this.top),O=t.top,st=b(t.bottom)-m,C=y+m,T=this.top+d;else if(r==="left")y=b(this.right),w=this.right-d,M=y-m,A=b(t.left)+m,R=t.right;else if(r==="right")y=b(this.left),A=t.left,R=b(t.right)-m,w=y+m,M=this.left+d;else if(e==="x"){if(r==="center")y=b((t.top+t.bottom)/2+.5);else if(F(r)){const U=Object.keys(r)[0],Q=r[U];y=b(this.chart.scales[U].getPixelForValue(Q))}O=t.top,st=t.bottom,C=y+m,T=C+d}else if(e==="y"){if(r==="center")y=b((t.left+t.right)/2);else if(F(r)){const U=Object.keys(r)[0],Q=r[U];y=b(this.chart.scales[U].getPixelForValue(Q))}w=y-m,M=w-d,A=t.left,R=t.right}const gt=S(s.ticks.maxTicksLimit,h),W=Math.max(1,Math.ceil(h/gt));for(x=0;x0&&(Ft-=St/2);break}Qt={left:Ft,top:Jt,width:St+Tt.width,height:Zt+Tt.height,color:W.backdropColor}}m.push({label:v,font:T,textOffset:R,options:{rotation:p,color:Q,strokeColor:Mt,strokeWidth:lt,textAlign:Gt,textBaseline:st,translation:[_,w],backdrop:Qt}})}return m}_getXAxisLabelAlignment(){const{position:t,ticks:e}=this.options;if(-Lt(this.labelRotation))return t==="top"?"left":"right";let s="center";return e.align==="start"?s="left":e.align==="end"?s="right":e.align==="inner"&&(s="inner"),s}_getYAxisLabelAlignment(t){const{position:e,ticks:{crossAlign:i,mirror:s,padding:o}}=this.options,r=this._getLabelSizes(),a=t+o,l=r.widest.width;let c,u;return e==="left"?s?(u=this.right+o,i==="near"?c="left":i==="center"?(c="center",u+=l/2):(c="right",u+=l)):(u=this.right-a,i==="near"?c="right":i==="center"?(c="center",u-=l/2):(c="left",u=this.left)):e==="right"?s?(u=this.left+o,i==="near"?c="right":i==="center"?(c="center",u-=l/2):(c="left",u-=l)):(u=this.left+a,i==="near"?c="left":i==="center"?(c="center",u+=l/2):(c="right",u=this.right)):c="right",{textAlign:c,x:u}}_computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.chart,e=this.options.position;if(e==="left"||e==="right")return{top:0,left:this.left,bottom:t.height,right:this.right};if(e==="top"||e==="bottom")return{top:this.top,left:0,bottom:this.bottom,right:t.width}}drawBackground(){const{ctx:t,options:{backgroundColor:e},left:i,top:s,width:o,height:r}=this;e&&(t.save(),t.fillStyle=e,t.fillRect(i,s,o,r),t.restore())}getLineWidthForValue(t){const e=this.options.grid;if(!this._isVisible()||!e.display)return 0;const s=this.ticks.findIndex(o=>o.value===t);return s>=0?e.setContext(this.getContext(s)).lineWidth:0}drawGrid(t){const e=this.options.grid,i=this.ctx,s=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let o,r;const a=(l,c,u)=>{!u.width||!u.color||(i.save(),i.lineWidth=u.width,i.strokeStyle=u.color,i.setLineDash(u.borderDash||[]),i.lineDashOffset=u.borderDashOffset,i.beginPath(),i.moveTo(l.x,l.y),i.lineTo(c.x,c.y),i.stroke(),i.restore())};if(e.display)for(o=0,r=s.length;o{this.draw(o)}}]:[{z:i,draw:o=>{this.drawBackground(),this.drawGrid(o),this.drawTitle()}},{z:s,draw:()=>{this.drawBorder()}},{z:e,draw:o=>{this.drawLabels(o)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),i=this.axis+"AxisID",s=[];let o,r;for(o=0,r=e.length;o{const i=e.split("."),s=i.pop(),o=[n].concat(i).join("."),r=t[e].split("."),a=r.pop(),l=r.join(".");nt.route(o,s,l,a)})}function Wd(n){return"id"in n&&"defaults"in n}class Hd{constructor(){this.controllers=new Rn(Kt,"datasets",!0),this.elements=new Rn(Rt,"elements"),this.plugins=new Rn(Object,"plugins"),this.scales=new Rn(ke,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,e,i){[...e].forEach(s=>{const o=i||this._getRegistryForType(s);i||o.isForType(s)||o===this.plugins&&s.id?this._exec(t,o,s):$(s,r=>{const a=i||this._getRegistryForType(r);this._exec(t,a,r)})})}_exec(t,e,i){const s=gs(t);X(i["before"+s],[],i),e[t](i),X(i["after"+s],[],i)}_getRegistryForType(t){for(let e=0;eo.filter(a=>!r.some(l=>a.plugin.id===l.plugin.id));this._notify(s(e,i),t,"stop"),this._notify(s(i,e),t,"start")}}function Yd(n){const t={},e=[],i=Object.keys(Et.plugins.items);for(let o=0;o1&&To(n[0].toLowerCase());if(i)return i}throw new Error(`Cannot determine type of '${n}' axis. Please provide 'axis' or 'position' option.`)}function So(n,t,e){if(e[t+"AxisID"]===n)return{axis:t}}function Gd(n,t){if(t.data&&t.data.datasets){const e=t.data.datasets.filter(i=>i.xAxisID===n||i.yAxisID===n);if(e.length)return So(n,"x",e[0])||So(n,"y",e[0])}return{}}function Qd(n,t){const e=Me[n.type]||{scales:{}},i=t.scales||{},s=Zi(n.type,t),o=Object.create(null);return Object.keys(i).forEach(r=>{const a=i[r];if(!F(a))return console.error(`Invalid scale configuration for scale: ${r}`);if(a._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${r}`);const l=Ji(r,a,Gd(r,n),nt.scales[a.type]),c=Xd(l,s),u=e.scales||{};o[r]=en(Object.create(null),[{axis:l},a,u[l],u[c]])}),n.data.datasets.forEach(r=>{const a=r.type||n.type,l=r.indexAxis||Zi(a,t),u=(Me[a]||{}).scales||{};Object.keys(u).forEach(h=>{const d=qd(h,l),f=r[d+"AxisID"]||d;o[f]=o[f]||Object.create(null),en(o[f],[{axis:d},i[f],u[h]])})}),Object.keys(o).forEach(r=>{const a=o[r];en(a,[nt.scales[a.type],nt.scale])}),o}function ua(n){const t=n.options||(n.options={});t.plugins=S(t.plugins,{}),t.scales=Qd(n,t)}function ha(n){return n=n||{},n.datasets=n.datasets||[],n.labels=n.labels||[],n}function Zd(n){return n=n||{},n.data=ha(n.data),ua(n),n}const Oo=new Map,da=new Set;function Fn(n,t){let e=Oo.get(n);return e||(e=t(),Oo.set(n,e),da.add(e)),e}const Ye=(n,t,e)=>{const i=ce(t,e);i!==void 0&&n.add(i)};class Jd{constructor(t){this._config=Zd(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=ha(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),ua(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return Fn(t,()=>[[`datasets.${t}`,""]])}datasetAnimationScopeKeys(t,e){return Fn(`${t}.transition.${e}`,()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]])}datasetElementScopeKeys(t,e){return Fn(`${t}-${e}`,()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]])}pluginScopeKeys(t){const e=t.id,i=this.type;return Fn(`${i}-plugin-${e}`,()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]])}_cachedScopes(t,e){const i=this._scopeCache;let s=i.get(t);return(!s||e)&&(s=new Map,i.set(t,s)),s}getOptionScopes(t,e,i){const{options:s,type:o}=this,r=this._cachedScopes(t,i),a=r.get(e);if(a)return a;const l=new Set;e.forEach(u=>{t&&(l.add(t),u.forEach(h=>Ye(l,t,h))),u.forEach(h=>Ye(l,s,h)),u.forEach(h=>Ye(l,Me[o]||{},h)),u.forEach(h=>Ye(l,nt,h)),u.forEach(h=>Ye(l,Ki,h))});const c=Array.from(l);return c.length===0&&c.push(Object.create(null)),da.has(e)&&r.set(e,c),c}chartOptionScopes(){const{options:t,type:e}=this;return[t,Me[e]||{},nt.datasets[e]||{},{type:e},nt,Ki]}resolveNamedOptions(t,e,i,s=[""]){const o={$shared:!0},{resolver:r,subPrefixes:a}=Ao(this._resolverCache,t,s);let l=r;if(ef(r,e)){o.$shared=!1,i=ue(i)?i():i;const c=this.createResolver(t,i,a);l=Ee(r,i,c)}for(const c of e)o[c]=l[c];return o}createResolver(t,e,i=[""],s){const{resolver:o}=Ao(this._resolverCache,t,i);return F(e)?Ee(o,e,void 0,s):o}}function Ao(n,t,e){let i=n.get(t);i||(i=new Map,n.set(t,i));const s=e.join();let o=i.get(s);return o||(o={resolver:_s(t,e),subPrefixes:e.filter(a=>!a.toLowerCase().includes("hover"))},i.set(s,o)),o}const tf=n=>F(n)&&Object.getOwnPropertyNames(n).some(t=>ue(n[t]));function ef(n,t){const{isScriptable:e,isIndexable:i}=Ur(n);for(const s of t){const o=e(s),r=i(s),a=(r||o)&&n[s];if(o&&(ue(a)||tf(a))||r&&K(a))return!0}return!1}var nf="4.4.1";const sf=["top","bottom","left","right","chartArea"];function Lo(n,t){return n==="top"||n==="bottom"||sf.indexOf(n)===-1&&t==="x"}function Ro(n,t){return function(e,i){return e[n]===i[n]?e[t]-i[t]:e[n]-i[n]}}function Fo(n){const t=n.chart,e=t.options.animation;t.notifyPlugins("afterRender"),X(e&&e.onComplete,[n],t)}function of(n){const t=n.chart,e=t.options.animation;X(e&&e.onProgress,[n],t)}function fa(n){return ws()&&typeof n=="string"?n=document.getElementById(n):n&&n.length&&(n=n[0]),n&&n.canvas&&(n=n.canvas),n}const qn={},Io=n=>{const t=fa(n);return Object.values(qn).filter(e=>e.canvas===t).pop()};function rf(n,t,e){const i=Object.keys(n);for(const s of i){const o=+s;if(o>=t){const r=n[s];delete n[s],(e>0||o>t)&&(n[o+e]=r)}}}function af(n,t,e,i){return!e||n.type==="mouseout"?null:i?t:n}function In(n,t,e){return n.options.clip?n[e]:t[e]}function lf(n,t){const{xScale:e,yScale:i}=n;return e&&i?{left:In(e,t,"left"),right:In(e,t,"right"),top:In(i,t,"top"),bottom:In(i,t,"bottom")}:t}class mt{static register(...t){Et.add(...t),Eo()}static unregister(...t){Et.remove(...t),Eo()}constructor(t,e){const i=this.config=new Jd(e),s=fa(t),o=Io(s);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas with ID '"+o.canvas.id+"' can be reused.");const r=i.createResolver(i.chartOptionScopes(),this.getContext());this.platform=new(i.platform||kd(s)),this.platform.updateConfig(i);const a=this.platform.acquireContext(s,r.aspectRatio),l=a&&a.canvas,c=l&&l.height,u=l&&l.width;if(this.id=lu(),this.ctx=a,this.canvas=l,this.width=u,this.height=c,this._options=r,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new Vd,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=ku(h=>this.update(h),r.resizeDelay||0),this._dataChanges=[],qn[this.id]=this,!a||!l){console.error("Failed to create chart: can't acquire context from the given item");return}jt.listen(this,"complete",Fo),jt.listen(this,"progress",of),this._initialize(),this.attached&&this.update()}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:i,height:s,_aspectRatio:o}=this;return H(t)?e&&o?o:s?i/s:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return Et}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():no(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return Js(this.canvas,this.ctx),this}stop(){return jt.stop(this),this}resize(t,e){jt.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const i=this.options,s=this.canvas,o=i.maintainAspectRatio&&this.aspectRatio,r=this.platform.getMaximumSize(s,t,e,o),a=i.devicePixelRatio||this.platform.getDevicePixelRatio(),l=this.width?"resize":"attach";this.width=r.width,this.height=r.height,this._aspectRatio=this.aspectRatio,no(this,a,!0)&&(this.notifyPlugins("resize",{size:r}),X(i.onResize,[this,r],this),this.attached&&this._doResize(l)&&this.render())}ensureScalesHaveIDs(){const e=this.options.scales||{};$(e,(i,s)=>{i.id=s})}buildOrUpdateScales(){const t=this.options,e=t.scales,i=this.scales,s=Object.keys(i).reduce((r,a)=>(r[a]=!1,r),{});let o=[];e&&(o=o.concat(Object.keys(e).map(r=>{const a=e[r],l=Ji(r,a),c=l==="r",u=l==="x";return{options:a,dposition:c?"chartArea":u?"bottom":"left",dtype:c?"radialLinear":u?"category":"linear"}}))),$(o,r=>{const a=r.options,l=a.id,c=Ji(l,a),u=S(a.type,r.dtype);(a.position===void 0||Lo(a.position,c)!==Lo(r.dposition))&&(a.position=r.dposition),s[l]=!0;let h=null;if(l in i&&i[l].type===u)h=i[l];else{const d=Et.getScale(u);h=new d({id:l,type:u,ctx:this.ctx,chart:this}),i[h.id]=h}h.init(a,t)}),$(s,(r,a)=>{r||delete i[a]}),$(i,r=>{se.configure(this,r,r.options),se.addBox(this,r)})}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,i=t.length;if(t.sort((s,o)=>s.index-o.index),i>e){for(let s=e;se.length&&delete this._stacks,t.forEach((i,s)=>{e.filter(o=>o===i._dataset).length===0&&this._destroyDatasetMeta(s)})}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let i,s;for(this._removeUnreferencedMetasets(),i=0,s=e.length;i{this.getDatasetMeta(e).controller.reset()},this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const i=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),s=this._animationsDisabled=!i.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0})===!1)return;const o=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let r=0;for(let c=0,u=this.data.datasets.length;c{c.reset()}),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(Ro("z","_idx"));const{_active:a,_lastEvent:l}=this;l?this._eventHandler(l,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){$(this.scales,t=>{se.removeBox(this,t)}),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),i=new Set(t.events);(!Us(e,i)||!!this._responsiveListeners!==t.responsive)&&(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:i,start:s,count:o}of e){const r=i==="_removeElements"?-o:o;rf(t,s,r)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,i=o=>new Set(t.filter(r=>r[0]===o).map((r,a)=>a+","+r.splice(1).join(","))),s=i(0);for(let o=1;oo.split(",")).map(o=>({method:o[1],start:+o[2],count:+o[3]}))}_updateLayout(t){if(this.notifyPlugins("beforeLayout",{cancelable:!0})===!1)return;se.update(this,this.width,this.height,t);const e=this.chartArea,i=e.width<=0||e.height<=0;this._layers=[],$(this.boxes,s=>{i&&s.position==="chartArea"||(s.configure&&s.configure(),this._layers.push(...s._layers()))},this),this._layers.forEach((s,o)=>{s._idx=o}),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})!==!1){for(let e=0,i=this.data.datasets.length;e=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,i=t._clip,s=!i.disabled,o=lf(t,this.chartArea),r={meta:t,index:t.index,cancelable:!0};this.notifyPlugins("beforeDatasetDraw",r)!==!1&&(s&&gi(e,{left:i.left===!1?0:o.left-i.left,right:i.right===!1?this.width:o.right+i.right,top:i.top===!1?0:o.top-i.top,bottom:i.bottom===!1?this.height:o.bottom+i.bottom}),t.controller.draw(),s&&pi(e),r.cancelable=!1,this.notifyPlugins("afterDatasetDraw",r))}isPointInArea(t){return Xt(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,i,s){const o=id.modes[e];return typeof o=="function"?o(this,t,i,s):[]}getDatasetMeta(t){const e=this.data.datasets[t],i=this._metasets;let s=i.filter(o=>o&&o._dataset===e).pop();return s||(s={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},i.push(s)),s}getContext(){return this.$context||(this.$context=he(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const i=this.getDatasetMeta(t);return typeof i.hidden=="boolean"?!i.hidden:!e.hidden}setDatasetVisibility(t,e){const i=this.getDatasetMeta(t);i.hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,i){const s=i?"show":"hide",o=this.getDatasetMeta(t),r=o.controller._resolveAnimations(void 0,s);cn(e)?(o.data[e].hidden=!i,this.update()):(this.setDatasetVisibility(t,i),r.update(o,{visible:i}),this.update(a=>a.datasetIndex===t?s:void 0))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),jt.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,o,r),t[o]=r},s=(o,r,a)=>{o.offsetX=r,o.offsetY=a,this._eventHandler(o)};$(this.options.events,o=>i(o,s))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,i=(l,c)=>{e.addEventListener(this,l,c),t[l]=c},s=(l,c)=>{t[l]&&(e.removeEventListener(this,l,c),delete t[l])},o=(l,c)=>{this.canvas&&this.resize(l,c)};let r;const a=()=>{s("attach",a),this.attached=!0,this.resize(),i("resize",o),i("detach",r)};r=()=>{this.attached=!1,s("resize",o),this._stop(),this._resize(0,0),i("attach",a)},e.isAttached(this.canvas)?a():r()}unbindEvents(){$(this._listeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._listeners={},$(this._responsiveListeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._responsiveListeners=void 0}updateHoverStyle(t,e,i){const s=i?"set":"remove";let o,r,a,l;for(e==="dataset"&&(o=this.getDatasetMeta(t[0].datasetIndex),o.controller["_"+s+"DatasetHoverStyle"]()),a=0,l=t.length;a{const a=this.getDatasetMeta(o);if(!a)throw new Error("No dataset found at index "+o);return{datasetIndex:o,element:a.data[r],index:r}});!Zn(i,e)&&(this._active=i,this._lastEvent=null,this._updateHoverStyles(i,e))}notifyPlugins(t,e,i){return this._plugins.notify(this,t,e,i)}isPluginEnabled(t){return this._plugins._cache.filter(e=>e.plugin.id===t).length===1}_updateHoverStyles(t,e,i){const s=this.options.hover,o=(l,c)=>l.filter(u=>!c.some(h=>u.datasetIndex===h.datasetIndex&&u.index===h.index)),r=o(e,t),a=i?t:o(t,e);r.length&&this.updateHoverStyle(r,s.mode,!1),a.length&&s.mode&&this.updateHoverStyle(a,s.mode,!0)}_eventHandler(t,e){const i={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},s=r=>(r.options.events||this.options.events).includes(t.native.type);if(this.notifyPlugins("beforeEvent",i,s)===!1)return;const o=this._handleEvent(t,e,i.inChartArea);return i.cancelable=!1,this.notifyPlugins("afterEvent",i,s),(o||i.changed)&&this.render(),this}_handleEvent(t,e,i){const{_active:s=[],options:o}=this,r=e,a=this._getActiveElements(t,s,i,r),l=gu(t),c=af(t,this._lastEvent,i,l);i&&(this._lastEvent=null,X(o.onHover,[t,a,this],this),l&&X(o.onClick,[t,a,this],this));const u=!Zn(a,s);return(u||e)&&(this._active=a,this._updateHoverStyles(a,s,e)),this._lastEvent=c,u}_getActiveElements(t,e,i,s){if(t.type==="mouseout")return[];if(!i)return e;const o=this.options.hover;return this.getElementsAtEventForMode(t,o.mode,o,s)}}k(mt,"defaults",nt),k(mt,"instances",qn),k(mt,"overrides",Me),k(mt,"registry",Et),k(mt,"version",nf),k(mt,"getChart",Io);function Eo(){return $(mt.instances,n=>n._plugins.invalidate())}function cf(n,t,e){const{startAngle:i,pixelMargin:s,x:o,y:r,outerRadius:a,innerRadius:l}=t;let c=s/a;n.beginPath(),n.arc(o,r,a,i-c,e+c),l>s?(c=s/l,n.arc(o,r,l,e+c,i-c,!0)):n.arc(o,r,s,e+at,i-at),n.closePath(),n.clip()}function uf(n){return ys(n,["outerStart","outerEnd","innerStart","innerEnd"])}function hf(n,t,e,i){const s=uf(n.options.borderRadius),o=(e-t)/2,r=Math.min(o,i*t/2),a=l=>{const c=(e-Math.min(o,l))*i/2;return ft(l,0,Math.min(o,c))};return{outerStart:a(s.outerStart),outerEnd:a(s.outerEnd),innerStart:ft(s.innerStart,0,r),innerEnd:ft(s.innerEnd,0,r)}}function Pe(n,t,e,i){return{x:e+n*Math.cos(t),y:i+n*Math.sin(t)}}function si(n,t,e,i,s,o){const{x:r,y:a,startAngle:l,pixelMargin:c,innerRadius:u}=t,h=Math.max(t.outerRadius+i+e-c,0),d=u>0?u+i+e+c:0;let f=0;const g=s-l;if(i){const W=u>0?u-i:0,U=h>0?h-i:0,Q=(W+U)/2,Mt=Q!==0?g*Q/(Q+i):g;f=(g-Mt)/2}const p=Math.max(.001,g*h-e/tt)/h,m=(g-p)/2,b=l+m+f,y=s-m-f,{outerStart:x,outerEnd:v,innerStart:_,innerEnd:w}=hf(t,d,h,y-b),C=h-x,M=h-v,T=b+x/C,A=y-v/M,O=d+_,R=d+w,st=b+_/O,gt=y-w/R;if(n.beginPath(),o){const W=(T+A)/2;if(n.arc(r,a,h,T,W),n.arc(r,a,h,W,A),v>0){const lt=Pe(M,A,r,a);n.arc(lt.x,lt.y,v,A,y+at)}const U=Pe(R,y,r,a);if(n.lineTo(U.x,U.y),w>0){const lt=Pe(R,gt,r,a);n.arc(lt.x,lt.y,w,y+at,gt+Math.PI)}const Q=(y-w/d+(b+_/d))/2;if(n.arc(r,a,d,y-w/d,Q,!0),n.arc(r,a,d,Q,b+_/d,!0),_>0){const lt=Pe(O,st,r,a);n.arc(lt.x,lt.y,_,st+Math.PI,b-at)}const Mt=Pe(C,b,r,a);if(n.lineTo(Mt.x,Mt.y),x>0){const lt=Pe(C,T,r,a);n.arc(lt.x,lt.y,x,b-at,T)}}else{n.moveTo(r,a);const W=Math.cos(T)*h+r,U=Math.sin(T)*h+a;n.lineTo(W,U);const Q=Math.cos(A)*h+r,Mt=Math.sin(A)*h+a;n.lineTo(Q,Mt)}n.closePath()}function df(n,t,e,i,s){const{fullCircles:o,startAngle:r,circumference:a}=t;let l=t.endAngle;if(o){si(n,t,e,i,l,s);for(let c=0;c=G||hn(r,l,c),m=qt(a,u+f,h+f);return p&&m}getCenterPoint(e){const{x:i,y:s,startAngle:o,endAngle:r,innerRadius:a,outerRadius:l}=this.getProps(["x","y","startAngle","endAngle","innerRadius","outerRadius"],e),{offset:c,spacing:u}=this.options,h=(o+r)/2,d=(a+l+u+c)/2;return{x:i+Math.cos(h)*d,y:s+Math.sin(h)*d}}tooltipPosition(e){return this.getCenterPoint(e)}draw(e){const{options:i,circumference:s}=this,o=(i.offset||0)/4,r=(i.spacing||0)/2,a=i.circular;if(this.pixelMargin=i.borderAlign==="inner"?.33:0,this.fullCircles=s>G?Math.floor(s/G):0,s===0||this.innerRadius<0||this.outerRadius<0)return;e.save();const l=(this.startAngle+this.endAngle)/2;e.translate(Math.cos(l)*o,Math.sin(l)*o);const c=1-Math.sin(Math.min(tt,s||0)),u=o*c;e.fillStyle=i.backgroundColor,e.strokeStyle=i.borderColor,df(e,this,u,r,a),ff(e,this,u,r,a),e.restore()}}k(Ze,"id","arc"),k(Ze,"defaults",{borderAlign:"center",borderColor:"#fff",borderDash:[],borderDashOffset:0,borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0,circular:!0}),k(Ze,"defaultRoutes",{backgroundColor:"backgroundColor"}),k(Ze,"descriptors",{_scriptable:!0,_indexable:e=>e!=="borderDash"});function ga(n,t,e=t){n.lineCap=S(e.borderCapStyle,t.borderCapStyle),n.setLineDash(S(e.borderDash,t.borderDash)),n.lineDashOffset=S(e.borderDashOffset,t.borderDashOffset),n.lineJoin=S(e.borderJoinStyle,t.borderJoinStyle),n.lineWidth=S(e.borderWidth,t.borderWidth),n.strokeStyle=S(e.borderColor,t.borderColor)}function gf(n,t,e){n.lineTo(e.x,e.y)}function pf(n){return n.stepped?Bu:n.tension||n.cubicInterpolationMode==="monotone"?Nu:gf}function pa(n,t,e={}){const i=n.length,{start:s=0,end:o=i-1}=e,{start:r,end:a}=t,l=Math.max(s,r),c=Math.min(o,a),u=sa&&o>a;return{count:i,start:l,loop:t.loop,ilen:c(r+(c?a-v:v))%o,x=()=>{p!==m&&(n.lineTo(u,m),n.lineTo(u,p),n.lineTo(u,b))};for(l&&(f=s[y(0)],n.moveTo(f.x,f.y)),d=0;d<=a;++d){if(f=s[y(d)],f.skip)continue;const v=f.x,_=f.y,w=v|0;w===g?(_m&&(m=_),u=(h*u+v)/++h):(x(),n.lineTo(v,_),g=w,h=0,p=m=_),b=_}x()}function ts(n){const t=n.options,e=t.borderDash&&t.borderDash.length;return!n._decimated&&!n._loop&&!t.tension&&t.cubicInterpolationMode!=="monotone"&&!t.stepped&&!e?bf:mf}function yf(n){return n.stepped?yh:n.tension||n.cubicInterpolationMode==="monotone"?_h:ye}function _f(n,t,e,i){let s=t._path;s||(s=t._path=new Path2D,t.path(s,e,i)&&s.closePath()),ga(n,t.options),n.stroke(s)}function xf(n,t,e,i){const{segments:s,options:o}=t,r=ts(t);for(const a of s)ga(n,o,a.style),n.beginPath(),r(n,t,a,{start:e,end:e+i-1})&&n.closePath(),n.stroke()}const vf=typeof Path2D=="function";function wf(n,t,e,i){vf&&!t.options.segment?_f(n,t,e,i):xf(n,t,e,i)}class oe extends Rt{constructor(t){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,t&&Object.assign(this,t)}updateControlPoints(t,e){const i=this.options;if((i.tension||i.cubicInterpolationMode==="monotone")&&!i.stepped&&!this._pointsUpdated){const s=i.spanGaps?this._loop:this._fullLoop;uh(this._points,i,t,s,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=Ch(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,i=t.length;return i&&e[t[i-1].end]}interpolate(t,e){const i=this.options,s=t[e],o=this.points,r=ta(this,{property:e,start:s,end:s});if(!r.length)return;const a=[],l=yf(i);let c,u;for(c=0,u=r.length;ct!=="borderDash"&&t!=="fill"});function zo(n,t,e,i){const s=n.options,{[e]:o}=n.getProps([e],i);return Math.abs(t-o)n.replace("rgb(","rgba(").replace(")",", 0.5)"));function ba(n){return es[n%es.length]}function ya(n){return Bo[n%Bo.length]}function Tf(n,t){return n.borderColor=ba(t),n.backgroundColor=ya(t),++t}function Sf(n,t){return n.backgroundColor=n.data.map(()=>ba(t++)),t}function Of(n,t){return n.backgroundColor=n.data.map(()=>ya(t++)),t}function Af(n){let t=0;return(e,i)=>{const s=n.getDatasetMeta(i).controller;s instanceof Le?t=Sf(e,t):s instanceof Un?t=Of(e,t):s&&(t=Tf(e,t))}}function No(n){let t;for(t in n)if(n[t].borderColor||n[t].backgroundColor)return!0;return!1}function Lf(n){return n&&(n.borderColor||n.backgroundColor)}var Rf={id:"colors",defaults:{enabled:!0,forceOverride:!1},beforeLayout(n,t,e){if(!e.enabled)return;const{data:{datasets:i},options:s}=n.config,{elements:o}=s;if(!e.forceOverride&&(No(i)||Lf(s)||o&&No(o)))return;const r=Af(n);i.forEach(r)}};function Ff(n,t,e){const i=n.segments,s=n.points,o=t.points,r=[];for(const a of i){let{start:l,end:c}=a;c=Cs(l,c,s);const u=ns(e,s[l],s[c],a.loop);if(!t.segments){r.push({source:a,target:u,start:s[l],end:s[c]});continue}const h=ta(t,u);for(const d of h){const f=ns(e,o[d.start],o[d.end],d.loop),g=Jr(a,s,f);for(const p of g)r.push({source:p,target:d,start:{[e]:Wo(u,f,"start",Math.max)},end:{[e]:Wo(u,f,"end",Math.min)}})}}return r}function ns(n,t,e,i){if(i)return;let s=t[n],o=e[n];return n==="angle"&&(s=Dt(s),o=Dt(o)),{property:n,start:s,end:o}}function If(n,t){const{x:e=null,y:i=null}=n||{},s=t.points,o=[];return t.segments.forEach(({start:r,end:a})=>{a=Cs(r,a,s);const l=s[r],c=s[a];i!==null?(o.push({x:l.x,y:i}),o.push({x:c.x,y:i})):e!==null&&(o.push({x:e,y:l.y}),o.push({x:e,y:c.y}))}),o}function Cs(n,t,e){for(;t>n;t--){const i=e[t];if(!isNaN(i.x)&&!isNaN(i.y))break}return t}function Wo(n,t,e,i){return n&&t?i(n[e],t[e]):n?n[e]:t?t[e]:0}function _a(n,t){let e=[],i=!1;return K(n)?(i=!0,e=n):e=If(n,t),e.length?new oe({points:e,options:{tension:0},_loop:i,_fullLoop:i}):null}function Ho(n){return n&&n.fill!==!1}function Ef(n,t,e){let s=n[t].fill;const o=[t];let r;if(!e)return s;for(;s!==!1&&o.indexOf(s)===-1;){if(!et(s))return s;if(r=n[s],!r)return!1;if(r.visible)return s;o.push(s),s=r.fill}return!1}function zf(n,t,e){const i=Hf(n);if(F(i))return isNaN(i.value)?!1:i;let s=parseFloat(i);return et(s)&&Math.floor(s)===s?Bf(i[0],t,s,e):["origin","start","end","stack","shape"].indexOf(i)>=0&&i}function Bf(n,t,e,i){return(n==="-"||n==="+")&&(e=t+e),e===t||e<0||e>=i?!1:e}function Nf(n,t){let e=null;return n==="start"?e=t.bottom:n==="end"?e=t.top:F(n)?e=t.getPixelForValue(n.value):t.getBasePixel&&(e=t.getBasePixel()),e}function Wf(n,t,e){let i;return n==="start"?i=e:n==="end"?i=t.options.reverse?t.min:t.max:F(n)?i=n.value:i=t.getBaseValue(),i}function Hf(n){const t=n.options,e=t.fill;let i=S(e&&e.target,e);return i===void 0&&(i=!!t.backgroundColor),i===!1||i===null?!1:i===!0?"origin":i}function Vf(n){const{scale:t,index:e,line:i}=n,s=[],o=i.segments,r=i.points,a=Yf(t,e);a.push(_a({x:null,y:t.bottom},i));for(let l=0;l=0;--r){const a=s[r].$filler;a&&(a.line.updateControlPoints(o,a.axis),i&&a.fill&&Ri(n.ctx,a,o))}},beforeDatasetsDraw(n,t,e){if(e.drawTime!=="beforeDatasetsDraw")return;const i=n.getSortedVisibleDatasetMetas();for(let s=i.length-1;s>=0;--s){const o=i[s].$filler;Ho(o)&&Ri(n.ctx,o,n.chartArea)}},beforeDatasetDraw(n,t,e){const i=t.meta.$filler;!Ho(i)||e.drawTime!=="beforeDatasetDraw"||Ri(n.ctx,i,n.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const Uo=(n,t)=>{let{boxHeight:e=t,boxWidth:i=t}=n;return n.usePointStyle&&(e=Math.min(e,t),i=n.pointStyleWidth||Math.min(i,t)),{boxWidth:i,boxHeight:e,itemHeight:Math.max(t,e)}},tg=(n,t)=>n!==null&&t!==null&&n.datasetIndex===t.datasetIndex&&n.index===t.index;class $o extends Rt{constructor(t){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this.legendItems=void 0,this.columnSizes=void 0,this.lineWidths=void 0,this.maxHeight=void 0,this.maxWidth=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.height=void 0,this.width=void 0,this._margins=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e,i){this.maxWidth=t,this.maxHeight=e,this._margins=i,this.setDimensions(),this.buildLabels(),this.fit()}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=this._margins.left,this.right=this.width):(this.height=this.maxHeight,this.top=this._margins.top,this.bottom=this.height)}buildLabels(){const t=this.options.labels||{};let e=X(t.generateLabels,[this.chart],this)||[];t.filter&&(e=e.filter(i=>t.filter(i,this.chart.data))),t.sort&&(e=e.sort((i,s)=>t.sort(i,s,this.chart.data))),this.options.reverse&&e.reverse(),this.legendItems=e}fit(){const{options:t,ctx:e}=this;if(!t.display){this.width=this.height=0;return}const i=t.labels,s=ut(i.font),o=s.size,r=this._computeTitleHeight(),{boxWidth:a,itemHeight:l}=Uo(i,o);let c,u;e.font=s.string,this.isHorizontal()?(c=this.maxWidth,u=this._fitRows(r,o,a,l)+10):(u=this.maxHeight,c=this._fitCols(r,s,a,l)+10),this.width=Math.min(c,t.maxWidth||this.maxWidth),this.height=Math.min(u,t.maxHeight||this.maxHeight)}_fitRows(t,e,i,s){const{ctx:o,maxWidth:r,options:{labels:{padding:a}}}=this,l=this.legendHitBoxes=[],c=this.lineWidths=[0],u=s+a;let h=t;o.textAlign="left",o.textBaseline="middle";let d=-1,f=-u;return this.legendItems.forEach((g,p)=>{const m=i+e/2+o.measureText(g.text).width;(p===0||c[c.length-1]+m+2*a>r)&&(h+=u,c[c.length-(p>0?0:1)]=0,f+=u,d++),l[p]={left:0,top:f,row:d,width:m,height:s},c[c.length-1]+=m+a}),h}_fitCols(t,e,i,s){const{ctx:o,maxHeight:r,options:{labels:{padding:a}}}=this,l=this.legendHitBoxes=[],c=this.columnSizes=[],u=r-t;let h=a,d=0,f=0,g=0,p=0;return this.legendItems.forEach((m,b)=>{const{itemWidth:y,itemHeight:x}=eg(i,e,o,m,s);b>0&&f+x+2*a>u&&(h+=d+a,c.push({width:d,height:f}),g+=d+a,p++,d=f=0),l[b]={left:g,top:f,col:p,width:y,height:x},d=Math.max(d,y),f+=x+a}),h+=d,c.push({width:d,height:f}),h}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:e,options:{align:i,labels:{padding:s},rtl:o}}=this,r=Fe(o,this.left,this.width);if(this.isHorizontal()){let a=0,l=kt(i,this.left+s,this.right-this.lineWidths[a]);for(const c of e)a!==c.row&&(a=c.row,l=kt(i,this.left+s,this.right-this.lineWidths[a])),c.top+=this.top+t+s,c.left=r.leftForLtr(r.x(l),c.width),l+=c.width+s}else{let a=0,l=kt(i,this.top+t+s,this.bottom-this.columnSizes[a].height);for(const c of e)c.col!==a&&(a=c.col,l=kt(i,this.top+t+s,this.bottom-this.columnSizes[a].height)),c.top=l,c.left+=this.left+s,c.left=r.leftForLtr(r.x(c.left),c.width),l+=c.height+s}}isHorizontal(){return this.options.position==="top"||this.options.position==="bottom"}draw(){if(this.options.display){const t=this.ctx;gi(t,this),this._draw(),pi(t)}}_draw(){const{options:t,columnSizes:e,lineWidths:i,ctx:s}=this,{align:o,labels:r}=t,a=nt.color,l=Fe(t.rtl,this.left,this.width),c=ut(r.font),{padding:u}=r,h=c.size,d=h/2;let f;this.drawTitle(),s.textAlign=l.textAlign("left"),s.textBaseline="middle",s.lineWidth=.5,s.font=c.string;const{boxWidth:g,boxHeight:p,itemHeight:m}=Uo(r,h),b=function(w,C,M){if(isNaN(g)||g<=0||isNaN(p)||p<0)return;s.save();const T=S(M.lineWidth,1);if(s.fillStyle=S(M.fillStyle,a),s.lineCap=S(M.lineCap,"butt"),s.lineDashOffset=S(M.lineDashOffset,0),s.lineJoin=S(M.lineJoin,"miter"),s.lineWidth=T,s.strokeStyle=S(M.strokeStyle,a),s.setLineDash(S(M.lineDash,[])),r.usePointStyle){const A={radius:p*Math.SQRT2/2,pointStyle:M.pointStyle,rotation:M.rotation,borderWidth:T},O=l.xPlus(w,g/2),R=C+d;Yr(s,A,O,R,r.pointStyleWidth&&g)}else{const A=C+Math.max((h-p)/2,0),O=l.leftForLtr(w,g),R=ve(M.borderRadius);s.beginPath(),Object.values(R).some(st=>st!==0)?dn(s,{x:O,y:A,w:g,h:p,radius:R}):s.rect(O,A,g,p),s.fill(),T!==0&&s.stroke()}s.restore()},y=function(w,C,M){Ie(s,M.text,w,C+m/2,c,{strikethrough:M.hidden,textAlign:l.textAlign(M.textAlign)})},x=this.isHorizontal(),v=this._computeTitleHeight();x?f={x:kt(o,this.left+u,this.right-i[0]),y:this.top+u+v,line:0}:f={x:this.left+u,y:kt(o,this.top+v+u,this.bottom-e[0].height),line:0},Gr(this.ctx,t.textDirection);const _=m+u;this.legendItems.forEach((w,C)=>{s.strokeStyle=w.fontColor,s.fillStyle=w.fontColor;const M=s.measureText(w.text).width,T=l.textAlign(w.textAlign||(w.textAlign=r.textAlign)),A=g+d+M;let O=f.x,R=f.y;l.setWidth(this.width),x?C>0&&O+A+u>this.right&&(R=f.y+=_,f.line++,O=f.x=kt(o,this.left+u,this.right-i[f.line])):C>0&&R+_>this.bottom&&(O=f.x=O+e[f.line].width+u,f.line++,R=f.y=kt(o,this.top+v+u,this.bottom-e[f.line].height));const st=l.x(O);if(b(st,R,w),O=Cu(T,O+g+d,x?O+A:this.right,t.rtl),y(l.x(O),R,w),x)f.x+=A+u;else if(typeof w.text!="string"){const gt=c.lineHeight;f.y+=va(w,gt)+u}else f.y+=_}),Qr(this.ctx,t.textDirection)}drawTitle(){const t=this.options,e=t.title,i=ut(e.font),s=yt(e.padding);if(!e.display)return;const o=Fe(t.rtl,this.left,this.width),r=this.ctx,a=e.position,l=i.size/2,c=s.top+l;let u,h=this.left,d=this.width;if(this.isHorizontal())d=Math.max(...this.lineWidths),u=this.top+c,h=kt(t.align,h,this.right-d);else{const g=this.columnSizes.reduce((p,m)=>Math.max(p,m.height),0);u=c+kt(t.align,this.top,this.bottom-g-t.labels.padding-this._computeTitleHeight())}const f=kt(a,h,h+d);r.textAlign=o.textAlign(Hr(a)),r.textBaseline="middle",r.strokeStyle=e.color,r.fillStyle=e.color,r.font=i.string,Ie(r,e.text,f,u,i)}_computeTitleHeight(){const t=this.options.title,e=ut(t.font),i=yt(t.padding);return t.display?e.lineHeight+i.height:0}_getLegendItemAt(t,e){let i,s,o;if(qt(t,this.left,this.right)&&qt(e,this.top,this.bottom)){for(o=this.legendHitBoxes,i=0;io.length>r.length?o:r)),t+e.size/2+i.measureText(s).width}function ig(n,t,e){let i=n;return typeof t.text!="string"&&(i=va(t,e)),i}function va(n,t){const e=n.text?n.text.length:0;return t*e}function sg(n,t){return!!((n==="mousemove"||n==="mouseout")&&(t.onHover||t.onLeave)||t.onClick&&(n==="click"||n==="mouseup"))}var og={id:"legend",_element:$o,start(n,t,e){const i=n.legend=new $o({ctx:n.ctx,options:e,chart:n});se.configure(n,i,e),se.addBox(n,i)},stop(n){se.removeBox(n,n.legend),delete n.legend},beforeUpdate(n,t,e){const i=n.legend;se.configure(n,i,e),i.options=e},afterUpdate(n){const t=n.legend;t.buildLabels(),t.adjustHitBoxes()},afterEvent(n,t){t.replay||n.legend.handleEvent(t.event)},defaults:{display:!0,position:"top",align:"center",fullSize:!0,reverse:!1,weight:1e3,onClick(n,t,e){const i=t.datasetIndex,s=e.chart;s.isDatasetVisible(i)?(s.hide(i),t.hidden=!0):(s.show(i),t.hidden=!1)},onHover:null,onLeave:null,labels:{color:n=>n.chart.options.color,boxWidth:40,padding:10,generateLabels(n){const t=n.data.datasets,{labels:{usePointStyle:e,pointStyle:i,textAlign:s,color:o,useBorderRadius:r,borderRadius:a}}=n.legend.options;return n._getSortedDatasetMetas().map(l=>{const c=l.controller.getStyle(e?0:void 0),u=yt(c.borderWidth);return{text:t[l.index].label,fillStyle:c.backgroundColor,fontColor:o,hidden:!l.visible,lineCap:c.borderCapStyle,lineDash:c.borderDash,lineDashOffset:c.borderDashOffset,lineJoin:c.borderJoinStyle,lineWidth:(u.width+u.height)/4,strokeStyle:c.borderColor,pointStyle:i||c.pointStyle,rotation:c.rotation,textAlign:s||c.textAlign,borderRadius:r&&(a||c.borderRadius),datasetIndex:l.index}},this)}},title:{color:n=>n.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:n=>!n.startsWith("on"),labels:{_scriptable:n=>!["generateLabels","filter","sort"].includes(n)}}};const Je={average(n){if(!n.length)return!1;let t,e,i=0,s=0,o=0;for(t=0,e=n.length;t-1?n.split(` -`):n}function rg(n,t){const{element:e,datasetIndex:i,index:s}=t,o=n.getDatasetMeta(i).controller,{label:r,value:a}=o.getLabelAndValue(s);return{chart:n,label:r,parsed:o.getParsed(s),raw:n.data.datasets[i].data[s],formattedValue:a,dataset:o.getDataset(),dataIndex:s,datasetIndex:i,element:e}}function qo(n,t){const e=n.chart.ctx,{body:i,footer:s,title:o}=n,{boxWidth:r,boxHeight:a}=t,l=ut(t.bodyFont),c=ut(t.titleFont),u=ut(t.footerFont),h=o.length,d=s.length,f=i.length,g=yt(t.padding);let p=g.height,m=0,b=i.reduce((v,_)=>v+_.before.length+_.lines.length+_.after.length,0);if(b+=n.beforeBody.length+n.afterBody.length,h&&(p+=h*c.lineHeight+(h-1)*t.titleSpacing+t.titleMarginBottom),b){const v=t.displayColors?Math.max(a,l.lineHeight):l.lineHeight;p+=f*v+(b-f)*l.lineHeight+(b-1)*t.bodySpacing}d&&(p+=t.footerMarginTop+d*u.lineHeight+(d-1)*t.footerSpacing);let y=0;const x=function(v){m=Math.max(m,e.measureText(v).width+y)};return e.save(),e.font=c.string,$(n.title,x),e.font=l.string,$(n.beforeBody.concat(n.afterBody),x),y=t.displayColors?r+2+t.boxPadding:0,$(i,v=>{$(v.before,x),$(v.lines,x),$(v.after,x)}),y=0,e.font=u.string,$(n.footer,x),e.restore(),m+=g.width,{width:m,height:p}}function ag(n,t){const{y:e,height:i}=t;return en.height-i/2?"bottom":"center"}function lg(n,t,e,i){const{x:s,width:o}=i,r=e.caretSize+e.caretPadding;if(n==="left"&&s+o+r>t.width||n==="right"&&s-o-r<0)return!0}function cg(n,t,e,i){const{x:s,width:o}=e,{width:r,chartArea:{left:a,right:l}}=n;let c="center";return i==="center"?c=s<=(a+l)/2?"left":"right":s<=o/2?c="left":s>=r-o/2&&(c="right"),lg(c,n,t,e)&&(c="center"),c}function Xo(n,t,e){const i=e.yAlign||t.yAlign||ag(n,e);return{xAlign:e.xAlign||t.xAlign||cg(n,t,e,i),yAlign:i}}function ug(n,t){let{x:e,width:i}=n;return t==="right"?e-=i:t==="center"&&(e-=i/2),e}function hg(n,t,e){let{y:i,height:s}=n;return t==="top"?i+=e:t==="bottom"?i-=s+e:i-=s/2,i}function Ko(n,t,e,i){const{caretSize:s,caretPadding:o,cornerRadius:r}=n,{xAlign:a,yAlign:l}=e,c=s+o,{topLeft:u,topRight:h,bottomLeft:d,bottomRight:f}=ve(r);let g=ug(t,a);const p=hg(t,l,c);return l==="center"?a==="left"?g+=c:a==="right"&&(g-=c):a==="left"?g-=Math.max(u,d)+s:a==="right"&&(g+=Math.max(h,f)+s),{x:ft(g,0,i.width-t.width),y:ft(p,0,i.height-t.height)}}function En(n,t,e){const i=yt(e.padding);return t==="center"?n.x+n.width/2:t==="right"?n.x+n.width-i.right:n.x+i.left}function Go(n){return It([],Ut(n))}function dg(n,t,e){return he(n,{tooltip:t,tooltipItems:e,type:"tooltip"})}function Qo(n,t){const e=t&&t.dataset&&t.dataset.tooltip&&t.dataset.tooltip.callbacks;return e?n.override(e):n}const wa={beforeTitle:Ht,title(n){if(n.length>0){const t=n[0],e=t.chart.data.labels,i=e?e.length:0;if(this&&this.options&&this.options.mode==="dataset")return t.dataset.label||"";if(t.label)return t.label;if(i>0&&t.dataIndex"u"?wa[t].call(e,i):s}class is extends Rt{constructor(t){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=t.chart,this.options=t.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const e=this.chart,i=this.options.setContext(this.getContext()),s=i.enabled&&e.options.animation&&i.animations,o=new ea(this.chart,s);return s._cacheable&&(this._cachedAnimations=Object.freeze(o)),o}getContext(){return this.$context||(this.$context=dg(this.chart.getContext(),this,this._tooltipItems))}getTitle(t,e){const{callbacks:i}=e,s=_t(i,"beforeTitle",this,t),o=_t(i,"title",this,t),r=_t(i,"afterTitle",this,t);let a=[];return a=It(a,Ut(s)),a=It(a,Ut(o)),a=It(a,Ut(r)),a}getBeforeBody(t,e){return Go(_t(e.callbacks,"beforeBody",this,t))}getBody(t,e){const{callbacks:i}=e,s=[];return $(t,o=>{const r={before:[],lines:[],after:[]},a=Qo(i,o);It(r.before,Ut(_t(a,"beforeLabel",this,o))),It(r.lines,_t(a,"label",this,o)),It(r.after,Ut(_t(a,"afterLabel",this,o))),s.push(r)}),s}getAfterBody(t,e){return Go(_t(e.callbacks,"afterBody",this,t))}getFooter(t,e){const{callbacks:i}=e,s=_t(i,"beforeFooter",this,t),o=_t(i,"footer",this,t),r=_t(i,"afterFooter",this,t);let a=[];return a=It(a,Ut(s)),a=It(a,Ut(o)),a=It(a,Ut(r)),a}_createItems(t){const e=this._active,i=this.chart.data,s=[],o=[],r=[];let a=[],l,c;for(l=0,c=e.length;lt.filter(u,h,d,i))),t.itemSort&&(a=a.sort((u,h)=>t.itemSort(u,h,i))),$(a,u=>{const h=Qo(t.callbacks,u);s.push(_t(h,"labelColor",this,u)),o.push(_t(h,"labelPointStyle",this,u)),r.push(_t(h,"labelTextColor",this,u))}),this.labelColors=s,this.labelPointStyles=o,this.labelTextColors=r,this.dataPoints=a,a}update(t,e){const i=this.options.setContext(this.getContext()),s=this._active;let o,r=[];if(!s.length)this.opacity!==0&&(o={opacity:0});else{const a=Je[i.position].call(this,s,this._eventPosition);r=this._createItems(i),this.title=this.getTitle(r,i),this.beforeBody=this.getBeforeBody(r,i),this.body=this.getBody(r,i),this.afterBody=this.getAfterBody(r,i),this.footer=this.getFooter(r,i);const l=this._size=qo(this,i),c=Object.assign({},a,l),u=Xo(this.chart,i,c),h=Ko(i,c,u,this.chart);this.xAlign=u.xAlign,this.yAlign=u.yAlign,o={opacity:1,x:h.x,y:h.y,width:l.width,height:l.height,caretX:a.x,caretY:a.y}}this._tooltipItems=r,this.$context=void 0,o&&this._resolveAnimations().update(this,o),t&&i.external&&i.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,i,s){const o=this.getCaretPosition(t,i,s);e.lineTo(o.x1,o.y1),e.lineTo(o.x2,o.y2),e.lineTo(o.x3,o.y3)}getCaretPosition(t,e,i){const{xAlign:s,yAlign:o}=this,{caretSize:r,cornerRadius:a}=i,{topLeft:l,topRight:c,bottomLeft:u,bottomRight:h}=ve(a),{x:d,y:f}=t,{width:g,height:p}=e;let m,b,y,x,v,_;return o==="center"?(v=f+p/2,s==="left"?(m=d,b=m-r,x=v+r,_=v-r):(m=d+g,b=m+r,x=v-r,_=v+r),y=m):(s==="left"?b=d+Math.max(l,u)+r:s==="right"?b=d+g-Math.max(c,h)-r:b=this.caretX,o==="top"?(x=f,v=x-r,m=b-r,y=b+r):(x=f+p,v=x+r,m=b+r,y=b-r),_=x),{x1:m,x2:b,x3:y,y1:x,y2:v,y3:_}}drawTitle(t,e,i){const s=this.title,o=s.length;let r,a,l;if(o){const c=Fe(i.rtl,this.x,this.width);for(t.x=En(this,i.titleAlign,i),e.textAlign=c.textAlign(i.titleAlign),e.textBaseline="middle",r=ut(i.titleFont),a=i.titleSpacing,e.fillStyle=i.titleColor,e.font=r.string,l=0;ly!==0)?(t.beginPath(),t.fillStyle=o.multiKeyBackground,dn(t,{x:p,y:g,w:c,h:l,radius:b}),t.fill(),t.stroke(),t.fillStyle=r.backgroundColor,t.beginPath(),dn(t,{x:m,y:g+1,w:c-2,h:l-2,radius:b}),t.fill()):(t.fillStyle=o.multiKeyBackground,t.fillRect(p,g,c,l),t.strokeRect(p,g,c,l),t.fillStyle=r.backgroundColor,t.fillRect(m,g+1,c-2,l-2))}t.fillStyle=this.labelTextColors[i]}drawBody(t,e,i){const{body:s}=this,{bodySpacing:o,bodyAlign:r,displayColors:a,boxHeight:l,boxWidth:c,boxPadding:u}=i,h=ut(i.bodyFont);let d=h.lineHeight,f=0;const g=Fe(i.rtl,this.x,this.width),p=function(M){e.fillText(M,g.x(t.x+f),t.y+d/2),t.y+=d+o},m=g.textAlign(r);let b,y,x,v,_,w,C;for(e.textAlign=r,e.textBaseline="middle",e.font=h.string,t.x=En(this,m,i),e.fillStyle=i.bodyColor,$(this.beforeBody,p),f=a&&m!=="right"?r==="center"?c/2+u:c+2+u:0,v=0,w=s.length;v0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,i=this.$animations,s=i&&i.x,o=i&&i.y;if(s||o){const r=Je[t.position].call(this,this._active,this._eventPosition);if(!r)return;const a=this._size=qo(this,t),l=Object.assign({},r,this._size),c=Xo(e,t,l),u=Ko(t,l,c,e);(s._to!==u.x||o._to!==u.y)&&(this.xAlign=c.xAlign,this.yAlign=c.yAlign,this.width=a.width,this.height=a.height,this.caretX=r.x,this.caretY=r.y,this._resolveAnimations().update(this,u))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let i=this.opacity;if(!i)return;this._updateAnimationTarget(e);const s={width:this.width,height:this.height},o={x:this.x,y:this.y};i=Math.abs(i)<.001?0:i;const r=yt(e.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&a&&(t.save(),t.globalAlpha=i,this.drawBackground(o,t,s,e),Gr(t,e.textDirection),o.y+=r.top,this.drawTitle(o,t,e),this.drawBody(o,t,e),this.drawFooter(o,t,e),Qr(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const i=this._active,s=t.map(({datasetIndex:a,index:l})=>{const c=this.chart.getDatasetMeta(a);if(!c)throw new Error("Cannot find a dataset at index "+a);return{datasetIndex:a,element:c.data[l],index:l}}),o=!Zn(i,s),r=this._positionChanged(s,e);(o||r)&&(this._active=s,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,i=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const s=this.options,o=this._active||[],r=this._getActiveElements(t,o,e,i),a=this._positionChanged(r,t),l=e||!Zn(r,o)||a;return l&&(this._active=r,(s.enabled||s.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),l}_getActiveElements(t,e,i,s){const o=this.options;if(t.type==="mouseout")return[];if(!s)return e.filter(a=>this.chart.data.datasets[a.datasetIndex]&&this.chart.getDatasetMeta(a.datasetIndex).controller.getParsed(a.index)!==void 0);const r=this.chart.getElementsAtEventForMode(t,o.mode,o,i);return o.reverse&&r.reverse(),r}_positionChanged(t,e){const{caretX:i,caretY:s,options:o}=this,r=Je[o.position].call(this,t,e);return r!==!1&&(i!==r.x||s!==r.y)}}k(is,"positioners",Je);var fg={id:"tooltip",_element:is,positioners:Je,afterInit(n,t,e){e&&(n.tooltip=new is({chart:n,options:e}))},beforeUpdate(n,t,e){n.tooltip&&n.tooltip.initialize(e)},reset(n,t,e){n.tooltip&&n.tooltip.initialize(e)},afterDraw(n){const t=n.tooltip;if(t&&t._willRender()){const e={tooltip:t};if(n.notifyPlugins("beforeTooltipDraw",{...e,cancelable:!0})===!1)return;t.draw(n.ctx),n.notifyPlugins("afterTooltipDraw",e)}},afterEvent(n,t){if(n.tooltip){const e=t.replay;n.tooltip.handleEvent(t.event,e,t.inChartArea)&&(t.changed=!0)}},defaults:{enabled:!0,external:null,position:"average",backgroundColor:"rgba(0,0,0,0.8)",titleColor:"#fff",titleFont:{weight:"bold"},titleSpacing:2,titleMarginBottom:6,titleAlign:"left",bodyColor:"#fff",bodySpacing:2,bodyFont:{},bodyAlign:"left",footerColor:"#fff",footerSpacing:2,footerMarginTop:6,footerFont:{weight:"bold"},footerAlign:"left",padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(n,t)=>t.bodyFont.size,boxWidth:(n,t)=>t.bodyFont.size,multiKeyBackground:"#fff",displayColors:!0,boxPadding:0,borderColor:"rgba(0,0,0,0)",borderWidth:0,animation:{duration:400,easing:"easeOutQuart"},animations:{numbers:{type:"number",properties:["x","y","width","height","caretX","caretY"]},opacity:{easing:"linear",duration:200}},callbacks:wa},defaultRoutes:{bodyFont:"font",footerFont:"font",titleFont:"font"},descriptors:{_scriptable:n=>n!=="filter"&&n!=="itemSort"&&n!=="external",_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]};const gg=(n,t,e,i)=>(typeof t=="string"?(e=n.push(t)-1,i.unshift({index:e,label:t})):isNaN(t)&&(e=null),e);function pg(n,t,e,i){const s=n.indexOf(t);if(s===-1)return gg(n,t,e,i);const o=n.lastIndexOf(t);return s!==o?e:s}const mg=(n,t)=>n===null?null:ft(Math.round(n),0,t);function Zo(n){const t=this.getLabels();return n>=0&&ne.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}}k(ss,"id","category"),k(ss,"defaults",{ticks:{callback:Zo}});function bg(n,t){const e=[],{bounds:s,step:o,min:r,max:a,precision:l,count:c,maxTicks:u,maxDigits:h,includeBounds:d}=n,f=o||1,g=u-1,{min:p,max:m}=t,b=!H(r),y=!H(a),x=!H(c),v=(m-p)/(h+1);let _=qs((m-p)/g/f)*f,w,C,M,T;if(_<1e-14&&!b&&!y)return[{value:p},{value:m}];T=Math.ceil(m/_)-Math.floor(p/_),T>g&&(_=qs(T*_/g/f)*f),H(l)||(w=Math.pow(10,l),_=Math.ceil(_*w)/w),s==="ticks"?(C=Math.floor(p/_)*_,M=Math.ceil(m/_)*_):(C=p,M=m),b&&y&&o&&yu((a-r)/o,_/1e3)?(T=Math.round(Math.min((a-r)/_,u)),_=(a-r)/T,C=r,M=a):x?(C=b?r:C,M=y?a:M,T=c-1,_=(M-C)/T):(T=(M-C)/_,nn(T,Math.round(T),_/1e3)?T=Math.round(T):T=Math.ceil(T));const A=Math.max(Xs(_),Xs(C));w=Math.pow(10,H(l)?A:l),C=Math.round(C*w)/w,M=Math.round(M*w)/w;let O=0;for(b&&(d&&C!==r?(e.push({value:r}),Ca)break;e.push({value:R})}return y&&d&&M!==a?e.length&&nn(e[e.length-1].value,a,Jo(a,v,n))?e[e.length-1].value=a:e.push({value:a}):(!y||M===a)&&e.push({value:M}),e}function Jo(n,t,{horizontal:e,minRotation:i}){const s=Lt(i),o=(e?Math.sin(s):Math.cos(s))||.001,r=.75*t*(""+n).length;return Math.min(t/o,r)}class oi extends ke{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(t,e){return H(t)||(typeof t=="number"||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:i}=this.getUserBounds();let{min:s,max:o}=this;const r=l=>s=e?s:l,a=l=>o=i?o:l;if(t){const l=Nt(s),c=Nt(o);l<0&&c<0?a(0):l>0&&c>0&&r(0)}if(s===o){let l=o===0?1:Math.abs(o*.05);a(o+l),t||r(s-l)}this.min=s,this.max=o}getTickLimit(){const t=this.options.ticks;let{maxTicksLimit:e,stepSize:i}=t,s;return i?(s=Math.ceil(this.max/i)-Math.floor(this.min/i)+1,s>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${i} would result generating up to ${s} ticks. Limiting to 1000.`),s=1e3)):(s=this.computeTickLimit(),e=e||11),e&&(s=Math.min(e,s)),s}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let i=this.getTickLimit();i=Math.max(2,i);const s={maxTicks:i,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:e.includeBounds!==!1},o=this._range||this,r=bg(s,o);return t.bounds==="ticks"&&Ir(r,this,"value"),t.reverse?(r.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),r}configure(){const t=this.ticks;let e=this.min,i=this.max;if(super.configure(),this.options.offset&&t.length){const s=(i-e)/Math.max(t.length-1,1)/2;e-=s,i+=s}this._startValue=e,this._endValue=i,this._valueRange=i-e}getLabelForValue(t){return _n(t,this.chart.options.locale,this.options.ticks.format)}}class os extends oi{determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=et(t)?t:0,this.max=et(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,i=Lt(this.options.ticks.minRotation),s=(t?Math.sin(i):Math.cos(i))||.001,o=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,o.lineHeight/s))}getPixelForValue(t){return t===null?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}k(os,"id","linear"),k(os,"defaults",{ticks:{callback:fi.formatters.numeric}});const gn=n=>Math.floor(ie(n)),pe=(n,t)=>Math.pow(10,gn(n)+t);function tr(n){return n/Math.pow(10,gn(n))===1}function er(n,t,e){const i=Math.pow(10,e),s=Math.floor(n/i);return Math.ceil(t/i)-s}function yg(n,t){const e=t-n;let i=gn(e);for(;er(n,t,i)>10;)i++;for(;er(n,t,i)<10;)i--;return Math.min(i,gn(n))}function _g(n,{min:t,max:e}){t=Pt(n.min,t);const i=[],s=gn(t);let o=yg(t,e),r=o<0?Math.pow(10,Math.abs(o)):1;const a=Math.pow(10,o),l=s>o?Math.pow(10,s):0,c=Math.round((t-l)*r)/r,u=Math.floor((t-l)/a/10)*a*10;let h=Math.floor((c-u)/Math.pow(10,o)),d=Pt(n.min,Math.round((l+u+h*Math.pow(10,o))*r)/r);for(;d=10?h=h<15?15:20:h++,h>=20&&(o++,h=2,r=o>=0?1:r),d=Math.round((l+u+h*Math.pow(10,o))*r)/r;const f=Pt(n.max,d);return i.push({value:f,major:tr(f),significand:h}),i}class nr extends ke{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._valueRange=0}parse(t,e){const i=oi.prototype.parse.apply(this,[t,e]);if(i===0){this._zero=!0;return}return et(i)&&i>0?i:null}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=et(t)?Math.max(0,t):null,this.max=et(e)?Math.max(0,e):null,this.options.beginAtZero&&(this._zero=!0),this._zero&&this.min!==this._suggestedMin&&!et(this._userMin)&&(this.min=t===pe(this.min,0)?pe(this.min,-1):pe(this.min,0)),this.handleTickRangeOptions()}handleTickRangeOptions(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let i=this.min,s=this.max;const o=a=>i=t?i:a,r=a=>s=e?s:a;i===s&&(i<=0?(o(1),r(10)):(o(pe(i,-1)),r(pe(s,1)))),i<=0&&o(pe(s,-1)),s<=0&&r(pe(i,1)),this.min=i,this.max=s}buildTicks(){const t=this.options,e={min:this._userMin,max:this._userMax},i=_g(e,this);return t.bounds==="ticks"&&Ir(i,this,"value"),t.reverse?(i.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),i}getLabelForValue(t){return t===void 0?"0":_n(t,this.chart.options.locale,this.options.ticks.format)}configure(){const t=this.min;super.configure(),this._startValue=ie(t),this._valueRange=ie(this.max)-ie(t)}getPixelForValue(t){return(t===void 0||t===0)&&(t=this.min),t===null||isNaN(t)?NaN:this.getPixelForDecimal(t===this.min?0:(ie(t)-this._startValue)/this._valueRange)}getValueForPixel(t){const e=this.getDecimalForPixel(t);return Math.pow(10,this._startValue+e*this._valueRange)}}k(nr,"id","logarithmic"),k(nr,"defaults",{ticks:{callback:fi.formatters.logarithmic,major:{enabled:!0}}});function rs(n){const t=n.ticks;if(t.display&&n.display){const e=yt(t.backdropPadding);return S(t.font&&t.font.size,nt.font.size)+e.height}return 0}function xg(n,t,e){return e=K(e)?e:[e],{w:zu(n,t.string,e),h:e.length*t.lineHeight}}function ir(n,t,e,i,s){return n===i||n===s?{start:t-e/2,end:t+e/2}:ns?{start:t-e,end:t}:{start:t,end:t+e}}function vg(n){const t={l:n.left+n._padding.left,r:n.right-n._padding.right,t:n.top+n._padding.top,b:n.bottom-n._padding.bottom},e=Object.assign({},t),i=[],s=[],o=n._pointLabels.length,r=n.options.pointLabels,a=r.centerPointLabels?tt/o:0;for(let l=0;lt.r&&(a=(i.end-t.r)/o,n.r=Math.max(n.r,t.r+a)),s.startt.b&&(l=(s.end-t.b)/r,n.b=Math.max(n.b,t.b+l))}function Mg(n,t,e){const i=n.drawingArea,{extra:s,additionalAngle:o,padding:r,size:a}=e,l=n.getPointPosition(t,i+s+r,o),c=Math.round(ps(Dt(l.angle+at))),u=Tg(l.y,a.h,c),h=Pg(c),d=Dg(l.x,a.w,h);return{visible:!0,x:l.x,y:u,textAlign:h,left:d,top:u,right:d+a.w,bottom:u+a.h}}function kg(n,t){if(!t)return!0;const{left:e,top:i,right:s,bottom:o}=n;return!(Xt({x:e,y:i},t)||Xt({x:e,y:o},t)||Xt({x:s,y:i},t)||Xt({x:s,y:o},t))}function Cg(n,t,e){const i=[],s=n._pointLabels.length,o=n.options,{centerPointLabels:r,display:a}=o.pointLabels,l={extra:rs(o)/2,additionalAngle:r?tt/s:0};let c;for(let u=0;u270||e<90)&&(n-=t),n}function Sg(n,t,e){const{left:i,top:s,right:o,bottom:r}=e,{backdropColor:a}=t;if(!H(a)){const l=ve(t.borderRadius),c=yt(t.backdropPadding);n.fillStyle=a;const u=i-c.left,h=s-c.top,d=o-i+c.width,f=r-s+c.height;Object.values(l).some(g=>g!==0)?(n.beginPath(),dn(n,{x:u,y:h,w:d,h:f,radius:l}),n.fill()):n.fillRect(u,h,d,f)}}function Og(n,t){const{ctx:e,options:{pointLabels:i}}=n;for(let s=t-1;s>=0;s--){const o=n._pointLabelItems[s];if(!o.visible)continue;const r=i.setContext(n.getPointLabelContext(s));Sg(e,r,o);const a=ut(r.font),{x:l,y:c,textAlign:u}=o;Ie(e,n._pointLabels[s],l,c+a.lineHeight/2,a,{color:r.color,textAlign:u,textBaseline:"middle"})}}function Ma(n,t,e,i){const{ctx:s}=n;if(e)s.arc(n.xCenter,n.yCenter,t,0,G);else{let o=n.getPointPosition(0,t);s.moveTo(o.x,o.y);for(let r=1;r{const s=X(this.options.pointLabels.callback,[e,i],this);return s||s===0?s:""}).filter((e,i)=>this.chart.getDataVisibility(i))}fit(){const t=this.options;t.display&&t.pointLabels.display?vg(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(t,e,i,s){this.xCenter+=Math.floor((t-e)/2),this.yCenter+=Math.floor((i-s)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(t,e,i,s))}getIndexAngle(t){const e=G/(this._pointLabels.length||1),i=this.options.startAngle||0;return Dt(t*e+Lt(i))}getDistanceFromCenterForValue(t){if(H(t))return NaN;const e=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-t)*e:(t-this.min)*e}getValueForDistanceFromCenter(t){if(H(t))return NaN;const e=t/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-e:this.min+e}getPointLabelContext(t){const e=this._pointLabels||[];if(t>=0&&t{if(h!==0){l=this.getDistanceFromCenterForValue(u.value);const d=this.getContext(h),f=s.setContext(d),g=o.setContext(d);Ag(this,f,l,r,g)}}),i.display){for(t.save(),a=r-1;a>=0;a--){const u=i.setContext(this.getPointLabelContext(a)),{color:h,lineWidth:d}=u;!d||!h||(t.lineWidth=d,t.strokeStyle=h,t.setLineDash(u.borderDash),t.lineDashOffset=u.borderDashOffset,l=this.getDistanceFromCenterForValue(e.ticks.reverse?this.min:this.max),c=this.getPointPosition(a,l),t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(c.x,c.y),t.stroke())}t.restore()}}drawBorder(){}drawLabels(){const t=this.ctx,e=this.options,i=e.ticks;if(!i.display)return;const s=this.getIndexAngle(0);let o,r;t.save(),t.translate(this.xCenter,this.yCenter),t.rotate(s),t.textAlign="center",t.textBaseline="middle",this.ticks.forEach((a,l)=>{if(l===0&&!e.reverse)return;const c=i.setContext(this.getContext(l)),u=ut(c.font);if(o=this.getDistanceFromCenterForValue(this.ticks[l].value),c.showLabelBackdrop){t.font=u.string,r=t.measureText(a.label).width,t.fillStyle=c.backdropColor;const h=yt(c.backdropPadding);t.fillRect(-r/2-h.left,-o-u.size/2-h.top,r+h.width,u.size+h.height)}Ie(t,a.label,0,-o,u,{color:c.color,strokeColor:c.textStrokeColor,strokeWidth:c.textStrokeWidth})}),t.restore()}drawTitle(){}}k(zn,"id","radialLinear"),k(zn,"defaults",{display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,lineWidth:1,borderDash:[],borderDashOffset:0},grid:{circular:!1},startAngle:0,ticks:{showLabelBackdrop:!0,callback:fi.formatters.numeric},pointLabels:{backdropColor:void 0,backdropPadding:2,display:!0,font:{size:10},callback(t){return t},padding:5,centerPointLabels:!1}}),k(zn,"defaultRoutes",{"angleLines.color":"borderColor","pointLabels.color":"color","ticks.color":"color"}),k(zn,"descriptors",{angleLines:{_fallback:"grid"}});const bi={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},xt=Object.keys(bi);function sr(n,t){return n-t}function or(n,t){if(H(t))return null;const e=n._adapter,{parser:i,round:s,isoWeekday:o}=n._parseOpts;let r=t;return typeof i=="function"&&(r=i(r)),et(r)||(r=typeof i=="string"?e.parse(r,i):e.parse(r)),r===null?null:(s&&(r=s==="week"&&(un(o)||o===!0)?e.startOf(r,"isoWeek",o):e.startOf(r,s)),+r)}function rr(n,t,e,i){const s=xt.length;for(let o=xt.indexOf(n);o=xt.indexOf(e);o--){const r=xt[o];if(bi[r].common&&n._adapter.diff(s,i,r)>=t-1)return r}return xt[e?xt.indexOf(e):0]}function Fg(n){for(let t=xt.indexOf(n)+1,e=xt.length;t=t?e[i]:e[s];n[o]=!0}}function Ig(n,t,e,i){const s=n._adapter,o=+s.startOf(t[0].value,i),r=t[t.length-1].value;let a,l;for(a=o;a<=r;a=+s.add(a,1,i))l=e[a],l>=0&&(t[l].major=!0);return t}function lr(n,t,e){const i=[],s={},o=t.length;let r,a;for(r=0;r+t.value))}initOffsets(t=[]){let e=0,i=0,s,o;this.options.offset&&t.length&&(s=this.getDecimalForValue(t[0]),t.length===1?e=1-s:e=(this.getDecimalForValue(t[1])-s)/2,o=this.getDecimalForValue(t[t.length-1]),t.length===1?i=o:i=(o-this.getDecimalForValue(t[t.length-2]))/2);const r=t.length<3?.5:.25;e=ft(e,0,r),i=ft(i,0,r),this._offsets={start:e,end:i,factor:1/(e+1+i)}}_generate(){const t=this._adapter,e=this.min,i=this.max,s=this.options,o=s.time,r=o.unit||rr(o.minUnit,e,i,this._getLabelCapacity(e)),a=S(s.ticks.stepSize,1),l=r==="week"?o.isoWeekday:!1,c=un(l)||l===!0,u={};let h=e,d,f;if(c&&(h=+t.startOf(h,"isoWeek",l)),h=+t.startOf(h,c?"day":r),t.diff(i,e,r)>1e5*a)throw new Error(e+" and "+i+" are too far apart with stepSize of "+a+" "+r);const g=s.ticks.source==="data"&&this.getDataTimestamps();for(d=h,f=0;d+p)}getLabelForValue(t){const e=this._adapter,i=this.options.time;return i.tooltipFormat?e.format(t,i.tooltipFormat):e.format(t,i.displayFormats.datetime)}format(t,e){const s=this.options.time.displayFormats,o=this._unit,r=e||s[o];return this._adapter.format(t,r)}_tickFormatFunction(t,e,i,s){const o=this.options,r=o.ticks.callback;if(r)return X(r,[t,e,i],this);const a=o.time.displayFormats,l=this._unit,c=this._majorUnit,u=l&&a[l],h=c&&a[c],d=i[e],f=c&&h&&d&&d.major;return this._adapter.format(t,s||(f?h:u))}generateTickLabels(t){let e,i,s;for(e=0,i=t.length;e0?a:1}getDataTimestamps(){let t=this._cache.data||[],e,i;if(t.length)return t;const s=this.getMatchingVisibleMetas();if(this._normalized&&s.length)return this._cache.data=s[0].controller.getAllParsedValues(this);for(e=0,i=s.length;e=n[i].pos&&t<=n[s].pos&&({lo:i,hi:s}=xe(n,"pos",t)),{pos:o,time:a}=n[i],{pos:r,time:l}=n[s]):(t>=n[i].time&&t<=n[s].time&&({lo:i,hi:s}=xe(n,"time",t)),{time:o,pos:a}=n[i],{time:r,pos:l}=n[s]);const c=r-o;return c?a+(l-a)*(t-o)/c:a}class cr extends pn{constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=Bn(e,this.min),this._tableRange=Bn(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:i}=this,s=[],o=[];let r,a,l,c,u;for(r=0,a=t.length;r=e&&c<=i&&s.push(c);if(s.length<2)return[{time:e,pos:0},{time:i,pos:1}];for(r=0,a=s.length;rs-o)}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const e=this.getDataTimestamps(),i=this.getLabelTimestamps();return e.length&&i.length?t=this.normalize(e.concat(i)):t=e.length?e:i,t=this._cache.all=t,t}getDecimalForValue(t){return(Bn(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const e=this._offsets,i=this.getDecimalForPixel(t)/e.factor-e.end;return Bn(this._table,i*this._tableRange+this._minPos,!0)}}k(cr,"id","timeseries"),k(cr,"defaults",pn.defaults);function yi(n){return n==="sankey"?{type:"sankey",data:{datasets:[]},options:{animations:!1}}:n==="pie"?{type:"pie",data:{datasets:[]}}:n==="column"?{type:"bar",data:{labels:[],datasets:[]},options:{plugins:{tooltip:{callbacks:{label:function(t){let e=t.dataset.currency_code;return V(t.raw,e)}}}},maintainAspectRatio:!1,scales:{}}}:n==="line"?{options:{plugins:{tooltip:{callbacks:{label:function(t){let e=t.dataset.currency_code;return V(t.raw,e)}}}},maintainAspectRatio:!1,scales:{x:{type:"time",time:{tooltipFormat:"PP"}}}},type:"line",data:{labels:[],datasets:[]}}:{}}let ri=new dt("#36a2eb"),Re=new dt("#ff6384"),mn=new dt("#4bc0c0"),ka=new dt("#ff9f40"),Eg=new dt("#9966ff"),zg=new dt("#ffcd56"),Bg=new dt("#c9cbcf"),ur=0;window.theme==="dark"&&(Re.darken(.3).desaturate(.3),mn.darken(.3).desaturate(.3),ri.darken(.3).desaturate(.3),ka.darken(.3).desaturate(.3));let Fi=[Re,ka,ri,mn,Eg,zg,Bg,mn];function De(n,t){let e={borderColor:Re.rgbString(),backgroundColor:Re.rgbString()},i;switch(n){default:let o=Math.floor(ur/2)%Fi.length;i=new dt(Fi[o].rgbString()),i.lighten(.38),e={borderColor:Fi[o].hexString(),backgroundColor:i.hexString()};break;case"spent":i=new dt(ri.rgbString()),i.lighten(.38),e={borderColor:ri.rgbString(),backgroundColor:i.rgbString()};break;case"left":i=new dt(mn.rgbString()),i.lighten(.38),e={borderColor:mn.rgbString(),backgroundColor:i.rgbString()};break;case"overspent":i=new dt(Re.rgbString()),i.lighten(.22),e={borderColor:Re.rgbString(),backgroundColor:i.rgbString()};break}return ur++,t==="border"?e.borderColor:t==="background"?e.backgroundColor:"#FF0000"}let me=[],je=null,Ii=null,Ei=!1;const Ng=()=>({loading:!1,loadingAccounts:!1,accountList:[],autoConversion:!1,chartOptions:null,switchAutoConversion(){this.autoConversion=!this.autoConversion,Bc("autoConversion",this.autoConversion)},getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Wt("dashboard-accounts-chart",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){console.log(s),this.drawChart(this.generateOptions(s)),this.loading=!1;return}new Nc().dashboard(n,t,null).then(r=>{this.chartData=r.data,window.store.set(e,r.data),console.log(r.data),this.drawChart(this.generateOptions(this.chartData)),this.loading=!1})},generateOptions(n){me=[];let t=yi("line");for(let e=0;e0){this.loadingAccounts=!1;return}const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Wt("dashboard-accounts-data",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){this.accountList=s,this.loadingAccounts=!1;return}const o=10;let r=0,a=0,l=[];Promise.all([bt("frontpageAccounts")]).then(c=>{r=c[0].length;for(let u in c[0]){let h=c[0];if(h.hasOwnProperty(u)){let d=h[u];new Ns().get(d,new Date(window.store.get("end"))).then(f=>{let g=f.data.data;const p={page:1,start:new Date(window.store.get("start")),end:new Date(window.store.get("end"))};new Ns().transactions(g.id,p).then(m=>{let b=[];for(let y=0;y=o);y++){let x=m.data.data[y],v={title:x.attributes.group_title===null?"":x.attributes.group_title,id:x.id,transactions:[]};for(let _=0;_y.order-x.order),this.accountList=l,this.loadingAccounts=!1,window.store.set(e,l))})})}}})},init(){Promise.all([bt("viewRange","1M"),bt("autoConversion",!1),bt("language","en_US")]).then(n=>{this.autoConversion=n[1],Ei=!0,this.loadChart(),this.loadAccounts()}),window.store.observe("end",()=>{Ei&&(Ii=null,this.accountList=[],this.loadChart(),this.loadAccounts())}),window.store.observe("autoConversion",()=>{Ei&&(this.loadChart(),this.loadAccounts())})}});let Wg=class{dashboard(t,e){let i=J(t,"y-MM-dd"),s=J(e,"y-MM-dd");return vt.get("/api/v2/chart/budget/dashboard",{params:{start:i,end:s}})}},Ue=[],Nn=null,te=null,zi=!1,Te;const Hg=()=>({loading:!1,autoConversion:!1,loadChart(){if(this.loading!==!0){if(this.loading=!0,te!==null){this.drawChart(this.generateOptions(te)),this.loading=!1;return}this.getFreshData()}},drawChart(n){if(Nn!==null){Nn.data.datasets=n.data.datasets,Nn.update();return}Nn=new mt(document.querySelector("#budget-chart"),n)},getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Wt("dashboard-budgets-chart",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){te=s,this.drawChart(this.generateOptions(te)),this.loading=!1;return}new Wg().dashboard(n,t,null).then(r=>{te=r.data,this.drawChart(this.generateOptions(te)),window.store.set(e,te),this.loading=!1})},generateOptions(n){Ue=[];let t=yi("column");t.options.locale=window.store.get("locale").replace("_","-"),t.options.plugins={tooltip:{callbacks:{title:function(e){return e.label},label:function(e){let i=e.dataset.label||"";return i&&(i+=": "),i+" "+V(e.parsed.y,Ue[e.parsed.x]??"EUR")}}}},t.data={labels:[],datasets:[{label:Te.t("firefly.spent"),data:[],borderWidth:1,stack:1,backgroundColor:De("spent","background"),borderColor:De("spent","border")},{label:Te.t("firefly.left"),data:[],borderWidth:1,stack:1,backgroundColor:De("left","background"),borderColor:De("left","border")},{label:Te.t("firefly.overspent"),data:[],borderWidth:1,stack:1,backgroundColor:De("overspent","background"),borderColor:De("overspent","border")}]};for(const e in n)if(n.hasOwnProperty(e)){let i=n[e],s=i.label+" ("+i.currency_code+")";t.data.labels.push(s),this.autoConversion&&(Ue.push(i.native_currency_code),t.data.datasets[0].data.push(parseFloat(i.native_entries.spent)*-1),t.data.datasets[1].data.push(parseFloat(i.native_entries.left)),t.data.datasets[2].data.push(parseFloat(i.native_entries.overspent))),this.autoConversion||(Ue.push(i.currency_code),t.data.datasets[0].data.push(parseFloat(i.entries.spent)*-1),t.data.datasets[1].data.push(parseFloat(i.entries.left)),t.data.datasets[2].data.push(parseFloat(i.entries.overspent)))}return t.options.scales={y:{ticks:{callback:function(e){return V(e,Ue[0]??"EUR")}}}},t},init(){Promise.all([bt("autoConversion",!1),bt("language","en_US")]).then(n=>{Te=new li;const t=n[1].replace("-","_");Te.locale=t,ci(Te,t).then(()=>{this.autoConversion=n[0],zi=!0,this.loading===!1&&this.loadChart()})}),window.store.observe("end",()=>{zi&&this.loading===!1&&(te=null,this.loadChart())}),window.store.observe("autoConversion",n=>{zi&&(this.autoConversion=n,this.loading===!1&&this.loadChart())})}});class Vg{dashboard(t,e){let i=J(t,"y-MM-dd"),s=J(e,"y-MM-dd");return vt.get("/api/v2/chart/category/dashboard",{params:{start:i,end:s}})}}let hr=[],$e=null,Se=null,Bi=!1;const Yg=()=>({loading:!1,autoConversion:!1,generateOptions(n){hr=[];let t=yi("column"),e={};for(const s in n)if(n.hasOwnProperty(s)){let o=n[s],r=o.currency_code;this.autoConversion&&(r=o.native_currency_code),e.hasOwnProperty(r)||(e[r]={name:r,yAxisID:"",data:{}},hr.push(r))}for(const s in n)if(n.hasOwnProperty(s)){let o=n[s],r=o.currency_code;this.autoConversion&&(r=o.native_currency_code);for(const a in e)if(e.hasOwnProperty(a)){let l=0;r===a&&(l=parseFloat(o.amount),""+o.currency_code,this.autoConversion&&(l=parseFloat(o.native_amount),""+o.native_currency_code)),e[a].data.hasOwnProperty(o.label)&&(e[a].data[o.label]=e[a].data[o.label]+l),e[a].data.hasOwnProperty(o.label)||(e[a].data[o.label]=l)}t.data.labels.includes(o.label)||t.data.labels.push(o.label)}let i=0;for(const s in e){let o="y"+s,r={label:s,currency_code:s,yAxisID:o,data:[]};for(const a in e[s].data)r.data.push(e[s].data[a]);t.data.datasets.push(r),t.options.scales.hasOwnProperty(o)||(t.options.scales[o]={beginAtZero:!0,type:"linear",position:i===1?"right":"left",ticks:{callback:function(a,l,c){return V(a,s)}}},i++)}return t},drawChart(n){if($e!==null){$e.options=n.options,$e.data=n.data,$e.update();return}$e=new mt(document.querySelector("#category-chart"),n)},getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Wt("dashboard-categories-chart",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){Se=s,this.drawChart(this.generateOptions(Se)),this.loading=!1;return}new Vg().dashboard(n,t,null).then(r=>{Se=r.data,this.drawChart(this.generateOptions(r.data)),window.store.set(e,Se),this.loading=!1})},loadChart(){if(this.loading!==!0){if(this.loading=!0,Se!==null){this.drawChart(this.generateOptions(Se)),this.loading=!1;return}this.getFreshData()}},init(){Promise.all([bt("autoConversion",!1)]).then(n=>{this.autoConversion=n[0],Bi=!0,this.loadChart()}),window.store.observe("end",()=>{Bi&&(this.chartData=null,this.loadChart())}),window.store.observe("autoConversion",n=>{Bi&&(this.autoConversion=n,this.loadChart())})}});let jg=class{list(t){return vt.get("/api/v2/transactions",{params:t})}};/*! - * chartjs-chart-sankey v0.12.0 - * https://github.com/kurkle/chartjs-chart-sankey#readme - * (c) 2022 Jukka Kurkela - * Released under the MIT license - */function Ug(n){const t=[],e=K(n)?n:H(n)?[]:[n];for(;e.length;){const i=e.pop();typeof i=="string"?t.unshift.apply(t,i.split(` -`)):Array.isArray(i)?e.push.apply(e,i):H(e)||t.unshift(""+i)}return t}function Ni(n){return!n||["min","max"].indexOf(n)===-1?"max":n}const ae=n=>n!==void 0;function $g(n,t){const e=new Set(t.map(r=>r.to)),i=new Set(t.map(r=>r.from)),s=new Set([...n.keys()]);let o=0;for(;s.size;){const r=qg([...s],e);for(const a of r){const l=n.get(a);ae(l.x)||(l.x=o),s.delete(a)}s.size&&(e.clear(),t.filter(a=>s.has(a.from)).forEach(a=>e.add(a.to)),o++)}return[...n.keys()].filter(r=>!i.has(r)).forEach(r=>{const a=n.get(r);a.column||(a.x=o)}),o}function qg(n,t){const e=n.filter(i=>!t.has(i));return e.length?e:n.slice(0,1)}const Xg=(n,t)=>n.x!==t.x?n.x-t.x:n.y-t.y;let Wn=-1;function Kg(){return Wn=Wn<100?Wn+1:0,Wn}function as(n,t,e=Kg()){let i=0;for(const s of n)s.node._visited!==e&&(s.node._visited=e,i+=s.node[t].length+as(s.node[t],t,e));return i}const Ca=n=>(t,e)=>as(t.node[n],n)-as(e.node[n],n)||t.node[n].length-e.node[n].length;function Ps(n,t){n.from.sort(Ca("from"));for(const e of n.from){const i=e.node;ae(i.y)||(i.y=t,Ps(i,t)),t=Math.max(i.y+i.out,t)}return t}function _e(n,t){n.to.sort(Ca("to"));for(const e of n.to){const i=e.node;ae(i.y)||(i.y=t,_e(i,t)),t=Math.max(i.y+i.in,t)}return t}function qe(n,t){return ae(n.y)?n.y:(n.y=t,t)}function Gg(n,t){const e=n.filter(u=>u.x===0),i=n.filter(u=>u.x===t),s=e.filter(u=>!ae(u.y)),o=i.filter(u=>!ae(u.y)),r=n.filter(u=>u.x>0&&u.xMath.max(u,h.y+h.out||0),0),l=i.reduce((u,h)=>Math.max(u,h.y+h.in||0),0),c=0;return a>=l?(s.forEach(u=>{a=qe(u,a),a=Math.max(a+u.out,_e(u,a))}),o.forEach(u=>{l=qe(u,l),l=Math.max(l+u.in,_e(u,l))})):(o.forEach(u=>{l=qe(u,l),l=Math.max(l+u.in,_e(u,l))}),s.forEach(u=>{a=qe(u,a),a=Math.max(a+u.out,_e(u,a))})),r.forEach(u=>{let h=n.filter(d=>d.x===u.x&&ae(d.y)).reduce((d,f)=>Math.max(d,f.y+Math.max(f.in,f.out)),0);h=qe(u,h),h=Math.max(h+u.in,Ps(u,h)),h=Math.max(h+u.out,_e(u,h)),c=Math.max(c,h)}),Math.max(a,l,c)}function Qg(n,t){n.sort((r,a)=>Math.max(a.in,a.out)-Math.max(r.in,r.out));const e=n[0];e.y=0;const i=Ps(e,0),s=_e(e,0),o=Gg(n,t);return Math.max(i,s,o)}function Zg(n,t){let e=0,i=0;for(let s=0;s<=t;s++){let o=i;const r=n.filter(a=>a.x===s).sort((a,l)=>a.priority-l.priority);i=r[0].to.filter(a=>a.node.x>s+1).reduce((a,l)=>a+l.flow,0)||0;for(const a of r)a.y=o,o+=Math.max(a.out,a.in);e=Math.max(o,e)}return e}function Jg(n,t){let e=1,i=0,s=0,o=0;const r=[];n.sort(Xg);for(const a of n){if(a.y){if(a.x===0)r.push(a.y);else{for(i!==a.x&&(i=a.x,s=0),e=s+1;ea.y);e++);s=e}a.y+=e*t,e++}o=Math.max(o,a.y+Math.max(a.in,a.out))}return o}function tp(n,t){n.forEach(e=>{const i=Math[t](e.in||e.out,e.out||e.in),s=il.node.y+l.node.out/2-(c.node.y+c.node.out/2)).forEach((l,c)=>{s?l.addY=c*(i-l.flow)/(a-1):(l.addY=r,r+=l.flow)}),r=0,a=e.to.length,e.to.sort((l,c)=>l.node.y+l.node.in/2-(c.node.y+c.node.in/2)).forEach((l,c)=>{o?l.addY=c*(i-l.flow)/(a-1):(l.addY=r,r+=l.flow)})})}function ep(n,t,e,i){const s=[...n.values()],o=$g(n,t),a=(e?Zg(s,o):Qg(s,o))*.03,l=Jg(s,a);return tp(s,i),{maxX:o,maxY:l}}function np(n){const t=new Map;for(let i=0;is.flow-i.flow;return[...t.values()].forEach(i=>{i.from=i.from.sort(e),i.from.forEach(s=>{s.node=t.get(s.key)}),i.to=i.to.sort(e),i.to.forEach(s=>{s.node=t.get(s.key)})}),t}function dr(n,t,e){for(const i of n)if(i.key===t&&i.index===e)return i.addY;return 0}class _i extends Kt{parseObjectData(t,e,i,s){const{from:o="from",to:r="to",flow:a="flow"}=this.options.parsing,l=e.map(({[o]:y,[r]:x,[a]:v})=>({from:y,to:x,flow:v})),{xScale:c,yScale:u}=t,h=[],d=this._nodes=np(l),{column:f,priority:g,size:p}=this.getDataset();if(g)for(const y of d.values())y.key in g&&(y.priority=g[y.key]);if(f)for(const y of d.values())y.key in f&&(y.column=!0,y.x=f[y.key]);const{maxX:m,maxY:b}=ep(d,l,!!g,Ni(p));this._maxX=m,this._maxY=b;for(let y=0,x=l.length;y1){const d=c-u*l/2+h;for(let f=0;fn.type==="data"?(n.parsed._custom.x-n.parsed.x)*200:void 0,delay:n=>n.type==="data"?n.parsed.x*500+n.dataIndex*20:void 0},colors:{type:"color",properties:["colorFrom","colorTo"]}},transitions:{hide:{animations:{colors:{type:"color",properties:["colorFrom","colorTo"],to:"transparent"}}},show:{animations:{colors:{type:"color",properties:["colorFrom","colorTo"],from:"transparent"}}}}};_i.overrides={interaction:{mode:"nearest",intersect:!0},datasets:{clip:!1,parsing:!0},plugins:{tooltip:{callbacks:{title(){return""},label(n){const t=n.dataset.data[n.dataIndex];return t.from+" -> "+t.to+": "+t.flow}}},legend:{display:!1}},scales:{x:{type:"linear",bounds:"data",display:!1,min:0,offset:!1},y:{type:"linear",bounds:"data",display:!1,min:0,reverse:!0,offset:!1}},layout:{padding:{top:3,left:3,right:13,bottom:3}}};const fr=(n,t,e,i)=>n({x:n.x+e*(t.x-n.x),y:n.y+e*(t.y-n.y)});function ip(n,{x:t,x2:e,options:i}){let s;i.colorMode==="from"?s=Ae(i.colorFrom).alpha(.5).rgbString():i.colorMode==="to"?s=Ae(i.colorTo).alpha(.5).rgbString():(s=n.createLinearGradient(t,0,e,0),s.addColorStop(0,Ae(i.colorFrom).alpha(.5).rgbString()),s.addColorStop(1,Ae(i.colorTo).alpha(.5).rgbString())),n.fillStyle=s,n.strokeStyle=s,n.lineWidth=.5}class Ds extends Rt{constructor(t){super(),this.options=void 0,this.x=void 0,this.y=void 0,this.x2=void 0,this.y2=void 0,this.height=void 0,t&&Object.assign(this,t)}draw(t){const e=this,{x:i,x2:s,y:o,y2:r,height:a,progress:l}=e,{cp1:c,cp2:u}=fr(i,o,s,r);l!==0&&(t.save(),l<1&&(t.beginPath(),t.rect(i,Math.min(o,r),(s-i)*l+1,Math.abs(r-o)+a+1),t.clip()),ip(t,e),t.beginPath(),t.moveTo(i,o),t.bezierCurveTo(c.x,c.y,u.x,u.y,s,r),t.lineTo(s,r+a),t.bezierCurveTo(u.x,u.y+a,c.x,c.y+a,i,o+a),t.lineTo(i,o),t.stroke(),t.closePath(),t.fill(),t.restore())}inRange(t,e,i){const{x:s,y:o,x2:r,y2:a,height:l}=this.getProps(["x","y","x2","y2","height"],i);if(tr)return!1;const{cp1:c,cp2:u}=fr(s,o,r,a),h=(t-s)/(r-s),d={x:s,y:o},f={x:r,y:a},g=Oe(d,c,h),p=Oe(c,u,h),m=Oe(u,f,h),b=Oe(g,p,h),y=Oe(p,m,h),x=Oe(b,y,h).y;return e>=x&&e<=x+l}inXRange(t,e){const{x:i,x2:s}=this.getProps(["x","x2"],e);return t>=i&&t<=s}inYRange(t,e){const{y:i,y2:s,height:o}=this.getProps(["y","y2","height"],e),r=Math.min(i,s),a=Math.max(i,s)+o;return t>=r&&t<=a}getCenterPoint(t){const{x:e,y:i,x2:s,y2:o,height:r}=this.getProps(["x","y","x2","y2","height"],t);return{x:(e+s)/2,y:(i+o+r)/2}}tooltipPosition(t){return this.getCenterPoint(t)}getRange(t){return t==="x"?this.width/2:this.height/2}}Ds.id="flow";Ds.defaults={colorFrom:"red",colorTo:"green",colorMode:"gradient",hoverColorFrom:(n,t)=>on(t.colorFrom),hoverColorTo:(n,t)=>on(t.colorTo)};mt.register({SankeyController:_i,Flow:Ds});const gr="dashboard-sankey-data";let pt,Wi=!1,Hn=null,Vt=[],ht=!1,L={category:null,unknown_category:null,in:null,out:null,unknown_source:null,unknown_dest:null,unknown_account:null,expense_account:null,revenue_account:null,budget:null,unknown_budget:null,all_money:null};const pr=function(n){return n.includes(L.revenue_account)?"forestgreen":n.includes("("+L.in+",")?"green":n.includes(L.budget)||n.includes(L.unknown_budget)?"Orchid":n.includes("("+L.out+",")?"MediumOrchid":n.includes(L.all_money)?"blue":"red"};function Xe(n,t,e,i){if(n==="category"&&t!==null&&e==="in")return L.category+' "'+t+'" ('+L.in+(ht?", "+i+")":")");if(n==="category"&&t===null&&e==="in")return L.unknown_category+" ("+L.in+(ht?", "+i+")":")");if(n==="category"&&t!==null&&e==="out")return L.category+' "'+t+'" ('+L.out+(ht?", "+i+")":")");if(n==="category"&&t===null&&e==="out")return L.unknown_category+" ("+L.out+(ht?", "+i+")":")");if(n==="account"&&t===null&&e==="in")return L.unknown_source+(ht?" ("+i+")":"");if(n==="account"&&t!==null&&e==="in")return L.revenue_account+'"'+t+'"'+(ht?" ("+i+")":"");if(n==="account"&&t===null&&e==="out")return L.unknown_dest+(ht?" ("+i+")":"");if(n==="account"&&t!==null&&e==="out")return L.expense_account+' "'+t+'"'+(ht?" ("+i+")":"");if(n==="budget"&&t!==null)return L.budget+' "'+t+'"'+(ht?" ("+i+")":"");if(n==="budget"&&t===null)return L.unknown_budget+(ht?" ("+i+")":"");console.error('Cannot handle: type:"'+n+'", dir: "'+e+'"')}function Ke(n,t,e){if(n==="category"&&t!==null)return L.category+' "'+t+'"'+(ht?" ("+e+")":"");if(n==="category"&&t===null)return L.unknown_category+(ht?" ("+e+")":"");if(n==="account"&&t===null)return L.unknown_account+(ht?" ("+e+")":"");if(n==="account"&&t!==null)return t+(ht?" ("+e+")":"");if(n==="budget"&&t!==null)return L.budget+' "'+t+'"'+(ht?" ("+e+")":"");if(n==="budget"&&t===null)return L.unknown_budget+(ht?" ("+e+")":"");console.error('Cannot handle: type:"'+n+'"')}const sp=()=>({loading:!1,autoConversion:!1,generateOptions(){let n=yi("sankey"),t={},e={};for(let s in Vt)if(Vt.hasOwnProperty(s)){let o=Vt[s];for(let r in o.attributes.transactions)if(o.attributes.transactions.hasOwnProperty(r)){let a=o.attributes.transactions[r],l=this.autoConversion?a.native_currency_code:a.currency_code,c=this.autoConversion?parseFloat(a.native_amount):parseFloat(a.amount),u;if(a.type==="deposit"){let h=Xe("category",a.category_name,"in",l),d=Xe("account",a.source_name,"in",l);e[h]=Ke("category",a.category_name,l),e[d]=Ke("account",a.source_name,l),u=d+"-"+h+"-"+l,t.hasOwnProperty(u)||(t[u]={from:d,to:h,amount:0}),t[u].amount+=c,u=h+"-"+L.all_money+"-"+l,t.hasOwnProperty(u)||(t[u]={from:h,to:L.all_money+(this.autoConversion?" ("+l+")":""),amount:0}),t[u].amount+=c}if(a.type==="withdrawal"){let h=Xe("budget",a.budget_name,"out",l);e[h]=Ke("budget",a.budget_name,l),u=L.all_money+"-"+h+"-"+l,t.hasOwnProperty(u)||(t[u]={from:L.all_money+(this.autoConversion?" ("+l+")":""),to:h,amount:0}),t[u].amount+=c;let d=Xe("category",a.category_name,"out",l);e[d]=Ke("category",a.category_name,l),u=h+"-"+d+"-"+l,t.hasOwnProperty(u)||(t[u]={from:h,to:d,amount:0}),t[u].amount+=c;let f=Xe("account",a.destination_name,"out",l);e[f]=Ke("account",a.destination_name,l),u=d+"-"+f+"-"+l,t.hasOwnProperty(u)||(t[u]={from:d,to:f,amount:0}),t[u].amount+=c}}}let i={label:"Firefly III dashboard sankey chart",data:[],colorFrom:s=>pr(s.dataset.data[s.dataIndex]?s.dataset.data[s.dataIndex].from:""),colorTo:s=>pr(s.dataset.data[s.dataIndex]?s.dataset.data[s.dataIndex].to:""),colorMode:"gradient",labels:e,size:"min"};for(let s in t)if(t.hasOwnProperty(s)){let o=t[s];i.data.push({from:o.from,to:o.to,flow:o.amount})}return n.data.datasets.push(i),n},drawChart(n){if(Hn!==null){Hn.data.datasets=n.data.datasets,Hn.update();return}Hn=new mt(document.querySelector("#sankey-chart"),n)},getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Wt(gr,n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){Vt=s,this.drawChart(this.generateOptions()),this.loading=!1;return}let o={start:J(n,"y-MM-dd"),end:J(t,"y-MM-dd"),type:"withdrawal,deposit",page:1};this.downloadTransactions(o)},downloadTransactions(n){const t=new Date(window.store.get("start")),e=new Date(window.store.get("end")),i=Wt(gr,t,e);new jg().list(n).then(o=>{if(Vt=[...Vt,...o.data.data],parseInt(o.data.meta.pagination.total_pages)>n.page){n.page++,this.downloadTransactions(n);return}window.store.set(i,Vt),this.drawChart(this.generateOptions()),this.loading=!1})},loadChart(){if(this.loading!==!0){if(this.loading=!0,Vt.length!==0){this.drawChart(this.generateOptions()),this.loading=!1;return}this.getFreshData()}},init(){Vt=[],Promise.all([bt("autoConversion",!1),bt("language","en_US")]).then(n=>{this.autoConversion=n[0],ht=n[0],pt=new li;const t=n[1].replace("-","_");pt.locale=t,ci(pt,t).then(()=>{L.all_money=pt.t("firefly.all_money"),L.category=pt.t("firefly.category"),L.in=pt.t("firefly.money_flowing_in"),L.out=pt.t("firefly.money_flowing_out"),L.unknown_category=pt.t("firefly.unknown_category_plain"),L.unknown_source=pt.t("firefly.unknown_source_plain"),L.unknown_dest=pt.t("firefly.unknown_dest_plain"),L.unknown_account=pt.t("firefly.unknown_any_plain"),L.unknown_budget=pt.t("firefly.unknown_budget_plain"),L.expense_account=pt.t("firefly.expense_account"),L.revenue_account=pt.t("firefly.revenue_account"),L.budget=pt.t("firefly.budget"),Wi=!0,this.loadChart()})}),window.store.observe("end",()=>{Wi&&(this.transactions=[],this.loadChart())}),window.store.observe("autoConversion",n=>{Wi&&(this.autoConversion=n,this.loadChart())})}});let op=class{list(t){return vt.get("/api/v2/subscriptions",{params:t})}paid(t){return vt.get("/api/v2/subscriptions/sum/paid",{params:t})}unpaid(t){return vt.get("/api/v2/subscriptions/sum/unpaid",{params:t})}},Hi=!1,ee,Ct={};function Pa(n){return new op().list(n).then(e=>{let i=e.data.data;for(let s in i)if(i.hasOwnProperty(s)){let o=i[s];if(o.attributes.active&&o.attributes.pay_dates.length>0){let r=o.attributes.object_group_id===null?0:o.attributes.object_group_id,a=o.attributes.object_group_title===null?ee.t("firefly.default_group_title_name_plain"):o.attributes.object_group_title,l=o.attributes.object_group_order===null?0:o.attributes.object_group_order;Ct.hasOwnProperty(r)||(Ct[r]={id:r,title:a,order:l,payment_info:{},bills:[]});let c={id:o.id,name:o.attributes.name,amount_min:o.attributes.amount_min,amount_max:o.attributes.amount_max,amount:(parseFloat(o.attributes.amount_max)+parseFloat(o.attributes.amount_min))/2,currency_code:o.attributes.currency_code,native_amount_min:o.attributes.native_amount_min,native_amount_max:o.attributes.native_amount_max,native_amount:(parseFloat(o.attributes.native_amount_max)+parseFloat(o.attributes.native_amount_min))/2,native_currency_code:o.attributes.native_currency_code,transactions:[],pay_dates:o.attributes.pay_dates,paid:o.attributes.paid_dates.length>0};c.expected_amount=n.autoConversion?V(c.native_amount,c.native_currency_code):V(c.amount,c.currency_code),c.expected_times=ee.t("firefly.subscr_expected_x_times",{times:o.attributes.pay_dates.length,amount:c.expected_amount});for(let u in o.attributes.paid_dates)if(o.attributes.paid_dates.hasOwnProperty(u)){const h=o.attributes.paid_dates[u];let d=100;n.autoConversion&&(d=Math.round(-100+parseFloat(h.native_amount)*-1/parseFloat(c.native_amount)*100)),n.autoConversion||(d=Math.round(-100+parseFloat(h.amount)*-1/parseFloat(c.amount)*100));let f={amount:n.autoConversion?V(h.native_amount,h.native_currency_code):V(h.amount,h.currency_code),percentage:d,date:J(new Date(h.date),"PP"),foreign_amount:null};h.foreign_currency_code!==null&&(f.foreign_amount=n.autoConversion?h.foreign_native_amount:h.foreign_amount,f.foreign_currency_code=n.autoConversion?h.native_currency_code:h.foreign_currency_code),c.transactions.push(f)}if(Ct[r].bills.push(c),o.attributes.paid_dates.length===0){const u=o.attributes.pay_dates.length*c.amount,h=o.attributes.pay_dates.length*c.native_amount;Ct[r].payment_info.hasOwnProperty(c.currency_code)||(Ct[r].payment_info[c.currency_code]={currency_code:c.currency_code,paid:0,unpaid:0,native_currency_code:c.native_currency_code,native_paid:0,native_unpaid:0}),Ct[r].payment_info[c.currency_code].unpaid+=u,Ct[r].payment_info[c.currency_code].native_unpaid+=h}if(o.attributes.paid_dates.length>0){for(let u in o.attributes.paid_dates)if(o.attributes.paid_dates.hasOwnProperty(u)){let h=o.attributes.paid_dates[u];Ct[r].payment_info.hasOwnProperty(h.currency_code)||(Ct[r].payment_info[h.currency_code]={currency_code:c.currency_code,paid:0,unpaid:0,native_currency_code:c.native_currency_code,native_paid:0,native_unpaid:0});const d=parseFloat(h.amount)*-1,f=parseFloat(h.native_amount)*-1;Ct[r].payment_info[h.currency_code].paid+=d,Ct[r].payment_info[h.currency_code].native_paid+=f}}}}return parseInt(e.data.meta.pagination.total_pages)>n.page?(n.page++,Pa(n)):Promise.resolve()})}const rp=()=>({loading:!1,autoConversion:!1,subscriptions:[],startSubscriptions(){this.loading=!0;let n=new Date(window.store.get("start")),t=new Date(window.store.get("end"));console.log("here we are");const e=window.store.get("cacheValid");let i=window.store.get(Wt("subscriptions-data-dashboard",n,t));e&&typeof i<"u",Ct={},this.subscriptions=[],console.log("cache is invalid, must download");let s={start:J(n,"y-MM-dd"),end:J(t,"y-MM-dd"),autoConversion:this.autoConversion,page:1};Pa(s).then(()=>{console.log("Done with download!");let o=Object.values(Ct);for(let r in o)if(o.hasOwnProperty(r)){let a=o[r];const l=Object.keys(a.payment_info);a.col_size=l.length===1?"col-6 offset-3":"col-6",a.chart_width=l.length===1?"50%":"100%",a.payment_length=l.length,this.subscriptions.push(a)}this.loading=!1})},drawPieChart(n,t,e){let i="#pie_"+n+"_"+e.currency_code;const s=this.autoConversion?e.native_unpaid:e.unpaid,o=this.autoConversion?e.native_paid:e.paid,r=this.autoConversion?e.native_currency_code:e.currency_code,l={type:"doughnut",data:{labels:[ee.t("firefly.paid"),ee.t("firefly.unpaid")],datasets:[{label:ee.t("firefly.subscriptions_in_group",{title:t}),data:[o,s],backgroundColor:["rgb(54, 162, 235)","rgb(255, 99, 132)"],hoverOffset:4}]},options:{plugins:{tooltip:{callbacks:{label:function(u){return u.dataset.label+": "+V(u.raw,r)}}}}}};var c=mt.getChart(document.querySelector(i));typeof c<"u"&&c.destroy(),new mt(document.querySelector(i),l)},init(){console.log("subscriptions init"),Promise.all([bt("autoConversion",!1),bt("language","en_US")]).then(n=>{console.log("subscriptions after promises"),this.autoConversion=n[0],Hi=!0,ee=new li;const t=n[1].replace("-","_");ee.locale=t,ci(ee,t).then(()=>{this.loading===!1&&this.startSubscriptions()})}),window.store.observe("end",()=>{Hi&&(console.log("subscriptions observe end"),this.loading===!1&&this.startSubscriptions())}),window.store.observe("autoConversion",n=>{Hi&&(console.log("subscriptions observe autoConversion"),this.autoConversion=n,this.loading===!1&&this.startSubscriptions())})}});class ap{list(t){return vt.get("/api/v2/piggy-banks",{params:t})}}let Yt={},Vi=!1,Vn;const mr="dashboard-piggies-data",lp=()=>({loading:!1,autoConversion:!1,sankeyGrouping:"account",piggies:[],getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Wt(mr,n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){Yt=s,this.parsePiggies(),this.loading=!1;return}let o={start:J(n,"y-MM-dd"),end:J(t,"y-MM-dd"),page:1};this.downloadPiggyBanks(o)},downloadPiggyBanks(n){const t=new Date(window.store.get("start")),e=new Date(window.store.get("end")),i=Wt(mr,t,e);new ap().list(n).then(o=>{if(Yt=[...Yt,...o.data.data],parseInt(o.data.meta.pagination.total_pages)>n.page){n.page++,this.downloadPiggyBanks(n);return}window.store.set(i,Yt),this.parsePiggies(),this.loading=!1})},parsePiggies(){let n=[];for(let t in Yt)if(Yt.hasOwnProperty(t)){let e=Yt[t];if(e.attributes.percentage>=100||e.attributes.percentage===0)continue;let i=e.object_group_title??Vn.t("firefly.default_group_title_name_plain");n.hasOwnProperty(i)||(n[i]={id:e.object_group_id??0,title:i,order:e.object_group_order??0,piggies:[]});let s={id:e.id,name:e.attributes.name,percentage:parseInt(e.attributes.percentage),amount:this.autoConversion?e.attributes.native_current_amount:e.attributes.current_amount,left_to_save:this.autoConversion?e.attributes.native_left_to_save:e.attributes.left_to_save,target_amount:this.autoConversion?e.attributes.native_target_amount:e.attributes.target_amount,save_per_month:this.autoConversion?e.attributes.native_save_per_month:e.attributes.save_per_month,currency_code:this.autoConversion?e.attributes.native_currency_code:e.attributes.currency_code};n[i].piggies.push(s)}this.piggies=Object.values(n)},loadPiggyBanks(){if(this.loading!==!0){if(this.loading=!0,this.piggies.length!==0){this.parsePiggies(),this.loading=!1;return}this.getFreshData()}},init(){Yt=[],Promise.all([bt("autoConversion",!1),bt("language","en_US")]).then(n=>{Vn=new li;const t=n[1].replace("-","_");Vn.locale=t,ci(Vn,t).then(()=>{Vi=!0,this.autoConversion=n[0],this.loadPiggyBanks()})}),window.store.observe("end",()=>{Vi&&(Yt=[],this.loadPiggyBanks())}),window.store.observe("autoConversion",n=>{Vi&&(this.autoConversion=n,this.loadPiggyBanks())})}});/*! - * chartjs-adapter-date-fns v3.0.0 - * https://www.chartjs.org - * (c) 2022 chartjs-adapter-date-fns Contributors - * Released under the MIT license - */const cp={datetime:"MMM d, yyyy, h:mm:ss aaaa",millisecond:"h:mm:ss.SSS aaaa",second:"h:mm:ss aaaa",minute:"h:mm aaaa",hour:"ha",day:"MMM d",week:"PP",month:"MMM yyyy",quarter:"qqq - yyyy",year:"yyyy"};sa._date.override({_id:"date-fns",formats:function(){return cp},parse:function(n,t){if(n===null||typeof n>"u")return null;const e=typeof n;return e==="number"||n instanceof Date?n=j(n):e==="string"&&(typeof t=="string"?n=pc(n,t,new Date,this.options):n=_c(n,this.options)),Na(n)?n.getTime():null},format:function(n,t){return J(n,t,this.options)},add:function(n,t,e){switch(e){case"millisecond":return ai(n,t);case"second":return Ja(n,t);case"minute":return Qa(n,t);case"hour":return qa(n,t);case"day":return br(n,t);case"week":return tl(n,t);case"month":return ls(n,t);case"quarter":return Za(n,t);case"year":return el(n,t);default:return n}},diff:function(n,t,e){switch(e){case"millisecond":return di(n,t);case"second":return ul(n,t);case"minute":return al(n,t);case"hour":return rl(n,t);case"day":return wr(n,t);case"week":return hl(n,t);case"month":return Mr(n,t);case"quarter":return cl(n,t);case"year":return dl(n,t);default:return 0}},startOf:function(n,t,e){switch(t){case"second":return yc(n);case"minute":return fl(n);case"hour":return bc(n);case"day":return Yi(n);case"week":return Rs(n);case"isoWeek":return Rs(n,{weekStartsOn:+e});case"month":return Va(n);case"quarter":return Ha(n);case"year":return Wa(n);default:return n}},endOf:function(n,t){switch(t){case"second":return bl(n);case"minute":return ml(n);case"hour":return pl(n);case"day":return yr(n);case"week":return ja(n);case"month":return _r(n);case"quarter":return Ya(n);case"year":return gl(n);default:return n}}});mt.register({LineController:jn,LineElement:oe,ArcElement:Ze,BarController:Yn,TimeScale:pn,PieController:Qi,BarElement:Kn,Filler:Jf,Colors:Rf,LinearScale:os,CategoryScale:ss,PointElement:Xn,Tooltip:fg,Legend:og});const Da={dates:Ua,boxes:Ec,accounts:Ng,budgets:Hg,categories:Yg,sankey:sp,subscriptions:rp,piggies:lp};function Ta(n){Object.keys(n).forEach(t=>{console.log(`Loading page component "${t}"`);let e=n[t]();Alpine.data(t,()=>e)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{Ta(Da)});window.bootstrapped&&Ta(Da); diff --git a/public/build/assets/dashboard-9f02f998.js b/public/build/assets/dashboard-9f02f998.js new file mode 100644 index 0000000000..dc57951b63 --- /dev/null +++ b/public/build/assets/dashboard-9f02f998.js @@ -0,0 +1,29 @@ +var ia=Object.defineProperty;var sa=(n,t,e)=>t in n?ia(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var _=(n,t,e)=>(sa(n,typeof t!="symbol"?t+"":t,e),e);import{t as R,c as rt,m as rn,a as an,b as $i,d as Gn,e as oa,f as qo,g as Uo,h as Xo,i as ra,j as aa,s as Ke,k as Ko,l as la,n as ca,o as Go,p as ha,q as us,r as ua,u as ds,v as da,w as dt,x as j,y as I,z as lt,P as fa,I as Qn,A as Zn,B as ga,C as pa,D as ma,E as ba,F as ya,G as _a,H as xa,J as wa}from"./load-translations-36a0ce82.js";function Jn(n,t){const e=+R(n);return rt(n,e+t)}function va(n,t){return Jn(n,t*rn)}function Ma(n,t){return Jn(n,t*an)}function ka(n,t){const e=t*3;return $i(n,e)}function Pa(n,t){return Jn(n,t*1e3)}function Da(n,t){const e=t*7;return Gn(n,e)}function Ca(n,t){return $i(n,t*12)}function Ye(n,t){const e=R(n),i=R(t),s=e.getTime()-i.getTime();return s<0?-1:s>0?1:s}function Sa(n,t){const e=R(n),i=R(t),s=e.getFullYear()-i.getFullYear(),o=e.getMonth()-i.getMonth();return s*12+o}function Oa(n,t){const e=R(n),i=R(t);return e.getFullYear()-i.getFullYear()}function Qo(n,t){const e=R(n),i=R(t),s=fs(e,i),o=Math.abs(oa(e,i));e.setDate(e.getDate()-s*o);const r=+(fs(e,i)===-s),a=s*(o-r);return a===0?0:a}function fs(n,t){const e=n.getFullYear()-t.getFullYear()||n.getMonth()-t.getMonth()||n.getDate()-t.getDate()||n.getHours()-t.getHours()||n.getMinutes()-t.getMinutes()||n.getSeconds()-t.getSeconds()||n.getMilliseconds()-t.getMilliseconds();return e<0?-1:e>0?1:e}function ti(n,t){return R(n).getTime()-R(t).getTime()}function ln(n){return n?Math[n]:Math.trunc}function Ta(n,t,e){const i=ti(n,t)/rn;return ln(e==null?void 0:e.roundingMethod)(i)}function Aa(n,t,e){const i=ti(n,t)/an;return ln(e==null?void 0:e.roundingMethod)(i)}function La(n){const t=R(n);return+qo(t)==+Uo(t)}function Zo(n,t){const e=R(n),i=R(t),s=Ye(e,i),o=Math.abs(Sa(e,i));let r;if(o<1)r=0;else{e.getMonth()===1&&e.getDate()>27&&e.setDate(30),e.setMonth(e.getMonth()-s*o);let a=Ye(e,i)===-s;La(R(n))&&o===1&&Ye(n,i)===1&&(a=!1),r=s*(o-Number(a))}return r===0?0:r}function Fa(n,t,e){const i=Zo(n,t)/3;return ln(e==null?void 0:e.roundingMethod)(i)}function Ra(n,t,e){const i=ti(n,t)/1e3;return ln(e==null?void 0:e.roundingMethod)(i)}function Ia(n,t,e){const i=Qo(n,t)/7;return ln(e==null?void 0:e.roundingMethod)(i)}function Ea(n,t){const e=R(n),i=R(t),s=Ye(e,i),o=Math.abs(Oa(e,i));e.setFullYear(1584),i.setFullYear(1584);const r=Ye(e,i)===-s,a=s*(o-+r);return a===0?0:a}function za(n){const t=R(n);return t.setSeconds(0,0),t}function Ba(n){const t=R(n),e=t.getFullYear();return t.setFullYear(e+1,0,0),t.setHours(23,59,59,999),t}function Na(n){const t=R(n);return t.setMinutes(59,59,999),t}function Wa(n){const t=R(n);return t.setSeconds(59,999),t}function Ha(n){const t=R(n);return t.setMilliseconds(999),t}function Va(){return Object.assign({},Xo())}function Ya(n){let e=R(n).getDay();return e===0&&(e=7),e}function ja(n,t){const e=t instanceof Date?rt(t,0):new t(0);return e.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),e.setHours(n.getHours(),n.getMinutes(),n.getSeconds(),n.getMilliseconds()),e}const $a=10;class Jo{constructor(){_(this,"subPriority",0)}validate(t,e){return!0}}class qa extends Jo{constructor(t,e,i,s,o){super(),this.value=t,this.validateValue=e,this.setValue=i,this.priority=s,o&&(this.subPriority=o)}validate(t,e){return this.validateValue(t,this.value,e)}set(t,e,i){return this.setValue(t,e,this.value,i)}}class Ua extends Jo{constructor(){super(...arguments);_(this,"priority",$a);_(this,"subPriority",-1)}set(e,i){return i.timestampIsSet?e:rt(e,ja(e,Date))}}class E{run(t,e,i,s){const o=this.parse(t,e,i,s);return o?{setter:new qa(o.value,this.validate,this.set,this.priority,this.subPriority),rest:o.rest}:null}validate(t,e,i){return!0}}class Xa extends E{constructor(){super(...arguments);_(this,"priority",140);_(this,"incompatibleTokens",["R","u","t","T"])}parse(e,i,s){switch(i){case"G":case"GG":case"GGG":return s.era(e,{width:"abbreviated"})||s.era(e,{width:"narrow"});case"GGGGG":return s.era(e,{width:"narrow"});case"GGGG":default:return s.era(e,{width:"wide"})||s.era(e,{width:"abbreviated"})||s.era(e,{width:"narrow"})}}set(e,i,s){return i.era=s,e.setFullYear(s,0,1),e.setHours(0,0,0,0),e}}const G={month:/^(1[0-2]|0?\d)/,date:/^(3[0-1]|[0-2]?\d)/,dayOfYear:/^(36[0-6]|3[0-5]\d|[0-2]?\d?\d)/,week:/^(5[0-3]|[0-4]?\d)/,hour23h:/^(2[0-3]|[0-1]?\d)/,hour24h:/^(2[0-4]|[0-1]?\d)/,hour11h:/^(1[0-1]|0?\d)/,hour12h:/^(1[0-2]|0?\d)/,minute:/^[0-5]?\d/,second:/^[0-5]?\d/,singleDigit:/^\d/,twoDigits:/^\d{1,2}/,threeDigits:/^\d{1,3}/,fourDigits:/^\d{1,4}/,anyDigitsSigned:/^-?\d+/,singleDigitSigned:/^-?\d/,twoDigitsSigned:/^-?\d{1,2}/,threeDigitsSigned:/^-?\d{1,3}/,fourDigitsSigned:/^-?\d{1,4}/},kt={basicOptionalMinutes:/^([+-])(\d{2})(\d{2})?|Z/,basic:/^([+-])(\d{2})(\d{2})|Z/,basicOptionalSeconds:/^([+-])(\d{2})(\d{2})((\d{2}))?|Z/,extended:/^([+-])(\d{2}):(\d{2})|Z/,extendedOptionalSeconds:/^([+-])(\d{2}):(\d{2})(:(\d{2}))?|Z/};function Q(n,t){return n&&{value:t(n.value),rest:n.rest}}function Y(n,t){const e=t.match(n);return e?{value:parseInt(e[0],10),rest:t.slice(e[0].length)}:null}function Pt(n,t){const e=t.match(n);if(!e)return null;if(e[0]==="Z")return{value:0,rest:t.slice(1)};const i=e[1]==="+"?1:-1,s=e[2]?parseInt(e[2],10):0,o=e[3]?parseInt(e[3],10):0,r=e[5]?parseInt(e[5],10):0;return{value:i*(s*rn+o*an+r*ra),rest:t.slice(e[0].length)}}function tr(n){return Y(G.anyDigitsSigned,n)}function X(n,t){switch(n){case 1:return Y(G.singleDigit,t);case 2:return Y(G.twoDigits,t);case 3:return Y(G.threeDigits,t);case 4:return Y(G.fourDigits,t);default:return Y(new RegExp("^\\d{1,"+n+"}"),t)}}function Wn(n,t){switch(n){case 1:return Y(G.singleDigitSigned,t);case 2:return Y(G.twoDigitsSigned,t);case 3:return Y(G.threeDigitsSigned,t);case 4:return Y(G.fourDigitsSigned,t);default:return Y(new RegExp("^-?\\d{1,"+n+"}"),t)}}function qi(n){switch(n){case"morning":return 4;case"evening":return 17;case"pm":case"noon":case"afternoon":return 12;case"am":case"midnight":case"night":default:return 0}}function er(n,t){const e=t>0,i=e?t:1-t;let s;if(i<=50)s=n||100;else{const o=i+50,r=Math.floor(o/100)*100,a=n>=o%100;s=n+r-(a?100:0)}return e?s:1-s}function nr(n){return n%400===0||n%4===0&&n%100!==0}class Ka extends E{constructor(){super(...arguments);_(this,"priority",130);_(this,"incompatibleTokens",["Y","R","u","w","I","i","e","c","t","T"])}parse(e,i,s){const o=r=>({year:r,isTwoDigitYear:i==="yy"});switch(i){case"y":return Q(X(4,e),o);case"yo":return Q(s.ordinalNumber(e,{unit:"year"}),o);default:return Q(X(i.length,e),o)}}validate(e,i){return i.isTwoDigitYear||i.year>0}set(e,i,s){const o=e.getFullYear();if(s.isTwoDigitYear){const a=er(s.year,o);return e.setFullYear(a,0,1),e.setHours(0,0,0,0),e}const r=!("era"in i)||i.era===1?s.year:1-s.year;return e.setFullYear(r,0,1),e.setHours(0,0,0,0),e}}class Ga extends E{constructor(){super(...arguments);_(this,"priority",130);_(this,"incompatibleTokens",["y","R","u","Q","q","M","L","I","d","D","i","t","T"])}parse(e,i,s){const o=r=>({year:r,isTwoDigitYear:i==="YY"});switch(i){case"Y":return Q(X(4,e),o);case"Yo":return Q(s.ordinalNumber(e,{unit:"year"}),o);default:return Q(X(i.length,e),o)}}validate(e,i){return i.isTwoDigitYear||i.year>0}set(e,i,s,o){const r=aa(e,o);if(s.isTwoDigitYear){const l=er(s.year,r);return e.setFullYear(l,0,o.firstWeekContainsDate),e.setHours(0,0,0,0),Ke(e,o)}const a=!("era"in i)||i.era===1?s.year:1-s.year;return e.setFullYear(a,0,o.firstWeekContainsDate),e.setHours(0,0,0,0),Ke(e,o)}}class Qa extends E{constructor(){super(...arguments);_(this,"priority",130);_(this,"incompatibleTokens",["G","y","Y","u","Q","q","M","L","w","d","D","e","c","t","T"])}parse(e,i){return Wn(i==="R"?4:i.length,e)}set(e,i,s){const o=rt(e,0);return o.setFullYear(s,0,4),o.setHours(0,0,0,0),Ko(o)}}class Za extends E{constructor(){super(...arguments);_(this,"priority",130);_(this,"incompatibleTokens",["G","y","Y","R","w","I","i","e","c","t","T"])}parse(e,i){return Wn(i==="u"?4:i.length,e)}set(e,i,s){return e.setFullYear(s,0,1),e.setHours(0,0,0,0),e}}class Ja extends E{constructor(){super(...arguments);_(this,"priority",120);_(this,"incompatibleTokens",["Y","R","q","M","L","w","I","d","D","i","e","c","t","T"])}parse(e,i,s){switch(i){case"Q":case"QQ":return X(i.length,e);case"Qo":return s.ordinalNumber(e,{unit:"quarter"});case"QQQ":return s.quarter(e,{width:"abbreviated",context:"formatting"})||s.quarter(e,{width:"narrow",context:"formatting"});case"QQQQQ":return s.quarter(e,{width:"narrow",context:"formatting"});case"QQQQ":default:return s.quarter(e,{width:"wide",context:"formatting"})||s.quarter(e,{width:"abbreviated",context:"formatting"})||s.quarter(e,{width:"narrow",context:"formatting"})}}validate(e,i){return i>=1&&i<=4}set(e,i,s){return e.setMonth((s-1)*3,1),e.setHours(0,0,0,0),e}}class tl extends E{constructor(){super(...arguments);_(this,"priority",120);_(this,"incompatibleTokens",["Y","R","Q","M","L","w","I","d","D","i","e","c","t","T"])}parse(e,i,s){switch(i){case"q":case"qq":return X(i.length,e);case"qo":return s.ordinalNumber(e,{unit:"quarter"});case"qqq":return s.quarter(e,{width:"abbreviated",context:"standalone"})||s.quarter(e,{width:"narrow",context:"standalone"});case"qqqqq":return s.quarter(e,{width:"narrow",context:"standalone"});case"qqqq":default:return s.quarter(e,{width:"wide",context:"standalone"})||s.quarter(e,{width:"abbreviated",context:"standalone"})||s.quarter(e,{width:"narrow",context:"standalone"})}}validate(e,i){return i>=1&&i<=4}set(e,i,s){return e.setMonth((s-1)*3,1),e.setHours(0,0,0,0),e}}class el extends E{constructor(){super(...arguments);_(this,"incompatibleTokens",["Y","R","q","Q","L","w","I","D","i","e","c","t","T"]);_(this,"priority",110)}parse(e,i,s){const o=r=>r-1;switch(i){case"M":return Q(Y(G.month,e),o);case"MM":return Q(X(2,e),o);case"Mo":return Q(s.ordinalNumber(e,{unit:"month"}),o);case"MMM":return s.month(e,{width:"abbreviated",context:"formatting"})||s.month(e,{width:"narrow",context:"formatting"});case"MMMMM":return s.month(e,{width:"narrow",context:"formatting"});case"MMMM":default:return s.month(e,{width:"wide",context:"formatting"})||s.month(e,{width:"abbreviated",context:"formatting"})||s.month(e,{width:"narrow",context:"formatting"})}}validate(e,i){return i>=0&&i<=11}set(e,i,s){return e.setMonth(s,1),e.setHours(0,0,0,0),e}}class nl extends E{constructor(){super(...arguments);_(this,"priority",110);_(this,"incompatibleTokens",["Y","R","q","Q","M","w","I","D","i","e","c","t","T"])}parse(e,i,s){const o=r=>r-1;switch(i){case"L":return Q(Y(G.month,e),o);case"LL":return Q(X(2,e),o);case"Lo":return Q(s.ordinalNumber(e,{unit:"month"}),o);case"LLL":return s.month(e,{width:"abbreviated",context:"standalone"})||s.month(e,{width:"narrow",context:"standalone"});case"LLLLL":return s.month(e,{width:"narrow",context:"standalone"});case"LLLL":default:return s.month(e,{width:"wide",context:"standalone"})||s.month(e,{width:"abbreviated",context:"standalone"})||s.month(e,{width:"narrow",context:"standalone"})}}validate(e,i){return i>=0&&i<=11}set(e,i,s){return e.setMonth(s,1),e.setHours(0,0,0,0),e}}function il(n,t,e){const i=R(n),s=la(i,e)-t;return i.setDate(i.getDate()-s*7),i}class sl extends E{constructor(){super(...arguments);_(this,"priority",100);_(this,"incompatibleTokens",["y","R","u","q","Q","M","L","I","d","D","i","t","T"])}parse(e,i,s){switch(i){case"w":return Y(G.week,e);case"wo":return s.ordinalNumber(e,{unit:"week"});default:return X(i.length,e)}}validate(e,i){return i>=1&&i<=53}set(e,i,s,o){return Ke(il(e,s,o),o)}}function ol(n,t){const e=R(n),i=ca(e)-t;return e.setDate(e.getDate()-i*7),e}class rl extends E{constructor(){super(...arguments);_(this,"priority",100);_(this,"incompatibleTokens",["y","Y","u","q","Q","M","L","w","d","D","e","c","t","T"])}parse(e,i,s){switch(i){case"I":return Y(G.week,e);case"Io":return s.ordinalNumber(e,{unit:"week"});default:return X(i.length,e)}}validate(e,i){return i>=1&&i<=53}set(e,i,s){return Ko(ol(e,s))}}const al=[31,28,31,30,31,30,31,31,30,31,30,31],ll=[31,29,31,30,31,30,31,31,30,31,30,31];class cl extends E{constructor(){super(...arguments);_(this,"priority",90);_(this,"subPriority",1);_(this,"incompatibleTokens",["Y","R","q","Q","w","I","D","i","e","c","t","T"])}parse(e,i,s){switch(i){case"d":return Y(G.date,e);case"do":return s.ordinalNumber(e,{unit:"date"});default:return X(i.length,e)}}validate(e,i){const s=e.getFullYear(),o=nr(s),r=e.getMonth();return o?i>=1&&i<=ll[r]:i>=1&&i<=al[r]}set(e,i,s){return e.setDate(s),e.setHours(0,0,0,0),e}}class hl extends E{constructor(){super(...arguments);_(this,"priority",90);_(this,"subpriority",1);_(this,"incompatibleTokens",["Y","R","q","Q","M","L","w","I","d","E","i","e","c","t","T"])}parse(e,i,s){switch(i){case"D":case"DD":return Y(G.dayOfYear,e);case"Do":return s.ordinalNumber(e,{unit:"date"});default:return X(i.length,e)}}validate(e,i){const s=e.getFullYear();return nr(s)?i>=1&&i<=366:i>=1&&i<=365}set(e,i,s){return e.setMonth(0,s),e.setHours(0,0,0,0),e}}function Ui(n,t,e){var u,d,f,g;const i=Xo(),s=(e==null?void 0:e.weekStartsOn)??((d=(u=e==null?void 0:e.locale)==null?void 0:u.options)==null?void 0:d.weekStartsOn)??i.weekStartsOn??((g=(f=i.locale)==null?void 0:f.options)==null?void 0:g.weekStartsOn)??0,o=R(n),r=o.getDay(),l=(t%7+7)%7,c=7-s,h=t<0||t>6?t-(r+c)%7:(l+c)%7-(r+c)%7;return Gn(o,h)}class ul extends E{constructor(){super(...arguments);_(this,"priority",90);_(this,"incompatibleTokens",["D","i","e","c","t","T"])}parse(e,i,s){switch(i){case"E":case"EE":case"EEE":return s.day(e,{width:"abbreviated",context:"formatting"})||s.day(e,{width:"short",context:"formatting"})||s.day(e,{width:"narrow",context:"formatting"});case"EEEEE":return s.day(e,{width:"narrow",context:"formatting"});case"EEEEEE":return s.day(e,{width:"short",context:"formatting"})||s.day(e,{width:"narrow",context:"formatting"});case"EEEE":default:return s.day(e,{width:"wide",context:"formatting"})||s.day(e,{width:"abbreviated",context:"formatting"})||s.day(e,{width:"short",context:"formatting"})||s.day(e,{width:"narrow",context:"formatting"})}}validate(e,i){return i>=0&&i<=6}set(e,i,s,o){return e=Ui(e,s,o),e.setHours(0,0,0,0),e}}class dl extends E{constructor(){super(...arguments);_(this,"priority",90);_(this,"incompatibleTokens",["y","R","u","q","Q","M","L","I","d","D","E","i","c","t","T"])}parse(e,i,s,o){const r=a=>{const l=Math.floor((a-1)/7)*7;return(a+o.weekStartsOn+6)%7+l};switch(i){case"e":case"ee":return Q(X(i.length,e),r);case"eo":return Q(s.ordinalNumber(e,{unit:"day"}),r);case"eee":return s.day(e,{width:"abbreviated",context:"formatting"})||s.day(e,{width:"short",context:"formatting"})||s.day(e,{width:"narrow",context:"formatting"});case"eeeee":return s.day(e,{width:"narrow",context:"formatting"});case"eeeeee":return s.day(e,{width:"short",context:"formatting"})||s.day(e,{width:"narrow",context:"formatting"});case"eeee":default:return s.day(e,{width:"wide",context:"formatting"})||s.day(e,{width:"abbreviated",context:"formatting"})||s.day(e,{width:"short",context:"formatting"})||s.day(e,{width:"narrow",context:"formatting"})}}validate(e,i){return i>=0&&i<=6}set(e,i,s,o){return e=Ui(e,s,o),e.setHours(0,0,0,0),e}}class fl extends E{constructor(){super(...arguments);_(this,"priority",90);_(this,"incompatibleTokens",["y","R","u","q","Q","M","L","I","d","D","E","i","e","t","T"])}parse(e,i,s,o){const r=a=>{const l=Math.floor((a-1)/7)*7;return(a+o.weekStartsOn+6)%7+l};switch(i){case"c":case"cc":return Q(X(i.length,e),r);case"co":return Q(s.ordinalNumber(e,{unit:"day"}),r);case"ccc":return s.day(e,{width:"abbreviated",context:"standalone"})||s.day(e,{width:"short",context:"standalone"})||s.day(e,{width:"narrow",context:"standalone"});case"ccccc":return s.day(e,{width:"narrow",context:"standalone"});case"cccccc":return s.day(e,{width:"short",context:"standalone"})||s.day(e,{width:"narrow",context:"standalone"});case"cccc":default:return s.day(e,{width:"wide",context:"standalone"})||s.day(e,{width:"abbreviated",context:"standalone"})||s.day(e,{width:"short",context:"standalone"})||s.day(e,{width:"narrow",context:"standalone"})}}validate(e,i){return i>=0&&i<=6}set(e,i,s,o){return e=Ui(e,s,o),e.setHours(0,0,0,0),e}}function gl(n,t){const e=R(n),i=Ya(e),s=t-i;return Gn(e,s)}class pl extends E{constructor(){super(...arguments);_(this,"priority",90);_(this,"incompatibleTokens",["y","Y","u","q","Q","M","L","w","d","D","E","e","c","t","T"])}parse(e,i,s){const o=r=>r===0?7:r;switch(i){case"i":case"ii":return X(i.length,e);case"io":return s.ordinalNumber(e,{unit:"day"});case"iii":return Q(s.day(e,{width:"abbreviated",context:"formatting"})||s.day(e,{width:"short",context:"formatting"})||s.day(e,{width:"narrow",context:"formatting"}),o);case"iiiii":return Q(s.day(e,{width:"narrow",context:"formatting"}),o);case"iiiiii":return Q(s.day(e,{width:"short",context:"formatting"})||s.day(e,{width:"narrow",context:"formatting"}),o);case"iiii":default:return Q(s.day(e,{width:"wide",context:"formatting"})||s.day(e,{width:"abbreviated",context:"formatting"})||s.day(e,{width:"short",context:"formatting"})||s.day(e,{width:"narrow",context:"formatting"}),o)}}validate(e,i){return i>=1&&i<=7}set(e,i,s){return e=gl(e,s),e.setHours(0,0,0,0),e}}class ml extends E{constructor(){super(...arguments);_(this,"priority",80);_(this,"incompatibleTokens",["b","B","H","k","t","T"])}parse(e,i,s){switch(i){case"a":case"aa":case"aaa":return s.dayPeriod(e,{width:"abbreviated",context:"formatting"})||s.dayPeriod(e,{width:"narrow",context:"formatting"});case"aaaaa":return s.dayPeriod(e,{width:"narrow",context:"formatting"});case"aaaa":default:return s.dayPeriod(e,{width:"wide",context:"formatting"})||s.dayPeriod(e,{width:"abbreviated",context:"formatting"})||s.dayPeriod(e,{width:"narrow",context:"formatting"})}}set(e,i,s){return e.setHours(qi(s),0,0,0),e}}class bl extends E{constructor(){super(...arguments);_(this,"priority",80);_(this,"incompatibleTokens",["a","B","H","k","t","T"])}parse(e,i,s){switch(i){case"b":case"bb":case"bbb":return s.dayPeriod(e,{width:"abbreviated",context:"formatting"})||s.dayPeriod(e,{width:"narrow",context:"formatting"});case"bbbbb":return s.dayPeriod(e,{width:"narrow",context:"formatting"});case"bbbb":default:return s.dayPeriod(e,{width:"wide",context:"formatting"})||s.dayPeriod(e,{width:"abbreviated",context:"formatting"})||s.dayPeriod(e,{width:"narrow",context:"formatting"})}}set(e,i,s){return e.setHours(qi(s),0,0,0),e}}class yl extends E{constructor(){super(...arguments);_(this,"priority",80);_(this,"incompatibleTokens",["a","b","t","T"])}parse(e,i,s){switch(i){case"B":case"BB":case"BBB":return s.dayPeriod(e,{width:"abbreviated",context:"formatting"})||s.dayPeriod(e,{width:"narrow",context:"formatting"});case"BBBBB":return s.dayPeriod(e,{width:"narrow",context:"formatting"});case"BBBB":default:return s.dayPeriod(e,{width:"wide",context:"formatting"})||s.dayPeriod(e,{width:"abbreviated",context:"formatting"})||s.dayPeriod(e,{width:"narrow",context:"formatting"})}}set(e,i,s){return e.setHours(qi(s),0,0,0),e}}class _l extends E{constructor(){super(...arguments);_(this,"priority",70);_(this,"incompatibleTokens",["H","K","k","t","T"])}parse(e,i,s){switch(i){case"h":return Y(G.hour12h,e);case"ho":return s.ordinalNumber(e,{unit:"hour"});default:return X(i.length,e)}}validate(e,i){return i>=1&&i<=12}set(e,i,s){const o=e.getHours()>=12;return o&&s<12?e.setHours(s+12,0,0,0):!o&&s===12?e.setHours(0,0,0,0):e.setHours(s,0,0,0),e}}class xl extends E{constructor(){super(...arguments);_(this,"priority",70);_(this,"incompatibleTokens",["a","b","h","K","k","t","T"])}parse(e,i,s){switch(i){case"H":return Y(G.hour23h,e);case"Ho":return s.ordinalNumber(e,{unit:"hour"});default:return X(i.length,e)}}validate(e,i){return i>=0&&i<=23}set(e,i,s){return e.setHours(s,0,0,0),e}}class wl extends E{constructor(){super(...arguments);_(this,"priority",70);_(this,"incompatibleTokens",["h","H","k","t","T"])}parse(e,i,s){switch(i){case"K":return Y(G.hour11h,e);case"Ko":return s.ordinalNumber(e,{unit:"hour"});default:return X(i.length,e)}}validate(e,i){return i>=0&&i<=11}set(e,i,s){return e.getHours()>=12&&s<12?e.setHours(s+12,0,0,0):e.setHours(s,0,0,0),e}}class vl extends E{constructor(){super(...arguments);_(this,"priority",70);_(this,"incompatibleTokens",["a","b","h","H","K","t","T"])}parse(e,i,s){switch(i){case"k":return Y(G.hour24h,e);case"ko":return s.ordinalNumber(e,{unit:"hour"});default:return X(i.length,e)}}validate(e,i){return i>=1&&i<=24}set(e,i,s){const o=s<=24?s%24:s;return e.setHours(o,0,0,0),e}}class Ml extends E{constructor(){super(...arguments);_(this,"priority",60);_(this,"incompatibleTokens",["t","T"])}parse(e,i,s){switch(i){case"m":return Y(G.minute,e);case"mo":return s.ordinalNumber(e,{unit:"minute"});default:return X(i.length,e)}}validate(e,i){return i>=0&&i<=59}set(e,i,s){return e.setMinutes(s,0,0),e}}class kl extends E{constructor(){super(...arguments);_(this,"priority",50);_(this,"incompatibleTokens",["t","T"])}parse(e,i,s){switch(i){case"s":return Y(G.second,e);case"so":return s.ordinalNumber(e,{unit:"second"});default:return X(i.length,e)}}validate(e,i){return i>=0&&i<=59}set(e,i,s){return e.setSeconds(s,0),e}}class Pl extends E{constructor(){super(...arguments);_(this,"priority",30);_(this,"incompatibleTokens",["t","T"])}parse(e,i){const s=o=>Math.floor(o*Math.pow(10,-i.length+3));return Q(X(i.length,e),s)}set(e,i,s){return e.setMilliseconds(s),e}}class Dl extends E{constructor(){super(...arguments);_(this,"priority",10);_(this,"incompatibleTokens",["t","T","x"])}parse(e,i){switch(i){case"X":return Pt(kt.basicOptionalMinutes,e);case"XX":return Pt(kt.basic,e);case"XXXX":return Pt(kt.basicOptionalSeconds,e);case"XXXXX":return Pt(kt.extendedOptionalSeconds,e);case"XXX":default:return Pt(kt.extended,e)}}set(e,i,s){return i.timestampIsSet?e:rt(e,e.getTime()-Go(e)-s)}}class Cl extends E{constructor(){super(...arguments);_(this,"priority",10);_(this,"incompatibleTokens",["t","T","X"])}parse(e,i){switch(i){case"x":return Pt(kt.basicOptionalMinutes,e);case"xx":return Pt(kt.basic,e);case"xxxx":return Pt(kt.basicOptionalSeconds,e);case"xxxxx":return Pt(kt.extendedOptionalSeconds,e);case"xxx":default:return Pt(kt.extended,e)}}set(e,i,s){return i.timestampIsSet?e:rt(e,e.getTime()-Go(e)-s)}}class Sl extends E{constructor(){super(...arguments);_(this,"priority",40);_(this,"incompatibleTokens","*")}parse(e){return tr(e)}set(e,i,s){return[rt(e,s*1e3),{timestampIsSet:!0}]}}class Ol extends E{constructor(){super(...arguments);_(this,"priority",20);_(this,"incompatibleTokens","*")}parse(e){return tr(e)}set(e,i,s){return[rt(e,s),{timestampIsSet:!0}]}}const Tl={G:new Xa,y:new Ka,Y:new Ga,R:new Qa,u:new Za,Q:new Ja,q:new tl,M:new el,L:new nl,w:new sl,I:new rl,d:new cl,D:new hl,E:new ul,e:new dl,c:new fl,i:new pl,a:new ml,b:new bl,B:new yl,h:new _l,H:new xl,K:new wl,k:new vl,m:new Ml,s:new kl,S:new Pl,X:new Dl,x:new Cl,t:new Sl,T:new Ol},Al=/[yYQqMLwIdDecihHKkms]o|(\w)\1*|''|'(''|[^'])+('|$)|./g,Ll=/P+p+|P+|p+|''|'(''|[^'])+('|$)|./g,Fl=/^'([^]*?)'?$/,Rl=/''/g,Il=/\S/,El=/[a-zA-Z]/;function zl(n,t,e,i){var p,m,b,y,w,M,x,P;const s=Va(),o=(i==null?void 0:i.locale)??s.locale??ha,r=(i==null?void 0:i.firstWeekContainsDate)??((m=(p=i==null?void 0:i.locale)==null?void 0:p.options)==null?void 0:m.firstWeekContainsDate)??s.firstWeekContainsDate??((y=(b=s.locale)==null?void 0:b.options)==null?void 0:y.firstWeekContainsDate)??1,a=(i==null?void 0:i.weekStartsOn)??((M=(w=i==null?void 0:i.locale)==null?void 0:w.options)==null?void 0:M.weekStartsOn)??s.weekStartsOn??((P=(x=s.locale)==null?void 0:x.options)==null?void 0:P.weekStartsOn)??0;if(t==="")return n===""?R(e):rt(e,NaN);const l={firstWeekContainsDate:r,weekStartsOn:a,locale:o},c=[new Ua],h=t.match(Ll).map(v=>{const k=v[0];if(k in us){const D=us[k];return D(v,o.formatLong)}return v}).join("").match(Al),u=[];for(let v of h){!(i!=null&&i.useAdditionalWeekYearTokens)&&ua(v)&&ds(v,t,n),!(i!=null&&i.useAdditionalDayOfYearTokens)&&da(v)&&ds(v,t,n);const k=v[0],D=Tl[k];if(D){const{incompatibleTokens:T}=D;if(Array.isArray(T)){const A=u.find(J=>T.includes(J.token)||J.token===k);if(A)throw new RangeError(`The format string mustn't contain \`${A.fullToken}\` and \`${v}\` at the same time`)}else if(D.incompatibleTokens==="*"&&u.length>0)throw new RangeError(`The format string mustn't contain \`${v}\` and any other token at the same time`);u.push({token:k,fullToken:v});const S=D.run(n,v,o.match,l);if(!S)return rt(e,NaN);c.push(S.setter),n=S.rest}else{if(k.match(El))throw new RangeError("Format string contains an unescaped latin alphabet character `"+k+"`");if(v==="''"?v="'":k==="'"&&(v=Bl(v)),n.indexOf(v)===0)n=n.slice(v.length);else return rt(e,NaN)}}if(n.length>0&&Il.test(n))return rt(e,NaN);const d=c.map(v=>v.priority).sort((v,k)=>k-v).filter((v,k,D)=>D.indexOf(v)===k).map(v=>c.filter(k=>k.priority===v).sort((k,D)=>D.subPriority-k.subPriority)).map(v=>v[0]);let f=R(e);if(isNaN(f.getTime()))return rt(e,NaN);const g={};for(const v of d){if(!v.validate(f,l))return rt(e,NaN);const k=v.set(f,g,l);Array.isArray(k)?(f=k[0],Object.assign(g,k[1])):f=k}return rt(e,f)}function Bl(n){return n.match(Fl)[1].replace(Rl,"'")}function Nl(n){const t=R(n);return t.setMinutes(0,0,0),t}function Wl(n){const t=R(n);return t.setMilliseconds(0),t}function Hl(n,t){const e=(t==null?void 0:t.additionalDigits)??2,i=$l(n);let s;if(i.date){const l=ql(i.date,e);s=Ul(l.restDateString,l.year)}if(!s||isNaN(s.getTime()))return new Date(NaN);const o=s.getTime();let r=0,a;if(i.time&&(r=Xl(i.time),isNaN(r)))return new Date(NaN);if(i.timezone){if(a=Kl(i.timezone),isNaN(a))return new Date(NaN)}else{const l=new Date(o+r),c=new Date(0);return c.setFullYear(l.getUTCFullYear(),l.getUTCMonth(),l.getUTCDate()),c.setHours(l.getUTCHours(),l.getUTCMinutes(),l.getUTCSeconds(),l.getUTCMilliseconds()),c}return new Date(o+r+a)}const fn={dateTimeDelimiter:/[T ]/,timeZoneDelimiter:/[Z ]/i,timezone:/([Z+-].*)$/},Vl=/^-?(?:(\d{3})|(\d{2})(?:-?(\d{2}))?|W(\d{2})(?:-?(\d{1}))?|)$/,Yl=/^(\d{2}(?:[.,]\d*)?)(?::?(\d{2}(?:[.,]\d*)?))?(?::?(\d{2}(?:[.,]\d*)?))?$/,jl=/^([+-])(\d{2})(?::?(\d{2}))?$/;function $l(n){const t={},e=n.split(fn.dateTimeDelimiter);let i;if(e.length>2)return t;if(/:/.test(e[0])?i=e[0]:(t.date=e[0],i=e[1],fn.timeZoneDelimiter.test(t.date)&&(t.date=n.split(fn.timeZoneDelimiter)[0],i=n.substr(t.date.length,n.length))),i){const s=fn.timezone.exec(i);s?(t.time=i.replace(s[1],""),t.timezone=s[1]):t.time=i}return t}function ql(n,t){const e=new RegExp("^(?:(\\d{4}|[+-]\\d{"+(4+t)+"})|(\\d{2}|[+-]\\d{"+(2+t)+"})$)"),i=n.match(e);if(!i)return{year:NaN,restDateString:""};const s=i[1]?parseInt(i[1]):null,o=i[2]?parseInt(i[2]):null;return{year:o===null?s:o*100,restDateString:n.slice((i[1]||i[2]).length)}}function Ul(n,t){if(t===null)return new Date(NaN);const e=n.match(Vl);if(!e)return new Date(NaN);const i=!!e[4],s=Ce(e[1]),o=Ce(e[2])-1,r=Ce(e[3]),a=Ce(e[4]),l=Ce(e[5])-1;if(i)return tc(t,a,l)?Gl(t,a,l):new Date(NaN);{const c=new Date(0);return!Zl(t,o,r)||!Jl(t,s)?new Date(NaN):(c.setUTCFullYear(t,o,Math.max(s,r)),c)}}function Ce(n){return n?parseInt(n):1}function Xl(n){const t=n.match(Yl);if(!t)return NaN;const e=li(t[1]),i=li(t[2]),s=li(t[3]);return ec(e,i,s)?e*rn+i*an+s*1e3:NaN}function li(n){return n&&parseFloat(n.replace(",","."))||0}function Kl(n){if(n==="Z")return 0;const t=n.match(jl);if(!t)return 0;const e=t[1]==="+"?-1:1,i=parseInt(t[2]),s=t[3]&&parseInt(t[3])||0;return nc(i,s)?e*(i*rn+s*an):NaN}function Gl(n,t,e){const i=new Date(0);i.setUTCFullYear(n,0,4);const s=i.getUTCDay()||7,o=(t-1)*7+e+1-s;return i.setUTCDate(i.getUTCDate()+o),i}const Ql=[31,null,31,30,31,30,31,31,30,31,30,31];function ir(n){return n%400===0||n%4===0&&n%100!==0}function Zl(n,t,e){return t>=0&&t<=11&&e>=1&&e<=(Ql[t]||(ir(n)?29:28))}function Jl(n,t){return t>=1&&t<=(ir(n)?366:365)}function tc(n,t,e){return t>=1&&t<=53&&e>=0&&e<=6}function ec(n,t,e){return n===24?t===0&&e===0:e>=0&&e<60&&t>=0&&t<60&&n>=0&&n<25}function nc(n,t){return t>=0&&t<=59}class ic{get(t,e,i){return dt.get("/api/v2/summary/basic",{params:{start:t,end:e,code:i}})}}function Ct(n,t,e){const i=j(t,"y-MM-dd")+"_"+j(e,"y-MM-dd")+"_"+n;return console.log("getCacheKey: "+i),String(i)}let ci=!1;const sc=()=>({balanceBox:{amounts:[],subtitles:[]},billBox:{paid:[],unpaid:[]},leftBox:{left:[],perDay:[]},netBox:{net:[]},autoConversion:!1,loading:!1,boxData:null,boxOptions:null,getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Ct("dashboard-boxes-data",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){this.boxData=s,this.generateOptions(this.boxData);return}new ic().get(j(n,"yyyy-MM-dd"),j(t,"yyyy-MM-dd"),null).then(r=>{this.boxData=r.data,window.store.set(e,r.data),this.generateOptions(this.boxData)})},generateOptions(n){this.balanceBox={amounts:[],subtitles:[]},this.billBox={paid:[],unpaid:[]},this.leftBox={left:[],perDay:[]},this.netBox={net:[]};let t={};for(const e in n)if(n.hasOwnProperty(e)){const i=n[e];if(!i.hasOwnProperty("key"))continue;let s=i.key;if(this.autoConversion){if(s.startsWith("balance-in-native")){this.balanceBox.amounts.push(I(i.value,i.currency_code)),t.hasOwnProperty(i.currency_code)||(t[i.currency_code]="");continue}if(s.startsWith("spent-in-native")){t.hasOwnProperty(i.currency_code)||(t[i.currency_code]=""),t[i.currency_code]=t[i.currency_code]+I(i.value,i.currency_code);continue}if(s.startsWith("earned-in-native")){t.hasOwnProperty(i.currency_code)||(t[i.currency_code]=""),t[i.currency_code]=I(i.value,i.currency_code)+" + "+t[i.currency_code];continue}if(s.startsWith("bills-unpaid-in-native")){this.billBox.unpaid.push(I(i.value,i.currency_code));continue}if(s.startsWith("bills-paid-in-native")){this.billBox.paid.push(I(i.value,i.currency_code));continue}if(s.startsWith("left-to-spend-in-native")){this.leftBox.left.push(I(i.value,i.currency_code));continue}if(s.startsWith("left-per-day-to-spend-in-native")){this.leftBox.perDay.push(I(i.value,i.currency_code));continue}if(s.startsWith("net-worth-in-native")){this.netBox.net.push(I(i.value,i.currency_code));continue}}if(!this.autoConversion&&!s.endsWith("native")){if(s.startsWith("balance-in-")){this.balanceBox.amounts.push(I(i.value,i.currency_code));continue}if(s.startsWith("spent-in-")){t.hasOwnProperty(i.currency_code)||(t[i.currency_code]=""),t[i.currency_code]=t[i.currency_code]+I(i.value,i.currency_code);continue}if(s.startsWith("earned-in-")){t.hasOwnProperty(i.currency_code)||(t[i.currency_code]=""),t[i.currency_code]=I(i.value,i.currency_code)+" + "+t[i.currency_code];continue}if(s.startsWith("bills-unpaid-in-")){this.billBox.unpaid.push(I(i.value,i.currency_code));continue}if(s.startsWith("bills-paid-in-")){this.billBox.paid.push(I(i.value,i.currency_code));continue}if(s.startsWith("left-to-spend-in-")){this.leftBox.left.push(I(i.value,i.currency_code));continue}if(s.startsWith("left-per-day-to-spend-in-")){this.leftBox.perDay.push(I(i.value,i.currency_code));continue}s.startsWith("net-worth-in-")&&this.netBox.net.push(I(i.value,i.currency_code))}}for(let e in t)t.hasOwnProperty(e)&&this.balanceBox.subtitles.push(t[e]);this.loading=!1},loadBoxes(){if(this.loading!==!0){if(this.loading=!0,this.boxData===null){this.getFreshData();return}this.generateOptions(this.boxData),this.loading=!1}},init(){Promise.all([lt("viewRange"),lt("autoConversion",!1)]).then(n=>{ci=!0,this.autoConversion=n[1],this.loadBoxes()}),window.store.observe("end",()=>{ci&&(this.boxData=null,this.loadBoxes())}),window.store.observe("autoConversion",n=>{ci&&(this.autoConversion=n,this.loadBoxes())})}});class oc{put(t,e){let i="/api/v1/preferences/"+t;return dt.put(i,{data:e})}}function rc(n,t=null){window.store.set(n,t),new oc().put(n,t).then(i=>{}).catch(()=>{new fa().post(n,t).then(s=>{})})}let ac=class{dashboard(t,e){let i=j(t,"y-MM-dd"),s=j(e,"y-MM-dd");return dt.get("/api/v2/chart/account/dashboard",{params:{start:i,end:s}})}expense(t,e){let i=j(t,"y-MM-dd"),s=j(e,"y-MM-dd");return dt.get("/api/v2/chart/account/expense-dashboard",{params:{start:i,end:s}})}},gs=class{get(t,e){let i={date:j(e,"y-MM-dd").slice(0,10)};return e?dt.get("/api/v2/accounts/"+t,{params:i}):dt.get("/api/v2/accounts/"+t)}transactions(t,e){const i={page:e.page??1};return e.hasOwnProperty("start")&&(i.start=j(e.start,"y-MM-dd")),e.hasOwnProperty("end")&&(i.end=j(e.end,"y-MM-dd")),dt.get("/api/v2/accounts/"+t+"/transactions",{params:i})}};/*! + * @kurkle/color v0.3.2 + * https://github.com/kurkle/color#readme + * (c) 2023 Jukka Kurkela + * Released under the MIT License + */function cn(n){return n+.5|0}const Nt=(n,t,e)=>Math.max(Math.min(n,e),t);function Ne(n){return Nt(cn(n*2.55),0,255)}function $t(n){return Nt(cn(n*255),0,255)}function Ft(n){return Nt(cn(n/2.55)/100,0,1)}function ps(n){return Nt(cn(n*100),0,100)}const yt={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},Ti=[..."0123456789ABCDEF"],lc=n=>Ti[n&15],cc=n=>Ti[(n&240)>>4]+Ti[n&15],gn=n=>(n&240)>>4===(n&15),hc=n=>gn(n.r)&&gn(n.g)&&gn(n.b)&&gn(n.a);function uc(n){var t=n.length,e;return n[0]==="#"&&(t===4||t===5?e={r:255&yt[n[1]]*17,g:255&yt[n[2]]*17,b:255&yt[n[3]]*17,a:t===5?yt[n[4]]*17:255}:(t===7||t===9)&&(e={r:yt[n[1]]<<4|yt[n[2]],g:yt[n[3]]<<4|yt[n[4]],b:yt[n[5]]<<4|yt[n[6]],a:t===9?yt[n[7]]<<4|yt[n[8]]:255})),e}const dc=(n,t)=>n<255?t(n):"";function fc(n){var t=hc(n)?lc:cc;return n?"#"+t(n.r)+t(n.g)+t(n.b)+dc(n.a,t):void 0}const gc=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function sr(n,t,e){const i=t*Math.min(e,1-e),s=(o,r=(o+n/30)%12)=>e-i*Math.max(Math.min(r-3,9-r,1),-1);return[s(0),s(8),s(4)]}function pc(n,t,e){const i=(s,o=(s+n/60)%6)=>e-e*t*Math.max(Math.min(o,4-o,1),0);return[i(5),i(3),i(1)]}function mc(n,t,e){const i=sr(n,1,.5);let s;for(t+e>1&&(s=1/(t+e),t*=s,e*=s),s=0;s<3;s++)i[s]*=1-t-e,i[s]+=t;return i}function bc(n,t,e,i,s){return n===s?(t-e)/i+(t.5?h/(2-o-r):h/(o+r),l=bc(e,i,s,h,o),l=l*60+.5),[l|0,c||0,a]}function Ki(n,t,e,i){return(Array.isArray(t)?n(t[0],t[1],t[2]):n(t,e,i)).map($t)}function Gi(n,t,e){return Ki(sr,n,t,e)}function yc(n,t,e){return Ki(mc,n,t,e)}function _c(n,t,e){return Ki(pc,n,t,e)}function or(n){return(n%360+360)%360}function xc(n){const t=gc.exec(n);let e=255,i;if(!t)return;t[5]!==i&&(e=t[6]?Ne(+t[5]):$t(+t[5]));const s=or(+t[2]),o=+t[3]/100,r=+t[4]/100;return t[1]==="hwb"?i=yc(s,o,r):t[1]==="hsv"?i=_c(s,o,r):i=Gi(s,o,r),{r:i[0],g:i[1],b:i[2],a:e}}function wc(n,t){var e=Xi(n);e[0]=or(e[0]+t),e=Gi(e),n.r=e[0],n.g=e[1],n.b=e[2]}function vc(n){if(!n)return;const t=Xi(n),e=t[0],i=ps(t[1]),s=ps(t[2]);return n.a<255?`hsla(${e}, ${i}%, ${s}%, ${Ft(n.a)})`:`hsl(${e}, ${i}%, ${s}%)`}const ms={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},bs={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};function Mc(){const n={},t=Object.keys(bs),e=Object.keys(ms);let i,s,o,r,a;for(i=0;i>16&255,o>>8&255,o&255]}return n}let pn;function kc(n){pn||(pn=Mc(),pn.transparent=[0,0,0,0]);const t=pn[n.toLowerCase()];return t&&{r:t[0],g:t[1],b:t[2],a:t.length===4?t[3]:255}}const Pc=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;function Dc(n){const t=Pc.exec(n);let e=255,i,s,o;if(t){if(t[7]!==i){const r=+t[7];e=t[8]?Ne(r):Nt(r*255,0,255)}return i=+t[1],s=+t[3],o=+t[5],i=255&(t[2]?Ne(i):Nt(i,0,255)),s=255&(t[4]?Ne(s):Nt(s,0,255)),o=255&(t[6]?Ne(o):Nt(o,0,255)),{r:i,g:s,b:o,a:e}}}function Cc(n){return n&&(n.a<255?`rgba(${n.r}, ${n.g}, ${n.b}, ${Ft(n.a)})`:`rgb(${n.r}, ${n.g}, ${n.b})`)}const hi=n=>n<=.0031308?n*12.92:Math.pow(n,1/2.4)*1.055-.055,de=n=>n<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4);function Sc(n,t,e){const i=de(Ft(n.r)),s=de(Ft(n.g)),o=de(Ft(n.b));return{r:$t(hi(i+e*(de(Ft(t.r))-i))),g:$t(hi(s+e*(de(Ft(t.g))-s))),b:$t(hi(o+e*(de(Ft(t.b))-o))),a:n.a+e*(t.a-n.a)}}function mn(n,t,e){if(n){let i=Xi(n);i[t]=Math.max(0,Math.min(i[t]+i[t]*e,t===0?360:1)),i=Gi(i),n.r=i[0],n.g=i[1],n.b=i[2]}}function rr(n,t){return n&&Object.assign(t||{},n)}function ys(n){var t={r:0,g:0,b:0,a:255};return Array.isArray(n)?n.length>=3&&(t={r:n[0],g:n[1],b:n[2],a:255},n.length>3&&(t.a=$t(n[3]))):(t=rr(n,{r:0,g:0,b:0,a:1}),t.a=$t(t.a)),t}function Oc(n){return n.charAt(0)==="r"?Dc(n):xc(n)}class it{constructor(t){if(t instanceof it)return t;const e=typeof t;let i;e==="object"?i=ys(t):e==="string"&&(i=uc(t)||kc(t)||Oc(t)),this._rgb=i,this._valid=!!i}get valid(){return this._valid}get rgb(){var t=rr(this._rgb);return t&&(t.a=Ft(t.a)),t}set rgb(t){this._rgb=ys(t)}rgbString(){return this._valid?Cc(this._rgb):void 0}hexString(){return this._valid?fc(this._rgb):void 0}hslString(){return this._valid?vc(this._rgb):void 0}mix(t,e){if(t){const i=this.rgb,s=t.rgb;let o;const r=e===o?.5:e,a=2*r-1,l=i.a-s.a,c=((a*l===-1?a:(a+l)/(1+a*l))+1)/2;o=1-c,i.r=255&c*i.r+o*s.r+.5,i.g=255&c*i.g+o*s.g+.5,i.b=255&c*i.b+o*s.b+.5,i.a=r*i.a+(1-r)*s.a,this.rgb=i}return this}interpolate(t,e){return t&&(this._rgb=Sc(this._rgb,t._rgb,e)),this}clone(){return new it(this.rgb)}alpha(t){return this._rgb.a=$t(t),this}clearer(t){const e=this._rgb;return e.a*=1-t,this}greyscale(){const t=this._rgb,e=cn(t.r*.3+t.g*.59+t.b*.11);return t.r=t.g=t.b=e,this}opaquer(t){const e=this._rgb;return e.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return mn(this._rgb,2,t),this}darken(t){return mn(this._rgb,2,-t),this}saturate(t){return mn(this._rgb,1,t),this}desaturate(t){return mn(this._rgb,1,-t),this}rotate(t){return wc(this._rgb,t),this}}/*! + * Chart.js v4.4.1 + * https://www.chartjs.org + * (c) 2023 Chart.js Contributors + * Released under the MIT License + */function St(){}const Tc=(()=>{let n=0;return()=>n++})();function F(n){return n===null||typeof n>"u"}function W(n){if(Array.isArray&&Array.isArray(n))return!0;const t=Object.prototype.toString.call(n);return t.slice(0,7)==="[object"&&t.slice(-6)==="Array]"}function L(n){return n!==null&&Object.prototype.toString.call(n)==="[object Object]"}function q(n){return(typeof n=="number"||n instanceof Number)&&isFinite(+n)}function mt(n,t){return q(n)?n:t}function C(n,t){return typeof n>"u"?t:n}const Ac=(n,t)=>typeof n=="string"&&n.endsWith("%")?parseFloat(n)/100:+n/t,ar=(n,t)=>typeof n=="string"&&n.endsWith("%")?parseFloat(n)/100*t:+n;function N(n,t,e){if(n&&typeof n.call=="function")return n.apply(e,t)}function z(n,t,e,i){let s,o,r;if(W(n))if(o=n.length,i)for(s=o-1;s>=0;s--)t.call(e,n[s],s);else for(s=0;sn,x:n=>n.x,y:n=>n.y};function Rc(n){const t=n.split("."),e=[];let i="";for(const s of t)i+=s,i.endsWith("\\")?i=i.slice(0,-1)+".":(e.push(i),i="");return e}function Ic(n){const t=Rc(n);return e=>{for(const i of t){if(i==="")break;e=e&&e[i]}return e}}function qt(n,t){return(_s[t]||(_s[t]=Ic(t)))(n)}function Qi(n){return n.charAt(0).toUpperCase()+n.slice(1)}const Qe=n=>typeof n<"u",Ut=n=>typeof n=="function",xs=(n,t)=>{if(n.size!==t.size)return!1;for(const e of n)if(!t.has(e))return!1;return!0};function Ec(n){return n.type==="mouseup"||n.type==="click"||n.type==="contextmenu"}const $=Math.PI,H=2*$,zc=H+$,Yn=Number.POSITIVE_INFINITY,Bc=$/180,Z=$/2,Qt=$/4,ws=$*2/3,Wt=Math.log10,Dt=Math.sign;function $e(n,t,e){return Math.abs(n-t)s-o).pop(),t}function Ze(n){return!isNaN(parseFloat(n))&&isFinite(n)}function Wc(n,t){const e=Math.round(n);return e-t<=n&&e+t>=n}function cr(n,t,e){let i,s,o;for(i=0,s=n.length;il&&c=Math.min(t,e)-i&&n<=Math.max(t,e)+i}function Ji(n,t,e){e=e||(r=>n[r]1;)o=s+i>>1,e(o)?s=o:i=o;return{lo:s,hi:i}}const oe=(n,t,e,i)=>Ji(n,e,i?s=>{const o=n[s][t];return on[s][t]Ji(n,e,i=>n[i][t]>=e);function jc(n,t,e){let i=0,s=n.length;for(;ii&&n[s-1]>e;)s--;return i>0||s{const i="_onData"+Qi(e),s=n[e];Object.defineProperty(n,e,{configurable:!0,enumerable:!1,value(...o){const r=s.apply(this,o);return n._chartjs.listeners.forEach(a=>{typeof a[i]=="function"&&a[i](...o)}),r}})})}function ks(n,t){const e=n._chartjs;if(!e)return;const i=e.listeners,s=i.indexOf(t);s!==-1&&i.splice(s,1),!(i.length>0)&&(ur.forEach(o=>{delete n[o]}),delete n._chartjs)}function dr(n){const t=new Set(n);return t.size===n.length?n:Array.from(t)}const fr=function(){return typeof window>"u"?function(n){return n()}:window.requestAnimationFrame}();function gr(n,t){let e=[],i=!1;return function(...s){e=s,i||(i=!0,fr.call(window,()=>{i=!1,n.apply(t,e)}))}}function qc(n,t){let e;return function(...i){return t?(clearTimeout(e),e=setTimeout(n,t,i)):n.apply(this,i),t}}const pr=n=>n==="start"?"left":n==="end"?"right":"center",gt=(n,t,e)=>n==="start"?t:n==="end"?e:(t+e)/2,Uc=(n,t,e,i)=>n===(i?"left":"right")?e:n==="center"?(t+e)/2:t;function Xc(n,t,e){const i=t.length;let s=0,o=i;if(n._sorted){const{iScale:r,_parsed:a}=n,l=r.axis,{min:c,max:h,minDefined:u,maxDefined:d}=r.getUserBounds();u&&(s=st(Math.min(oe(a,l,c).lo,e?i:oe(t,l,r.getPixelForValue(c)).lo),0,i-1)),d?o=st(Math.max(oe(a,r.axis,h,!0).hi+1,e?0:oe(t,l,r.getPixelForValue(h),!0).hi+1),s,i)-s:o=i-s}return{start:s,count:o}}function Kc(n){const{xScale:t,yScale:e,_scaleRanges:i}=n,s={xmin:t.min,xmax:t.max,ymin:e.min,ymax:e.max};if(!i)return n._scaleRanges=s,!0;const o=i.xmin!==t.min||i.xmax!==t.max||i.ymin!==e.min||i.ymax!==e.max;return Object.assign(i,s),o}const bn=n=>n===0||n===1,Ps=(n,t,e)=>-(Math.pow(2,10*(n-=1))*Math.sin((n-t)*H/e)),Ds=(n,t,e)=>Math.pow(2,-10*n)*Math.sin((n-t)*H/e)+1,qe={linear:n=>n,easeInQuad:n=>n*n,easeOutQuad:n=>-n*(n-2),easeInOutQuad:n=>(n/=.5)<1?.5*n*n:-.5*(--n*(n-2)-1),easeInCubic:n=>n*n*n,easeOutCubic:n=>(n-=1)*n*n+1,easeInOutCubic:n=>(n/=.5)<1?.5*n*n*n:.5*((n-=2)*n*n+2),easeInQuart:n=>n*n*n*n,easeOutQuart:n=>-((n-=1)*n*n*n-1),easeInOutQuart:n=>(n/=.5)<1?.5*n*n*n*n:-.5*((n-=2)*n*n*n-2),easeInQuint:n=>n*n*n*n*n,easeOutQuint:n=>(n-=1)*n*n*n*n+1,easeInOutQuint:n=>(n/=.5)<1?.5*n*n*n*n*n:.5*((n-=2)*n*n*n*n+2),easeInSine:n=>-Math.cos(n*Z)+1,easeOutSine:n=>Math.sin(n*Z),easeInOutSine:n=>-.5*(Math.cos($*n)-1),easeInExpo:n=>n===0?0:Math.pow(2,10*(n-1)),easeOutExpo:n=>n===1?1:-Math.pow(2,-10*n)+1,easeInOutExpo:n=>bn(n)?n:n<.5?.5*Math.pow(2,10*(n*2-1)):.5*(-Math.pow(2,-10*(n*2-1))+2),easeInCirc:n=>n>=1?n:-(Math.sqrt(1-n*n)-1),easeOutCirc:n=>Math.sqrt(1-(n-=1)*n),easeInOutCirc:n=>(n/=.5)<1?-.5*(Math.sqrt(1-n*n)-1):.5*(Math.sqrt(1-(n-=2)*n)+1),easeInElastic:n=>bn(n)?n:Ps(n,.075,.3),easeOutElastic:n=>bn(n)?n:Ds(n,.075,.3),easeInOutElastic(n){return bn(n)?n:n<.5?.5*Ps(n*2,.1125,.45):.5+.5*Ds(n*2-1,.1125,.45)},easeInBack(n){return n*n*((1.70158+1)*n-1.70158)},easeOutBack(n){return(n-=1)*n*((1.70158+1)*n+1.70158)+1},easeInOutBack(n){let t=1.70158;return(n/=.5)<1?.5*(n*n*(((t*=1.525)+1)*n-t)):.5*((n-=2)*n*(((t*=1.525)+1)*n+t)+2)},easeInBounce:n=>1-qe.easeOutBounce(1-n),easeOutBounce(n){return n<1/2.75?7.5625*n*n:n<2/2.75?7.5625*(n-=1.5/2.75)*n+.75:n<2.5/2.75?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375},easeInOutBounce:n=>n<.5?qe.easeInBounce(n*2)*.5:qe.easeOutBounce(n*2-1)*.5+.5};function ts(n){if(n&&typeof n=="object"){const t=n.toString();return t==="[object CanvasPattern]"||t==="[object CanvasGradient]"}return!1}function ye(n){return ts(n)?n:new it(n)}function Ue(n){return ts(n)?n:new it(n).saturate(.5).darken(.1).hexString()}const Gc=["x","y","borderWidth","radius","tension"],Qc=["color","borderColor","backgroundColor"];function Zc(n){n.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),n.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:t=>t!=="onProgress"&&t!=="onComplete"&&t!=="fn"}),n.set("animations",{colors:{type:"color",properties:Qc},numbers:{type:"number",properties:Gc}}),n.describe("animations",{_fallback:"animation"}),n.set("transitions",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:"transparent"},visible:{type:"boolean",duration:0}}},hide:{animations:{colors:{to:"transparent"},visible:{type:"boolean",easing:"linear",fn:t=>t|0}}}})}function Jc(n){n.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})}const Cs=new Map;function th(n,t){t=t||{};const e=n+JSON.stringify(t);let i=Cs.get(e);return i||(i=new Intl.NumberFormat(n,t),Cs.set(e,i)),i}function hn(n,t,e){return th(t,e).format(n)}const mr={values(n){return W(n)?n:""+n},numeric(n,t,e){if(n===0)return"0";const i=this.chart.options.locale;let s,o=n;if(e.length>1){const c=Math.max(Math.abs(e[0].value),Math.abs(e[e.length-1].value));(c<1e-4||c>1e15)&&(s="scientific"),o=eh(n,e)}const r=Wt(Math.abs(o)),a=isNaN(r)?1:Math.max(Math.min(-1*Math.floor(r),20),0),l={notation:s,minimumFractionDigits:a,maximumFractionDigits:a};return Object.assign(l,this.options.ticks.format),hn(n,i,l)},logarithmic(n,t,e){if(n===0)return"0";const i=e[t].significand||n/Math.pow(10,Math.floor(Wt(n)));return[1,2,3,5,10,15].includes(i)||t>.8*e.length?mr.numeric.call(this,n,t,e):""}};function eh(n,t){let e=t.length>3?t[2].value-t[1].value:t[1].value-t[0].value;return Math.abs(e)>=1&&n!==Math.floor(n)&&(e=n-Math.floor(n)),e}var ei={formatters:mr};function nh(n){n.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,e)=>e.lineWidth,tickColor:(t,e)=>e.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:ei.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),n.route("scale.ticks","color","","color"),n.route("scale.grid","color","","borderColor"),n.route("scale.border","color","","borderColor"),n.route("scale.title","color","","color"),n.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&t!=="callback"&&t!=="parser",_indexable:t=>t!=="borderDash"&&t!=="tickBorderDash"&&t!=="dash"}),n.describe("scales",{_fallback:"scale"}),n.describe("scale.ticks",{_scriptable:t=>t!=="backdropPadding"&&t!=="callback",_indexable:t=>t!=="backdropPadding"})}const le=Object.create(null),Li=Object.create(null);function Xe(n,t){if(!t)return n;const e=t.split(".");for(let i=0,s=e.length;ii.chart.platform.getDevicePixelRatio(),this.elements={},this.events=["mousemove","mouseout","click","touchstart","touchmove"],this.font={family:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:12,style:"normal",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(i,s)=>Ue(s.backgroundColor),this.hoverBorderColor=(i,s)=>Ue(s.borderColor),this.hoverColor=(i,s)=>Ue(s.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(t),this.apply(e)}set(t,e){return ui(this,t,e)}get(t){return Xe(this,t)}describe(t,e){return ui(Li,t,e)}override(t,e){return ui(le,t,e)}route(t,e,i,s){const o=Xe(this,t),r=Xe(this,i),a="_"+e;Object.defineProperties(o,{[a]:{value:o[e],writable:!0},[e]:{enumerable:!0,get(){const l=this[a],c=r[s];return L(l)?Object.assign({},c,l):C(l,c)},set(l){this[a]=l}}})}apply(t){t.forEach(e=>e(this))}}var U=new ih({_scriptable:n=>!n.startsWith("on"),_indexable:n=>n!=="events",hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[Zc,Jc,nh]);function sh(n){return!n||F(n.size)||F(n.family)?null:(n.style?n.style+" ":"")+(n.weight?n.weight+" ":"")+n.size+"px "+n.family}function jn(n,t,e,i,s){let o=t[s];return o||(o=t[s]=n.measureText(s).width,e.push(s)),o>i&&(i=o),i}function oh(n,t,e,i){i=i||{};let s=i.data=i.data||{},o=i.garbageCollect=i.garbageCollect||[];i.font!==t&&(s=i.data={},o=i.garbageCollect=[],i.font=t),n.save(),n.font=t;let r=0;const a=e.length;let l,c,h,u,d;for(l=0;le.length){for(l=0;l0&&n.stroke()}}function It(n,t,e){return e=e||.5,!t||n&&n.x>t.left-e&&n.xt.top-e&&n.y0&&o.strokeColor!=="";let l,c;for(n.save(),n.font=s.string,lh(n,o),l=0;l+n||0;function es(n,t){const e={},i=L(t),s=i?Object.keys(t):t,o=L(n)?i?r=>C(n[r],n[t[r]]):r=>n[r]:()=>n;for(const r of s)e[r]=gh(o(r));return e}function yr(n){return es(n,{top:"y",right:"x",bottom:"y",left:"x"})}function re(n){return es(n,["topLeft","topRight","bottomLeft","bottomRight"])}function ct(n){const t=yr(n);return t.width=t.left+t.right,t.height=t.top+t.bottom,t}function tt(n,t){n=n||{},t=t||U.font;let e=C(n.size,t.size);typeof e=="string"&&(e=parseInt(e,10));let i=C(n.style,t.style);i&&!(""+i).match(dh)&&(console.warn('Invalid font style specified: "'+i+'"'),i=void 0);const s={family:C(n.family,t.family),lineHeight:fh(C(n.lineHeight,t.lineHeight),e),size:e,style:i,weight:C(n.weight,t.weight),string:""};return s.string=sh(s),s}function yn(n,t,e,i){let s=!0,o,r,a;for(o=0,r=n.length;oe&&a===0?0:a+l;return{min:r(i,-Math.abs(o)),max:r(s,o)}}function Xt(n,t){return Object.assign(Object.create(n),t)}function ns(n,t=[""],e,i,s=()=>n[0]){const o=e||n;typeof i>"u"&&(i=vr("_fallback",n));const r={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:n,_rootScopes:o,_fallback:i,_getTarget:s,override:a=>ns([a,...n],t,o,i)};return new Proxy(r,{deleteProperty(a,l){return delete a[l],delete a._keys,delete n[0][l],!0},get(a,l){return xr(a,l,()=>Mh(l,t,n,a))},getOwnPropertyDescriptor(a,l){return Reflect.getOwnPropertyDescriptor(a._scopes[0],l)},getPrototypeOf(){return Reflect.getPrototypeOf(n[0])},has(a,l){return Ts(a).includes(l)},ownKeys(a){return Ts(a)},set(a,l,c){const h=a._storage||(a._storage=s());return a[l]=h[l]=c,delete a._keys,!0}})}function Me(n,t,e,i){const s={_cacheable:!1,_proxy:n,_context:t,_subProxy:e,_stack:new Set,_descriptors:_r(n,i),setContext:o=>Me(n,o,e,i),override:o=>Me(n.override(o),t,e,i)};return new Proxy(s,{deleteProperty(o,r){return delete o[r],delete n[r],!0},get(o,r,a){return xr(o,r,()=>bh(o,r,a))},getOwnPropertyDescriptor(o,r){return o._descriptors.allKeys?Reflect.has(n,r)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(n,r)},getPrototypeOf(){return Reflect.getPrototypeOf(n)},has(o,r){return Reflect.has(n,r)},ownKeys(){return Reflect.ownKeys(n)},set(o,r,a){return n[r]=a,delete o[r],!0}})}function _r(n,t={scriptable:!0,indexable:!0}){const{_scriptable:e=t.scriptable,_indexable:i=t.indexable,_allKeys:s=t.allKeys}=n;return{allKeys:s,scriptable:e,indexable:i,isScriptable:Ut(e)?e:()=>e,isIndexable:Ut(i)?i:()=>i}}const mh=(n,t)=>n?n+Qi(t):t,is=(n,t)=>L(t)&&n!=="adapters"&&(Object.getPrototypeOf(t)===null||t.constructor===Object);function xr(n,t,e){if(Object.prototype.hasOwnProperty.call(n,t))return n[t];const i=e();return n[t]=i,i}function bh(n,t,e){const{_proxy:i,_context:s,_subProxy:o,_descriptors:r}=n;let a=i[t];return Ut(a)&&r.isScriptable(t)&&(a=yh(t,a,n,e)),W(a)&&a.length&&(a=_h(t,a,n,r.isIndexable)),is(t,a)&&(a=Me(a,s,o&&o[t],r)),a}function yh(n,t,e,i){const{_proxy:s,_context:o,_subProxy:r,_stack:a}=e;if(a.has(n))throw new Error("Recursion detected: "+Array.from(a).join("->")+"->"+n);a.add(n);let l=t(o,r||i);return a.delete(n),is(n,l)&&(l=ss(s._scopes,s,n,l)),l}function _h(n,t,e,i){const{_proxy:s,_context:o,_subProxy:r,_descriptors:a}=e;if(typeof o.index<"u"&&i(n))return t[o.index%t.length];if(L(t[0])){const l=t,c=s._scopes.filter(h=>h!==l);t=[];for(const h of l){const u=ss(c,s,n,h);t.push(Me(u,o,r&&r[n],a))}}return t}function wr(n,t,e){return Ut(n)?n(t,e):n}const xh=(n,t)=>n===!0?t:typeof n=="string"?qt(t,n):void 0;function wh(n,t,e,i,s){for(const o of t){const r=xh(e,o);if(r){n.add(r);const a=wr(r._fallback,e,s);if(typeof a<"u"&&a!==e&&a!==i)return a}else if(r===!1&&typeof i<"u"&&e!==i)return null}return!1}function ss(n,t,e,i){const s=t._rootScopes,o=wr(t._fallback,e,i),r=[...n,...s],a=new Set;a.add(i);let l=Os(a,r,e,o||e,i);return l===null||typeof o<"u"&&o!==e&&(l=Os(a,r,o,l,i),l===null)?!1:ns(Array.from(a),[""],s,o,()=>vh(t,e,i))}function Os(n,t,e,i,s){for(;e;)e=wh(n,t,e,i,s);return e}function vh(n,t,e){const i=n._getTarget();t in i||(i[t]={});const s=i[t];return W(s)&&L(e)?e:s||{}}function Mh(n,t,e,i){let s;for(const o of t)if(s=vr(mh(o,n),e),typeof s<"u")return is(n,s)?ss(e,i,n,s):s}function vr(n,t){for(const e of t){if(!e)continue;const i=e[n];if(typeof i<"u")return i}}function Ts(n){let t=n._keys;return t||(t=n._keys=kh(n._scopes)),t}function kh(n){const t=new Set;for(const e of n)for(const i of Object.keys(e).filter(s=>!s.startsWith("_")))t.add(i);return Array.from(t)}function Ph(n,t,e,i){const{iScale:s}=n,{key:o="r"}=this._parsing,r=new Array(i);let a,l,c,h;for(a=0,l=i;atn==="x"?"y":"x";function Ch(n,t,e,i){const s=n.skip?t:n,o=t,r=e.skip?t:e,a=Ai(o,s),l=Ai(r,o);let c=a/(a+l),h=l/(a+l);c=isNaN(c)?0:c,h=isNaN(h)?0:h;const u=i*c,d=i*h;return{previous:{x:o.x-u*(r.x-s.x),y:o.y-u*(r.y-s.y)},next:{x:o.x+d*(r.x-s.x),y:o.y+d*(r.y-s.y)}}}function Sh(n,t,e){const i=n.length;let s,o,r,a,l,c=ke(n,0);for(let h=0;h!c.skip)),t.cubicInterpolationMode==="monotone")Th(n,s);else{let c=i?n[n.length-1]:n[0];for(o=0,r=n.length;on.ownerDocument.defaultView.getComputedStyle(n,null);function Fh(n,t){return si(n).getPropertyValue(t)}const Rh=["top","right","bottom","left"];function ae(n,t,e){const i={};e=e?"-"+e:"";for(let s=0;s<4;s++){const o=Rh[s];i[o]=parseFloat(n[t+"-"+o+e])||0}return i.width=i.left+i.right,i.height=i.top+i.bottom,i}const Ih=(n,t,e)=>(n>0||t>0)&&(!e||!e.shadowRoot);function Eh(n,t){const e=n.touches,i=e&&e.length?e[0]:n,{offsetX:s,offsetY:o}=i;let r=!1,a,l;if(Ih(s,o,n.target))a=s,l=o;else{const c=t.getBoundingClientRect();a=i.clientX-c.left,l=i.clientY-c.top,r=!0}return{x:a,y:l,box:r}}function ne(n,t){if("native"in n)return n;const{canvas:e,currentDevicePixelRatio:i}=t,s=si(e),o=s.boxSizing==="border-box",r=ae(s,"padding"),a=ae(s,"border","width"),{x:l,y:c,box:h}=Eh(n,e),u=r.left+(h&&a.left),d=r.top+(h&&a.top);let{width:f,height:g}=t;return o&&(f-=r.width+a.width,g-=r.height+a.height),{x:Math.round((l-u)/f*e.width/i),y:Math.round((c-d)/g*e.height/i)}}function zh(n,t,e){let i,s;if(t===void 0||e===void 0){const o=rs(n);if(!o)t=n.clientWidth,e=n.clientHeight;else{const r=o.getBoundingClientRect(),a=si(o),l=ae(a,"border","width"),c=ae(a,"padding");t=r.width-c.width-l.width,e=r.height-c.height-l.height,i=$n(a.maxWidth,o,"clientWidth"),s=$n(a.maxHeight,o,"clientHeight")}}return{width:t,height:e,maxWidth:i||Yn,maxHeight:s||Yn}}const xn=n=>Math.round(n*10)/10;function Bh(n,t,e,i){const s=si(n),o=ae(s,"margin"),r=$n(s.maxWidth,n,"clientWidth")||Yn,a=$n(s.maxHeight,n,"clientHeight")||Yn,l=zh(n,t,e);let{width:c,height:h}=l;if(s.boxSizing==="content-box"){const d=ae(s,"border","width"),f=ae(s,"padding");c-=f.width+d.width,h-=f.height+d.height}return c=Math.max(0,c-o.width),h=Math.max(0,i?c/i:h-o.height),c=xn(Math.min(c,r,l.maxWidth)),h=xn(Math.min(h,a,l.maxHeight)),c&&!h&&(h=xn(c/2)),(t!==void 0||e!==void 0)&&i&&l.height&&h>l.height&&(h=l.height,c=xn(Math.floor(h*i))),{width:c,height:h}}function As(n,t,e){const i=t||1,s=Math.floor(n.height*i),o=Math.floor(n.width*i);n.height=Math.floor(n.height),n.width=Math.floor(n.width);const r=n.canvas;return r.style&&(e||!r.style.height&&!r.style.width)&&(r.style.height=`${n.height}px`,r.style.width=`${n.width}px`),n.currentDevicePixelRatio!==i||r.height!==s||r.width!==o?(n.currentDevicePixelRatio=i,r.height=s,r.width=o,n.ctx.setTransform(i,0,0,i,0,0),!0):!1}const Nh=function(){let n=!1;try{const t={get passive(){return n=!0,!1}};os()&&(window.addEventListener("test",null,t),window.removeEventListener("test",null,t))}catch{}return n}();function Ls(n,t){const e=Fh(n,t),i=e&&e.match(/^(\d+)(\.\d+)?px$/);return i?+i[1]:void 0}function ie(n,t,e,i){return{x:n.x+e*(t.x-n.x),y:n.y+e*(t.y-n.y)}}function Wh(n,t,e,i){return{x:n.x+e*(t.x-n.x),y:i==="middle"?e<.5?n.y:t.y:i==="after"?e<1?n.y:t.y:e>0?t.y:n.y}}function Hh(n,t,e,i){const s={x:n.cp2x,y:n.cp2y},o={x:t.cp1x,y:t.cp1y},r=ie(n,s,e),a=ie(s,o,e),l=ie(o,t,e),c=ie(r,a,e),h=ie(a,l,e);return ie(c,h,e)}const Vh=function(n,t){return{x(e){return n+n+t-e},setWidth(e){t=e},textAlign(e){return e==="center"?e:e==="right"?"left":"right"},xPlus(e,i){return e-i},leftForLtr(e,i){return e-i}}},Yh=function(){return{x(n){return n},setWidth(n){},textAlign(n){return n},xPlus(n,t){return n+t},leftForLtr(n,t){return n}}};function we(n,t,e){return n?Vh(t,e):Yh()}function kr(n,t){let e,i;(t==="ltr"||t==="rtl")&&(e=n.canvas.style,i=[e.getPropertyValue("direction"),e.getPropertyPriority("direction")],e.setProperty("direction",t,"important"),n.prevTextDirection=i)}function Pr(n,t){t!==void 0&&(delete n.prevTextDirection,n.canvas.style.setProperty("direction",t[0],t[1]))}function Dr(n){return n==="angle"?{between:Je,compare:Hc,normalize:bt}:{between:Rt,compare:(t,e)=>t-e,normalize:t=>t}}function Fs({start:n,end:t,count:e,loop:i,style:s}){return{start:n%e,end:t%e,loop:i&&(t-n+1)%e===0,style:s}}function jh(n,t,e){const{property:i,start:s,end:o}=e,{between:r,normalize:a}=Dr(i),l=t.length;let{start:c,end:h,loop:u}=n,d,f;if(u){for(c+=l,h+=l,d=0,f=l;dl(s,w,b)&&a(s,w)!==0,x=()=>a(o,b)===0||l(o,w,b),P=()=>p||M(),v=()=>!p||x();for(let k=h,D=h;k<=u;++k)y=t[k%r],!y.skip&&(b=c(y[i]),b!==w&&(p=l(b,s,o),m===null&&P()&&(m=a(b,s)===0?k:D),m!==null&&v()&&(g.push(Fs({start:m,end:k,loop:d,count:r,style:f})),m=null),D=k,w=b));return m!==null&&g.push(Fs({start:m,end:u,loop:d,count:r,style:f})),g}function Sr(n,t){const e=[],i=n.segments;for(let s=0;ss&&n[o%t].skip;)o--;return o%=t,{start:s,end:o}}function qh(n,t,e,i){const s=n.length,o=[];let r=t,a=n[t],l;for(l=t+1;l<=e;++l){const c=n[l%s];c.skip||c.stop?a.skip||(i=!1,o.push({start:t%s,end:(l-1)%s,loop:i}),t=r=c.stop?l:null):(r=l,a.skip&&(t=l)),a=c}return r!==null&&o.push({start:t%s,end:r%s,loop:i}),o}function Uh(n,t){const e=n.points,i=n.options.spanGaps,s=e.length;if(!s)return[];const o=!!n._loop,{start:r,end:a}=$h(e,s,o,i);if(i===!0)return Rs(n,[{start:r,end:a,loop:o}],e,t);const l=aa({chart:t,initial:e.initial,numSteps:r,currentStep:Math.min(i-e.start,r)}))}_refresh(){this._request||(this._running=!0,this._request=fr.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(t=Date.now()){let e=0;this._charts.forEach((i,s)=>{if(!i.running||!i.items.length)return;const o=i.items;let r=o.length-1,a=!1,l;for(;r>=0;--r)l=o[r],l._active?(l._total>i.duration&&(i.duration=l._total),l.tick(t),a=!0):(o[r]=o[o.length-1],o.pop());a&&(s.draw(),this._notify(s,i,t,"progress")),o.length||(i.running=!1,this._notify(s,i,t,"complete"),i.initial=!1),e+=o.length}),this._lastDate=t,e===0&&(this._running=!1)}_getAnims(t){const e=this._charts;let i=e.get(t);return i||(i={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,i)),i}listen(t,e,i){this._getAnims(t).listeners[e].push(i)}add(t,e){!e||!e.length||this._getAnims(t).items.push(...e)}has(t){return this._getAnims(t).items.length>0}start(t){const e=this._charts.get(t);e&&(e.running=!0,e.start=Date.now(),e.duration=e.items.reduce((i,s)=>Math.max(i,s._duration),0),this._refresh())}running(t){if(!this._running)return!1;const e=this._charts.get(t);return!(!e||!e.running||!e.items.length)}stop(t){const e=this._charts.get(t);if(!e||!e.items.length)return;const i=e.items;let s=i.length-1;for(;s>=0;--s)i[s].cancel();e.items=[],this._notify(t,e,Date.now(),"complete")}remove(t){return this._charts.delete(t)}}var At=new Gh;const Es="transparent",Qh={boolean(n,t,e){return e>.5?t:n},color(n,t,e){const i=ye(n||Es),s=i.valid&&ye(t||Es);return s&&s.valid?s.mix(i,e).hexString():t},number(n,t,e){return n+(t-n)*e}};class Zh{constructor(t,e,i,s){const o=e[i];s=yn([t.to,s,o,t.from]);const r=yn([t.from,o,s]);this._active=!0,this._fn=t.fn||Qh[t.type||typeof r],this._easing=qe[t.easing]||qe.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=i,this._from=r,this._to=s,this._promises=void 0}active(){return this._active}update(t,e,i){if(this._active){this._notify(!1);const s=this._target[this._prop],o=i-this._start,r=this._duration-o;this._start=i,this._duration=Math.floor(Math.max(r,t.duration)),this._total+=o,this._loop=!!t.loop,this._to=yn([t.to,e,s,t.from]),this._from=yn([t.from,s,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,i=this._duration,s=this._prop,o=this._from,r=this._loop,a=this._to;let l;if(this._active=o!==a&&(r||e1?2-l:l,l=this._easing(Math.min(1,Math.max(0,l))),this._target[s]=this._fn(o,a,l)}wait(){const t=this._promises||(this._promises=[]);return new Promise((e,i)=>{t.push({res:e,rej:i})})}_notify(t){const e=t?"res":"rej",i=this._promises||[];for(let s=0;s{const o=t[s];if(!L(o))return;const r={};for(const a of e)r[a]=o[a];(W(o.properties)&&o.properties||[s]).forEach(a=>{(a===s||!i.has(a))&&i.set(a,r)})})}_animateOptions(t,e){const i=e.options,s=tu(t,i);if(!s)return[];const o=this._createAnimations(s,i);return i.$shared&&Jh(t.options.$animations,i).then(()=>{t.options=i},()=>{}),o}_createAnimations(t,e){const i=this._properties,s=[],o=t.$animations||(t.$animations={}),r=Object.keys(e),a=Date.now();let l;for(l=r.length-1;l>=0;--l){const c=r[l];if(c.charAt(0)==="$")continue;if(c==="options"){s.push(...this._animateOptions(t,e));continue}const h=e[c];let u=o[c];const d=i.get(c);if(u)if(d&&u.active()){u.update(d,h,a);continue}else u.cancel();if(!d||!d.duration){t[c]=h;continue}o[c]=u=new Zh(d,t,c,h),s.push(u)}return s}update(t,e){if(this._properties.size===0){Object.assign(t,e);return}const i=this._createAnimations(t,e);if(i.length)return At.add(this._chart,i),!0}}function Jh(n,t){const e=[],i=Object.keys(t);for(let s=0;s0||!e&&o<0)return s.index}return null}function Hs(n,t){const{chart:e,_cachedMeta:i}=n,s=e._stacks||(e._stacks={}),{iScale:o,vScale:r,index:a}=i,l=o.axis,c=r.axis,h=su(o,r,i),u=t.length;let d;for(let f=0;fe[i].axis===t).shift()}function au(n,t){return Xt(n,{active:!1,dataset:void 0,datasetIndex:t,index:t,mode:"default",type:"dataset"})}function lu(n,t,e){return Xt(n,{active:!1,dataIndex:t,parsed:void 0,raw:void 0,element:e,index:t,mode:"default",type:"data"})}function Se(n,t){const e=n.controller.index,i=n.vScale&&n.vScale.axis;if(i){t=t||n._parsed;for(const s of t){const o=s._stacks;if(!o||o[i]===void 0||o[i][e]===void 0)return;delete o[i][e],o[i]._visualValues!==void 0&&o[i]._visualValues[e]!==void 0&&delete o[i]._visualValues[e]}}}const fi=n=>n==="reset"||n==="none",Vs=(n,t)=>t?n:Object.assign({},n),cu=(n,t,e)=>n&&!t.hidden&&t._stacked&&{keys:Tr(e,!0),values:null};class Et{constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=Ns(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled("filler")&&console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options")}updateIndex(t){this.index!==t&&Se(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,i=this.getDataset(),s=(u,d,f,g)=>u==="x"?d:u==="r"?g:f,o=e.xAxisID=C(i.xAxisID,di(t,"x")),r=e.yAxisID=C(i.yAxisID,di(t,"y")),a=e.rAxisID=C(i.rAxisID,di(t,"r")),l=e.indexAxis,c=e.iAxisID=s(l,o,r,a),h=e.vAxisID=s(l,r,o,a);e.xScale=this.getScaleForId(o),e.yScale=this.getScaleForId(r),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(c),e.vScale=this.getScaleForId(h)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&ks(this._data,this),t._stacked&&Se(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),i=this._data;if(L(e))this._data=iu(e);else if(i!==e){if(i){ks(i,this);const s=this._cachedMeta;Se(s),s._parsed=[]}e&&Object.isExtensible(e)&&$c(e,this),this._syncList=[],this._data=e}}addElements(){const t=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(t.dataset=new this.datasetElementType)}buildOrUpdateElements(t){const e=this._cachedMeta,i=this.getDataset();let s=!1;this._dataCheck();const o=e._stacked;e._stacked=Ns(e.vScale,e),e.stack!==i.stack&&(s=!0,Se(e),e.stack=i.stack),this._resyncElements(t),(s||o!==e._stacked)&&Hs(this,e._parsed)}configure(){const t=this.chart.config,e=t.datasetScopeKeys(this._type),i=t.getOptionScopes(this.getDataset(),e,!0);this.options=t.createResolver(i,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(t,e){const{_cachedMeta:i,_data:s}=this,{iScale:o,_stacked:r}=i,a=o.axis;let l=t===0&&e===s.length?!0:i._sorted,c=t>0&&i._parsed[t-1],h,u,d;if(this._parsing===!1)i._parsed=s,i._sorted=!0,d=s;else{W(s[t])?d=this.parseArrayData(i,s,t,e):L(s[t])?d=this.parseObjectData(i,s,t,e):d=this.parsePrimitiveData(i,s,t,e);const f=()=>u[a]===null||c&&u[a]p||u=0;--d)if(!g()){this.updateRangeFromParsed(c,t,f,l);break}}return c}getAllParsedValues(t){const e=this._cachedMeta._parsed,i=[];let s,o,r;for(s=0,o=e.length;s=0&&tthis.getContext(i,s,e),p=c.resolveNamedOptions(d,f,g,u);return p.$shared&&(p.$shared=l,o[r]=Object.freeze(Vs(p,l))),p}_resolveAnimations(t,e,i){const s=this.chart,o=this._cachedDataOpts,r=`animation-${e}`,a=o[r];if(a)return a;let l;if(s.options.animation!==!1){const h=this.chart.config,u=h.datasetAnimationScopeKeys(this._type,e),d=h.getOptionScopes(this.getDataset(),u);l=h.createResolver(d,this.getContext(t,i,e))}const c=new Or(s,l&&l.animations);return l&&l._cacheable&&(o[r]=Object.freeze(c)),c}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||fi(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const i=this.resolveDataElementOptions(t,e),s=this._sharedOptions,o=this.getSharedOptions(i),r=this.includeOptions(e,o)||o!==s;return this.updateSharedOptions(o,e,i),{sharedOptions:o,includeOptions:r}}updateElement(t,e,i,s){fi(s)?Object.assign(t,i):this._resolveAnimations(e,s).update(t,i)}updateSharedOptions(t,e,i){t&&!fi(e)&&this._resolveAnimations(void 0,e).update(t,i)}_setStyle(t,e,i,s){t.active=s;const o=this.getStyle(e,s);this._resolveAnimations(e,i,s).update(t,{options:!s&&this.getSharedOptions(o)||o})}removeHoverStyle(t,e,i){this._setStyle(t,i,"active",!1)}setHoverStyle(t,e,i){this._setStyle(t,i,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const e=this._data,i=this._cachedMeta.data;for(const[a,l,c]of this._syncList)this[a](l,c);this._syncList=[];const s=i.length,o=e.length,r=Math.min(o,s);r&&this.parse(0,r),o>s?this._insertElements(s,o-s,t):o{for(c.length+=e,a=c.length-1;a>=r;a--)c[a]=c[a-e]};for(l(o),a=t;as-o))}return n._cache.$bar}function uu(n){const t=n.iScale,e=hu(t,n.type);let i=t._length,s,o,r,a;const l=()=>{r===32767||r===-32768||(Qe(a)&&(i=Math.min(i,Math.abs(r-a)||i)),a=r)};for(s=0,o=e.length;s0?s[n-1]:null,a=nMath.abs(a)&&(l=a,c=r),t[e.axis]=c,t._custom={barStart:l,barEnd:c,start:s,end:o,min:r,max:a}}function Ar(n,t,e,i){return W(n)?gu(n,t,e,i):t[e.axis]=e.parse(n,i),t}function Ys(n,t,e,i){const s=n.iScale,o=n.vScale,r=s.getLabels(),a=s===o,l=[];let c,h,u,d;for(c=e,h=e+i;c=e?1:-1)}function mu(n){let t,e,i,s,o;return n.horizontal?(t=n.base>n.x,e="left",i="right"):(t=n.basel.controller.options.grouped),o=i.options.stacked,r=[],a=l=>{const c=l.controller.getParsed(e),h=c&&c[l.vScale.axis];if(F(h)||isNaN(h))return!0};for(const l of s)if(!(e!==void 0&&a(l))&&((o===!1||r.indexOf(l.stack)===-1||o===void 0&&l.stack===void 0)&&r.push(l.stack),l.index===t))break;return r.length||r.push(void 0),r}_getStackCount(t){return this._getStacks(void 0,t).length}_getStackIndex(t,e,i){const s=this._getStacks(t,i),o=e!==void 0?s.indexOf(e):-1;return o===-1?s.length-1:o}_getRuler(){const t=this.options,e=this._cachedMeta,i=e.iScale,s=[];let o,r;for(o=0,r=e.data.length;oJe(w,a,l,!0)?1:Math.max(M,M*e,x,x*e),g=(w,M,x)=>Je(w,a,l,!0)?-1:Math.min(M,M*e,x,x*e),p=f(0,c,u),m=f(Z,h,d),b=g($,c,u),y=g($+Z,h,d);i=(p-b)/2,s=(m-y)/2,o=-(p+b)/2,r=-(m+y)/2}return{ratioX:i,ratioY:s,offsetX:o,offsetY:r}}class _e extends Et{constructor(t,e){super(t,e),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(t,e){const i=this.getDataset().data,s=this._cachedMeta;if(this._parsing===!1)s._parsed=i;else{let o=l=>+i[l];if(L(i[t])){const{key:l="value"}=this._parsing;o=c=>+qt(i[c],l)}let r,a;for(r=t,a=t+e;r0&&!isNaN(t)?H*(Math.abs(t)/e):0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],o=hn(e._parsed[t],i.options.locale);return{label:s[t]||"",value:o}}getMaxBorderWidth(t){let e=0;const i=this.chart;let s,o,r,a,l;if(!t){for(s=0,o=i.data.datasets.length;st!=="spacing",_indexable:t=>t!=="spacing"&&!t.startsWith("borderDash")&&!t.startsWith("hoverBorderDash")}),_(_e,"overrides",{aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i,color:s}}=t.legend.options;return e.labels.map((o,r)=>{const l=t.getDatasetMeta(0).controller.getStyle(r);return{text:o,fillStyle:l.backgroundColor,strokeStyle:l.borderColor,fontColor:s,lineWidth:l.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(r),index:r}})}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}}});class Rn extends Et{initialize(){this.enableOptionSharing=!0,this.supportsDecimation=!0,super.initialize()}update(t){const e=this._cachedMeta,{dataset:i,data:s=[],_dataset:o}=e,r=this.chart._animationsDisabled;let{start:a,count:l}=Xc(e,s,r);this._drawStart=a,this._drawCount=l,Kc(e)&&(a=0,l=s.length),i._chart=this.chart,i._datasetIndex=this.index,i._decimated=!!o._decimated,i.points=s;const c=this.resolveDatasetElementOptions(t);this.options.showLine||(c.borderWidth=0),c.segment=this.options.segment,this.updateElement(i,void 0,{animated:!r,options:c},t),this.updateElements(s,a,l,t)}updateElements(t,e,i,s){const o=s==="reset",{iScale:r,vScale:a,_stacked:l,_dataset:c}=this._cachedMeta,{sharedOptions:h,includeOptions:u}=this._getSharedOptions(e,s),d=r.axis,f=a.axis,{spanGaps:g,segment:p}=this.options,m=Ze(g)?g:Number.POSITIVE_INFINITY,b=this.chart._animationsDisabled||o||s==="none",y=e+i,w=t.length;let M=e>0&&this.getParsed(e-1);for(let x=0;x=y){v.skip=!0;continue}const k=this.getParsed(x),D=F(k[f]),T=v[d]=r.getPixelForValue(k[d],x),S=v[f]=o||D?a.getBasePixel():a.getPixelForValue(l?this.applyStack(a,k,l):k[f],x);v.skip=isNaN(T)||isNaN(S)||D,v.stop=x>0&&Math.abs(k[d]-M[d])>m,p&&(v.parsed=k,v.raw=c.data[x]),u&&(v.options=h||this.resolveDataElementOptions(x,P.active?"active":s)),b||this.updateElement(P,x,v,s),M=k}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,i=e.options&&e.options.borderWidth||0,s=t.data||[];if(!s.length)return i;const o=s[0].size(this.resolveDataElementOptions(0)),r=s[s.length-1].size(this.resolveDataElementOptions(s.length-1));return Math.max(i,o,r)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}}_(Rn,"id","line"),_(Rn,"defaults",{datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1}),_(Rn,"overrides",{scales:{_index_:{type:"category"},_value_:{type:"linear"}}});class In extends Et{constructor(t,e){super(t,e),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],o=hn(e._parsed[t].r,i.options.locale);return{label:s[t]||"",value:o}}parseObjectData(t,e,i,s){return Ph.bind(this)(t,e,i,s)}update(t){const e=this._cachedMeta.data;this._updateRadius(),this.updateElements(e,0,e.length,t)}getMinMax(){const t=this._cachedMeta,e={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY};return t.data.forEach((i,s)=>{const o=this.getParsed(s).r;!isNaN(o)&&this.chart.getDataVisibility(s)&&(oe.max&&(e.max=o))}),e}_updateRadius(){const t=this.chart,e=t.chartArea,i=t.options,s=Math.min(e.right-e.left,e.bottom-e.top),o=Math.max(s/2,0),r=Math.max(i.cutoutPercentage?o/100*i.cutoutPercentage:1,0),a=(o-r)/t.getVisibleDatasetCount();this.outerRadius=o-a*this.index,this.innerRadius=this.outerRadius-a}updateElements(t,e,i,s){const o=s==="reset",r=this.chart,l=r.options.animation,c=this._cachedMeta.rScale,h=c.xCenter,u=c.yCenter,d=c.getIndexAngle(0)-.5*$;let f=d,g;const p=360/this.countVisibleElements();for(g=0;g{!isNaN(this.getParsed(s).r)&&this.chart.getDataVisibility(s)&&e++}),e}_computeAngle(t,e,i){return this.chart.getDataVisibility(t)?xt(this.resolveDataElementOptions(t,e).angle||i):0}}_(In,"id","polarArea"),_(In,"defaults",{dataElementType:"arc",animation:{animateRotate:!0,animateScale:!0},animations:{numbers:{type:"number",properties:["x","y","startAngle","endAngle","innerRadius","outerRadius"]}},indexAxis:"r",startAngle:0}),_(In,"overrides",{aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i,color:s}}=t.legend.options;return e.labels.map((o,r)=>{const l=t.getDatasetMeta(0).controller.getStyle(r);return{text:o,fillStyle:l.backgroundColor,strokeStyle:l.borderColor,fontColor:s,lineWidth:l.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(r),index:r}})}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}},scales:{r:{type:"radialLinear",angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}});class Ri extends _e{}_(Ri,"id","pie"),_(Ri,"defaults",{cutout:0,rotation:0,circumference:360,radius:"100%"});function Jt(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class as{constructor(t){_(this,"options");this.options=t||{}}static override(t){Object.assign(as.prototype,t)}init(){}formats(){return Jt()}parse(){return Jt()}format(){return Jt()}add(){return Jt()}diff(){return Jt()}startOf(){return Jt()}endOf(){return Jt()}}var Lr={_date:as};function wu(n,t,e,i){const{controller:s,data:o,_sorted:r}=n,a=s._cachedMeta.iScale;if(a&&t===a.axis&&t!=="r"&&r&&o.length){const l=a._reversePixels?Yc:oe;if(i){if(s._sharedOptions){const c=o[0],h=typeof c.getRange=="function"&&c.getRange(t);if(h){const u=l(o,t,e-h),d=l(o,t,e+h);return{lo:u.lo,hi:d.hi}}}}else return l(o,t,e)}return{lo:0,hi:o.length-1}}function un(n,t,e,i,s){const o=n.getSortedVisibleDatasetMetas(),r=e[t];for(let a=0,l=o.length;a{l[r](t[e],s)&&(o.push({element:l,datasetIndex:c,index:h}),a=a||l.inRange(t.x,t.y,s))}),i&&!a?[]:o}var Pu={evaluateInteractionItems:un,modes:{index(n,t,e,i){const s=ne(t,n),o=e.axis||"x",r=e.includeInvisible||!1,a=e.intersect?pi(n,s,o,i,r):mi(n,s,o,!1,i,r),l=[];return a.length?(n.getSortedVisibleDatasetMetas().forEach(c=>{const h=a[0].index,u=c.data[h];u&&!u.skip&&l.push({element:u,datasetIndex:c.index,index:h})}),l):[]},dataset(n,t,e,i){const s=ne(t,n),o=e.axis||"xy",r=e.includeInvisible||!1;let a=e.intersect?pi(n,s,o,i,r):mi(n,s,o,!1,i,r);if(a.length>0){const l=a[0].datasetIndex,c=n.getDatasetMeta(l).data;a=[];for(let h=0;he.pos===t)}function Us(n,t){return n.filter(e=>Fr.indexOf(e.pos)===-1&&e.box.axis===t)}function Te(n,t){return n.sort((e,i)=>{const s=t?i:e,o=t?e:i;return s.weight===o.weight?s.index-o.index:s.weight-o.weight})}function Du(n){const t=[];let e,i,s,o,r,a;for(e=0,i=(n||[]).length;ec.box.fullSize),!0),i=Te(Oe(t,"left"),!0),s=Te(Oe(t,"right")),o=Te(Oe(t,"top"),!0),r=Te(Oe(t,"bottom")),a=Us(t,"x"),l=Us(t,"y");return{fullSize:e,leftAndTop:i.concat(o),rightAndBottom:s.concat(l).concat(r).concat(a),chartArea:Oe(t,"chartArea"),vertical:i.concat(s).concat(l),horizontal:o.concat(r).concat(a)}}function Xs(n,t,e,i){return Math.max(n[e],t[e])+Math.max(n[i],t[i])}function Rr(n,t){n.top=Math.max(n.top,t.top),n.left=Math.max(n.left,t.left),n.bottom=Math.max(n.bottom,t.bottom),n.right=Math.max(n.right,t.right)}function Tu(n,t,e,i){const{pos:s,box:o}=e,r=n.maxPadding;if(!L(s)){e.size&&(n[s]-=e.size);const u=i[e.stack]||{size:0,count:1};u.size=Math.max(u.size,e.horizontal?o.height:o.width),e.size=u.size/u.count,n[s]+=e.size}o.getPadding&&Rr(r,o.getPadding());const a=Math.max(0,t.outerWidth-Xs(r,n,"left","right")),l=Math.max(0,t.outerHeight-Xs(r,n,"top","bottom")),c=a!==n.w,h=l!==n.h;return n.w=a,n.h=l,e.horizontal?{same:c,other:h}:{same:h,other:c}}function Au(n){const t=n.maxPadding;function e(i){const s=Math.max(t[i]-n[i],0);return n[i]+=s,s}n.y+=e("top"),n.x+=e("left"),e("right"),e("bottom")}function Lu(n,t){const e=t.maxPadding;function i(s){const o={left:0,top:0,right:0,bottom:0};return s.forEach(r=>{o[r]=Math.max(t[r],e[r])}),o}return i(n?["left","right"]:["top","bottom"])}function We(n,t,e,i){const s=[];let o,r,a,l,c,h;for(o=0,r=n.length,c=0;o{typeof p.beforeLayout=="function"&&p.beforeLayout()});const h=l.reduce((p,m)=>m.box.options&&m.box.options.display===!1?p:p+1,0)||1,u=Object.freeze({outerWidth:t,outerHeight:e,padding:s,availableWidth:o,availableHeight:r,vBoxMaxWidth:o/2/h,hBoxMaxHeight:r/2}),d=Object.assign({},s);Rr(d,ct(i));const f=Object.assign({maxPadding:d,w:o,h:r,x:s.left,y:s.top},s),g=Su(l.concat(c),u);We(a.fullSize,f,u,g),We(l,f,u,g),We(c,f,u,g)&&We(l,f,u,g),Au(f),Ks(a.leftAndTop,f,u,g),f.x+=f.w,f.y+=f.h,Ks(a.rightAndBottom,f,u,g),n.chartArea={left:f.left,top:f.top,right:f.left+f.w,bottom:f.top+f.h,height:f.h,width:f.w},z(a.chartArea,p=>{const m=p.box;Object.assign(m,n.chartArea),m.update(f.w,f.h,{left:0,top:0,right:0,bottom:0})})}};class Ir{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,i){}removeEventListener(t,e,i){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,i,s){return e=Math.max(0,e||t.width),i=i||t.height,{width:e,height:Math.max(0,s?Math.floor(e/s):i)}}isAttached(t){return!0}updateConfig(t){}}class Fu extends Ir{acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}updateConfig(t){t.options.animation=!1}}const En="$chartjs",Ru={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},Gs=n=>n===null||n==="";function Iu(n,t){const e=n.style,i=n.getAttribute("height"),s=n.getAttribute("width");if(n[En]={initial:{height:i,width:s,style:{display:e.display,height:e.height,width:e.width}}},e.display=e.display||"block",e.boxSizing=e.boxSizing||"border-box",Gs(s)){const o=Ls(n,"width");o!==void 0&&(n.width=o)}if(Gs(i))if(n.style.height==="")n.height=n.width/(t||2);else{const o=Ls(n,"height");o!==void 0&&(n.height=o)}return n}const Er=Nh?{passive:!0}:!1;function Eu(n,t,e){n.addEventListener(t,e,Er)}function zu(n,t,e){n.canvas.removeEventListener(t,e,Er)}function Bu(n,t){const e=Ru[n.type]||n.type,{x:i,y:s}=ne(n,t);return{type:e,chart:t,native:n,x:i!==void 0?i:null,y:s!==void 0?s:null}}function qn(n,t){for(const e of n)if(e===t||e.contains(t))return!0}function Nu(n,t,e){const i=n.canvas,s=new MutationObserver(o=>{let r=!1;for(const a of o)r=r||qn(a.addedNodes,i),r=r&&!qn(a.removedNodes,i);r&&e()});return s.observe(document,{childList:!0,subtree:!0}),s}function Wu(n,t,e){const i=n.canvas,s=new MutationObserver(o=>{let r=!1;for(const a of o)r=r||qn(a.removedNodes,i),r=r&&!qn(a.addedNodes,i);r&&e()});return s.observe(document,{childList:!0,subtree:!0}),s}const en=new Map;let Qs=0;function zr(){const n=window.devicePixelRatio;n!==Qs&&(Qs=n,en.forEach((t,e)=>{e.currentDevicePixelRatio!==n&&t()}))}function Hu(n,t){en.size||window.addEventListener("resize",zr),en.set(n,t)}function Vu(n){en.delete(n),en.size||window.removeEventListener("resize",zr)}function Yu(n,t,e){const i=n.canvas,s=i&&rs(i);if(!s)return;const o=gr((a,l)=>{const c=s.clientWidth;e(a,l),c{const l=a[0],c=l.contentRect.width,h=l.contentRect.height;c===0&&h===0||o(c,h)});return r.observe(s),Hu(n,o),r}function bi(n,t,e){e&&e.disconnect(),t==="resize"&&Vu(n)}function ju(n,t,e){const i=n.canvas,s=gr(o=>{n.ctx!==null&&e(Bu(o,n))},n);return Eu(i,t,s),s}class $u extends Ir{acquireContext(t,e){const i=t&&t.getContext&&t.getContext("2d");return i&&i.canvas===t?(Iu(t,e),i):null}releaseContext(t){const e=t.canvas;if(!e[En])return!1;const i=e[En].initial;["height","width"].forEach(o=>{const r=i[o];F(r)?e.removeAttribute(o):e.setAttribute(o,r)});const s=i.style||{};return Object.keys(s).forEach(o=>{e.style[o]=s[o]}),e.width=e.width,delete e[En],!0}addEventListener(t,e,i){this.removeEventListener(t,e);const s=t.$proxies||(t.$proxies={}),r={attach:Nu,detach:Wu,resize:Yu}[e]||ju;s[e]=r(t,e,i)}removeEventListener(t,e){const i=t.$proxies||(t.$proxies={}),s=i[e];if(!s)return;({attach:bi,detach:bi,resize:bi}[e]||zu)(t,e,s),i[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,i,s){return Bh(t,e,i,s)}isAttached(t){const e=rs(t);return!!(e&&e.isConnected)}}function qu(n){return!os()||typeof OffscreenCanvas<"u"&&n instanceof OffscreenCanvas?Fu:$u}class wt{constructor(){_(this,"x");_(this,"y");_(this,"active",!1);_(this,"options");_(this,"$animations")}tooltipPosition(t){const{x:e,y:i}=this.getProps(["x","y"],t);return{x:e,y:i}}hasValue(){return Ze(this.x)&&Ze(this.y)}getProps(t,e){const i=this.$animations;if(!e||!i)return this;const s={};return t.forEach(o=>{s[o]=i[o]&&i[o].active()?i[o]._to:this[o]}),s}}_(wt,"defaults",{}),_(wt,"defaultRoutes");function Uu(n,t){const e=n.options.ticks,i=Xu(n),s=Math.min(e.maxTicksLimit||i,i),o=e.major.enabled?Gu(t):[],r=o.length,a=o[0],l=o[r-1],c=[];if(r>s)return Qu(t,c,o,r/s),c;const h=Ku(o,t,s);if(r>0){let u,d;const f=r>1?Math.round((l-a)/(r-1)):null;for(vn(t,c,h,F(f)?0:a-f,a),u=0,d=r-1;us)return l}return Math.max(s,1)}function Gu(n){const t=[];let e,i;for(e=0,i=n.length;en==="left"?"right":n==="right"?"left":n,Zs=(n,t,e)=>t==="top"||t==="left"?n[t]+e:n[t]-e,Js=(n,t)=>Math.min(t||n,n);function to(n,t){const e=[],i=n.length/t,s=n.length;let o=0;for(;or+a)))return l}function ed(n,t){z(n,e=>{const i=e.gc,s=i.length/2;let o;if(s>t){for(o=0;oi?i:e,i=s&&e>i?e:i,{min:mt(e,mt(i,e)),max:mt(i,mt(e,i))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){N(this.options.beforeUpdate,[this])}update(t,e,i){const{beginAtZero:s,grace:o,ticks:r}=this.options,a=r.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=i=Object.assign({left:0,right:0,top:0,bottom:0},i),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+i.left+i.right:this.height+i.top+i.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=ph(this,o,s),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const l=a=o||i<=1||!this.isHorizontal()){this.labelRotation=s;return}const h=this._getLabelSizes(),u=h.widest.width,d=h.highest.height,f=st(this.chart.width-u,0,this.maxWidth);a=t.offset?this.maxWidth/i:f/(i-1),u+6>a&&(a=f/(i-(t.offset?.5:1)),l=this.maxHeight-Ae(t.grid)-e.padding-eo(t.title,this.chart.options.font),c=Math.sqrt(u*u+d*d),r=Zi(Math.min(Math.asin(st((h.highest.height+6)/a,-1,1)),Math.asin(st(l/c,-1,1))-Math.asin(st(d/c,-1,1)))),r=Math.max(s,Math.min(o,r))),this.labelRotation=r}afterCalculateLabelRotation(){N(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){N(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:i,title:s,grid:o}}=this,r=this._isVisible(),a=this.isHorizontal();if(r){const l=eo(s,e.options.font);if(a?(t.width=this.maxWidth,t.height=Ae(o)+l):(t.height=this.maxHeight,t.width=Ae(o)+l),i.display&&this.ticks.length){const{first:c,last:h,widest:u,highest:d}=this._getLabelSizes(),f=i.padding*2,g=xt(this.labelRotation),p=Math.cos(g),m=Math.sin(g);if(a){const b=i.mirror?0:m*u.width+p*d.height;t.height=Math.min(this.maxHeight,t.height+b+f)}else{const b=i.mirror?0:p*u.width+m*d.height;t.width=Math.min(this.maxWidth,t.width+b+f)}this._calculatePadding(c,h,m,p)}}this._handleMargins(),a?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,i,s){const{ticks:{align:o,padding:r},position:a}=this.options,l=this.labelRotation!==0,c=a!=="top"&&this.axis==="x";if(this.isHorizontal()){const h=this.getPixelForTick(0)-this.left,u=this.right-this.getPixelForTick(this.ticks.length-1);let d=0,f=0;l?c?(d=s*t.width,f=i*e.height):(d=i*t.height,f=s*e.width):o==="start"?f=e.width:o==="end"?d=t.width:o!=="inner"&&(d=t.width/2,f=e.width/2),this.paddingLeft=Math.max((d-h+r)*this.width/(this.width-h),0),this.paddingRight=Math.max((f-u+r)*this.width/(this.width-u),0)}else{let h=e.height/2,u=t.height/2;o==="start"?(h=0,u=t.height):o==="end"&&(h=e.height,u=0),this.paddingTop=h+r,this.paddingBottom=u+r}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){N(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return e==="top"||e==="bottom"||t==="x"}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){this.beforeTickToLabelConversion(),this.generateTickLabels(t);let e,i;for(e=0,i=t.length;e({width:r[D]||0,height:a[D]||0});return{first:k(0),last:k(e-1),widest:k(P),highest:k(v),widths:r,heights:a}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return Vc(this._alignToPixels?Zt(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&ta*s?a/i:l/s:l*s0}_computeGridLineItems(t){const e=this.axis,i=this.chart,s=this.options,{grid:o,position:r,border:a}=s,l=o.offset,c=this.isHorizontal(),u=this.ticks.length+(l?1:0),d=Ae(o),f=[],g=a.setContext(this.getContext()),p=g.display?g.width:0,m=p/2,b=function(V){return Zt(i,V,p)};let y,w,M,x,P,v,k,D,T,S,A,J;if(r==="top")y=b(this.bottom),v=this.bottom-d,D=y-m,S=b(t.top)+m,J=t.bottom;else if(r==="bottom")y=b(this.top),S=t.top,J=b(t.bottom)-m,v=y+m,D=this.top+d;else if(r==="left")y=b(this.right),P=this.right-d,k=y-m,T=b(t.left)+m,A=t.right;else if(r==="right")y=b(this.left),T=t.left,A=b(t.right)-m,P=y+m,k=this.left+d;else if(e==="x"){if(r==="center")y=b((t.top+t.bottom)/2+.5);else if(L(r)){const V=Object.keys(r)[0],K=r[V];y=b(this.chart.scales[V].getPixelForValue(K))}S=t.top,J=t.bottom,v=y+m,D=v+d}else if(e==="y"){if(r==="center")y=b((t.left+t.right)/2);else if(L(r)){const V=Object.keys(r)[0],K=r[V];y=b(this.chart.scales[V].getPixelForValue(K))}P=y-m,k=P-d,T=t.left,A=t.right}const ft=C(s.ticks.maxTicksLimit,u),B=Math.max(1,Math.ceil(u/ft));for(w=0;w0&&(Gt-=Kt/2);break}dn={left:Gt,top:De,width:Kt+ue.width,height:Pe+ue.height,color:B.backdropColor}}m.push({label:M,font:D,textOffset:A,options:{rotation:p,color:K,strokeColor:_t,strokeWidth:nt,textAlign:he,textBaseline:J,translation:[x,P],backdrop:dn}})}return m}_getXAxisLabelAlignment(){const{position:t,ticks:e}=this.options;if(-xt(this.labelRotation))return t==="top"?"left":"right";let s="center";return e.align==="start"?s="left":e.align==="end"?s="right":e.align==="inner"&&(s="inner"),s}_getYAxisLabelAlignment(t){const{position:e,ticks:{crossAlign:i,mirror:s,padding:o}}=this.options,r=this._getLabelSizes(),a=t+o,l=r.widest.width;let c,h;return e==="left"?s?(h=this.right+o,i==="near"?c="left":i==="center"?(c="center",h+=l/2):(c="right",h+=l)):(h=this.right-a,i==="near"?c="right":i==="center"?(c="center",h-=l/2):(c="left",h=this.left)):e==="right"?s?(h=this.left+o,i==="near"?c="right":i==="center"?(c="center",h-=l/2):(c="left",h-=l)):(h=this.left+a,i==="near"?c="left":i==="center"?(c="center",h+=l/2):(c="right",h=this.right)):c="right",{textAlign:c,x:h}}_computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.chart,e=this.options.position;if(e==="left"||e==="right")return{top:0,left:this.left,bottom:t.height,right:this.right};if(e==="top"||e==="bottom")return{top:this.top,left:0,bottom:this.bottom,right:t.width}}drawBackground(){const{ctx:t,options:{backgroundColor:e},left:i,top:s,width:o,height:r}=this;e&&(t.save(),t.fillStyle=e,t.fillRect(i,s,o,r),t.restore())}getLineWidthForValue(t){const e=this.options.grid;if(!this._isVisible()||!e.display)return 0;const s=this.ticks.findIndex(o=>o.value===t);return s>=0?e.setContext(this.getContext(s)).lineWidth:0}drawGrid(t){const e=this.options.grid,i=this.ctx,s=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let o,r;const a=(l,c,h)=>{!h.width||!h.color||(i.save(),i.lineWidth=h.width,i.strokeStyle=h.color,i.setLineDash(h.borderDash||[]),i.lineDashOffset=h.borderDashOffset,i.beginPath(),i.moveTo(l.x,l.y),i.lineTo(c.x,c.y),i.stroke(),i.restore())};if(e.display)for(o=0,r=s.length;o{this.draw(o)}}]:[{z:i,draw:o=>{this.drawBackground(),this.drawGrid(o),this.drawTitle()}},{z:s,draw:()=>{this.drawBorder()}},{z:e,draw:o=>{this.drawLabels(o)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),i=this.axis+"AxisID",s=[];let o,r;for(o=0,r=e.length;o{const i=e.split("."),s=i.pop(),o=[n].concat(i).join("."),r=t[e].split("."),a=r.pop(),l=r.join(".");U.route(o,s,l,a)})}function ld(n){return"id"in n&&"defaults"in n}class cd{constructor(){this.controllers=new Mn(Et,"datasets",!0),this.elements=new Mn(wt,"elements"),this.plugins=new Mn(Object,"plugins"),this.scales=new Mn(ce,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,e,i){[...e].forEach(s=>{const o=i||this._getRegistryForType(s);i||o.isForType(s)||o===this.plugins&&s.id?this._exec(t,o,s):z(s,r=>{const a=i||this._getRegistryForType(r);this._exec(t,a,r)})})}_exec(t,e,i){const s=Qi(t);N(i["before"+s],[],i),e[t](i),N(i["after"+s],[],i)}_getRegistryForType(t){for(let e=0;eo.filter(a=>!r.some(l=>a.plugin.id===l.plugin.id));this._notify(s(e,i),t,"stop"),this._notify(s(i,e),t,"start")}}function ud(n){const t={},e=[],i=Object.keys(Mt.plugins.items);for(let o=0;o1&&no(n[0].toLowerCase());if(i)return i}throw new Error(`Cannot determine type of '${n}' axis. Please provide 'axis' or 'position' option.`)}function io(n,t,e){if(e[t+"AxisID"]===n)return{axis:t}}function yd(n,t){if(t.data&&t.data.datasets){const e=t.data.datasets.filter(i=>i.xAxisID===n||i.yAxisID===n);if(e.length)return io(n,"x",e[0])||io(n,"y",e[0])}return{}}function _d(n,t){const e=le[n.type]||{scales:{}},i=t.scales||{},s=Ii(n.type,t),o=Object.create(null);return Object.keys(i).forEach(r=>{const a=i[r];if(!L(a))return console.error(`Invalid scale configuration for scale: ${r}`);if(a._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${r}`);const l=Ei(r,a,yd(r,n),U.scales[a.type]),c=md(l,s),h=e.scales||{};o[r]=je(Object.create(null),[{axis:l},a,h[l],h[c]])}),n.data.datasets.forEach(r=>{const a=r.type||n.type,l=r.indexAxis||Ii(a,t),h=(le[a]||{}).scales||{};Object.keys(h).forEach(u=>{const d=pd(u,l),f=r[d+"AxisID"]||d;o[f]=o[f]||Object.create(null),je(o[f],[{axis:d},i[f],h[u]])})}),Object.keys(o).forEach(r=>{const a=o[r];je(a,[U.scales[a.type],U.scale])}),o}function Br(n){const t=n.options||(n.options={});t.plugins=C(t.plugins,{}),t.scales=_d(n,t)}function Nr(n){return n=n||{},n.datasets=n.datasets||[],n.labels=n.labels||[],n}function xd(n){return n=n||{},n.data=Nr(n.data),Br(n),n}const so=new Map,Wr=new Set;function kn(n,t){let e=so.get(n);return e||(e=t(),so.set(n,e),Wr.add(e)),e}const Le=(n,t,e)=>{const i=qt(t,e);i!==void 0&&n.add(i)};class wd{constructor(t){this._config=xd(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=Nr(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),Br(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return kn(t,()=>[[`datasets.${t}`,""]])}datasetAnimationScopeKeys(t,e){return kn(`${t}.transition.${e}`,()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]])}datasetElementScopeKeys(t,e){return kn(`${t}-${e}`,()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]])}pluginScopeKeys(t){const e=t.id,i=this.type;return kn(`${i}-plugin-${e}`,()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]])}_cachedScopes(t,e){const i=this._scopeCache;let s=i.get(t);return(!s||e)&&(s=new Map,i.set(t,s)),s}getOptionScopes(t,e,i){const{options:s,type:o}=this,r=this._cachedScopes(t,i),a=r.get(e);if(a)return a;const l=new Set;e.forEach(h=>{t&&(l.add(t),h.forEach(u=>Le(l,t,u))),h.forEach(u=>Le(l,s,u)),h.forEach(u=>Le(l,le[o]||{},u)),h.forEach(u=>Le(l,U,u)),h.forEach(u=>Le(l,Li,u))});const c=Array.from(l);return c.length===0&&c.push(Object.create(null)),Wr.has(e)&&r.set(e,c),c}chartOptionScopes(){const{options:t,type:e}=this;return[t,le[e]||{},U.datasets[e]||{},{type:e},U,Li]}resolveNamedOptions(t,e,i,s=[""]){const o={$shared:!0},{resolver:r,subPrefixes:a}=oo(this._resolverCache,t,s);let l=r;if(Md(r,e)){o.$shared=!1,i=Ut(i)?i():i;const c=this.createResolver(t,i,a);l=Me(r,i,c)}for(const c of e)o[c]=l[c];return o}createResolver(t,e,i=[""],s){const{resolver:o}=oo(this._resolverCache,t,i);return L(e)?Me(o,e,void 0,s):o}}function oo(n,t,e){let i=n.get(t);i||(i=new Map,n.set(t,i));const s=e.join();let o=i.get(s);return o||(o={resolver:ns(t,e),subPrefixes:e.filter(a=>!a.toLowerCase().includes("hover"))},i.set(s,o)),o}const vd=n=>L(n)&&Object.getOwnPropertyNames(n).some(t=>Ut(n[t]));function Md(n,t){const{isScriptable:e,isIndexable:i}=_r(n);for(const s of t){const o=e(s),r=i(s),a=(r||o)&&n[s];if(o&&(Ut(a)||vd(a))||r&&W(a))return!0}return!1}var kd="4.4.1";const Pd=["top","bottom","left","right","chartArea"];function ro(n,t){return n==="top"||n==="bottom"||Pd.indexOf(n)===-1&&t==="x"}function ao(n,t){return function(e,i){return e[n]===i[n]?e[t]-i[t]:e[n]-i[n]}}function lo(n){const t=n.chart,e=t.options.animation;t.notifyPlugins("afterRender"),N(e&&e.onComplete,[n],t)}function Dd(n){const t=n.chart,e=t.options.animation;N(e&&e.onProgress,[n],t)}function Hr(n){return os()&&typeof n=="string"?n=document.getElementById(n):n&&n.length&&(n=n[0]),n&&n.canvas&&(n=n.canvas),n}const zn={},co=n=>{const t=Hr(n);return Object.values(zn).filter(e=>e.canvas===t).pop()};function Cd(n,t,e){const i=Object.keys(n);for(const s of i){const o=+s;if(o>=t){const r=n[s];delete n[s],(e>0||o>t)&&(n[o+e]=r)}}}function Sd(n,t,e,i){return!e||n.type==="mouseout"?null:i?t:n}function Pn(n,t,e){return n.options.clip?n[e]:t[e]}function Od(n,t){const{xScale:e,yScale:i}=n;return e&&i?{left:Pn(e,t,"left"),right:Pn(e,t,"right"),top:Pn(i,t,"top"),bottom:Pn(i,t,"bottom")}:t}class at{static register(...t){Mt.add(...t),ho()}static unregister(...t){Mt.remove(...t),ho()}constructor(t,e){const i=this.config=new wd(e),s=Hr(t),o=co(s);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas with ID '"+o.canvas.id+"' can be reused.");const r=i.createResolver(i.chartOptionScopes(),this.getContext());this.platform=new(i.platform||qu(s)),this.platform.updateConfig(i);const a=this.platform.acquireContext(s,r.aspectRatio),l=a&&a.canvas,c=l&&l.height,h=l&&l.width;if(this.id=Tc(),this.ctx=a,this.canvas=l,this.width=h,this.height=c,this._options=r,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new hd,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=qc(u=>this.update(u),r.resizeDelay||0),this._dataChanges=[],zn[this.id]=this,!a||!l){console.error("Failed to create chart: can't acquire context from the given item");return}At.listen(this,"complete",lo),At.listen(this,"progress",Dd),this._initialize(),this.attached&&this.update()}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:i,height:s,_aspectRatio:o}=this;return F(t)?e&&o?o:s?i/s:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return Mt}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():As(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return Ss(this.canvas,this.ctx),this}stop(){return At.stop(this),this}resize(t,e){At.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const i=this.options,s=this.canvas,o=i.maintainAspectRatio&&this.aspectRatio,r=this.platform.getMaximumSize(s,t,e,o),a=i.devicePixelRatio||this.platform.getDevicePixelRatio(),l=this.width?"resize":"attach";this.width=r.width,this.height=r.height,this._aspectRatio=this.aspectRatio,As(this,a,!0)&&(this.notifyPlugins("resize",{size:r}),N(i.onResize,[this,r],this),this.attached&&this._doResize(l)&&this.render())}ensureScalesHaveIDs(){const e=this.options.scales||{};z(e,(i,s)=>{i.id=s})}buildOrUpdateScales(){const t=this.options,e=t.scales,i=this.scales,s=Object.keys(i).reduce((r,a)=>(r[a]=!1,r),{});let o=[];e&&(o=o.concat(Object.keys(e).map(r=>{const a=e[r],l=Ei(r,a),c=l==="r",h=l==="x";return{options:a,dposition:c?"chartArea":h?"bottom":"left",dtype:c?"radialLinear":h?"category":"linear"}}))),z(o,r=>{const a=r.options,l=a.id,c=Ei(l,a),h=C(a.type,r.dtype);(a.position===void 0||ro(a.position,c)!==ro(r.dposition))&&(a.position=r.dposition),s[l]=!0;let u=null;if(l in i&&i[l].type===h)u=i[l];else{const d=Mt.getScale(h);u=new d({id:l,type:h,ctx:this.ctx,chart:this}),i[u.id]=u}u.init(a,t)}),z(s,(r,a)=>{r||delete i[a]}),z(i,r=>{Ht.configure(this,r,r.options),Ht.addBox(this,r)})}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,i=t.length;if(t.sort((s,o)=>s.index-o.index),i>e){for(let s=e;se.length&&delete this._stacks,t.forEach((i,s)=>{e.filter(o=>o===i._dataset).length===0&&this._destroyDatasetMeta(s)})}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let i,s;for(this._removeUnreferencedMetasets(),i=0,s=e.length;i{this.getDatasetMeta(e).controller.reset()},this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const i=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),s=this._animationsDisabled=!i.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0})===!1)return;const o=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let r=0;for(let c=0,h=this.data.datasets.length;c{c.reset()}),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(ao("z","_idx"));const{_active:a,_lastEvent:l}=this;l?this._eventHandler(l,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){z(this.scales,t=>{Ht.removeBox(this,t)}),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),i=new Set(t.events);(!xs(e,i)||!!this._responsiveListeners!==t.responsive)&&(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:i,start:s,count:o}of e){const r=i==="_removeElements"?-o:o;Cd(t,s,r)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,i=o=>new Set(t.filter(r=>r[0]===o).map((r,a)=>a+","+r.splice(1).join(","))),s=i(0);for(let o=1;oo.split(",")).map(o=>({method:o[1],start:+o[2],count:+o[3]}))}_updateLayout(t){if(this.notifyPlugins("beforeLayout",{cancelable:!0})===!1)return;Ht.update(this,this.width,this.height,t);const e=this.chartArea,i=e.width<=0||e.height<=0;this._layers=[],z(this.boxes,s=>{i&&s.position==="chartArea"||(s.configure&&s.configure(),this._layers.push(...s._layers()))},this),this._layers.forEach((s,o)=>{s._idx=o}),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})!==!1){for(let e=0,i=this.data.datasets.length;e=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,i=t._clip,s=!i.disabled,o=Od(t,this.chartArea),r={meta:t,index:t.index,cancelable:!0};this.notifyPlugins("beforeDatasetDraw",r)!==!1&&(s&&ni(e,{left:i.left===!1?0:o.left-i.left,right:i.right===!1?this.width:o.right+i.right,top:i.top===!1?0:o.top-i.top,bottom:i.bottom===!1?this.height:o.bottom+i.bottom}),t.controller.draw(),s&&ii(e),r.cancelable=!1,this.notifyPlugins("afterDatasetDraw",r))}isPointInArea(t){return It(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,i,s){const o=Pu.modes[e];return typeof o=="function"?o(this,t,i,s):[]}getDatasetMeta(t){const e=this.data.datasets[t],i=this._metasets;let s=i.filter(o=>o&&o._dataset===e).pop();return s||(s={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},i.push(s)),s}getContext(){return this.$context||(this.$context=Xt(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const i=this.getDatasetMeta(t);return typeof i.hidden=="boolean"?!i.hidden:!e.hidden}setDatasetVisibility(t,e){const i=this.getDatasetMeta(t);i.hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,i){const s=i?"show":"hide",o=this.getDatasetMeta(t),r=o.controller._resolveAnimations(void 0,s);Qe(e)?(o.data[e].hidden=!i,this.update()):(this.setDatasetVisibility(t,i),r.update(o,{visible:i}),this.update(a=>a.datasetIndex===t?s:void 0))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),At.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,o,r),t[o]=r},s=(o,r,a)=>{o.offsetX=r,o.offsetY=a,this._eventHandler(o)};z(this.options.events,o=>i(o,s))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,i=(l,c)=>{e.addEventListener(this,l,c),t[l]=c},s=(l,c)=>{t[l]&&(e.removeEventListener(this,l,c),delete t[l])},o=(l,c)=>{this.canvas&&this.resize(l,c)};let r;const a=()=>{s("attach",a),this.attached=!0,this.resize(),i("resize",o),i("detach",r)};r=()=>{this.attached=!1,s("resize",o),this._stop(),this._resize(0,0),i("attach",a)},e.isAttached(this.canvas)?a():r()}unbindEvents(){z(this._listeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._listeners={},z(this._responsiveListeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._responsiveListeners=void 0}updateHoverStyle(t,e,i){const s=i?"set":"remove";let o,r,a,l;for(e==="dataset"&&(o=this.getDatasetMeta(t[0].datasetIndex),o.controller["_"+s+"DatasetHoverStyle"]()),a=0,l=t.length;a{const a=this.getDatasetMeta(o);if(!a)throw new Error("No dataset found at index "+o);return{datasetIndex:o,element:a.data[r],index:r}});!Hn(i,e)&&(this._active=i,this._lastEvent=null,this._updateHoverStyles(i,e))}notifyPlugins(t,e,i){return this._plugins.notify(this,t,e,i)}isPluginEnabled(t){return this._plugins._cache.filter(e=>e.plugin.id===t).length===1}_updateHoverStyles(t,e,i){const s=this.options.hover,o=(l,c)=>l.filter(h=>!c.some(u=>h.datasetIndex===u.datasetIndex&&h.index===u.index)),r=o(e,t),a=i?t:o(t,e);r.length&&this.updateHoverStyle(r,s.mode,!1),a.length&&s.mode&&this.updateHoverStyle(a,s.mode,!0)}_eventHandler(t,e){const i={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},s=r=>(r.options.events||this.options.events).includes(t.native.type);if(this.notifyPlugins("beforeEvent",i,s)===!1)return;const o=this._handleEvent(t,e,i.inChartArea);return i.cancelable=!1,this.notifyPlugins("afterEvent",i,s),(o||i.changed)&&this.render(),this}_handleEvent(t,e,i){const{_active:s=[],options:o}=this,r=e,a=this._getActiveElements(t,s,i,r),l=Ec(t),c=Sd(t,this._lastEvent,i,l);i&&(this._lastEvent=null,N(o.onHover,[t,a,this],this),l&&N(o.onClick,[t,a,this],this));const h=!Hn(a,s);return(h||e)&&(this._active=a,this._updateHoverStyles(a,s,e)),this._lastEvent=c,h}_getActiveElements(t,e,i,s){if(t.type==="mouseout")return[];if(!i)return e;const o=this.options.hover;return this.getElementsAtEventForMode(t,o.mode,o,s)}}_(at,"defaults",U),_(at,"instances",zn),_(at,"overrides",le),_(at,"registry",Mt),_(at,"version",kd),_(at,"getChart",co);function ho(){return z(at.instances,n=>n._plugins.invalidate())}function Td(n,t,e){const{startAngle:i,pixelMargin:s,x:o,y:r,outerRadius:a,innerRadius:l}=t;let c=s/a;n.beginPath(),n.arc(o,r,a,i-c,e+c),l>s?(c=s/l,n.arc(o,r,l,e+c,i-c,!0)):n.arc(o,r,s,e+Z,i-Z),n.closePath(),n.clip()}function Ad(n){return es(n,["outerStart","outerEnd","innerStart","innerEnd"])}function Ld(n,t,e,i){const s=Ad(n.options.borderRadius),o=(e-t)/2,r=Math.min(o,i*t/2),a=l=>{const c=(e-Math.min(o,l))*i/2;return st(l,0,Math.min(o,c))};return{outerStart:a(s.outerStart),outerEnd:a(s.outerEnd),innerStart:st(s.innerStart,0,r),innerEnd:st(s.innerEnd,0,r)}}function fe(n,t,e,i){return{x:e+n*Math.cos(t),y:i+n*Math.sin(t)}}function Un(n,t,e,i,s,o){const{x:r,y:a,startAngle:l,pixelMargin:c,innerRadius:h}=t,u=Math.max(t.outerRadius+i+e-c,0),d=h>0?h+i+e+c:0;let f=0;const g=s-l;if(i){const B=h>0?h-i:0,V=u>0?u-i:0,K=(B+V)/2,_t=K!==0?g*K/(K+i):g;f=(g-_t)/2}const p=Math.max(.001,g*u-e/$)/u,m=(g-p)/2,b=l+m+f,y=s-m-f,{outerStart:w,outerEnd:M,innerStart:x,innerEnd:P}=Ld(t,d,u,y-b),v=u-w,k=u-M,D=b+w/v,T=y-M/k,S=d+x,A=d+P,J=b+x/S,ft=y-P/A;if(n.beginPath(),o){const B=(D+T)/2;if(n.arc(r,a,u,D,B),n.arc(r,a,u,B,T),M>0){const nt=fe(k,T,r,a);n.arc(nt.x,nt.y,M,T,y+Z)}const V=fe(A,y,r,a);if(n.lineTo(V.x,V.y),P>0){const nt=fe(A,ft,r,a);n.arc(nt.x,nt.y,P,y+Z,ft+Math.PI)}const K=(y-P/d+(b+x/d))/2;if(n.arc(r,a,d,y-P/d,K,!0),n.arc(r,a,d,K,b+x/d,!0),x>0){const nt=fe(S,J,r,a);n.arc(nt.x,nt.y,x,J+Math.PI,b-Z)}const _t=fe(v,b,r,a);if(n.lineTo(_t.x,_t.y),w>0){const nt=fe(v,D,r,a);n.arc(nt.x,nt.y,w,b-Z,D)}}else{n.moveTo(r,a);const B=Math.cos(D)*u+r,V=Math.sin(D)*u+a;n.lineTo(B,V);const K=Math.cos(T)*u+r,_t=Math.sin(T)*u+a;n.lineTo(K,_t)}n.closePath()}function Fd(n,t,e,i,s){const{fullCircles:o,startAngle:r,circumference:a}=t;let l=t.endAngle;if(o){Un(n,t,e,i,l,s);for(let c=0;c=H||Je(r,l,c),m=Rt(a,h+f,u+f);return p&&m}getCenterPoint(e){const{x:i,y:s,startAngle:o,endAngle:r,innerRadius:a,outerRadius:l}=this.getProps(["x","y","startAngle","endAngle","innerRadius","outerRadius"],e),{offset:c,spacing:h}=this.options,u=(o+r)/2,d=(a+l+h+c)/2;return{x:i+Math.cos(u)*d,y:s+Math.sin(u)*d}}tooltipPosition(e){return this.getCenterPoint(e)}draw(e){const{options:i,circumference:s}=this,o=(i.offset||0)/4,r=(i.spacing||0)/2,a=i.circular;if(this.pixelMargin=i.borderAlign==="inner"?.33:0,this.fullCircles=s>H?Math.floor(s/H):0,s===0||this.innerRadius<0||this.outerRadius<0)return;e.save();const l=(this.startAngle+this.endAngle)/2;e.translate(Math.cos(l)*o,Math.sin(l)*o);const c=1-Math.sin(Math.min($,s||0)),h=o*c;e.fillStyle=i.backgroundColor,e.strokeStyle=i.borderColor,Fd(e,this,h,r,a),Rd(e,this,h,r,a),e.restore()}}_(He,"id","arc"),_(He,"defaults",{borderAlign:"center",borderColor:"#fff",borderDash:[],borderDashOffset:0,borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0,circular:!0}),_(He,"defaultRoutes",{backgroundColor:"backgroundColor"}),_(He,"descriptors",{_scriptable:!0,_indexable:e=>e!=="borderDash"});function Vr(n,t,e=t){n.lineCap=C(e.borderCapStyle,t.borderCapStyle),n.setLineDash(C(e.borderDash,t.borderDash)),n.lineDashOffset=C(e.borderDashOffset,t.borderDashOffset),n.lineJoin=C(e.borderJoinStyle,t.borderJoinStyle),n.lineWidth=C(e.borderWidth,t.borderWidth),n.strokeStyle=C(e.borderColor,t.borderColor)}function Id(n,t,e){n.lineTo(e.x,e.y)}function Ed(n){return n.stepped?rh:n.tension||n.cubicInterpolationMode==="monotone"?ah:Id}function Yr(n,t,e={}){const i=n.length,{start:s=0,end:o=i-1}=e,{start:r,end:a}=t,l=Math.max(s,r),c=Math.min(o,a),h=sa&&o>a;return{count:i,start:l,loop:t.loop,ilen:c(r+(c?a-M:M))%o,w=()=>{p!==m&&(n.lineTo(h,m),n.lineTo(h,p),n.lineTo(h,b))};for(l&&(f=s[y(0)],n.moveTo(f.x,f.y)),d=0;d<=a;++d){if(f=s[y(d)],f.skip)continue;const M=f.x,x=f.y,P=M|0;P===g?(xm&&(m=x),h=(u*h+M)/++u):(w(),n.lineTo(M,x),g=P,u=0,p=m=x),b=x}w()}function zi(n){const t=n.options,e=t.borderDash&&t.borderDash.length;return!n._decimated&&!n._loop&&!t.tension&&t.cubicInterpolationMode!=="monotone"&&!t.stepped&&!e?Bd:zd}function Nd(n){return n.stepped?Wh:n.tension||n.cubicInterpolationMode==="monotone"?Hh:ie}function Wd(n,t,e,i){let s=t._path;s||(s=t._path=new Path2D,t.path(s,e,i)&&s.closePath()),Vr(n,t.options),n.stroke(s)}function Hd(n,t,e,i){const{segments:s,options:o}=t,r=zi(t);for(const a of s)Vr(n,o,a.style),n.beginPath(),r(n,t,a,{start:e,end:e+i-1})&&n.closePath(),n.stroke()}const Vd=typeof Path2D=="function";function Yd(n,t,e,i){Vd&&!t.options.segment?Wd(n,t,e,i):Hd(n,t,e,i)}class Vt extends wt{constructor(t){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,t&&Object.assign(this,t)}updateControlPoints(t,e){const i=this.options;if((i.tension||i.cubicInterpolationMode==="monotone")&&!i.stepped&&!this._pointsUpdated){const s=i.spanGaps?this._loop:this._fullLoop;Lh(this._points,i,t,s,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=Uh(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,i=t.length;return i&&e[t[i-1].end]}interpolate(t,e){const i=this.options,s=t[e],o=this.points,r=Sr(this,{property:e,start:s,end:s});if(!r.length)return;const a=[],l=Nd(i);let c,h;for(c=0,h=r.length;ct!=="borderDash"&&t!=="fill"});function uo(n,t,e,i){const s=n.options,{[e]:o}=n.getProps([e],i);return Math.abs(t-o)n.replace("rgb(","rgba(").replace(")",", 0.5)"));function $r(n){return Bi[n%Bi.length]}function qr(n){return fo[n%fo.length]}function Kd(n,t){return n.borderColor=$r(t),n.backgroundColor=qr(t),++t}function Gd(n,t){return n.backgroundColor=n.data.map(()=>$r(t++)),t}function Qd(n,t){return n.backgroundColor=n.data.map(()=>qr(t++)),t}function Zd(n){let t=0;return(e,i)=>{const s=n.getDatasetMeta(i).controller;s instanceof _e?t=Gd(e,t):s instanceof In?t=Qd(e,t):s&&(t=Kd(e,t))}}function go(n){let t;for(t in n)if(n[t].borderColor||n[t].backgroundColor)return!0;return!1}function Jd(n){return n&&(n.borderColor||n.backgroundColor)}var tf={id:"colors",defaults:{enabled:!0,forceOverride:!1},beforeLayout(n,t,e){if(!e.enabled)return;const{data:{datasets:i},options:s}=n.config,{elements:o}=s;if(!e.forceOverride&&(go(i)||Jd(s)||o&&go(o)))return;const r=Zd(n);i.forEach(r)}};function ef(n,t,e){const i=n.segments,s=n.points,o=t.points,r=[];for(const a of i){let{start:l,end:c}=a;c=ls(l,c,s);const h=Ni(e,s[l],s[c],a.loop);if(!t.segments){r.push({source:a,target:h,start:s[l],end:s[c]});continue}const u=Sr(t,h);for(const d of u){const f=Ni(e,o[d.start],o[d.end],d.loop),g=Cr(a,s,f);for(const p of g)r.push({source:p,target:d,start:{[e]:po(h,f,"start",Math.max)},end:{[e]:po(h,f,"end",Math.min)}})}}return r}function Ni(n,t,e,i){if(i)return;let s=t[n],o=e[n];return n==="angle"&&(s=bt(s),o=bt(o)),{property:n,start:s,end:o}}function nf(n,t){const{x:e=null,y:i=null}=n||{},s=t.points,o=[];return t.segments.forEach(({start:r,end:a})=>{a=ls(r,a,s);const l=s[r],c=s[a];i!==null?(o.push({x:l.x,y:i}),o.push({x:c.x,y:i})):e!==null&&(o.push({x:e,y:l.y}),o.push({x:e,y:c.y}))}),o}function ls(n,t,e){for(;t>n;t--){const i=e[t];if(!isNaN(i.x)&&!isNaN(i.y))break}return t}function po(n,t,e,i){return n&&t?i(n[e],t[e]):n?n[e]:t?t[e]:0}function Ur(n,t){let e=[],i=!1;return W(n)?(i=!0,e=n):e=nf(n,t),e.length?new Vt({points:e,options:{tension:0},_loop:i,_fullLoop:i}):null}function mo(n){return n&&n.fill!==!1}function sf(n,t,e){let s=n[t].fill;const o=[t];let r;if(!e)return s;for(;s!==!1&&o.indexOf(s)===-1;){if(!q(s))return s;if(r=n[s],!r)return!1;if(r.visible)return s;o.push(s),s=r.fill}return!1}function of(n,t,e){const i=cf(n);if(L(i))return isNaN(i.value)?!1:i;let s=parseFloat(i);return q(s)&&Math.floor(s)===s?rf(i[0],t,s,e):["origin","start","end","stack","shape"].indexOf(i)>=0&&i}function rf(n,t,e,i){return(n==="-"||n==="+")&&(e=t+e),e===t||e<0||e>=i?!1:e}function af(n,t){let e=null;return n==="start"?e=t.bottom:n==="end"?e=t.top:L(n)?e=t.getPixelForValue(n.value):t.getBasePixel&&(e=t.getBasePixel()),e}function lf(n,t,e){let i;return n==="start"?i=e:n==="end"?i=t.options.reverse?t.min:t.max:L(n)?i=n.value:i=t.getBaseValue(),i}function cf(n){const t=n.options,e=t.fill;let i=C(e&&e.target,e);return i===void 0&&(i=!!t.backgroundColor),i===!1||i===null?!1:i===!0?"origin":i}function hf(n){const{scale:t,index:e,line:i}=n,s=[],o=i.segments,r=i.points,a=uf(t,e);a.push(Ur({x:null,y:t.bottom},i));for(let l=0;l=0;--r){const a=s[r].$filler;a&&(a.line.updateControlPoints(o,a.axis),i&&a.fill&&xi(n.ctx,a,o))}},beforeDatasetsDraw(n,t,e){if(e.drawTime!=="beforeDatasetsDraw")return;const i=n.getSortedVisibleDatasetMetas();for(let s=i.length-1;s>=0;--s){const o=i[s].$filler;mo(o)&&xi(n.ctx,o,n.chartArea)}},beforeDatasetDraw(n,t,e){const i=t.meta.$filler;!mo(i)||e.drawTime!=="beforeDatasetDraw"||xi(n.ctx,i,n.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const xo=(n,t)=>{let{boxHeight:e=t,boxWidth:i=t}=n;return n.usePointStyle&&(e=Math.min(e,t),i=n.pointStyleWidth||Math.min(i,t)),{boxWidth:i,boxHeight:e,itemHeight:Math.max(t,e)}},vf=(n,t)=>n!==null&&t!==null&&n.datasetIndex===t.datasetIndex&&n.index===t.index;class wo extends wt{constructor(t){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this.legendItems=void 0,this.columnSizes=void 0,this.lineWidths=void 0,this.maxHeight=void 0,this.maxWidth=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.height=void 0,this.width=void 0,this._margins=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e,i){this.maxWidth=t,this.maxHeight=e,this._margins=i,this.setDimensions(),this.buildLabels(),this.fit()}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=this._margins.left,this.right=this.width):(this.height=this.maxHeight,this.top=this._margins.top,this.bottom=this.height)}buildLabels(){const t=this.options.labels||{};let e=N(t.generateLabels,[this.chart],this)||[];t.filter&&(e=e.filter(i=>t.filter(i,this.chart.data))),t.sort&&(e=e.sort((i,s)=>t.sort(i,s,this.chart.data))),this.options.reverse&&e.reverse(),this.legendItems=e}fit(){const{options:t,ctx:e}=this;if(!t.display){this.width=this.height=0;return}const i=t.labels,s=tt(i.font),o=s.size,r=this._computeTitleHeight(),{boxWidth:a,itemHeight:l}=xo(i,o);let c,h;e.font=s.string,this.isHorizontal()?(c=this.maxWidth,h=this._fitRows(r,o,a,l)+10):(h=this.maxHeight,c=this._fitCols(r,s,a,l)+10),this.width=Math.min(c,t.maxWidth||this.maxWidth),this.height=Math.min(h,t.maxHeight||this.maxHeight)}_fitRows(t,e,i,s){const{ctx:o,maxWidth:r,options:{labels:{padding:a}}}=this,l=this.legendHitBoxes=[],c=this.lineWidths=[0],h=s+a;let u=t;o.textAlign="left",o.textBaseline="middle";let d=-1,f=-h;return this.legendItems.forEach((g,p)=>{const m=i+e/2+o.measureText(g.text).width;(p===0||c[c.length-1]+m+2*a>r)&&(u+=h,c[c.length-(p>0?0:1)]=0,f+=h,d++),l[p]={left:0,top:f,row:d,width:m,height:s},c[c.length-1]+=m+a}),u}_fitCols(t,e,i,s){const{ctx:o,maxHeight:r,options:{labels:{padding:a}}}=this,l=this.legendHitBoxes=[],c=this.columnSizes=[],h=r-t;let u=a,d=0,f=0,g=0,p=0;return this.legendItems.forEach((m,b)=>{const{itemWidth:y,itemHeight:w}=Mf(i,e,o,m,s);b>0&&f+w+2*a>h&&(u+=d+a,c.push({width:d,height:f}),g+=d+a,p++,d=f=0),l[b]={left:g,top:f,col:p,width:y,height:w},d=Math.max(d,y),f+=w+a}),u+=d,c.push({width:d,height:f}),u}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:e,options:{align:i,labels:{padding:s},rtl:o}}=this,r=we(o,this.left,this.width);if(this.isHorizontal()){let a=0,l=gt(i,this.left+s,this.right-this.lineWidths[a]);for(const c of e)a!==c.row&&(a=c.row,l=gt(i,this.left+s,this.right-this.lineWidths[a])),c.top+=this.top+t+s,c.left=r.leftForLtr(r.x(l),c.width),l+=c.width+s}else{let a=0,l=gt(i,this.top+t+s,this.bottom-this.columnSizes[a].height);for(const c of e)c.col!==a&&(a=c.col,l=gt(i,this.top+t+s,this.bottom-this.columnSizes[a].height)),c.top=l,c.left+=this.left+s,c.left=r.leftForLtr(r.x(c.left),c.width),l+=c.height+s}}isHorizontal(){return this.options.position==="top"||this.options.position==="bottom"}draw(){if(this.options.display){const t=this.ctx;ni(t,this),this._draw(),ii(t)}}_draw(){const{options:t,columnSizes:e,lineWidths:i,ctx:s}=this,{align:o,labels:r}=t,a=U.color,l=we(t.rtl,this.left,this.width),c=tt(r.font),{padding:h}=r,u=c.size,d=u/2;let f;this.drawTitle(),s.textAlign=l.textAlign("left"),s.textBaseline="middle",s.lineWidth=.5,s.font=c.string;const{boxWidth:g,boxHeight:p,itemHeight:m}=xo(r,u),b=function(P,v,k){if(isNaN(g)||g<=0||isNaN(p)||p<0)return;s.save();const D=C(k.lineWidth,1);if(s.fillStyle=C(k.fillStyle,a),s.lineCap=C(k.lineCap,"butt"),s.lineDashOffset=C(k.lineDashOffset,0),s.lineJoin=C(k.lineJoin,"miter"),s.lineWidth=D,s.strokeStyle=C(k.strokeStyle,a),s.setLineDash(C(k.lineDash,[])),r.usePointStyle){const T={radius:p*Math.SQRT2/2,pointStyle:k.pointStyle,rotation:k.rotation,borderWidth:D},S=l.xPlus(P,g/2),A=v+d;br(s,T,S,A,r.pointStyleWidth&&g)}else{const T=v+Math.max((u-p)/2,0),S=l.leftForLtr(P,g),A=re(k.borderRadius);s.beginPath(),Object.values(A).some(J=>J!==0)?tn(s,{x:S,y:T,w:g,h:p,radius:A}):s.rect(S,T,g,p),s.fill(),D!==0&&s.stroke()}s.restore()},y=function(P,v,k){ve(s,k.text,P,v+m/2,c,{strikethrough:k.hidden,textAlign:l.textAlign(k.textAlign)})},w=this.isHorizontal(),M=this._computeTitleHeight();w?f={x:gt(o,this.left+h,this.right-i[0]),y:this.top+h+M,line:0}:f={x:this.left+h,y:gt(o,this.top+M+h,this.bottom-e[0].height),line:0},kr(this.ctx,t.textDirection);const x=m+h;this.legendItems.forEach((P,v)=>{s.strokeStyle=P.fontColor,s.fillStyle=P.fontColor;const k=s.measureText(P.text).width,D=l.textAlign(P.textAlign||(P.textAlign=r.textAlign)),T=g+d+k;let S=f.x,A=f.y;l.setWidth(this.width),w?v>0&&S+T+h>this.right&&(A=f.y+=x,f.line++,S=f.x=gt(o,this.left+h,this.right-i[f.line])):v>0&&A+x>this.bottom&&(S=f.x=S+e[f.line].width+h,f.line++,A=f.y=gt(o,this.top+M+h,this.bottom-e[f.line].height));const J=l.x(S);if(b(J,A,P),S=Uc(D,S+g+d,w?S+T:this.right,t.rtl),y(l.x(S),A,P),w)f.x+=T+h;else if(typeof P.text!="string"){const ft=c.lineHeight;f.y+=Kr(P,ft)+h}else f.y+=x}),Pr(this.ctx,t.textDirection)}drawTitle(){const t=this.options,e=t.title,i=tt(e.font),s=ct(e.padding);if(!e.display)return;const o=we(t.rtl,this.left,this.width),r=this.ctx,a=e.position,l=i.size/2,c=s.top+l;let h,u=this.left,d=this.width;if(this.isHorizontal())d=Math.max(...this.lineWidths),h=this.top+c,u=gt(t.align,u,this.right-d);else{const g=this.columnSizes.reduce((p,m)=>Math.max(p,m.height),0);h=c+gt(t.align,this.top,this.bottom-g-t.labels.padding-this._computeTitleHeight())}const f=gt(a,u,u+d);r.textAlign=o.textAlign(pr(a)),r.textBaseline="middle",r.strokeStyle=e.color,r.fillStyle=e.color,r.font=i.string,ve(r,e.text,f,h,i)}_computeTitleHeight(){const t=this.options.title,e=tt(t.font),i=ct(t.padding);return t.display?e.lineHeight+i.height:0}_getLegendItemAt(t,e){let i,s,o;if(Rt(t,this.left,this.right)&&Rt(e,this.top,this.bottom)){for(o=this.legendHitBoxes,i=0;io.length>r.length?o:r)),t+e.size/2+i.measureText(s).width}function Pf(n,t,e){let i=n;return typeof t.text!="string"&&(i=Kr(t,e)),i}function Kr(n,t){const e=n.text?n.text.length:0;return t*e}function Df(n,t){return!!((n==="mousemove"||n==="mouseout")&&(t.onHover||t.onLeave)||t.onClick&&(n==="click"||n==="mouseup"))}var Cf={id:"legend",_element:wo,start(n,t,e){const i=n.legend=new wo({ctx:n.ctx,options:e,chart:n});Ht.configure(n,i,e),Ht.addBox(n,i)},stop(n){Ht.removeBox(n,n.legend),delete n.legend},beforeUpdate(n,t,e){const i=n.legend;Ht.configure(n,i,e),i.options=e},afterUpdate(n){const t=n.legend;t.buildLabels(),t.adjustHitBoxes()},afterEvent(n,t){t.replay||n.legend.handleEvent(t.event)},defaults:{display:!0,position:"top",align:"center",fullSize:!0,reverse:!1,weight:1e3,onClick(n,t,e){const i=t.datasetIndex,s=e.chart;s.isDatasetVisible(i)?(s.hide(i),t.hidden=!0):(s.show(i),t.hidden=!1)},onHover:null,onLeave:null,labels:{color:n=>n.chart.options.color,boxWidth:40,padding:10,generateLabels(n){const t=n.data.datasets,{labels:{usePointStyle:e,pointStyle:i,textAlign:s,color:o,useBorderRadius:r,borderRadius:a}}=n.legend.options;return n._getSortedDatasetMetas().map(l=>{const c=l.controller.getStyle(e?0:void 0),h=ct(c.borderWidth);return{text:t[l.index].label,fillStyle:c.backgroundColor,fontColor:o,hidden:!l.visible,lineCap:c.borderCapStyle,lineDash:c.borderDash,lineDashOffset:c.borderDashOffset,lineJoin:c.borderJoinStyle,lineWidth:(h.width+h.height)/4,strokeStyle:c.borderColor,pointStyle:i||c.pointStyle,rotation:c.rotation,textAlign:s||c.textAlign,borderRadius:r&&(a||c.borderRadius),datasetIndex:l.index}},this)}},title:{color:n=>n.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:n=>!n.startsWith("on"),labels:{_scriptable:n=>!["generateLabels","filter","sort"].includes(n)}}};const Ve={average(n){if(!n.length)return!1;let t,e,i=0,s=0,o=0;for(t=0,e=n.length;t-1?n.split(` +`):n}function Sf(n,t){const{element:e,datasetIndex:i,index:s}=t,o=n.getDatasetMeta(i).controller,{label:r,value:a}=o.getLabelAndValue(s);return{chart:n,label:r,parsed:o.getParsed(s),raw:n.data.datasets[i].data[s],formattedValue:a,dataset:o.getDataset(),dataIndex:s,datasetIndex:i,element:e}}function vo(n,t){const e=n.chart.ctx,{body:i,footer:s,title:o}=n,{boxWidth:r,boxHeight:a}=t,l=tt(t.bodyFont),c=tt(t.titleFont),h=tt(t.footerFont),u=o.length,d=s.length,f=i.length,g=ct(t.padding);let p=g.height,m=0,b=i.reduce((M,x)=>M+x.before.length+x.lines.length+x.after.length,0);if(b+=n.beforeBody.length+n.afterBody.length,u&&(p+=u*c.lineHeight+(u-1)*t.titleSpacing+t.titleMarginBottom),b){const M=t.displayColors?Math.max(a,l.lineHeight):l.lineHeight;p+=f*M+(b-f)*l.lineHeight+(b-1)*t.bodySpacing}d&&(p+=t.footerMarginTop+d*h.lineHeight+(d-1)*t.footerSpacing);let y=0;const w=function(M){m=Math.max(m,e.measureText(M).width+y)};return e.save(),e.font=c.string,z(n.title,w),e.font=l.string,z(n.beforeBody.concat(n.afterBody),w),y=t.displayColors?r+2+t.boxPadding:0,z(i,M=>{z(M.before,w),z(M.lines,w),z(M.after,w)}),y=0,e.font=h.string,z(n.footer,w),e.restore(),m+=g.width,{width:m,height:p}}function Of(n,t){const{y:e,height:i}=t;return en.height-i/2?"bottom":"center"}function Tf(n,t,e,i){const{x:s,width:o}=i,r=e.caretSize+e.caretPadding;if(n==="left"&&s+o+r>t.width||n==="right"&&s-o-r<0)return!0}function Af(n,t,e,i){const{x:s,width:o}=e,{width:r,chartArea:{left:a,right:l}}=n;let c="center";return i==="center"?c=s<=(a+l)/2?"left":"right":s<=o/2?c="left":s>=r-o/2&&(c="right"),Tf(c,n,t,e)&&(c="center"),c}function Mo(n,t,e){const i=e.yAlign||t.yAlign||Of(n,e);return{xAlign:e.xAlign||t.xAlign||Af(n,t,e,i),yAlign:i}}function Lf(n,t){let{x:e,width:i}=n;return t==="right"?e-=i:t==="center"&&(e-=i/2),e}function Ff(n,t,e){let{y:i,height:s}=n;return t==="top"?i+=e:t==="bottom"?i-=s+e:i-=s/2,i}function ko(n,t,e,i){const{caretSize:s,caretPadding:o,cornerRadius:r}=n,{xAlign:a,yAlign:l}=e,c=s+o,{topLeft:h,topRight:u,bottomLeft:d,bottomRight:f}=re(r);let g=Lf(t,a);const p=Ff(t,l,c);return l==="center"?a==="left"?g+=c:a==="right"&&(g-=c):a==="left"?g-=Math.max(h,d)+s:a==="right"&&(g+=Math.max(u,f)+s),{x:st(g,0,i.width-t.width),y:st(p,0,i.height-t.height)}}function Dn(n,t,e){const i=ct(e.padding);return t==="center"?n.x+n.width/2:t==="right"?n.x+n.width-i.right:n.x+i.left}function Po(n){return vt([],Lt(n))}function Rf(n,t,e){return Xt(n,{tooltip:t,tooltipItems:e,type:"tooltip"})}function Do(n,t){const e=t&&t.dataset&&t.dataset.tooltip&&t.dataset.tooltip.callbacks;return e?n.override(e):n}const Gr={beforeTitle:St,title(n){if(n.length>0){const t=n[0],e=t.chart.data.labels,i=e?e.length:0;if(this&&this.options&&this.options.mode==="dataset")return t.dataset.label||"";if(t.label)return t.label;if(i>0&&t.dataIndex"u"?Gr[t].call(e,i):s}class Wi extends wt{constructor(t){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=t.chart,this.options=t.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const e=this.chart,i=this.options.setContext(this.getContext()),s=i.enabled&&e.options.animation&&i.animations,o=new Or(this.chart,s);return s._cacheable&&(this._cachedAnimations=Object.freeze(o)),o}getContext(){return this.$context||(this.$context=Rf(this.chart.getContext(),this,this._tooltipItems))}getTitle(t,e){const{callbacks:i}=e,s=ht(i,"beforeTitle",this,t),o=ht(i,"title",this,t),r=ht(i,"afterTitle",this,t);let a=[];return a=vt(a,Lt(s)),a=vt(a,Lt(o)),a=vt(a,Lt(r)),a}getBeforeBody(t,e){return Po(ht(e.callbacks,"beforeBody",this,t))}getBody(t,e){const{callbacks:i}=e,s=[];return z(t,o=>{const r={before:[],lines:[],after:[]},a=Do(i,o);vt(r.before,Lt(ht(a,"beforeLabel",this,o))),vt(r.lines,ht(a,"label",this,o)),vt(r.after,Lt(ht(a,"afterLabel",this,o))),s.push(r)}),s}getAfterBody(t,e){return Po(ht(e.callbacks,"afterBody",this,t))}getFooter(t,e){const{callbacks:i}=e,s=ht(i,"beforeFooter",this,t),o=ht(i,"footer",this,t),r=ht(i,"afterFooter",this,t);let a=[];return a=vt(a,Lt(s)),a=vt(a,Lt(o)),a=vt(a,Lt(r)),a}_createItems(t){const e=this._active,i=this.chart.data,s=[],o=[],r=[];let a=[],l,c;for(l=0,c=e.length;lt.filter(h,u,d,i))),t.itemSort&&(a=a.sort((h,u)=>t.itemSort(h,u,i))),z(a,h=>{const u=Do(t.callbacks,h);s.push(ht(u,"labelColor",this,h)),o.push(ht(u,"labelPointStyle",this,h)),r.push(ht(u,"labelTextColor",this,h))}),this.labelColors=s,this.labelPointStyles=o,this.labelTextColors=r,this.dataPoints=a,a}update(t,e){const i=this.options.setContext(this.getContext()),s=this._active;let o,r=[];if(!s.length)this.opacity!==0&&(o={opacity:0});else{const a=Ve[i.position].call(this,s,this._eventPosition);r=this._createItems(i),this.title=this.getTitle(r,i),this.beforeBody=this.getBeforeBody(r,i),this.body=this.getBody(r,i),this.afterBody=this.getAfterBody(r,i),this.footer=this.getFooter(r,i);const l=this._size=vo(this,i),c=Object.assign({},a,l),h=Mo(this.chart,i,c),u=ko(i,c,h,this.chart);this.xAlign=h.xAlign,this.yAlign=h.yAlign,o={opacity:1,x:u.x,y:u.y,width:l.width,height:l.height,caretX:a.x,caretY:a.y}}this._tooltipItems=r,this.$context=void 0,o&&this._resolveAnimations().update(this,o),t&&i.external&&i.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,i,s){const o=this.getCaretPosition(t,i,s);e.lineTo(o.x1,o.y1),e.lineTo(o.x2,o.y2),e.lineTo(o.x3,o.y3)}getCaretPosition(t,e,i){const{xAlign:s,yAlign:o}=this,{caretSize:r,cornerRadius:a}=i,{topLeft:l,topRight:c,bottomLeft:h,bottomRight:u}=re(a),{x:d,y:f}=t,{width:g,height:p}=e;let m,b,y,w,M,x;return o==="center"?(M=f+p/2,s==="left"?(m=d,b=m-r,w=M+r,x=M-r):(m=d+g,b=m+r,w=M-r,x=M+r),y=m):(s==="left"?b=d+Math.max(l,h)+r:s==="right"?b=d+g-Math.max(c,u)-r:b=this.caretX,o==="top"?(w=f,M=w-r,m=b-r,y=b+r):(w=f+p,M=w+r,m=b+r,y=b-r),x=w),{x1:m,x2:b,x3:y,y1:w,y2:M,y3:x}}drawTitle(t,e,i){const s=this.title,o=s.length;let r,a,l;if(o){const c=we(i.rtl,this.x,this.width);for(t.x=Dn(this,i.titleAlign,i),e.textAlign=c.textAlign(i.titleAlign),e.textBaseline="middle",r=tt(i.titleFont),a=i.titleSpacing,e.fillStyle=i.titleColor,e.font=r.string,l=0;ly!==0)?(t.beginPath(),t.fillStyle=o.multiKeyBackground,tn(t,{x:p,y:g,w:c,h:l,radius:b}),t.fill(),t.stroke(),t.fillStyle=r.backgroundColor,t.beginPath(),tn(t,{x:m,y:g+1,w:c-2,h:l-2,radius:b}),t.fill()):(t.fillStyle=o.multiKeyBackground,t.fillRect(p,g,c,l),t.strokeRect(p,g,c,l),t.fillStyle=r.backgroundColor,t.fillRect(m,g+1,c-2,l-2))}t.fillStyle=this.labelTextColors[i]}drawBody(t,e,i){const{body:s}=this,{bodySpacing:o,bodyAlign:r,displayColors:a,boxHeight:l,boxWidth:c,boxPadding:h}=i,u=tt(i.bodyFont);let d=u.lineHeight,f=0;const g=we(i.rtl,this.x,this.width),p=function(k){e.fillText(k,g.x(t.x+f),t.y+d/2),t.y+=d+o},m=g.textAlign(r);let b,y,w,M,x,P,v;for(e.textAlign=r,e.textBaseline="middle",e.font=u.string,t.x=Dn(this,m,i),e.fillStyle=i.bodyColor,z(this.beforeBody,p),f=a&&m!=="right"?r==="center"?c/2+h:c+2+h:0,M=0,P=s.length;M0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,i=this.$animations,s=i&&i.x,o=i&&i.y;if(s||o){const r=Ve[t.position].call(this,this._active,this._eventPosition);if(!r)return;const a=this._size=vo(this,t),l=Object.assign({},r,this._size),c=Mo(e,t,l),h=ko(t,l,c,e);(s._to!==h.x||o._to!==h.y)&&(this.xAlign=c.xAlign,this.yAlign=c.yAlign,this.width=a.width,this.height=a.height,this.caretX=r.x,this.caretY=r.y,this._resolveAnimations().update(this,h))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let i=this.opacity;if(!i)return;this._updateAnimationTarget(e);const s={width:this.width,height:this.height},o={x:this.x,y:this.y};i=Math.abs(i)<.001?0:i;const r=ct(e.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&a&&(t.save(),t.globalAlpha=i,this.drawBackground(o,t,s,e),kr(t,e.textDirection),o.y+=r.top,this.drawTitle(o,t,e),this.drawBody(o,t,e),this.drawFooter(o,t,e),Pr(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const i=this._active,s=t.map(({datasetIndex:a,index:l})=>{const c=this.chart.getDatasetMeta(a);if(!c)throw new Error("Cannot find a dataset at index "+a);return{datasetIndex:a,element:c.data[l],index:l}}),o=!Hn(i,s),r=this._positionChanged(s,e);(o||r)&&(this._active=s,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,i=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const s=this.options,o=this._active||[],r=this._getActiveElements(t,o,e,i),a=this._positionChanged(r,t),l=e||!Hn(r,o)||a;return l&&(this._active=r,(s.enabled||s.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),l}_getActiveElements(t,e,i,s){const o=this.options;if(t.type==="mouseout")return[];if(!s)return e.filter(a=>this.chart.data.datasets[a.datasetIndex]&&this.chart.getDatasetMeta(a.datasetIndex).controller.getParsed(a.index)!==void 0);const r=this.chart.getElementsAtEventForMode(t,o.mode,o,i);return o.reverse&&r.reverse(),r}_positionChanged(t,e){const{caretX:i,caretY:s,options:o}=this,r=Ve[o.position].call(this,t,e);return r!==!1&&(i!==r.x||s!==r.y)}}_(Wi,"positioners",Ve);var If={id:"tooltip",_element:Wi,positioners:Ve,afterInit(n,t,e){e&&(n.tooltip=new Wi({chart:n,options:e}))},beforeUpdate(n,t,e){n.tooltip&&n.tooltip.initialize(e)},reset(n,t,e){n.tooltip&&n.tooltip.initialize(e)},afterDraw(n){const t=n.tooltip;if(t&&t._willRender()){const e={tooltip:t};if(n.notifyPlugins("beforeTooltipDraw",{...e,cancelable:!0})===!1)return;t.draw(n.ctx),n.notifyPlugins("afterTooltipDraw",e)}},afterEvent(n,t){if(n.tooltip){const e=t.replay;n.tooltip.handleEvent(t.event,e,t.inChartArea)&&(t.changed=!0)}},defaults:{enabled:!0,external:null,position:"average",backgroundColor:"rgba(0,0,0,0.8)",titleColor:"#fff",titleFont:{weight:"bold"},titleSpacing:2,titleMarginBottom:6,titleAlign:"left",bodyColor:"#fff",bodySpacing:2,bodyFont:{},bodyAlign:"left",footerColor:"#fff",footerSpacing:2,footerMarginTop:6,footerFont:{weight:"bold"},footerAlign:"left",padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(n,t)=>t.bodyFont.size,boxWidth:(n,t)=>t.bodyFont.size,multiKeyBackground:"#fff",displayColors:!0,boxPadding:0,borderColor:"rgba(0,0,0,0)",borderWidth:0,animation:{duration:400,easing:"easeOutQuart"},animations:{numbers:{type:"number",properties:["x","y","width","height","caretX","caretY"]},opacity:{easing:"linear",duration:200}},callbacks:Gr},defaultRoutes:{bodyFont:"font",footerFont:"font",titleFont:"font"},descriptors:{_scriptable:n=>n!=="filter"&&n!=="itemSort"&&n!=="external",_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]};const Ef=(n,t,e,i)=>(typeof t=="string"?(e=n.push(t)-1,i.unshift({index:e,label:t})):isNaN(t)&&(e=null),e);function zf(n,t,e,i){const s=n.indexOf(t);if(s===-1)return Ef(n,t,e,i);const o=n.lastIndexOf(t);return s!==o?e:s}const Bf=(n,t)=>n===null?null:st(Math.round(n),0,t);function Co(n){const t=this.getLabels();return n>=0&&ne.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}}_(Hi,"id","category"),_(Hi,"defaults",{ticks:{callback:Co}});function Nf(n,t){const e=[],{bounds:s,step:o,min:r,max:a,precision:l,count:c,maxTicks:h,maxDigits:u,includeBounds:d}=n,f=o||1,g=h-1,{min:p,max:m}=t,b=!F(r),y=!F(a),w=!F(c),M=(m-p)/(u+1);let x=vs((m-p)/g/f)*f,P,v,k,D;if(x<1e-14&&!b&&!y)return[{value:p},{value:m}];D=Math.ceil(m/x)-Math.floor(p/x),D>g&&(x=vs(D*x/g/f)*f),F(l)||(P=Math.pow(10,l),x=Math.ceil(x*P)/P),s==="ticks"?(v=Math.floor(p/x)*x,k=Math.ceil(m/x)*x):(v=p,k=m),b&&y&&o&&Wc((a-r)/o,x/1e3)?(D=Math.round(Math.min((a-r)/x,h)),x=(a-r)/D,v=r,k=a):w?(v=b?r:v,k=y?a:k,D=c-1,x=(k-v)/D):(D=(k-v)/x,$e(D,Math.round(D),x/1e3)?D=Math.round(D):D=Math.ceil(D));const T=Math.max(Ms(x),Ms(v));P=Math.pow(10,F(l)?T:l),v=Math.round(v*P)/P,k=Math.round(k*P)/P;let S=0;for(b&&(d&&v!==r?(e.push({value:r}),va)break;e.push({value:A})}return y&&d&&k!==a?e.length&&$e(e[e.length-1].value,a,So(a,M,n))?e[e.length-1].value=a:e.push({value:a}):(!y||k===a)&&e.push({value:k}),e}function So(n,t,{horizontal:e,minRotation:i}){const s=xt(i),o=(e?Math.sin(s):Math.cos(s))||.001,r=.75*t*(""+n).length;return Math.min(t/o,r)}class Xn extends ce{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(t,e){return F(t)||(typeof t=="number"||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:i}=this.getUserBounds();let{min:s,max:o}=this;const r=l=>s=e?s:l,a=l=>o=i?o:l;if(t){const l=Dt(s),c=Dt(o);l<0&&c<0?a(0):l>0&&c>0&&r(0)}if(s===o){let l=o===0?1:Math.abs(o*.05);a(o+l),t||r(s-l)}this.min=s,this.max=o}getTickLimit(){const t=this.options.ticks;let{maxTicksLimit:e,stepSize:i}=t,s;return i?(s=Math.ceil(this.max/i)-Math.floor(this.min/i)+1,s>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${i} would result generating up to ${s} ticks. Limiting to 1000.`),s=1e3)):(s=this.computeTickLimit(),e=e||11),e&&(s=Math.min(e,s)),s}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let i=this.getTickLimit();i=Math.max(2,i);const s={maxTicks:i,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:e.includeBounds!==!1},o=this._range||this,r=Nf(s,o);return t.bounds==="ticks"&&cr(r,this,"value"),t.reverse?(r.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),r}configure(){const t=this.ticks;let e=this.min,i=this.max;if(super.configure(),this.options.offset&&t.length){const s=(i-e)/Math.max(t.length-1,1)/2;e-=s,i+=s}this._startValue=e,this._endValue=i,this._valueRange=i-e}getLabelForValue(t){return hn(t,this.chart.options.locale,this.options.ticks.format)}}class Vi extends Xn{determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=q(t)?t:0,this.max=q(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,i=xt(this.options.ticks.minRotation),s=(t?Math.sin(i):Math.cos(i))||.001,o=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,o.lineHeight/s))}getPixelForValue(t){return t===null?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}_(Vi,"id","linear"),_(Vi,"defaults",{ticks:{callback:ei.formatters.numeric}});const nn=n=>Math.floor(Wt(n)),te=(n,t)=>Math.pow(10,nn(n)+t);function Oo(n){return n/Math.pow(10,nn(n))===1}function To(n,t,e){const i=Math.pow(10,e),s=Math.floor(n/i);return Math.ceil(t/i)-s}function Wf(n,t){const e=t-n;let i=nn(e);for(;To(n,t,i)>10;)i++;for(;To(n,t,i)<10;)i--;return Math.min(i,nn(n))}function Hf(n,{min:t,max:e}){t=mt(n.min,t);const i=[],s=nn(t);let o=Wf(t,e),r=o<0?Math.pow(10,Math.abs(o)):1;const a=Math.pow(10,o),l=s>o?Math.pow(10,s):0,c=Math.round((t-l)*r)/r,h=Math.floor((t-l)/a/10)*a*10;let u=Math.floor((c-h)/Math.pow(10,o)),d=mt(n.min,Math.round((l+h+u*Math.pow(10,o))*r)/r);for(;d=10?u=u<15?15:20:u++,u>=20&&(o++,u=2,r=o>=0?1:r),d=Math.round((l+h+u*Math.pow(10,o))*r)/r;const f=mt(n.max,d);return i.push({value:f,major:Oo(f),significand:u}),i}class Ao extends ce{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._valueRange=0}parse(t,e){const i=Xn.prototype.parse.apply(this,[t,e]);if(i===0){this._zero=!0;return}return q(i)&&i>0?i:null}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=q(t)?Math.max(0,t):null,this.max=q(e)?Math.max(0,e):null,this.options.beginAtZero&&(this._zero=!0),this._zero&&this.min!==this._suggestedMin&&!q(this._userMin)&&(this.min=t===te(this.min,0)?te(this.min,-1):te(this.min,0)),this.handleTickRangeOptions()}handleTickRangeOptions(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let i=this.min,s=this.max;const o=a=>i=t?i:a,r=a=>s=e?s:a;i===s&&(i<=0?(o(1),r(10)):(o(te(i,-1)),r(te(s,1)))),i<=0&&o(te(s,-1)),s<=0&&r(te(i,1)),this.min=i,this.max=s}buildTicks(){const t=this.options,e={min:this._userMin,max:this._userMax},i=Hf(e,this);return t.bounds==="ticks"&&cr(i,this,"value"),t.reverse?(i.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),i}getLabelForValue(t){return t===void 0?"0":hn(t,this.chart.options.locale,this.options.ticks.format)}configure(){const t=this.min;super.configure(),this._startValue=Wt(t),this._valueRange=Wt(this.max)-Wt(t)}getPixelForValue(t){return(t===void 0||t===0)&&(t=this.min),t===null||isNaN(t)?NaN:this.getPixelForDecimal(t===this.min?0:(Wt(t)-this._startValue)/this._valueRange)}getValueForPixel(t){const e=this.getDecimalForPixel(t);return Math.pow(10,this._startValue+e*this._valueRange)}}_(Ao,"id","logarithmic"),_(Ao,"defaults",{ticks:{callback:ei.formatters.logarithmic,major:{enabled:!0}}});function Yi(n){const t=n.ticks;if(t.display&&n.display){const e=ct(t.backdropPadding);return C(t.font&&t.font.size,U.font.size)+e.height}return 0}function Vf(n,t,e){return e=W(e)?e:[e],{w:oh(n,t.string,e),h:e.length*t.lineHeight}}function Lo(n,t,e,i,s){return n===i||n===s?{start:t-e/2,end:t+e/2}:ns?{start:t-e,end:t}:{start:t,end:t+e}}function Yf(n){const t={l:n.left+n._padding.left,r:n.right-n._padding.right,t:n.top+n._padding.top,b:n.bottom-n._padding.bottom},e=Object.assign({},t),i=[],s=[],o=n._pointLabels.length,r=n.options.pointLabels,a=r.centerPointLabels?$/o:0;for(let l=0;lt.r&&(a=(i.end-t.r)/o,n.r=Math.max(n.r,t.r+a)),s.startt.b&&(l=(s.end-t.b)/r,n.b=Math.max(n.b,t.b+l))}function $f(n,t,e){const i=n.drawingArea,{extra:s,additionalAngle:o,padding:r,size:a}=e,l=n.getPointPosition(t,i+s+r,o),c=Math.round(Zi(bt(l.angle+Z))),h=Gf(l.y,a.h,c),u=Xf(c),d=Kf(l.x,a.w,u);return{visible:!0,x:l.x,y:h,textAlign:u,left:d,top:h,right:d+a.w,bottom:h+a.h}}function qf(n,t){if(!t)return!0;const{left:e,top:i,right:s,bottom:o}=n;return!(It({x:e,y:i},t)||It({x:e,y:o},t)||It({x:s,y:i},t)||It({x:s,y:o},t))}function Uf(n,t,e){const i=[],s=n._pointLabels.length,o=n.options,{centerPointLabels:r,display:a}=o.pointLabels,l={extra:Yi(o)/2,additionalAngle:r?$/s:0};let c;for(let h=0;h270||e<90)&&(n-=t),n}function Qf(n,t,e){const{left:i,top:s,right:o,bottom:r}=e,{backdropColor:a}=t;if(!F(a)){const l=re(t.borderRadius),c=ct(t.backdropPadding);n.fillStyle=a;const h=i-c.left,u=s-c.top,d=o-i+c.width,f=r-s+c.height;Object.values(l).some(g=>g!==0)?(n.beginPath(),tn(n,{x:h,y:u,w:d,h:f,radius:l}),n.fill()):n.fillRect(h,u,d,f)}}function Zf(n,t){const{ctx:e,options:{pointLabels:i}}=n;for(let s=t-1;s>=0;s--){const o=n._pointLabelItems[s];if(!o.visible)continue;const r=i.setContext(n.getPointLabelContext(s));Qf(e,r,o);const a=tt(r.font),{x:l,y:c,textAlign:h}=o;ve(e,n._pointLabels[s],l,c+a.lineHeight/2,a,{color:r.color,textAlign:h,textBaseline:"middle"})}}function Qr(n,t,e,i){const{ctx:s}=n;if(e)s.arc(n.xCenter,n.yCenter,t,0,H);else{let o=n.getPointPosition(0,t);s.moveTo(o.x,o.y);for(let r=1;r{const s=N(this.options.pointLabels.callback,[e,i],this);return s||s===0?s:""}).filter((e,i)=>this.chart.getDataVisibility(i))}fit(){const t=this.options;t.display&&t.pointLabels.display?Yf(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(t,e,i,s){this.xCenter+=Math.floor((t-e)/2),this.yCenter+=Math.floor((i-s)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(t,e,i,s))}getIndexAngle(t){const e=H/(this._pointLabels.length||1),i=this.options.startAngle||0;return bt(t*e+xt(i))}getDistanceFromCenterForValue(t){if(F(t))return NaN;const e=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-t)*e:(t-this.min)*e}getValueForDistanceFromCenter(t){if(F(t))return NaN;const e=t/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-e:this.min+e}getPointLabelContext(t){const e=this._pointLabels||[];if(t>=0&&t{if(u!==0){l=this.getDistanceFromCenterForValue(h.value);const d=this.getContext(u),f=s.setContext(d),g=o.setContext(d);Jf(this,f,l,r,g)}}),i.display){for(t.save(),a=r-1;a>=0;a--){const h=i.setContext(this.getPointLabelContext(a)),{color:u,lineWidth:d}=h;!d||!u||(t.lineWidth=d,t.strokeStyle=u,t.setLineDash(h.borderDash),t.lineDashOffset=h.borderDashOffset,l=this.getDistanceFromCenterForValue(e.ticks.reverse?this.min:this.max),c=this.getPointPosition(a,l),t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(c.x,c.y),t.stroke())}t.restore()}}drawBorder(){}drawLabels(){const t=this.ctx,e=this.options,i=e.ticks;if(!i.display)return;const s=this.getIndexAngle(0);let o,r;t.save(),t.translate(this.xCenter,this.yCenter),t.rotate(s),t.textAlign="center",t.textBaseline="middle",this.ticks.forEach((a,l)=>{if(l===0&&!e.reverse)return;const c=i.setContext(this.getContext(l)),h=tt(c.font);if(o=this.getDistanceFromCenterForValue(this.ticks[l].value),c.showLabelBackdrop){t.font=h.string,r=t.measureText(a.label).width,t.fillStyle=c.backdropColor;const u=ct(c.backdropPadding);t.fillRect(-r/2-u.left,-o-h.size/2-u.top,r+u.width,h.size+u.height)}ve(t,a.label,0,-o,h,{color:c.color,strokeColor:c.textStrokeColor,strokeWidth:c.textStrokeWidth})}),t.restore()}drawTitle(){}}_(Cn,"id","radialLinear"),_(Cn,"defaults",{display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,lineWidth:1,borderDash:[],borderDashOffset:0},grid:{circular:!1},startAngle:0,ticks:{showLabelBackdrop:!0,callback:ei.formatters.numeric},pointLabels:{backdropColor:void 0,backdropPadding:2,display:!0,font:{size:10},callback(t){return t},padding:5,centerPointLabels:!1}}),_(Cn,"defaultRoutes",{"angleLines.color":"borderColor","pointLabels.color":"color","ticks.color":"color"}),_(Cn,"descriptors",{angleLines:{_fallback:"grid"}});const oi={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},ut=Object.keys(oi);function Fo(n,t){return n-t}function Ro(n,t){if(F(t))return null;const e=n._adapter,{parser:i,round:s,isoWeekday:o}=n._parseOpts;let r=t;return typeof i=="function"&&(r=i(r)),q(r)||(r=typeof i=="string"?e.parse(r,i):e.parse(r)),r===null?null:(s&&(r=s==="week"&&(Ze(o)||o===!0)?e.startOf(r,"isoWeek",o):e.startOf(r,s)),+r)}function Io(n,t,e,i){const s=ut.length;for(let o=ut.indexOf(n);o=ut.indexOf(e);o--){const r=ut[o];if(oi[r].common&&n._adapter.diff(s,i,r)>=t-1)return r}return ut[e?ut.indexOf(e):0]}function ng(n){for(let t=ut.indexOf(n)+1,e=ut.length;t=t?e[i]:e[s];n[o]=!0}}function ig(n,t,e,i){const s=n._adapter,o=+s.startOf(t[0].value,i),r=t[t.length-1].value;let a,l;for(a=o;a<=r;a=+s.add(a,1,i))l=e[a],l>=0&&(t[l].major=!0);return t}function zo(n,t,e){const i=[],s={},o=t.length;let r,a;for(r=0;r+t.value))}initOffsets(t=[]){let e=0,i=0,s,o;this.options.offset&&t.length&&(s=this.getDecimalForValue(t[0]),t.length===1?e=1-s:e=(this.getDecimalForValue(t[1])-s)/2,o=this.getDecimalForValue(t[t.length-1]),t.length===1?i=o:i=(o-this.getDecimalForValue(t[t.length-2]))/2);const r=t.length<3?.5:.25;e=st(e,0,r),i=st(i,0,r),this._offsets={start:e,end:i,factor:1/(e+1+i)}}_generate(){const t=this._adapter,e=this.min,i=this.max,s=this.options,o=s.time,r=o.unit||Io(o.minUnit,e,i,this._getLabelCapacity(e)),a=C(s.ticks.stepSize,1),l=r==="week"?o.isoWeekday:!1,c=Ze(l)||l===!0,h={};let u=e,d,f;if(c&&(u=+t.startOf(u,"isoWeek",l)),u=+t.startOf(u,c?"day":r),t.diff(i,e,r)>1e5*a)throw new Error(e+" and "+i+" are too far apart with stepSize of "+a+" "+r);const g=s.ticks.source==="data"&&this.getDataTimestamps();for(d=u,f=0;d+p)}getLabelForValue(t){const e=this._adapter,i=this.options.time;return i.tooltipFormat?e.format(t,i.tooltipFormat):e.format(t,i.displayFormats.datetime)}format(t,e){const s=this.options.time.displayFormats,o=this._unit,r=e||s[o];return this._adapter.format(t,r)}_tickFormatFunction(t,e,i,s){const o=this.options,r=o.ticks.callback;if(r)return N(r,[t,e,i],this);const a=o.time.displayFormats,l=this._unit,c=this._majorUnit,h=l&&a[l],u=c&&a[c],d=i[e],f=c&&u&&d&&d.major;return this._adapter.format(t,s||(f?u:h))}generateTickLabels(t){let e,i,s;for(e=0,i=t.length;e0?a:1}getDataTimestamps(){let t=this._cache.data||[],e,i;if(t.length)return t;const s=this.getMatchingVisibleMetas();if(this._normalized&&s.length)return this._cache.data=s[0].controller.getAllParsedValues(this);for(e=0,i=s.length;e=n[i].pos&&t<=n[s].pos&&({lo:i,hi:s}=oe(n,"pos",t)),{pos:o,time:a}=n[i],{pos:r,time:l}=n[s]):(t>=n[i].time&&t<=n[s].time&&({lo:i,hi:s}=oe(n,"time",t)),{time:o,pos:a}=n[i],{time:r,pos:l}=n[s]);const c=r-o;return c?a+(l-a)*(t-o)/c:a}class Bo extends sn{constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=Sn(e,this.min),this._tableRange=Sn(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:i}=this,s=[],o=[];let r,a,l,c,h;for(r=0,a=t.length;r=e&&c<=i&&s.push(c);if(s.length<2)return[{time:e,pos:0},{time:i,pos:1}];for(r=0,a=s.length;rs-o)}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const e=this.getDataTimestamps(),i=this.getLabelTimestamps();return e.length&&i.length?t=this.normalize(e.concat(i)):t=e.length?e:i,t=this._cache.all=t,t}getDecimalForValue(t){return(Sn(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const e=this._offsets,i=this.getDecimalForPixel(t)/e.factor-e.end;return Sn(this._table,i*this._tableRange+this._minPos,!0)}}_(Bo,"id","timeseries"),_(Bo,"defaults",sn.defaults);function ri(n){return n==="sankey"?{type:"sankey",data:{datasets:[]},options:{animations:!1}}:n==="pie"?{type:"pie",data:{datasets:[]}}:n==="column"?{type:"bar",data:{labels:[],datasets:[]},options:{plugins:{tooltip:{callbacks:{label:function(t){let e=t.dataset.currency_code;return I(t.raw,e)}}}},maintainAspectRatio:!1,scales:{}}}:n==="line"?{options:{plugins:{tooltip:{callbacks:{label:function(t){let e=t.dataset.currency_code;return I(t.raw,e)}}}},maintainAspectRatio:!1,scales:{x:{type:"time",time:{tooltipFormat:"PP"}}}},type:"line",data:{labels:[],datasets:[]}}:{}}let Kn=new it("#36a2eb"),xe=new it("#ff6384"),on=new it("#4bc0c0"),Zr=new it("#ff9f40"),sg=new it("#9966ff"),og=new it("#ffcd56"),rg=new it("#c9cbcf"),No=0;window.theme==="dark"&&(xe.darken(.3).desaturate(.3),on.darken(.3).desaturate(.3),Kn.darken(.3).desaturate(.3),Zr.darken(.3).desaturate(.3));let wi=[xe,Zr,Kn,on,sg,og,rg,on];function ge(n,t){let e={borderColor:xe.rgbString(),backgroundColor:xe.rgbString()},i;switch(n){default:let o=Math.floor(No/2)%wi.length;i=new it(wi[o].rgbString()),i.lighten(.38),e={borderColor:wi[o].hexString(),backgroundColor:i.hexString()};break;case"spent":i=new it(Kn.rgbString()),i.lighten(.38),e={borderColor:Kn.rgbString(),backgroundColor:i.rgbString()};break;case"left":i=new it(on.rgbString()),i.lighten(.38),e={borderColor:on.rgbString(),backgroundColor:i.rgbString()};break;case"overspent":i=new it(xe.rgbString()),i.lighten(.22),e={borderColor:xe.rgbString(),backgroundColor:i.rgbString()};break}return No++,t==="border"?e.borderColor:t==="background"?e.backgroundColor:"#FF0000"}let ee=[],Fe=null,vi=null,Mi=!1;const ag=()=>({loading:!1,loadingAccounts:!1,accountList:[],autoConversion:!1,chartOptions:null,switchAutoConversion(){this.autoConversion=!this.autoConversion,rc("autoConversion",this.autoConversion)},getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Ct("dashboard-accounts-chart",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){console.log(s),this.drawChart(this.generateOptions(s)),this.loading=!1;return}new ac().dashboard(n,t,null).then(r=>{this.chartData=r.data,window.store.set(e,r.data),console.log(r.data),this.drawChart(this.generateOptions(this.chartData)),this.loading=!1})},generateOptions(n){ee=[];let t=ri("line");for(let e=0;e0){this.loadingAccounts=!1;return}const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Ct("dashboard-accounts-data",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){this.accountList=s,this.loadingAccounts=!1;return}const o=10;let r=0,a=0,l=[];Promise.all([lt("frontpageAccounts")]).then(c=>{r=c[0].length;for(let h in c[0]){let u=c[0];if(u.hasOwnProperty(h)){let d=u[h];new gs().get(d,new Date(window.store.get("end"))).then(f=>{let g=f.data.data;const p={page:1,start:new Date(window.store.get("start")),end:new Date(window.store.get("end"))};new gs().transactions(g.id,p).then(m=>{let b=[];for(let y=0;y=o);y++){let w=m.data.data[y],M={title:w.attributes.group_title===null?"":w.attributes.group_title,id:w.id,transactions:[]};for(let x=0;xy.order-w.order),this.accountList=l,this.loadingAccounts=!1,window.store.set(e,l))})})}}})},init(){Promise.all([lt("viewRange","1M"),lt("autoConversion",!1),lt("language","en_US")]).then(n=>{this.autoConversion=n[1],Mi=!0,this.loadChart(),this.loadAccounts()}),window.store.observe("end",()=>{Mi&&(vi=null,this.accountList=[],this.loadChart(),this.loadAccounts())}),window.store.observe("autoConversion",()=>{Mi&&(this.loadChart(),this.loadAccounts())})}});let lg=class{dashboard(t,e){let i=j(t,"y-MM-dd"),s=j(e,"y-MM-dd");return dt.get("/api/v2/chart/budget/dashboard",{params:{start:i,end:s}})}},Re=[],On=null,zt=null,ki=!1,pe;const cg=()=>({loading:!1,autoConversion:!1,loadChart(){if(this.loading!==!0){if(this.loading=!0,zt!==null){this.drawChart(this.generateOptions(zt)),this.loading=!1;return}this.getFreshData()}},drawChart(n){if(On!==null){On.data.datasets=n.data.datasets,On.update();return}On=new at(document.querySelector("#budget-chart"),n)},getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Ct("dashboard-budgets-chart",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){zt=s,this.drawChart(this.generateOptions(zt)),this.loading=!1;return}new lg().dashboard(n,t,null).then(r=>{zt=r.data,this.drawChart(this.generateOptions(zt)),window.store.set(e,zt),this.loading=!1})},generateOptions(n){Re=[];let t=ri("column");t.options.locale=window.store.get("locale").replace("_","-"),t.options.plugins={tooltip:{callbacks:{title:function(e){return e.label},label:function(e){let i=e.dataset.label||"";return i&&(i+=": "),i+" "+I(e.parsed.y,Re[e.parsed.x]??"EUR")}}}},t.data={labels:[],datasets:[{label:pe.t("firefly.spent"),data:[],borderWidth:1,stack:1,backgroundColor:ge("spent","background"),borderColor:ge("spent","border")},{label:pe.t("firefly.left"),data:[],borderWidth:1,stack:1,backgroundColor:ge("left","background"),borderColor:ge("left","border")},{label:pe.t("firefly.overspent"),data:[],borderWidth:1,stack:1,backgroundColor:ge("overspent","background"),borderColor:ge("overspent","border")}]};for(const e in n)if(n.hasOwnProperty(e)){let i=n[e],s=i.label+" ("+i.currency_code+")";t.data.labels.push(s),this.autoConversion&&(Re.push(i.native_currency_code),t.data.datasets[0].data.push(parseFloat(i.native_entries.spent)*-1),t.data.datasets[1].data.push(parseFloat(i.native_entries.left)),t.data.datasets[2].data.push(parseFloat(i.native_entries.overspent))),this.autoConversion||(Re.push(i.currency_code),t.data.datasets[0].data.push(parseFloat(i.entries.spent)*-1),t.data.datasets[1].data.push(parseFloat(i.entries.left)),t.data.datasets[2].data.push(parseFloat(i.entries.overspent)))}return t.options.scales={y:{ticks:{callback:function(e){return I(e,Re[0]??"EUR")}}}},t},init(){Promise.all([lt("autoConversion",!1),lt("language","en_US")]).then(n=>{pe=new Qn;const t=n[1].replace("-","_");pe.locale=t,Zn(pe,t).then(()=>{this.autoConversion=n[0],ki=!0,this.loading===!1&&this.loadChart()})}),window.store.observe("end",()=>{ki&&this.loading===!1&&(zt=null,this.loadChart())}),window.store.observe("autoConversion",n=>{ki&&(this.autoConversion=n,this.loading===!1&&this.loadChart())})}});class hg{dashboard(t,e){let i=j(t,"y-MM-dd"),s=j(e,"y-MM-dd");return dt.get("/api/v2/chart/category/dashboard",{params:{start:i,end:s}})}}let Wo=[],Ie=null,me=null,Pi=!1;const ug=()=>({loading:!1,autoConversion:!1,generateOptions(n){Wo=[];let t=ri("column"),e={};for(const s in n)if(n.hasOwnProperty(s)){let o=n[s],r=o.currency_code;this.autoConversion&&(r=o.native_currency_code),e.hasOwnProperty(r)||(e[r]={name:r,yAxisID:"",data:{}},Wo.push(r))}for(const s in n)if(n.hasOwnProperty(s)){let o=n[s],r=o.currency_code;this.autoConversion&&(r=o.native_currency_code);for(const a in e)if(e.hasOwnProperty(a)){let l=0;r===a&&(l=parseFloat(o.amount),""+o.currency_code,this.autoConversion&&(l=parseFloat(o.native_amount),""+o.native_currency_code)),e[a].data.hasOwnProperty(o.label)&&(e[a].data[o.label]=e[a].data[o.label]+l),e[a].data.hasOwnProperty(o.label)||(e[a].data[o.label]=l)}t.data.labels.includes(o.label)||t.data.labels.push(o.label)}let i=0;for(const s in e){let o="y"+s,r={label:s,currency_code:s,yAxisID:o,data:[]};for(const a in e[s].data)r.data.push(e[s].data[a]);t.data.datasets.push(r),t.options.scales.hasOwnProperty(o)||(t.options.scales[o]={beginAtZero:!0,type:"linear",position:i===1?"right":"left",ticks:{callback:function(a,l,c){return I(a,s)}}},i++)}return t},drawChart(n){if(Ie!==null){Ie.options=n.options,Ie.data=n.data,Ie.update();return}Ie=new at(document.querySelector("#category-chart"),n)},getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Ct("dashboard-categories-chart",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){me=s,this.drawChart(this.generateOptions(me)),this.loading=!1;return}new hg().dashboard(n,t,null).then(r=>{me=r.data,this.drawChart(this.generateOptions(r.data)),window.store.set(e,me),this.loading=!1})},loadChart(){if(this.loading!==!0){if(this.loading=!0,me!==null){this.drawChart(this.generateOptions(me)),this.loading=!1;return}this.getFreshData()}},init(){Promise.all([lt("autoConversion",!1)]).then(n=>{this.autoConversion=n[0],Pi=!0,this.loadChart()}),window.store.observe("end",()=>{Pi&&(this.chartData=null,this.loadChart())}),window.store.observe("autoConversion",n=>{Pi&&(this.autoConversion=n,this.loadChart())})}});let dg=class{list(t){return dt.get("/api/v2/transactions",{params:t})}};/*! + * chartjs-chart-sankey v0.12.0 + * https://github.com/kurkle/chartjs-chart-sankey#readme + * (c) 2022 Jukka Kurkela + * Released under the MIT license + */function fg(n){const t=[],e=W(n)?n:F(n)?[]:[n];for(;e.length;){const i=e.pop();typeof i=="string"?t.unshift.apply(t,i.split(` +`)):Array.isArray(i)?e.push.apply(e,i):F(e)||t.unshift(""+i)}return t}function Di(n){return!n||["min","max"].indexOf(n)===-1?"max":n}const jt=n=>n!==void 0;function gg(n,t){const e=new Set(t.map(r=>r.to)),i=new Set(t.map(r=>r.from)),s=new Set([...n.keys()]);let o=0;for(;s.size;){const r=pg([...s],e);for(const a of r){const l=n.get(a);jt(l.x)||(l.x=o),s.delete(a)}s.size&&(e.clear(),t.filter(a=>s.has(a.from)).forEach(a=>e.add(a.to)),o++)}return[...n.keys()].filter(r=>!i.has(r)).forEach(r=>{const a=n.get(r);a.column||(a.x=o)}),o}function pg(n,t){const e=n.filter(i=>!t.has(i));return e.length?e:n.slice(0,1)}const mg=(n,t)=>n.x!==t.x?n.x-t.x:n.y-t.y;let Tn=-1;function bg(){return Tn=Tn<100?Tn+1:0,Tn}function ji(n,t,e=bg()){let i=0;for(const s of n)s.node._visited!==e&&(s.node._visited=e,i+=s.node[t].length+ji(s.node[t],t,e));return i}const Jr=n=>(t,e)=>ji(t.node[n],n)-ji(e.node[n],n)||t.node[n].length-e.node[n].length;function cs(n,t){n.from.sort(Jr("from"));for(const e of n.from){const i=e.node;jt(i.y)||(i.y=t,cs(i,t)),t=Math.max(i.y+i.out,t)}return t}function se(n,t){n.to.sort(Jr("to"));for(const e of n.to){const i=e.node;jt(i.y)||(i.y=t,se(i,t)),t=Math.max(i.y+i.in,t)}return t}function Ee(n,t){return jt(n.y)?n.y:(n.y=t,t)}function yg(n,t){const e=n.filter(h=>h.x===0),i=n.filter(h=>h.x===t),s=e.filter(h=>!jt(h.y)),o=i.filter(h=>!jt(h.y)),r=n.filter(h=>h.x>0&&h.xMath.max(h,u.y+u.out||0),0),l=i.reduce((h,u)=>Math.max(h,u.y+u.in||0),0),c=0;return a>=l?(s.forEach(h=>{a=Ee(h,a),a=Math.max(a+h.out,se(h,a))}),o.forEach(h=>{l=Ee(h,l),l=Math.max(l+h.in,se(h,l))})):(o.forEach(h=>{l=Ee(h,l),l=Math.max(l+h.in,se(h,l))}),s.forEach(h=>{a=Ee(h,a),a=Math.max(a+h.out,se(h,a))})),r.forEach(h=>{let u=n.filter(d=>d.x===h.x&&jt(d.y)).reduce((d,f)=>Math.max(d,f.y+Math.max(f.in,f.out)),0);u=Ee(h,u),u=Math.max(u+h.in,cs(h,u)),u=Math.max(u+h.out,se(h,u)),c=Math.max(c,u)}),Math.max(a,l,c)}function _g(n,t){n.sort((r,a)=>Math.max(a.in,a.out)-Math.max(r.in,r.out));const e=n[0];e.y=0;const i=cs(e,0),s=se(e,0),o=yg(n,t);return Math.max(i,s,o)}function xg(n,t){let e=0,i=0;for(let s=0;s<=t;s++){let o=i;const r=n.filter(a=>a.x===s).sort((a,l)=>a.priority-l.priority);i=r[0].to.filter(a=>a.node.x>s+1).reduce((a,l)=>a+l.flow,0)||0;for(const a of r)a.y=o,o+=Math.max(a.out,a.in);e=Math.max(o,e)}return e}function wg(n,t){let e=1,i=0,s=0,o=0;const r=[];n.sort(mg);for(const a of n){if(a.y){if(a.x===0)r.push(a.y);else{for(i!==a.x&&(i=a.x,s=0),e=s+1;ea.y);e++);s=e}a.y+=e*t,e++}o=Math.max(o,a.y+Math.max(a.in,a.out))}return o}function vg(n,t){n.forEach(e=>{const i=Math[t](e.in||e.out,e.out||e.in),s=il.node.y+l.node.out/2-(c.node.y+c.node.out/2)).forEach((l,c)=>{s?l.addY=c*(i-l.flow)/(a-1):(l.addY=r,r+=l.flow)}),r=0,a=e.to.length,e.to.sort((l,c)=>l.node.y+l.node.in/2-(c.node.y+c.node.in/2)).forEach((l,c)=>{o?l.addY=c*(i-l.flow)/(a-1):(l.addY=r,r+=l.flow)})})}function Mg(n,t,e,i){const s=[...n.values()],o=gg(n,t),a=(e?xg(s,o):_g(s,o))*.03,l=wg(s,a);return vg(s,i),{maxX:o,maxY:l}}function kg(n){const t=new Map;for(let i=0;is.flow-i.flow;return[...t.values()].forEach(i=>{i.from=i.from.sort(e),i.from.forEach(s=>{s.node=t.get(s.key)}),i.to=i.to.sort(e),i.to.forEach(s=>{s.node=t.get(s.key)})}),t}function Ho(n,t,e){for(const i of n)if(i.key===t&&i.index===e)return i.addY;return 0}class ai extends Et{parseObjectData(t,e,i,s){const{from:o="from",to:r="to",flow:a="flow"}=this.options.parsing,l=e.map(({[o]:y,[r]:w,[a]:M})=>({from:y,to:w,flow:M})),{xScale:c,yScale:h}=t,u=[],d=this._nodes=kg(l),{column:f,priority:g,size:p}=this.getDataset();if(g)for(const y of d.values())y.key in g&&(y.priority=g[y.key]);if(f)for(const y of d.values())y.key in f&&(y.column=!0,y.x=f[y.key]);const{maxX:m,maxY:b}=Mg(d,l,!!g,Di(p));this._maxX=m,this._maxY=b;for(let y=0,w=l.length;y1){const d=c-h*l/2+u;for(let f=0;fn.type==="data"?(n.parsed._custom.x-n.parsed.x)*200:void 0,delay:n=>n.type==="data"?n.parsed.x*500+n.dataIndex*20:void 0},colors:{type:"color",properties:["colorFrom","colorTo"]}},transitions:{hide:{animations:{colors:{type:"color",properties:["colorFrom","colorTo"],to:"transparent"}}},show:{animations:{colors:{type:"color",properties:["colorFrom","colorTo"],from:"transparent"}}}}};ai.overrides={interaction:{mode:"nearest",intersect:!0},datasets:{clip:!1,parsing:!0},plugins:{tooltip:{callbacks:{title(){return""},label(n){const t=n.dataset.data[n.dataIndex];return t.from+" -> "+t.to+": "+t.flow}}},legend:{display:!1}},scales:{x:{type:"linear",bounds:"data",display:!1,min:0,offset:!1},y:{type:"linear",bounds:"data",display:!1,min:0,reverse:!0,offset:!1}},layout:{padding:{top:3,left:3,right:13,bottom:3}}};const Vo=(n,t,e,i)=>n({x:n.x+e*(t.x-n.x),y:n.y+e*(t.y-n.y)});function Pg(n,{x:t,x2:e,options:i}){let s;i.colorMode==="from"?s=ye(i.colorFrom).alpha(.5).rgbString():i.colorMode==="to"?s=ye(i.colorTo).alpha(.5).rgbString():(s=n.createLinearGradient(t,0,e,0),s.addColorStop(0,ye(i.colorFrom).alpha(.5).rgbString()),s.addColorStop(1,ye(i.colorTo).alpha(.5).rgbString())),n.fillStyle=s,n.strokeStyle=s,n.lineWidth=.5}class hs extends wt{constructor(t){super(),this.options=void 0,this.x=void 0,this.y=void 0,this.x2=void 0,this.y2=void 0,this.height=void 0,t&&Object.assign(this,t)}draw(t){const e=this,{x:i,x2:s,y:o,y2:r,height:a,progress:l}=e,{cp1:c,cp2:h}=Vo(i,o,s,r);l!==0&&(t.save(),l<1&&(t.beginPath(),t.rect(i,Math.min(o,r),(s-i)*l+1,Math.abs(r-o)+a+1),t.clip()),Pg(t,e),t.beginPath(),t.moveTo(i,o),t.bezierCurveTo(c.x,c.y,h.x,h.y,s,r),t.lineTo(s,r+a),t.bezierCurveTo(h.x,h.y+a,c.x,c.y+a,i,o+a),t.lineTo(i,o),t.stroke(),t.closePath(),t.fill(),t.restore())}inRange(t,e,i){const{x:s,y:o,x2:r,y2:a,height:l}=this.getProps(["x","y","x2","y2","height"],i);if(tr)return!1;const{cp1:c,cp2:h}=Vo(s,o,r,a),u=(t-s)/(r-s),d={x:s,y:o},f={x:r,y:a},g=be(d,c,u),p=be(c,h,u),m=be(h,f,u),b=be(g,p,u),y=be(p,m,u),w=be(b,y,u).y;return e>=w&&e<=w+l}inXRange(t,e){const{x:i,x2:s}=this.getProps(["x","x2"],e);return t>=i&&t<=s}inYRange(t,e){const{y:i,y2:s,height:o}=this.getProps(["y","y2","height"],e),r=Math.min(i,s),a=Math.max(i,s)+o;return t>=r&&t<=a}getCenterPoint(t){const{x:e,y:i,x2:s,y2:o,height:r}=this.getProps(["x","y","x2","y2","height"],t);return{x:(e+s)/2,y:(i+o+r)/2}}tooltipPosition(t){return this.getCenterPoint(t)}getRange(t){return t==="x"?this.width/2:this.height/2}}hs.id="flow";hs.defaults={colorFrom:"red",colorTo:"green",colorMode:"gradient",hoverColorFrom:(n,t)=>Ue(t.colorFrom),hoverColorTo:(n,t)=>Ue(t.colorTo)};at.register({SankeyController:ai,Flow:hs});const Yo="dashboard-sankey-data";let ot,Ci=!1,An=null,Ot=[],et=!1,O={category:null,unknown_category:null,in:null,out:null,unknown_source:null,unknown_dest:null,unknown_account:null,expense_account:null,revenue_account:null,budget:null,unknown_budget:null,all_money:null};const jo=function(n){return n.includes(O.revenue_account)?"forestgreen":n.includes("("+O.in+",")?"green":n.includes(O.budget)||n.includes(O.unknown_budget)?"Orchid":n.includes("("+O.out+",")?"MediumOrchid":n.includes(O.all_money)?"blue":"red"};function ze(n,t,e,i){if(n==="category"&&t!==null&&e==="in")return O.category+' "'+t+'" ('+O.in+(et?", "+i+")":")");if(n==="category"&&t===null&&e==="in")return O.unknown_category+" ("+O.in+(et?", "+i+")":")");if(n==="category"&&t!==null&&e==="out")return O.category+' "'+t+'" ('+O.out+(et?", "+i+")":")");if(n==="category"&&t===null&&e==="out")return O.unknown_category+" ("+O.out+(et?", "+i+")":")");if(n==="account"&&t===null&&e==="in")return O.unknown_source+(et?" ("+i+")":"");if(n==="account"&&t!==null&&e==="in")return O.revenue_account+'"'+t+'"'+(et?" ("+i+")":"");if(n==="account"&&t===null&&e==="out")return O.unknown_dest+(et?" ("+i+")":"");if(n==="account"&&t!==null&&e==="out")return O.expense_account+' "'+t+'"'+(et?" ("+i+")":"");if(n==="budget"&&t!==null)return O.budget+' "'+t+'"'+(et?" ("+i+")":"");if(n==="budget"&&t===null)return O.unknown_budget+(et?" ("+i+")":"");console.error('Cannot handle: type:"'+n+'", dir: "'+e+'"')}function Be(n,t,e){if(n==="category"&&t!==null)return O.category+' "'+t+'"'+(et?" ("+e+")":"");if(n==="category"&&t===null)return O.unknown_category+(et?" ("+e+")":"");if(n==="account"&&t===null)return O.unknown_account+(et?" ("+e+")":"");if(n==="account"&&t!==null)return t+(et?" ("+e+")":"");if(n==="budget"&&t!==null)return O.budget+' "'+t+'"'+(et?" ("+e+")":"");if(n==="budget"&&t===null)return O.unknown_budget+(et?" ("+e+")":"");console.error('Cannot handle: type:"'+n+'"')}const Dg=()=>({loading:!1,autoConversion:!1,generateOptions(){let n=ri("sankey"),t={},e={};for(let s in Ot)if(Ot.hasOwnProperty(s)){let o=Ot[s];for(let r in o.attributes.transactions)if(o.attributes.transactions.hasOwnProperty(r)){let a=o.attributes.transactions[r],l=this.autoConversion?a.native_currency_code:a.currency_code,c=this.autoConversion?parseFloat(a.native_amount):parseFloat(a.amount),h;if(a.type==="deposit"){let u=ze("category",a.category_name,"in",l),d=ze("account",a.source_name,"in",l);e[u]=Be("category",a.category_name,l),e[d]=Be("account",a.source_name,l),h=d+"-"+u+"-"+l,t.hasOwnProperty(h)||(t[h]={from:d,to:u,amount:0}),t[h].amount+=c,h=u+"-"+O.all_money+"-"+l,t.hasOwnProperty(h)||(t[h]={from:u,to:O.all_money+(this.autoConversion?" ("+l+")":""),amount:0}),t[h].amount+=c}if(a.type==="withdrawal"){let u=ze("budget",a.budget_name,"out",l);e[u]=Be("budget",a.budget_name,l),h=O.all_money+"-"+u+"-"+l,t.hasOwnProperty(h)||(t[h]={from:O.all_money+(this.autoConversion?" ("+l+")":""),to:u,amount:0}),t[h].amount+=c;let d=ze("category",a.category_name,"out",l);e[d]=Be("category",a.category_name,l),h=u+"-"+d+"-"+l,t.hasOwnProperty(h)||(t[h]={from:u,to:d,amount:0}),t[h].amount+=c;let f=ze("account",a.destination_name,"out",l);e[f]=Be("account",a.destination_name,l),h=d+"-"+f+"-"+l,t.hasOwnProperty(h)||(t[h]={from:d,to:f,amount:0}),t[h].amount+=c}}}let i={label:"Firefly III dashboard sankey chart",data:[],colorFrom:s=>jo(s.dataset.data[s.dataIndex]?s.dataset.data[s.dataIndex].from:""),colorTo:s=>jo(s.dataset.data[s.dataIndex]?s.dataset.data[s.dataIndex].to:""),colorMode:"gradient",labels:e,size:"min"};for(let s in t)if(t.hasOwnProperty(s)){let o=t[s];i.data.push({from:o.from,to:o.to,flow:o.amount})}return n.data.datasets.push(i),n},drawChart(n){if(An!==null){An.data.datasets=n.data.datasets,An.update();return}An=new at(document.querySelector("#sankey-chart"),n)},getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Ct(Yo,n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){Ot=s,this.drawChart(this.generateOptions()),this.loading=!1;return}let o={start:j(n,"y-MM-dd"),end:j(t,"y-MM-dd"),type:"withdrawal,deposit",page:1};this.downloadTransactions(o)},downloadTransactions(n){const t=new Date(window.store.get("start")),e=new Date(window.store.get("end")),i=Ct(Yo,t,e);new dg().list(n).then(o=>{if(Ot=[...Ot,...o.data.data],parseInt(o.data.meta.pagination.total_pages)>n.page){n.page++,this.downloadTransactions(n);return}window.store.set(i,Ot),this.drawChart(this.generateOptions()),this.loading=!1})},loadChart(){if(this.loading!==!0){if(this.loading=!0,Ot.length!==0){this.drawChart(this.generateOptions()),this.loading=!1;return}this.getFreshData()}},init(){Ot=[],Promise.all([lt("autoConversion",!1),lt("language","en_US")]).then(n=>{this.autoConversion=n[0],et=n[0],ot=new Qn;const t=n[1].replace("-","_");ot.locale=t,Zn(ot,t).then(()=>{O.all_money=ot.t("firefly.all_money"),O.category=ot.t("firefly.category"),O.in=ot.t("firefly.money_flowing_in"),O.out=ot.t("firefly.money_flowing_out"),O.unknown_category=ot.t("firefly.unknown_category_plain"),O.unknown_source=ot.t("firefly.unknown_source_plain"),O.unknown_dest=ot.t("firefly.unknown_dest_plain"),O.unknown_account=ot.t("firefly.unknown_any_plain"),O.unknown_budget=ot.t("firefly.unknown_budget_plain"),O.expense_account=ot.t("firefly.expense_account"),O.revenue_account=ot.t("firefly.revenue_account"),O.budget=ot.t("firefly.budget"),Ci=!0,this.loadChart()})}),window.store.observe("end",()=>{Ci&&(this.transactions=[],this.loadChart())}),window.store.observe("autoConversion",n=>{Ci&&(this.autoConversion=n,this.loadChart())})}});let Cg=class{list(t){return dt.get("/api/v2/subscriptions",{params:t})}paid(t){return dt.get("/api/v2/subscriptions/sum/paid",{params:t})}unpaid(t){return dt.get("/api/v2/subscriptions/sum/unpaid",{params:t})}},Si=!1,Bt,pt={};function ta(n){return new Cg().list(n).then(e=>{let i=e.data.data;for(let s in i)if(i.hasOwnProperty(s)){let o=i[s];if(o.attributes.active&&o.attributes.pay_dates.length>0){let r=o.attributes.object_group_id===null?0:o.attributes.object_group_id,a=o.attributes.object_group_title===null?Bt.t("firefly.default_group_title_name_plain"):o.attributes.object_group_title,l=o.attributes.object_group_order===null?0:o.attributes.object_group_order;pt.hasOwnProperty(r)||(pt[r]={id:r,title:a,order:l,payment_info:{},bills:[]});let c={id:o.id,name:o.attributes.name,amount_min:o.attributes.amount_min,amount_max:o.attributes.amount_max,amount:(parseFloat(o.attributes.amount_max)+parseFloat(o.attributes.amount_min))/2,currency_code:o.attributes.currency_code,native_amount_min:o.attributes.native_amount_min,native_amount_max:o.attributes.native_amount_max,native_amount:(parseFloat(o.attributes.native_amount_max)+parseFloat(o.attributes.native_amount_min))/2,native_currency_code:o.attributes.native_currency_code,transactions:[],pay_dates:o.attributes.pay_dates,paid:o.attributes.paid_dates.length>0};c.expected_amount=n.autoConversion?I(c.native_amount,c.native_currency_code):I(c.amount,c.currency_code),c.expected_times=Bt.t("firefly.subscr_expected_x_times",{times:o.attributes.pay_dates.length,amount:c.expected_amount});for(let h in o.attributes.paid_dates)if(o.attributes.paid_dates.hasOwnProperty(h)){const u=o.attributes.paid_dates[h];let d=100;n.autoConversion&&(d=Math.round(-100+parseFloat(u.native_amount)*-1/parseFloat(c.native_amount)*100)),n.autoConversion||(d=Math.round(-100+parseFloat(u.amount)*-1/parseFloat(c.amount)*100));let f={amount:n.autoConversion?I(u.native_amount,u.native_currency_code):I(u.amount,u.currency_code),percentage:d,date:j(new Date(u.date),"PP"),foreign_amount:null};u.foreign_currency_code!==null&&(f.foreign_amount=n.autoConversion?u.foreign_native_amount:u.foreign_amount,f.foreign_currency_code=n.autoConversion?u.native_currency_code:u.foreign_currency_code),c.transactions.push(f)}if(pt[r].bills.push(c),o.attributes.paid_dates.length===0){const h=o.attributes.pay_dates.length*c.amount,u=o.attributes.pay_dates.length*c.native_amount;pt[r].payment_info.hasOwnProperty(c.currency_code)||(pt[r].payment_info[c.currency_code]={currency_code:c.currency_code,paid:0,unpaid:0,native_currency_code:c.native_currency_code,native_paid:0,native_unpaid:0}),pt[r].payment_info[c.currency_code].unpaid+=h,pt[r].payment_info[c.currency_code].native_unpaid+=u}if(o.attributes.paid_dates.length>0){for(let h in o.attributes.paid_dates)if(o.attributes.paid_dates.hasOwnProperty(h)){let u=o.attributes.paid_dates[h];pt[r].payment_info.hasOwnProperty(u.currency_code)||(pt[r].payment_info[u.currency_code]={currency_code:c.currency_code,paid:0,unpaid:0,native_currency_code:c.native_currency_code,native_paid:0,native_unpaid:0});const d=parseFloat(u.amount)*-1,f=parseFloat(u.native_amount)*-1;pt[r].payment_info[u.currency_code].paid+=d,pt[r].payment_info[u.currency_code].native_paid+=f}}}}return parseInt(e.data.meta.pagination.total_pages)>n.page?(n.page++,ta(n)):Promise.resolve()})}const Sg=()=>({loading:!1,autoConversion:!1,subscriptions:[],startSubscriptions(){this.loading=!0;let n=new Date(window.store.get("start")),t=new Date(window.store.get("end"));console.log("here we are");const e=window.store.get("cacheValid");let i=window.store.get(Ct("subscriptions-data-dashboard",n,t));e&&typeof i<"u",pt={},this.subscriptions=[],console.log("cache is invalid, must download");let s={start:j(n,"y-MM-dd"),end:j(t,"y-MM-dd"),autoConversion:this.autoConversion,page:1};ta(s).then(()=>{console.log("Done with download!");let o=Object.values(pt);for(let r in o)if(o.hasOwnProperty(r)){let a=o[r];const l=Object.keys(a.payment_info);a.col_size=l.length===1?"col-6 offset-3":"col-6",a.chart_width=l.length===1?"50%":"100%",a.payment_length=l.length,this.subscriptions.push(a)}this.loading=!1})},drawPieChart(n,t,e){let i="#pie_"+n+"_"+e.currency_code;const s=this.autoConversion?e.native_unpaid:e.unpaid,o=this.autoConversion?e.native_paid:e.paid,r=this.autoConversion?e.native_currency_code:e.currency_code,l={type:"doughnut",data:{labels:[Bt.t("firefly.paid"),Bt.t("firefly.unpaid")],datasets:[{label:Bt.t("firefly.subscriptions_in_group",{title:t}),data:[o,s],backgroundColor:["rgb(54, 162, 235)","rgb(255, 99, 132)"],hoverOffset:4}]},options:{plugins:{tooltip:{callbacks:{label:function(h){return h.dataset.label+": "+I(h.raw,r)}}}}}};var c=at.getChart(document.querySelector(i));typeof c<"u"&&c.destroy(),new at(document.querySelector(i),l)},init(){console.log("subscriptions init"),Promise.all([lt("autoConversion",!1),lt("language","en_US")]).then(n=>{console.log("subscriptions after promises"),this.autoConversion=n[0],Si=!0,Bt=new Qn;const t=n[1].replace("-","_");Bt.locale=t,Zn(Bt,t).then(()=>{this.loading===!1&&this.startSubscriptions()})}),window.store.observe("end",()=>{Si&&(console.log("subscriptions observe end"),this.loading===!1&&this.startSubscriptions())}),window.store.observe("autoConversion",n=>{Si&&(console.log("subscriptions observe autoConversion"),this.autoConversion=n,this.loading===!1&&this.startSubscriptions())})}});class Og{list(t){return dt.get("/api/v2/piggy-banks",{params:t})}}let Tt={},Oi=!1,Ln;const $o="dashboard-piggies-data",Tg=()=>({loading:!1,autoConversion:!1,sankeyGrouping:"account",piggies:[],getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Ct($o,n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){Tt=s,this.parsePiggies(),this.loading=!1;return}let o={start:j(n,"y-MM-dd"),end:j(t,"y-MM-dd"),page:1};this.downloadPiggyBanks(o)},downloadPiggyBanks(n){const t=new Date(window.store.get("start")),e=new Date(window.store.get("end")),i=Ct($o,t,e);new Og().list(n).then(o=>{if(Tt=[...Tt,...o.data.data],parseInt(o.data.meta.pagination.total_pages)>n.page){n.page++,this.downloadPiggyBanks(n);return}window.store.set(i,Tt),this.parsePiggies(),this.loading=!1})},parsePiggies(){let n=[];for(let t in Tt)if(Tt.hasOwnProperty(t)){let e=Tt[t];if(e.attributes.percentage>=100||e.attributes.percentage===0)continue;let i=e.object_group_title??Ln.t("firefly.default_group_title_name_plain");n.hasOwnProperty(i)||(n[i]={id:e.object_group_id??0,title:i,order:e.object_group_order??0,piggies:[]});let s={id:e.id,name:e.attributes.name,percentage:parseInt(e.attributes.percentage),amount:this.autoConversion?e.attributes.native_current_amount:e.attributes.current_amount,left_to_save:this.autoConversion?e.attributes.native_left_to_save:e.attributes.left_to_save,target_amount:this.autoConversion?e.attributes.native_target_amount:e.attributes.target_amount,save_per_month:this.autoConversion?e.attributes.native_save_per_month:e.attributes.save_per_month,currency_code:this.autoConversion?e.attributes.native_currency_code:e.attributes.currency_code};n[i].piggies.push(s)}this.piggies=Object.values(n)},loadPiggyBanks(){if(this.loading!==!0){if(this.loading=!0,this.piggies.length!==0){this.parsePiggies(),this.loading=!1;return}this.getFreshData()}},init(){Tt=[],Promise.all([lt("autoConversion",!1),lt("language","en_US")]).then(n=>{Ln=new Qn;const t=n[1].replace("-","_");Ln.locale=t,Zn(Ln,t).then(()=>{Oi=!0,this.autoConversion=n[0],this.loadPiggyBanks()})}),window.store.observe("end",()=>{Oi&&(Tt=[],this.loadPiggyBanks())}),window.store.observe("autoConversion",n=>{Oi&&(this.autoConversion=n,this.loadPiggyBanks())})}});/*! + * chartjs-adapter-date-fns v3.0.0 + * https://www.chartjs.org + * (c) 2022 chartjs-adapter-date-fns Contributors + * Released under the MIT license + */const Ag={datetime:"MMM d, yyyy, h:mm:ss aaaa",millisecond:"h:mm:ss.SSS aaaa",second:"h:mm:ss aaaa",minute:"h:mm aaaa",hour:"ha",day:"MMM d",week:"PP",month:"MMM yyyy",quarter:"qqq - yyyy",year:"yyyy"};Lr._date.override({_id:"date-fns",formats:function(){return Ag},parse:function(n,t){if(n===null||typeof n>"u")return null;const e=typeof n;return e==="number"||n instanceof Date?n=R(n):e==="string"&&(typeof t=="string"?n=zl(n,t,new Date,this.options):n=Hl(n,this.options)),ga(n)?n.getTime():null},format:function(n,t){return j(n,t,this.options)},add:function(n,t,e){switch(e){case"millisecond":return Jn(n,t);case"second":return Pa(n,t);case"minute":return Ma(n,t);case"hour":return va(n,t);case"day":return Gn(n,t);case"week":return Da(n,t);case"month":return $i(n,t);case"quarter":return ka(n,t);case"year":return Ca(n,t);default:return n}},diff:function(n,t,e){switch(e){case"millisecond":return ti(n,t);case"second":return Ra(n,t);case"minute":return Aa(n,t);case"hour":return Ta(n,t);case"day":return Qo(n,t);case"week":return Ia(n,t);case"month":return Zo(n,t);case"quarter":return Fa(n,t);case"year":return Ea(n,t);default:return 0}},startOf:function(n,t,e){switch(t){case"second":return Wl(n);case"minute":return za(n);case"hour":return Nl(n);case"day":return ya(n);case"week":return Ke(n);case"isoWeek":return Ke(n,{weekStartsOn:+e});case"month":return ba(n);case"quarter":return ma(n);case"year":return pa(n);default:return n}},endOf:function(n,t){switch(t){case"second":return Ha(n);case"minute":return Wa(n);case"hour":return Na(n);case"day":return qo(n);case"week":return xa(n);case"month":return Uo(n);case"quarter":return _a(n);case"year":return Ba(n);default:return n}}});at.register({LineController:Rn,LineElement:Vt,ArcElement:He,BarController:Fn,TimeScale:sn,PieController:Ri,BarElement:Nn,Filler:wf,Colors:tf,LinearScale:Vi,CategoryScale:Hi,PointElement:Bn,Tooltip:If,Legend:Cf});const ea={dates:wa,boxes:sc,accounts:ag,budgets:cg,categories:ug,sankey:Dg,subscriptions:Sg,piggies:Tg};function na(n){Object.keys(n).forEach(t=>{console.log(`Loading page component "${t}"`);let e=n[t]();Alpine.data(t,()=>e)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{na(ea)});window.bootstrapped&&na(ea); diff --git a/public/build/assets/load-translations-36a0ce82.js b/public/build/assets/load-translations-36a0ce82.js new file mode 100644 index 0000000000..75ee60712e --- /dev/null +++ b/public/build/assets/load-translations-36a0ce82.js @@ -0,0 +1,19 @@ +function bind$4(e,t){return function(){return e.apply(t,arguments)}}const{toString:toString$7}=Object.prototype,{getPrototypeOf}=Object,kindOf=(e=>t=>{const a=toString$7.call(t);return e[a]||(e[a]=a.slice(8,-1).toLowerCase())})(Object.create(null)),kindOfTest=e=>(e=e.toLowerCase(),t=>kindOf(t)===e),typeOfTest=e=>t=>typeof t===e,{isArray:isArray$c}=Array,isUndefined=typeOfTest("undefined");function isBuffer$3(e){return e!==null&&!isUndefined(e)&&e.constructor!==null&&!isUndefined(e.constructor)&&isFunction$5(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}const isArrayBuffer=kindOfTest("ArrayBuffer");function isArrayBufferView(e){let t;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?t=ArrayBuffer.isView(e):t=e&&e.buffer&&isArrayBuffer(e.buffer),t}const isString$1=typeOfTest("string"),isFunction$5=typeOfTest("function"),isNumber=typeOfTest("number"),isObject$b=e=>e!==null&&typeof e=="object",isBoolean=e=>e===!0||e===!1,isPlainObject=e=>{if(kindOf(e)!=="object")return!1;const t=getPrototypeOf(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(Symbol.toStringTag in e)&&!(Symbol.iterator in e)},isDate$1=kindOfTest("Date"),isFile=kindOfTest("File"),isBlob=kindOfTest("Blob"),isFileList=kindOfTest("FileList"),isStream=e=>isObject$b(e)&&isFunction$5(e.pipe),isFormData=e=>{let t;return e&&(typeof FormData=="function"&&e instanceof FormData||isFunction$5(e.append)&&((t=kindOf(e))==="formdata"||t==="object"&&isFunction$5(e.toString)&&e.toString()==="[object FormData]"))},isURLSearchParams=kindOfTest("URLSearchParams"),trim$2=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");function forEach(e,t,{allOwnKeys:a=!1}={}){if(e===null||typeof e>"u")return;let n,r;if(typeof e!="object"&&(e=[e]),isArray$c(e))for(n=0,r=e.length;n0;)if(r=a[n],t===r.toLowerCase())return r;return null}const _global=(()=>typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:global)(),isContextDefined=e=>!isUndefined(e)&&e!==_global;function merge(){const{caseless:e}=isContextDefined(this)&&this||{},t={},a=(n,r)=>{const i=e&&findKey$1(t,r)||r;isPlainObject(t[i])&&isPlainObject(n)?t[i]=merge(t[i],n):isPlainObject(n)?t[i]=merge({},n):isArray$c(n)?t[i]=n.slice():t[i]=n};for(let n=0,r=arguments.length;n(forEach(t,(r,i)=>{a&&isFunction$5(r)?e[i]=bind$4(r,a):e[i]=r},{allOwnKeys:n}),e),stripBOM=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),inherits=(e,t,a,n)=>{e.prototype=Object.create(t.prototype,n),e.prototype.constructor=e,Object.defineProperty(e,"super",{value:t.prototype}),a&&Object.assign(e.prototype,a)},toFlatObject=(e,t,a,n)=>{let r,i,o;const s={};if(t=t||{},e==null)return t;do{for(r=Object.getOwnPropertyNames(e),i=r.length;i-- >0;)o=r[i],(!n||n(o,e,t))&&!s[o]&&(t[o]=e[o],s[o]=!0);e=a!==!1&&getPrototypeOf(e)}while(e&&(!a||a(e,t))&&e!==Object.prototype);return t},endsWith=(e,t,a)=>{e=String(e),(a===void 0||a>e.length)&&(a=e.length),a-=t.length;const n=e.indexOf(t,a);return n!==-1&&n===a},toArray=e=>{if(!e)return null;if(isArray$c(e))return e;let t=e.length;if(!isNumber(t))return null;const a=new Array(t);for(;t-- >0;)a[t]=e[t];return a},isTypedArray$3=(e=>t=>e&&t instanceof e)(typeof Uint8Array<"u"&&getPrototypeOf(Uint8Array)),forEachEntry=(e,t)=>{const n=(e&&e[Symbol.iterator]).call(e);let r;for(;(r=n.next())&&!r.done;){const i=r.value;t.call(e,i[0],i[1])}},matchAll=(e,t)=>{let a;const n=[];for(;(a=e.exec(t))!==null;)n.push(a);return n},isHTMLForm=kindOfTest("HTMLFormElement"),toCamelCase=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(a,n,r){return n.toUpperCase()+r}),hasOwnProperty$c=(({hasOwnProperty:e})=>(t,a)=>e.call(t,a))(Object.prototype),isRegExp=kindOfTest("RegExp"),reduceDescriptors=(e,t)=>{const a=Object.getOwnPropertyDescriptors(e),n={};forEach(a,(r,i)=>{let o;(o=t(r,i,e))!==!1&&(n[i]=o||r)}),Object.defineProperties(e,n)},freezeMethods=e=>{reduceDescriptors(e,(t,a)=>{if(isFunction$5(e)&&["arguments","caller","callee"].indexOf(a)!==-1)return!1;const n=e[a];if(isFunction$5(n)){if(t.enumerable=!1,"writable"in t){t.writable=!1;return}t.set||(t.set=()=>{throw Error("Can not rewrite read-only method '"+a+"'")})}})},toObjectSet=(e,t)=>{const a={},n=r=>{r.forEach(i=>{a[i]=!0})};return isArray$c(e)?n(e):n(String(e).split(t)),a},noop$3=()=>{},toFiniteNumber=(e,t)=>(e=+e,Number.isFinite(e)?e:t),ALPHA="abcdefghijklmnopqrstuvwxyz",DIGIT="0123456789",ALPHABET={DIGIT,ALPHA,ALPHA_DIGIT:ALPHA+ALPHA.toUpperCase()+DIGIT},generateString=(e=16,t=ALPHABET.ALPHA_DIGIT)=>{let a="";const{length:n}=t;for(;e--;)a+=t[Math.random()*n|0];return a};function isSpecCompliantForm(e){return!!(e&&isFunction$5(e.append)&&e[Symbol.toStringTag]==="FormData"&&e[Symbol.iterator])}const toJSONObject=e=>{const t=new Array(10),a=(n,r)=>{if(isObject$b(n)){if(t.indexOf(n)>=0)return;if(!("toJSON"in n)){t[r]=n;const i=isArray$c(n)?[]:{};return forEach(n,(o,s)=>{const u=a(o,r+1);!isUndefined(u)&&(i[s]=u)}),t[r]=void 0,i}}return n};return a(e,0)},isAsyncFn=kindOfTest("AsyncFunction"),isThenable=e=>e&&(isObject$b(e)||isFunction$5(e))&&isFunction$5(e.then)&&isFunction$5(e.catch),utils$1={isArray:isArray$c,isArrayBuffer,isBuffer:isBuffer$3,isFormData,isArrayBufferView,isString:isString$1,isNumber,isBoolean,isObject:isObject$b,isPlainObject,isUndefined,isDate:isDate$1,isFile,isBlob,isRegExp,isFunction:isFunction$5,isStream,isURLSearchParams,isTypedArray:isTypedArray$3,isFileList,forEach,merge,extend,trim:trim$2,stripBOM,inherits,toFlatObject,kindOf,kindOfTest,endsWith,toArray,forEachEntry,matchAll,isHTMLForm,hasOwnProperty:hasOwnProperty$c,hasOwnProp:hasOwnProperty$c,reduceDescriptors,freezeMethods,toObjectSet,toCamelCase,noop:noop$3,toFiniteNumber,findKey:findKey$1,global:_global,isContextDefined,ALPHABET,generateString,isSpecCompliantForm,toJSONObject,isAsyncFn,isThenable};function AxiosError(e,t,a,n,r){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error().stack,this.message=e,this.name="AxiosError",t&&(this.code=t),a&&(this.config=a),n&&(this.request=n),r&&(this.response=r)}utils$1.inherits(AxiosError,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:utils$1.toJSONObject(this.config),code:this.code,status:this.response&&this.response.status?this.response.status:null}}});const prototype$1=AxiosError.prototype,descriptors={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach(e=>{descriptors[e]={value:e}});Object.defineProperties(AxiosError,descriptors);Object.defineProperty(prototype$1,"isAxiosError",{value:!0});AxiosError.from=(e,t,a,n,r,i)=>{const o=Object.create(prototype$1);return utils$1.toFlatObject(e,o,function(u){return u!==Error.prototype},s=>s!=="isAxiosError"),AxiosError.call(o,e.message,t,a,n,r),o.cause=e,o.name=e.name,i&&Object.assign(o,i),o};const httpAdapter=null;function isVisitable(e){return utils$1.isPlainObject(e)||utils$1.isArray(e)}function removeBrackets(e){return utils$1.endsWith(e,"[]")?e.slice(0,-2):e}function renderKey(e,t,a){return e?e.concat(t).map(function(r,i){return r=removeBrackets(r),!a&&i?"["+r+"]":r}).join(a?".":""):t}function isFlatArray(e){return utils$1.isArray(e)&&!e.some(isVisitable)}const predicates=utils$1.toFlatObject(utils$1,{},null,function(t){return/^is[A-Z]/.test(t)});function toFormData(e,t,a){if(!utils$1.isObject(e))throw new TypeError("target must be an object");t=t||new FormData,a=utils$1.toFlatObject(a,{metaTokens:!0,dots:!1,indexes:!1},!1,function(O,A){return!utils$1.isUndefined(A[O])});const n=a.metaTokens,r=a.visitor||d,i=a.dots,o=a.indexes,u=(a.Blob||typeof Blob<"u"&&Blob)&&utils$1.isSpecCompliantForm(t);if(!utils$1.isFunction(r))throw new TypeError("visitor must be a function");function l(M){if(M===null)return"";if(utils$1.isDate(M))return M.toISOString();if(!u&&utils$1.isBlob(M))throw new AxiosError("Blob is not supported. Use a Buffer instead.");return utils$1.isArrayBuffer(M)||utils$1.isTypedArray(M)?u&&typeof Blob=="function"?new Blob([M]):Buffer.from(M):M}function d(M,O,A){let D=M;if(M&&!A&&typeof M=="object"){if(utils$1.endsWith(O,"{}"))O=n?O:O.slice(0,-2),M=JSON.stringify(M);else if(utils$1.isArray(M)&&isFlatArray(M)||(utils$1.isFileList(M)||utils$1.endsWith(O,"[]"))&&(D=utils$1.toArray(M)))return O=removeBrackets(O),D.forEach(function(C,N){!(utils$1.isUndefined(C)||C===null)&&t.append(o===!0?renderKey([O],N,i):o===null?O:O+"[]",l(C))}),!1}return isVisitable(M)?!0:(t.append(renderKey(A,O,i),l(M)),!1)}const h=[],y=Object.assign(predicates,{defaultVisitor:d,convertValue:l,isVisitable});function T(M,O){if(!utils$1.isUndefined(M)){if(h.indexOf(M)!==-1)throw Error("Circular reference detected in "+O.join("."));h.push(M),utils$1.forEach(M,function(D,_){(!(utils$1.isUndefined(D)||D===null)&&r.call(t,D,utils$1.isString(_)?_.trim():_,O,y))===!0&&T(D,O?O.concat(_):[_])}),h.pop()}}if(!utils$1.isObject(e))throw new TypeError("data must be an object");return T(e),t}function encode$1(e){const t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,function(n){return t[n]})}function AxiosURLSearchParams(e,t){this._pairs=[],e&&toFormData(e,this,t)}const prototype=AxiosURLSearchParams.prototype;prototype.append=function(t,a){this._pairs.push([t,a])};prototype.toString=function(t){const a=t?function(n){return t.call(this,n,encode$1)}:encode$1;return this._pairs.map(function(r){return a(r[0])+"="+a(r[1])},"").join("&")};function encode(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}function buildURL(e,t,a){if(!t)return e;const n=a&&a.encode||encode,r=a&&a.serialize;let i;if(r?i=r(t,a):i=utils$1.isURLSearchParams(t)?t.toString():new AxiosURLSearchParams(t,a).toString(n),i){const o=e.indexOf("#");o!==-1&&(e=e.slice(0,o)),e+=(e.indexOf("?")===-1?"?":"&")+i}return e}class InterceptorManager{constructor(){this.handlers=[]}use(t,a,n){return this.handlers.push({fulfilled:t,rejected:a,synchronous:n?n.synchronous:!1,runWhen:n?n.runWhen:null}),this.handlers.length-1}eject(t){this.handlers[t]&&(this.handlers[t]=null)}clear(){this.handlers&&(this.handlers=[])}forEach(t){utils$1.forEach(this.handlers,function(n){n!==null&&t(n)})}}const InterceptorManager$1=InterceptorManager,transitionalDefaults={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},URLSearchParams$1=typeof URLSearchParams<"u"?URLSearchParams:AxiosURLSearchParams,FormData$1=typeof FormData<"u"?FormData:null,Blob$1=typeof Blob<"u"?Blob:null,platform$1={isBrowser:!0,classes:{URLSearchParams:URLSearchParams$1,FormData:FormData$1,Blob:Blob$1},protocols:["http","https","file","blob","url","data"]},hasBrowserEnv=typeof window<"u"&&typeof document<"u",hasStandardBrowserEnv=(e=>hasBrowserEnv&&["ReactNative","NativeScript","NS"].indexOf(e)<0)(typeof navigator<"u"&&navigator.product),hasStandardBrowserWebWorkerEnv=(()=>typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope&&typeof self.importScripts=="function")(),utils=Object.freeze(Object.defineProperty({__proto__:null,hasBrowserEnv,hasStandardBrowserEnv,hasStandardBrowserWebWorkerEnv},Symbol.toStringTag,{value:"Module"})),platform={...utils,...platform$1};function toURLEncodedForm(e,t){return toFormData(e,new platform.classes.URLSearchParams,Object.assign({visitor:function(a,n,r,i){return platform.isNode&&utils$1.isBuffer(a)?(this.append(n,a.toString("base64")),!1):i.defaultVisitor.apply(this,arguments)}},t))}function parsePropPath(e){return utils$1.matchAll(/\w+|\[(\w*)]/g,e).map(t=>t[0]==="[]"?"":t[1]||t[0])}function arrayToObject(e){const t={},a=Object.keys(e);let n;const r=a.length;let i;for(n=0;n=a.length;return o=!o&&utils$1.isArray(r)?r.length:o,u?(utils$1.hasOwnProp(r,o)?r[o]=[r[o],n]:r[o]=n,!s):((!r[o]||!utils$1.isObject(r[o]))&&(r[o]=[]),t(a,n,r[o],i)&&utils$1.isArray(r[o])&&(r[o]=arrayToObject(r[o])),!s)}if(utils$1.isFormData(e)&&utils$1.isFunction(e.entries)){const a={};return utils$1.forEachEntry(e,(n,r)=>{t(parsePropPath(n),r,a,0)}),a}return null}function stringifySafely(e,t,a){if(utils$1.isString(e))try{return(t||JSON.parse)(e),utils$1.trim(e)}catch(n){if(n.name!=="SyntaxError")throw n}return(a||JSON.stringify)(e)}const defaults={transitional:transitionalDefaults,adapter:["xhr","http"],transformRequest:[function(t,a){const n=a.getContentType()||"",r=n.indexOf("application/json")>-1,i=utils$1.isObject(t);if(i&&utils$1.isHTMLForm(t)&&(t=new FormData(t)),utils$1.isFormData(t))return r&&r?JSON.stringify(formDataToJSON(t)):t;if(utils$1.isArrayBuffer(t)||utils$1.isBuffer(t)||utils$1.isStream(t)||utils$1.isFile(t)||utils$1.isBlob(t))return t;if(utils$1.isArrayBufferView(t))return t.buffer;if(utils$1.isURLSearchParams(t))return a.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),t.toString();let s;if(i){if(n.indexOf("application/x-www-form-urlencoded")>-1)return toURLEncodedForm(t,this.formSerializer).toString();if((s=utils$1.isFileList(t))||n.indexOf("multipart/form-data")>-1){const u=this.env&&this.env.FormData;return toFormData(s?{"files[]":t}:t,u&&new u,this.formSerializer)}}return i||r?(a.setContentType("application/json",!1),stringifySafely(t)):t}],transformResponse:[function(t){const a=this.transitional||defaults.transitional,n=a&&a.forcedJSONParsing,r=this.responseType==="json";if(t&&utils$1.isString(t)&&(n&&!this.responseType||r)){const o=!(a&&a.silentJSONParsing)&&r;try{return JSON.parse(t)}catch(s){if(o)throw s.name==="SyntaxError"?AxiosError.from(s,AxiosError.ERR_BAD_RESPONSE,this,null,this.response):s}}return t}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:platform.classes.FormData,Blob:platform.classes.Blob},validateStatus:function(t){return t>=200&&t<300},headers:{common:{Accept:"application/json, text/plain, */*","Content-Type":void 0}}};utils$1.forEach(["delete","get","head","post","put","patch"],e=>{defaults.headers[e]={}});const defaults$1=defaults,ignoreDuplicateOf=utils$1.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),parseHeaders=e=>{const t={};let a,n,r;return e&&e.split(` +`).forEach(function(o){r=o.indexOf(":"),a=o.substring(0,r).trim().toLowerCase(),n=o.substring(r+1).trim(),!(!a||t[a]&&ignoreDuplicateOf[a])&&(a==="set-cookie"?t[a]?t[a].push(n):t[a]=[n]:t[a]=t[a]?t[a]+", "+n:n)}),t},$internals=Symbol("internals");function normalizeHeader(e){return e&&String(e).trim().toLowerCase()}function normalizeValue(e){return e===!1||e==null?e:utils$1.isArray(e)?e.map(normalizeValue):String(e)}function parseTokens(e){const t=Object.create(null),a=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let n;for(;n=a.exec(e);)t[n[1]]=n[2];return t}const isValidHeaderName=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function matchHeaderValue(e,t,a,n,r){if(utils$1.isFunction(n))return n.call(this,t,a);if(r&&(t=a),!!utils$1.isString(t)){if(utils$1.isString(n))return t.indexOf(n)!==-1;if(utils$1.isRegExp(n))return n.test(t)}}function formatHeader(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(t,a,n)=>a.toUpperCase()+n)}function buildAccessors(e,t){const a=utils$1.toCamelCase(" "+t);["get","set","has"].forEach(n=>{Object.defineProperty(e,n+a,{value:function(r,i,o){return this[n].call(this,t,r,i,o)},configurable:!0})})}class AxiosHeaders{constructor(t){t&&this.set(t)}set(t,a,n){const r=this;function i(s,u,l){const d=normalizeHeader(u);if(!d)throw new Error("header name must be a non-empty string");const h=utils$1.findKey(r,d);(!h||r[h]===void 0||l===!0||l===void 0&&r[h]!==!1)&&(r[h||u]=normalizeValue(s))}const o=(s,u)=>utils$1.forEach(s,(l,d)=>i(l,d,u));return utils$1.isPlainObject(t)||t instanceof this.constructor?o(t,a):utils$1.isString(t)&&(t=t.trim())&&!isValidHeaderName(t)?o(parseHeaders(t),a):t!=null&&i(a,t,n),this}get(t,a){if(t=normalizeHeader(t),t){const n=utils$1.findKey(this,t);if(n){const r=this[n];if(!a)return r;if(a===!0)return parseTokens(r);if(utils$1.isFunction(a))return a.call(this,r,n);if(utils$1.isRegExp(a))return a.exec(r);throw new TypeError("parser must be boolean|regexp|function")}}}has(t,a){if(t=normalizeHeader(t),t){const n=utils$1.findKey(this,t);return!!(n&&this[n]!==void 0&&(!a||matchHeaderValue(this,this[n],n,a)))}return!1}delete(t,a){const n=this;let r=!1;function i(o){if(o=normalizeHeader(o),o){const s=utils$1.findKey(n,o);s&&(!a||matchHeaderValue(n,n[s],s,a))&&(delete n[s],r=!0)}}return utils$1.isArray(t)?t.forEach(i):i(t),r}clear(t){const a=Object.keys(this);let n=a.length,r=!1;for(;n--;){const i=a[n];(!t||matchHeaderValue(this,this[i],i,t,!0))&&(delete this[i],r=!0)}return r}normalize(t){const a=this,n={};return utils$1.forEach(this,(r,i)=>{const o=utils$1.findKey(n,i);if(o){a[o]=normalizeValue(r),delete a[i];return}const s=t?formatHeader(i):String(i).trim();s!==i&&delete a[i],a[s]=normalizeValue(r),n[s]=!0}),this}concat(...t){return this.constructor.concat(this,...t)}toJSON(t){const a=Object.create(null);return utils$1.forEach(this,(n,r)=>{n!=null&&n!==!1&&(a[r]=t&&utils$1.isArray(n)?n.join(", "):n)}),a}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([t,a])=>t+": "+a).join(` +`)}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(t){return t instanceof this?t:new this(t)}static concat(t,...a){const n=new this(t);return a.forEach(r=>n.set(r)),n}static accessor(t){const n=(this[$internals]=this[$internals]={accessors:{}}).accessors,r=this.prototype;function i(o){const s=normalizeHeader(o);n[s]||(buildAccessors(r,o),n[s]=!0)}return utils$1.isArray(t)?t.forEach(i):i(t),this}}AxiosHeaders.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);utils$1.reduceDescriptors(AxiosHeaders.prototype,({value:e},t)=>{let a=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(n){this[a]=n}}});utils$1.freezeMethods(AxiosHeaders);const AxiosHeaders$1=AxiosHeaders;function transformData(e,t){const a=this||defaults$1,n=t||a,r=AxiosHeaders$1.from(n.headers);let i=n.data;return utils$1.forEach(e,function(s){i=s.call(a,i,r.normalize(),t?t.status:void 0)}),r.normalize(),i}function isCancel(e){return!!(e&&e.__CANCEL__)}function CanceledError(e,t,a){AxiosError.call(this,e??"canceled",AxiosError.ERR_CANCELED,t,a),this.name="CanceledError"}utils$1.inherits(CanceledError,AxiosError,{__CANCEL__:!0});function settle(e,t,a){const n=a.config.validateStatus;!a.status||!n||n(a.status)?e(a):t(new AxiosError("Request failed with status code "+a.status,[AxiosError.ERR_BAD_REQUEST,AxiosError.ERR_BAD_RESPONSE][Math.floor(a.status/100)-4],a.config,a.request,a))}const cookies=platform.hasStandardBrowserEnv?{write(e,t,a,n,r,i){const o=[e+"="+encodeURIComponent(t)];utils$1.isNumber(a)&&o.push("expires="+new Date(a).toGMTString()),utils$1.isString(n)&&o.push("path="+n),utils$1.isString(r)&&o.push("domain="+r),i===!0&&o.push("secure"),document.cookie=o.join("; ")},read(e){const t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove(e){this.write(e,"",Date.now()-864e5)}}:{write(){},read(){return null},remove(){}};function isAbsoluteURL(e){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)}function combineURLs(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}function buildFullPath(e,t){return e&&!isAbsoluteURL(t)?combineURLs(e,t):t}const isURLSameOrigin=platform.hasStandardBrowserEnv?function(){const t=/(msie|trident)/i.test(navigator.userAgent),a=document.createElement("a");let n;function r(i){let o=i;return t&&(a.setAttribute("href",o),o=a.href),a.setAttribute("href",o),{href:a.href,protocol:a.protocol?a.protocol.replace(/:$/,""):"",host:a.host,search:a.search?a.search.replace(/^\?/,""):"",hash:a.hash?a.hash.replace(/^#/,""):"",hostname:a.hostname,port:a.port,pathname:a.pathname.charAt(0)==="/"?a.pathname:"/"+a.pathname}}return n=r(window.location.href),function(o){const s=utils$1.isString(o)?r(o):o;return s.protocol===n.protocol&&s.host===n.host}}():function(){return function(){return!0}}();function parseProtocol(e){const t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||""}function speedometer(e,t){e=e||10;const a=new Array(e),n=new Array(e);let r=0,i=0,o;return t=t!==void 0?t:1e3,function(u){const l=Date.now(),d=n[i];o||(o=l),a[r]=u,n[r]=l;let h=i,y=0;for(;h!==r;)y+=a[h++],h=h%e;if(r=(r+1)%e,r===i&&(i=(i+1)%e),l-o{const i=r.loaded,o=r.lengthComputable?r.total:void 0,s=i-a,u=n(s),l=i<=o;a=i;const d={loaded:i,total:o,progress:o?i/o:void 0,bytes:s,rate:u||void 0,estimated:u&&o&&l?(o-i)/u:void 0,event:r};d[t?"download":"upload"]=!0,e(d)}}const isXHRAdapterSupported=typeof XMLHttpRequest<"u",xhrAdapter=isXHRAdapterSupported&&function(e){return new Promise(function(a,n){let r=e.data;const i=AxiosHeaders$1.from(e.headers).normalize();let{responseType:o,withXSRFToken:s}=e,u;function l(){e.cancelToken&&e.cancelToken.unsubscribe(u),e.signal&&e.signal.removeEventListener("abort",u)}let d;if(utils$1.isFormData(r)){if(platform.hasStandardBrowserEnv||platform.hasStandardBrowserWebWorkerEnv)i.setContentType(!1);else if((d=i.getContentType())!==!1){const[O,...A]=d?d.split(";").map(D=>D.trim()).filter(Boolean):[];i.setContentType([O||"multipart/form-data",...A].join("; "))}}let h=new XMLHttpRequest;if(e.auth){const O=e.auth.username||"",A=e.auth.password?unescape(encodeURIComponent(e.auth.password)):"";i.set("Authorization","Basic "+btoa(O+":"+A))}const y=buildFullPath(e.baseURL,e.url);h.open(e.method.toUpperCase(),buildURL(y,e.params,e.paramsSerializer),!0),h.timeout=e.timeout;function T(){if(!h)return;const O=AxiosHeaders$1.from("getAllResponseHeaders"in h&&h.getAllResponseHeaders()),D={data:!o||o==="text"||o==="json"?h.responseText:h.response,status:h.status,statusText:h.statusText,headers:O,config:e,request:h};settle(function(C){a(C),l()},function(C){n(C),l()},D),h=null}if("onloadend"in h?h.onloadend=T:h.onreadystatechange=function(){!h||h.readyState!==4||h.status===0&&!(h.responseURL&&h.responseURL.indexOf("file:")===0)||setTimeout(T)},h.onabort=function(){h&&(n(new AxiosError("Request aborted",AxiosError.ECONNABORTED,e,h)),h=null)},h.onerror=function(){n(new AxiosError("Network Error",AxiosError.ERR_NETWORK,e,h)),h=null},h.ontimeout=function(){let A=e.timeout?"timeout of "+e.timeout+"ms exceeded":"timeout exceeded";const D=e.transitional||transitionalDefaults;e.timeoutErrorMessage&&(A=e.timeoutErrorMessage),n(new AxiosError(A,D.clarifyTimeoutError?AxiosError.ETIMEDOUT:AxiosError.ECONNABORTED,e,h)),h=null},platform.hasStandardBrowserEnv&&(s&&utils$1.isFunction(s)&&(s=s(e)),s||s!==!1&&isURLSameOrigin(y))){const O=e.xsrfHeaderName&&e.xsrfCookieName&&cookies.read(e.xsrfCookieName);O&&i.set(e.xsrfHeaderName,O)}r===void 0&&i.setContentType(null),"setRequestHeader"in h&&utils$1.forEach(i.toJSON(),function(A,D){h.setRequestHeader(D,A)}),utils$1.isUndefined(e.withCredentials)||(h.withCredentials=!!e.withCredentials),o&&o!=="json"&&(h.responseType=e.responseType),typeof e.onDownloadProgress=="function"&&h.addEventListener("progress",progressEventReducer(e.onDownloadProgress,!0)),typeof e.onUploadProgress=="function"&&h.upload&&h.upload.addEventListener("progress",progressEventReducer(e.onUploadProgress)),(e.cancelToken||e.signal)&&(u=O=>{h&&(n(!O||O.type?new CanceledError(null,e,h):O),h.abort(),h=null)},e.cancelToken&&e.cancelToken.subscribe(u),e.signal&&(e.signal.aborted?u():e.signal.addEventListener("abort",u)));const M=parseProtocol(y);if(M&&platform.protocols.indexOf(M)===-1){n(new AxiosError("Unsupported protocol "+M+":",AxiosError.ERR_BAD_REQUEST,e));return}h.send(r||null)})},knownAdapters={http:httpAdapter,xhr:xhrAdapter};utils$1.forEach(knownAdapters,(e,t)=>{if(e){try{Object.defineProperty(e,"name",{value:t})}catch{}Object.defineProperty(e,"adapterName",{value:t})}});const renderReason=e=>`- ${e}`,isResolvedHandle=e=>utils$1.isFunction(e)||e===null||e===!1,adapters={getAdapter:e=>{e=utils$1.isArray(e)?e:[e];const{length:t}=e;let a,n;const r={};for(let i=0;i`adapter ${s} `+(u===!1?"is not supported by the environment":"is not available in the build"));let o=t?i.length>1?`since : +`+i.map(renderReason).join(` +`):" "+renderReason(i[0]):"as no adapter specified";throw new AxiosError("There is no suitable adapter to dispatch the request "+o,"ERR_NOT_SUPPORT")}return n},adapters:knownAdapters};function throwIfCancellationRequested(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new CanceledError(null,e)}function dispatchRequest(e){return throwIfCancellationRequested(e),e.headers=AxiosHeaders$1.from(e.headers),e.data=transformData.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1),adapters.getAdapter(e.adapter||defaults$1.adapter)(e).then(function(n){return throwIfCancellationRequested(e),n.data=transformData.call(e,e.transformResponse,n),n.headers=AxiosHeaders$1.from(n.headers),n},function(n){return isCancel(n)||(throwIfCancellationRequested(e),n&&n.response&&(n.response.data=transformData.call(e,e.transformResponse,n.response),n.response.headers=AxiosHeaders$1.from(n.response.headers))),Promise.reject(n)})}const headersToObject=e=>e instanceof AxiosHeaders$1?e.toJSON():e;function mergeConfig(e,t){t=t||{};const a={};function n(l,d,h){return utils$1.isPlainObject(l)&&utils$1.isPlainObject(d)?utils$1.merge.call({caseless:h},l,d):utils$1.isPlainObject(d)?utils$1.merge({},d):utils$1.isArray(d)?d.slice():d}function r(l,d,h){if(utils$1.isUndefined(d)){if(!utils$1.isUndefined(l))return n(void 0,l,h)}else return n(l,d,h)}function i(l,d){if(!utils$1.isUndefined(d))return n(void 0,d)}function o(l,d){if(utils$1.isUndefined(d)){if(!utils$1.isUndefined(l))return n(void 0,l)}else return n(void 0,d)}function s(l,d,h){if(h in t)return n(l,d);if(h in e)return n(void 0,l)}const u={url:i,method:i,data:i,baseURL:o,transformRequest:o,transformResponse:o,paramsSerializer:o,timeout:o,timeoutMessage:o,withCredentials:o,withXSRFToken:o,adapter:o,responseType:o,xsrfCookieName:o,xsrfHeaderName:o,onUploadProgress:o,onDownloadProgress:o,decompress:o,maxContentLength:o,maxBodyLength:o,beforeRedirect:o,transport:o,httpAgent:o,httpsAgent:o,cancelToken:o,socketPath:o,responseEncoding:o,validateStatus:s,headers:(l,d)=>r(headersToObject(l),headersToObject(d),!0)};return utils$1.forEach(Object.keys(Object.assign({},e,t)),function(d){const h=u[d]||r,y=h(e[d],t[d],d);utils$1.isUndefined(y)&&h!==s||(a[d]=y)}),a}const VERSION$1="1.6.2",validators$1={};["object","boolean","number","function","string","symbol"].forEach((e,t)=>{validators$1[e]=function(n){return typeof n===e||"a"+(t<1?"n ":" ")+e}});const deprecatedWarnings={};validators$1.transitional=function(t,a,n){function r(i,o){return"[Axios v"+VERSION$1+"] Transitional option '"+i+"'"+o+(n?". "+n:"")}return(i,o,s)=>{if(t===!1)throw new AxiosError(r(o," has been removed"+(a?" in "+a:"")),AxiosError.ERR_DEPRECATED);return a&&!deprecatedWarnings[o]&&(deprecatedWarnings[o]=!0,console.warn(r(o," has been deprecated since v"+a+" and will be removed in the near future"))),t?t(i,o,s):!0}};function assertOptions(e,t,a){if(typeof e!="object")throw new AxiosError("options must be an object",AxiosError.ERR_BAD_OPTION_VALUE);const n=Object.keys(e);let r=n.length;for(;r-- >0;){const i=n[r],o=t[i];if(o){const s=e[i],u=s===void 0||o(s,i,e);if(u!==!0)throw new AxiosError("option "+i+" must be "+u,AxiosError.ERR_BAD_OPTION_VALUE);continue}if(a!==!0)throw new AxiosError("Unknown option "+i,AxiosError.ERR_BAD_OPTION)}}const validator={assertOptions,validators:validators$1},validators=validator.validators;class Axios{constructor(t){this.defaults=t,this.interceptors={request:new InterceptorManager$1,response:new InterceptorManager$1}}request(t,a){typeof t=="string"?(a=a||{},a.url=t):a=t||{},a=mergeConfig(this.defaults,a);const{transitional:n,paramsSerializer:r,headers:i}=a;n!==void 0&&validator.assertOptions(n,{silentJSONParsing:validators.transitional(validators.boolean),forcedJSONParsing:validators.transitional(validators.boolean),clarifyTimeoutError:validators.transitional(validators.boolean)},!1),r!=null&&(utils$1.isFunction(r)?a.paramsSerializer={serialize:r}:validator.assertOptions(r,{encode:validators.function,serialize:validators.function},!0)),a.method=(a.method||this.defaults.method||"get").toLowerCase();let o=i&&utils$1.merge(i.common,i[a.method]);i&&utils$1.forEach(["delete","get","head","post","put","patch","common"],M=>{delete i[M]}),a.headers=AxiosHeaders$1.concat(o,i);const s=[];let u=!0;this.interceptors.request.forEach(function(O){typeof O.runWhen=="function"&&O.runWhen(a)===!1||(u=u&&O.synchronous,s.unshift(O.fulfilled,O.rejected))});const l=[];this.interceptors.response.forEach(function(O){l.push(O.fulfilled,O.rejected)});let d,h=0,y;if(!u){const M=[dispatchRequest.bind(this),void 0];for(M.unshift.apply(M,s),M.push.apply(M,l),y=M.length,d=Promise.resolve(a);h{if(!n._listeners)return;let i=n._listeners.length;for(;i-- >0;)n._listeners[i](r);n._listeners=null}),this.promise.then=r=>{let i;const o=new Promise(s=>{n.subscribe(s),i=s}).then(r);return o.cancel=function(){n.unsubscribe(i)},o},t(function(i,o,s){n.reason||(n.reason=new CanceledError(i,o,s),a(n.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(t){if(this.reason){t(this.reason);return}this._listeners?this._listeners.push(t):this._listeners=[t]}unsubscribe(t){if(!this._listeners)return;const a=this._listeners.indexOf(t);a!==-1&&this._listeners.splice(a,1)}static source(){let t;return{token:new CancelToken(function(r){t=r}),cancel:t}}}const CancelToken$1=CancelToken;function spread(e){return function(a){return e.apply(null,a)}}function isAxiosError(e){return utils$1.isObject(e)&&e.isAxiosError===!0}const HttpStatusCode={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(HttpStatusCode).forEach(([e,t])=>{HttpStatusCode[t]=e});const HttpStatusCode$1=HttpStatusCode;function createInstance(e){const t=new Axios$1(e),a=bind$4(Axios$1.prototype.request,t);return utils$1.extend(a,Axios$1.prototype,t,{allOwnKeys:!0}),utils$1.extend(a,t,null,{allOwnKeys:!0}),a.create=function(r){return createInstance(mergeConfig(e,r))},a}const axios=createInstance(defaults$1);axios.Axios=Axios$1;axios.CanceledError=CanceledError;axios.CancelToken=CancelToken$1;axios.isCancel=isCancel;axios.VERSION=VERSION$1;axios.toFormData=toFormData;axios.AxiosError=AxiosError;axios.Cancel=axios.CanceledError;axios.all=function(t){return Promise.all(t)};axios.spread=spread;axios.isAxiosError=isAxiosError;axios.mergeConfig=mergeConfig;axios.AxiosHeaders=AxiosHeaders$1;axios.formToJSON=e=>formDataToJSON(utils$1.isHTMLForm(e)?new FormData(e):e);axios.getAdapter=adapters.getAdapter;axios.HttpStatusCode=HttpStatusCode$1;axios.default=axios;const axios$1=axios;var commonjsGlobal=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function getDefaultExportFromCjs(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var assign=make_assign(),create$2=make_create(),trim$1=make_trim(),Global$5=typeof window<"u"?window:commonjsGlobal,util$7={assign,create:create$2,trim:trim$1,bind:bind$3,slice:slice$2,each:each$8,map,pluck:pluck$1,isList:isList$1,isFunction:isFunction$4,isObject:isObject$a,Global:Global$5};function make_assign(){return Object.assign?Object.assign:function(t,a,n,r){for(var i=1;i"u"?null:console;if(e){var t=e.warn?e.warn:e.log;t.apply(e,arguments)}}function createStore(e,t,a){a||(a=""),e&&!isList(e)&&(e=[e]),t&&!isList(t)&&(t=[t]);var n=a?"__storejs_"+a+"_":"",r=a?new RegExp("^"+n):null,i=/^[a-zA-Z0-9_\-]*$/;if(!i.test(a))throw new Error("store.js namespaces can only have alphanumerics + underscores and dashes");var o={_namespacePrefix:n,_namespaceRegexp:r,_testStorage:function(u){try{var l="__storejs__test__";u.write(l,l);var d=u.read(l)===l;return u.remove(l),d}catch{return!1}},_assignPluginFnProp:function(u,l){var d=this[l];this[l]=function(){var y=slice$1(arguments,0),T=this;function M(){if(d)return each$7(arguments,function(A,D){y[D]=A}),d.apply(T,y)}var O=[M].concat(y);return u.apply(T,O)}},_serialize:function(u){return JSON.stringify(u)},_deserialize:function(u,l){if(!u)return l;var d="";try{d=JSON.parse(u)}catch{d=u}return d!==void 0?d:l},_addStorage:function(u){this.enabled||this._testStorage(u)&&(this.storage=u,this.enabled=!0)},_addPlugin:function(u){var l=this;if(isList(u)){each$7(u,function(y){l._addPlugin(y)});return}var d=pluck(this.plugins,function(y){return u===y});if(!d){if(this.plugins.push(u),!isFunction$3(u))throw new Error("Plugins must be function values that return objects");var h=u.call(this);if(!isObject$9(h))throw new Error("Plugins must return an object of function properties");each$7(h,function(y,T){if(!isFunction$3(y))throw new Error("Bad plugin property: "+T+" from plugin "+u.name+". Plugins should only return functions.");l._assignPluginFnProp(y,T)})}},addStorage:function(u){_warn("store.addStorage(storage) is deprecated. Use createStore([storages])"),this._addStorage(u)}},s=create$1(o,storeAPI,{plugins:[]});return s.raw={},each$7(s,function(u,l){isFunction$3(u)&&(s.raw[l]=bind$2(s,u))}),each$7(e,function(u){s._addStorage(u)}),each$7(t,function(u){s._addPlugin(u)}),s}var util$5=util$7,Global$4=util$5.Global,localStorage_1={name:"localStorage",read:read$6,write:write$6,each:each$6,remove:remove$5,clearAll:clearAll$5};function localStorage(){return Global$4.localStorage}function read$6(e){return localStorage().getItem(e)}function write$6(e,t){return localStorage().setItem(e,t)}function each$6(e){for(var t=localStorage().length-1;t>=0;t--){var a=localStorage().key(t);e(read$6(a),a)}}function remove$5(e){return localStorage().removeItem(e)}function clearAll$5(){return localStorage().clear()}var util$4=util$7,Global$3=util$4.Global,oldFFGlobalStorage={name:"oldFF-globalStorage",read:read$5,write:write$5,each:each$5,remove:remove$4,clearAll:clearAll$4},globalStorage=Global$3.globalStorage;function read$5(e){return globalStorage[e]}function write$5(e,t){globalStorage[e]=t}function each$5(e){for(var t=globalStorage.length-1;t>=0;t--){var a=globalStorage.key(t);e(globalStorage[a],a)}}function remove$4(e){return globalStorage.removeItem(e)}function clearAll$4(){each$5(function(e,t){delete globalStorage[e]})}var util$3=util$7,Global$2=util$3.Global,oldIEUserDataStorage={name:"oldIE-userDataStorage",write:write$4,read:read$4,each:each$4,remove:remove$3,clearAll:clearAll$3},storageName="storejs",doc$1=Global$2.document,_withStorageEl=_makeIEStorageElFunction(),disable=(Global$2.navigator?Global$2.navigator.userAgent:"").match(/ (MSIE 8|MSIE 9|MSIE 10)\./);function write$4(e,t){if(!disable){var a=fixKey(e);_withStorageEl(function(n){n.setAttribute(a,t),n.save(storageName)})}}function read$4(e){if(!disable){var t=fixKey(e),a=null;return _withStorageEl(function(n){a=n.getAttribute(t)}),a}}function each$4(e){_withStorageEl(function(t){for(var a=t.XMLDocument.documentElement.attributes,n=a.length-1;n>=0;n--){var r=a[n];e(t.getAttribute(r.name),r.name)}})}function remove$3(e){var t=fixKey(e);_withStorageEl(function(a){a.removeAttribute(t),a.save(storageName)})}function clearAll$3(){_withStorageEl(function(e){var t=e.XMLDocument.documentElement.attributes;e.load(storageName);for(var a=t.length-1;a>=0;a--)e.removeAttribute(t[a].name);e.save(storageName)})}var forbiddenCharsRegex=new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]","g");function fixKey(e){return e.replace(/^\d/,"___$&").replace(forbiddenCharsRegex,"___")}function _makeIEStorageElFunction(){if(!doc$1||!doc$1.documentElement||!doc$1.documentElement.addBehavior)return null;var e="script",t,a,n;try{a=new ActiveXObject("htmlfile"),a.open(),a.write("<"+e+">document.w=window'),a.close(),t=a.w.frames[0].document,n=t.createElement("div")}catch{n=doc$1.createElement("div"),t=doc$1.body}return function(r){var i=[].slice.call(arguments,0);i.unshift(n),t.appendChild(n),n.addBehavior("#default#userData"),n.load(storageName),r.apply(this,i),t.removeChild(n)}}var util$2=util$7,Global$1=util$2.Global,trim=util$2.trim,cookieStorage={name:"cookieStorage",read:read$3,write:write$3,each:each$3,remove:remove$2,clearAll:clearAll$2},doc=Global$1.document;function read$3(e){if(!e||!_has(e))return null;var t="(?:^|.*;\\s*)"+escape(e).replace(/[\-\.\+\*]/g,"\\$&")+"\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*";return unescape(doc.cookie.replace(new RegExp(t),"$1"))}function each$3(e){for(var t=doc.cookie.split(/; ?/g),a=t.length-1;a>=0;a--)if(trim(t[a])){var n=t[a].split("="),r=unescape(n[0]),i=unescape(n[1]);e(i,r)}}function write$3(e,t){e&&(doc.cookie=escape(e)+"="+escape(t)+"; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/")}function remove$2(e){!e||!_has(e)||(doc.cookie=escape(e)+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/")}function clearAll$2(){each$3(function(e,t){remove$2(t)})}function _has(e){return new RegExp("(?:^|;\\s*)"+escape(e).replace(/[\-\.\+\*]/g,"\\$&")+"\\s*\\=").test(doc.cookie)}var util$1=util$7,Global=util$1.Global,sessionStorage_1={name:"sessionStorage",read:read$2,write:write$2,each:each$2,remove:remove$1,clearAll:clearAll$1};function sessionStorage(){return Global.sessionStorage}function read$2(e){return sessionStorage().getItem(e)}function write$2(e,t){return sessionStorage().setItem(e,t)}function each$2(e){for(var t=sessionStorage().length-1;t>=0;t--){var a=sessionStorage().key(t);e(read$2(a),a)}}function remove$1(e){return sessionStorage().removeItem(e)}function clearAll$1(){return sessionStorage().clear()}var memoryStorage_1={name:"memoryStorage",read:read$1,write:write$1,each:each$1,remove,clearAll},memoryStorage={};function read$1(e){return memoryStorage[e]}function write$1(e,t){memoryStorage[e]=t}function each$1(e){for(var t in memoryStorage)memoryStorage.hasOwnProperty(t)&&e(memoryStorage[t],t)}function remove(e){delete memoryStorage[e]}function clearAll(e){memoryStorage={}}var all=[localStorage_1,oldFFGlobalStorage,oldIEUserDataStorage,cookieStorage,sessionStorage_1,memoryStorage_1],json2$1={},hasRequiredJson2;function requireJson2(){return hasRequiredJson2||(hasRequiredJson2=1,typeof JSON!="object"&&(JSON={}),function(){var rx_one=/^[\],:{}\s]*$/,rx_two=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,rx_three=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,rx_four=/(?:^|:|,)(?:\s*\[)+/g,rx_escapable=/[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,rx_dangerous=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;function f(e){return e<10?"0"+e:e}function this_value(){return this.valueOf()}typeof Date.prototype.toJSON!="function"&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},Boolean.prototype.toJSON=this_value,Number.prototype.toJSON=this_value,String.prototype.toJSON=this_value);var gap,indent,meta,rep;function quote(e){return rx_escapable.lastIndex=0,rx_escapable.test(e)?'"'+e.replace(rx_escapable,function(t){var a=meta[t];return typeof a=="string"?a:"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+e+'"'}function str(e,t){var a,n,r,i,o=gap,s,u=t[e];switch(u&&typeof u=="object"&&typeof u.toJSON=="function"&&(u=u.toJSON(e)),typeof rep=="function"&&(u=rep.call(t,e,u)),typeof u){case"string":return quote(u);case"number":return isFinite(u)?String(u):"null";case"boolean":case"null":return String(u);case"object":if(!u)return"null";if(gap+=indent,s=[],Object.prototype.toString.apply(u)==="[object Array]"){for(i=u.length,a=0;alastFlushedIndex&&queue.splice(t,1)}function queueFlush(){!flushing&&!flushPending&&(flushPending=!0,queueMicrotask(flushJobs))}function flushJobs(){flushPending=!1,flushing=!0;for(let e=0;ee.effect(t,{scheduler:a=>{shouldSchedule?scheduler(a):a()}}),raw=e.raw}function overrideEffect(e){effect$3=e}function elementBoundEffect(e){let t=()=>{};return[n=>{let r=effect$3(n);return e._x_effects||(e._x_effects=new Set,e._x_runEffects=()=>{e._x_effects.forEach(i=>i())}),e._x_effects.add(r),t=()=>{r!==void 0&&(e._x_effects.delete(r),release(r))},r},()=>{t()}]}function dispatch(e,t,a={}){e.dispatchEvent(new CustomEvent(t,{detail:a,bubbles:!0,composed:!0,cancelable:!0}))}function walk(e,t){if(typeof ShadowRoot=="function"&&e instanceof ShadowRoot){Array.from(e.children).forEach(r=>walk(r,t));return}let a=!1;if(t(e,()=>a=!0),a)return;let n=e.firstElementChild;for(;n;)walk(n,t),n=n.nextElementSibling}function warn(e,...t){console.warn(`Alpine Warning: ${e}`,...t)}var started=!1;function start$1(){started&&warn("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."),started=!0,document.body||warn("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's `