From 1d138eed8d91bf4024d40e28de89268a23386d04 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 28 Oct 2023 06:58:33 +0200 Subject: [PATCH] Refactor currency repository. --- .../Autocomplete/CurrencyController.php | 2 +- .../TransactionCurrency/DestroyController.php | 2 +- .../TransactionCurrency/ListController.php | 21 - .../TransactionCurrency/ShowController.php | 6 +- .../TransactionCurrency/StoreController.php | 2 +- .../TransactionCurrency/UpdateController.php | 13 +- .../Controllers/Summary/BasicController.php | 2 +- .../Controllers/Chart/CategoryController.php | 2 +- .../Controllers/Summary/BasicController.php | 2 +- .../CorrectOpeningBalanceCurrencies.php | 2 +- .../Commands/Upgrade/AccountCurrencies.php | 2 +- .../Commands/Upgrade/BudgetLimitCurrency.php | 2 +- app/Factory/TransactionJournalFactory.php | 2 +- app/Helpers/Report/NetWorth.php | 2 +- app/Helpers/Report/PopupReport.php | 2 +- .../Budget/BudgetLimitController.php | 2 +- .../Controllers/Budget/IndexController.php | 2 +- .../Controllers/Chart/AccountController.php | 2 +- app/Http/Controllers/CurrencyController.php | 48 -- app/Http/Controllers/JavascriptController.php | 2 +- app/Http/Controllers/Json/BoxController.php | 2 +- app/Http/Controllers/NewUserController.php | 5 +- .../TransactionCurrency/CreateController.php | 2 +- .../TransactionCurrency/DeleteController.php | 2 +- .../TransactionCurrency/EditController.php | 2 +- .../TransactionCurrency/IndexController.php | 20 +- app/Models/Account.php | 9 + app/Models/TransactionCurrency.php | 8 +- app/Models/UserGroup.php | 2 + app/Providers/CurrencyServiceProvider.php | 15 + app/Repositories/Account/AccountTasker.php | 2 +- .../Currency/CurrencyRepository.php | 503 +----------------- .../Currency/CurrencyRepositoryInterface.php | 204 +------ .../Currency/CurrencyRepository.php | 418 +++++++++++++++ .../Currency/CurrencyRepositoryInterface.php | 129 +++++ .../Internal/Update/JournalUpdateService.php | 2 +- app/Support/Amount.php | 24 +- app/Support/Form/CurrencyForm.php | 2 +- .../Http/Controllers/ChartGeneration.php | 5 +- app/Support/Search/OperatorQuerySearch.php | 2 +- app/User.php | 2 + composer.lock | 14 +- 42 files changed, 676 insertions(+), 818 deletions(-) delete mode 100644 app/Http/Controllers/CurrencyController.php create mode 100644 app/Repositories/UserGroups/Currency/CurrencyRepository.php create mode 100644 app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php diff --git a/app/Api/V1/Controllers/Autocomplete/CurrencyController.php b/app/Api/V1/Controllers/Autocomplete/CurrencyController.php index 2250b32f33..4478213d73 100644 --- a/app/Api/V1/Controllers/Autocomplete/CurrencyController.php +++ b/app/Api/V1/Controllers/Autocomplete/CurrencyController.php @@ -26,7 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\User; use Illuminate\Http\JsonResponse; diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php index 1cfb07d391..a115094368 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php @@ -27,7 +27,7 @@ namespace FireflyIII\Api\V1\Controllers\Models\TransactionCurrency; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Http\JsonResponse; diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php index cd52b0060b..5360c6ead8 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php @@ -38,7 +38,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use FireflyIII\Support\Http\Api\AccountFilter; @@ -65,26 +64,6 @@ class ListController extends Controller use AccountFilter; use TransactionFilter; - private CurrencyRepositoryInterface $repository; - - /** - * CurrencyRepository constructor. - * - - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - $this->repository = app(CurrencyRepositoryInterface::class); - $this->repository->setUser(auth()->user()); - - return $next($request); - } - ); - } - /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/listAccountByCurrency diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php index 6f2d001fea..0b22a06dac 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php @@ -27,7 +27,7 @@ namespace FireflyIII\Api\V1\Controllers\Models\TransactionCurrency; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Transformers\CurrencyTransformer; @@ -116,7 +116,7 @@ class ShowController extends Controller /** @var User $user */ $user = auth()->user(); $manager = $this->getManager(); - $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); + $defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); $this->parameters->set('defaultCurrency', $defaultCurrency); // update fields with user info. @@ -146,7 +146,7 @@ class ShowController extends Controller /** @var User $user */ $user = auth()->user(); $manager = $this->getManager(); - $currency = app('amount')->getDefaultCurrencyByUser($user); + $currency = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); // update fields with user info. $currency->refreshForUser($user); diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php index 12a8db0d50..f37f58e672 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php @@ -27,7 +27,7 @@ namespace FireflyIII\Api\V1\Controllers\Models\TransactionCurrency; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Models\TransactionCurrency\StoreRequest; use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Transformers\CurrencyTransformer; diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php index 4401c8a50a..aea98eb6d6 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php @@ -28,7 +28,7 @@ use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Models\TransactionCurrency\UpdateRequest; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Transformers\CurrencyTransformer; @@ -83,6 +83,10 @@ class UpdateController extends Controller if ($this->repository->currencyInUse($currency)) { return response()->json([], 409); } + // must not be the only one in use: + if (1 === $this->repository->get()->count()) { + return response()->json([], 409); + } /** @var User $user */ $user = auth()->user(); $this->repository->disable($currency); @@ -180,6 +184,13 @@ class UpdateController extends Controller /** @var User $user */ $user = auth()->user(); + + // safety catch on currency disablement. + $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); app('preferences')->mark(); diff --git a/app/Api/V1/Controllers/Summary/BasicController.php b/app/Api/V1/Controllers/Summary/BasicController.php index 28ec3b227a..4958b2a72d 100644 --- a/app/Api/V1/Controllers/Summary/BasicController.php +++ b/app/Api/V1/Controllers/Summary/BasicController.php @@ -39,7 +39,7 @@ use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\User; use Illuminate\Http\JsonResponse; diff --git a/app/Api/V2/Controllers/Chart/CategoryController.php b/app/Api/V2/Controllers/Chart/CategoryController.php index 0ca99d6c36..6346c40f66 100644 --- a/app/Api/V2/Controllers/Chart/CategoryController.php +++ b/app/Api/V2/Controllers/Chart/CategoryController.php @@ -33,7 +33,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\AccountType; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Api\CleansChartData; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; diff --git a/app/Api/V2/Controllers/Summary/BasicController.php b/app/Api/V2/Controllers/Summary/BasicController.php index 3b35875c86..bc88c3dd50 100644 --- a/app/Api/V2/Controllers/Summary/BasicController.php +++ b/app/Api/V2/Controllers/Summary/BasicController.php @@ -41,7 +41,7 @@ use FireflyIII\Repositories\UserGroups\Bill\BillRepositoryInterface; use FireflyIII\Repositories\UserGroups\Budget\AvailableBudgetRepositoryInterface; use FireflyIII\Repositories\UserGroups\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\UserGroups\Budget\OperationsRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\User; diff --git a/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php b/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php index 6fb2ea7116..0ee4229960 100644 --- a/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php +++ b/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php @@ -162,6 +162,6 @@ class CorrectOpeningBalanceCurrencies extends Command $repos = app(AccountRepositoryInterface::class); $repos->setUser($account->user); - return $repos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUser($account->user); + return $repos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->userGroup); } } diff --git a/app/Console/Commands/Upgrade/AccountCurrencies.php b/app/Console/Commands/Upgrade/AccountCurrencies.php index c470b70a1d..da588a219d 100644 --- a/app/Console/Commands/Upgrade/AccountCurrencies.php +++ b/app/Console/Commands/Upgrade/AccountCurrencies.php @@ -129,7 +129,7 @@ class AccountCurrencies extends Command $accounts = $this->accountRepos->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); // get user's currency preference: - $defaultCurrency = app('amount')->getDefaultCurrencyByUser($user); + $defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); /** @var Account $account */ foreach ($accounts as $account) { diff --git a/app/Console/Commands/Upgrade/BudgetLimitCurrency.php b/app/Console/Commands/Upgrade/BudgetLimitCurrency.php index 7cbd97aba6..476b5ff1ac 100644 --- a/app/Console/Commands/Upgrade/BudgetLimitCurrency.php +++ b/app/Console/Commands/Upgrade/BudgetLimitCurrency.php @@ -77,7 +77,7 @@ class BudgetLimitCurrency extends Command if (null !== $budget) { $user = $budget->user; if (null !== $user) { - $currency = app('amount')->getDefaultCurrencyByUser($user); + $currency = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); $budgetLimit->transaction_currency_id = $currency->id; $budgetLimit->save(); $this->friendlyInfo( diff --git a/app/Factory/TransactionJournalFactory.php b/app/Factory/TransactionJournalFactory.php index 7321c86b81..14b71ee641 100644 --- a/app/Factory/TransactionJournalFactory.php +++ b/app/Factory/TransactionJournalFactory.php @@ -39,7 +39,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\TransactionType\TransactionTypeRepositoryInterface; use FireflyIII\Services\Internal\Destroy\JournalDestroyService; diff --git a/app/Helpers/Report/NetWorth.php b/app/Helpers/Report/NetWorth.php index f8505c7321..99a15e67c1 100644 --- a/app/Helpers/Report/NetWorth.php +++ b/app/Helpers/Report/NetWorth.php @@ -29,7 +29,7 @@ use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\UserGroup; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface; use FireflyIII\Support\CacheProperties; use FireflyIII\Support\Http\Api\ExchangeRateConverter; diff --git a/app/Helpers/Report/PopupReport.php b/app/Helpers/Report/PopupReport.php index 94b56ac7fd..287cbca014 100644 --- a/app/Helpers/Report/PopupReport.php +++ b/app/Helpers/Report/PopupReport.php @@ -29,7 +29,7 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\Category; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use Illuminate\Support\Collection; diff --git a/app/Http/Controllers/Budget/BudgetLimitController.php b/app/Http/Controllers/Budget/BudgetLimitController.php index ee6c02d6b2..8cc83335cd 100644 --- a/app/Http/Controllers/Budget/BudgetLimitController.php +++ b/app/Http/Controllers/Budget/BudgetLimitController.php @@ -32,7 +32,7 @@ use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Controllers\DateCalculation; use Illuminate\Contracts\View\Factory; use Illuminate\Http\JsonResponse; diff --git a/app/Http/Controllers/Budget/IndexController.php b/app/Http/Controllers/Budget/IndexController.php index 550e1348a2..22eb85d586 100644 --- a/app/Http/Controllers/Budget/IndexController.php +++ b/app/Http/Controllers/Budget/IndexController.php @@ -34,7 +34,7 @@ use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Controllers\DateCalculation; use Illuminate\Contracts\View\Factory; use Illuminate\Http\JsonResponse; diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php index 00c6a5c661..ab4cdf48e3 100644 --- a/app/Http/Controllers/Chart/AccountController.php +++ b/app/Http/Controllers/Chart/AccountController.php @@ -33,7 +33,7 @@ use FireflyIII\Models\AccountType; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\CacheProperties; use FireflyIII\Support\Http\Controllers\AugumentData; use FireflyIII\Support\Http\Controllers\ChartGeneration; diff --git a/app/Http/Controllers/CurrencyController.php b/app/Http/Controllers/CurrencyController.php deleted file mode 100644 index 203cf27074..0000000000 --- a/app/Http/Controllers/CurrencyController.php +++ /dev/null @@ -1,48 +0,0 @@ -. - */ -declare(strict_types=1); - -namespace FireflyIII\Http\Controllers; - -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Http\Requests\CurrencyFormRequest; -use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; -use FireflyIII\Repositories\User\UserRepositoryInterface; -use FireflyIII\User; -use Illuminate\Contracts\View\Factory; -use Illuminate\Http\JsonResponse; -use Illuminate\Http\RedirectResponse; -use Illuminate\Http\Request; -use Illuminate\Pagination\LengthAwarePaginator; -use Illuminate\Routing\Redirector; -use Illuminate\Support\Facades\Log; -use Illuminate\View\View; -use Psr\Container\ContainerExceptionInterface; -use Psr\Container\NotFoundExceptionInterface; - -/** - * Class CurrencyController. - */ -class CurrencyController extends Controller -{ -} diff --git a/app/Http/Controllers/JavascriptController.php b/app/Http/Controllers/JavascriptController.php index 66ec5e7242..614f491830 100644 --- a/app/Http/Controllers/JavascriptController.php +++ b/app/Http/Controllers/JavascriptController.php @@ -29,7 +29,7 @@ use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Controllers\GetConfigurationData; use Illuminate\Http\Request; use Illuminate\Http\Response; diff --git a/app/Http/Controllers/Json/BoxController.php b/app/Http/Controllers/Json/BoxController.php index c47451fa71..5e270878d3 100644 --- a/app/Http/Controllers/Json/BoxController.php +++ b/app/Http/Controllers/Json/BoxController.php @@ -35,7 +35,7 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Log; diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php index 5f85744279..3a2d8e8680 100644 --- a/app/Http/Controllers/NewUserController.php +++ b/app/Http/Controllers/NewUserController.php @@ -27,7 +27,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Requests\NewUserFormRequest; use FireflyIII\Models\AccountType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Controllers\CreateStuff; use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; @@ -41,8 +41,7 @@ class NewUserController extends Controller { use CreateStuff; - /** @var AccountRepositoryInterface The account repository */ - private $repository; + private AccountRepositoryInterface $repository; /** * NewUserController constructor. diff --git a/app/Http/Controllers/TransactionCurrency/CreateController.php b/app/Http/Controllers/TransactionCurrency/CreateController.php index f293a9dd80..92a26333cf 100644 --- a/app/Http/Controllers/TransactionCurrency/CreateController.php +++ b/app/Http/Controllers/TransactionCurrency/CreateController.php @@ -7,7 +7,7 @@ namespace FireflyIII\Http\Controllers\TransactionCurrency; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\CurrencyFormRequest; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Contracts\View\Factory; diff --git a/app/Http/Controllers/TransactionCurrency/DeleteController.php b/app/Http/Controllers/TransactionCurrency/DeleteController.php index 87a1ad2217..76f95452e4 100644 --- a/app/Http/Controllers/TransactionCurrency/DeleteController.php +++ b/app/Http/Controllers/TransactionCurrency/DeleteController.php @@ -6,7 +6,7 @@ namespace FireflyIII\Http\Controllers\TransactionCurrency; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Contracts\View\Factory; diff --git a/app/Http/Controllers/TransactionCurrency/EditController.php b/app/Http/Controllers/TransactionCurrency/EditController.php index 3ffd571991..037163caa4 100644 --- a/app/Http/Controllers/TransactionCurrency/EditController.php +++ b/app/Http/Controllers/TransactionCurrency/EditController.php @@ -7,7 +7,7 @@ namespace FireflyIII\Http\Controllers\TransactionCurrency; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\CurrencyFormRequest; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Contracts\View\Factory; diff --git a/app/Http/Controllers/TransactionCurrency/IndexController.php b/app/Http/Controllers/TransactionCurrency/IndexController.php index 2692e8be87..ab750dac49 100644 --- a/app/Http/Controllers/TransactionCurrency/IndexController.php +++ b/app/Http/Controllers/TransactionCurrency/IndexController.php @@ -4,18 +4,14 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\TransactionCurrency; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Contracts\View\Factory; -use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; -use Illuminate\Routing\Redirector; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -58,12 +54,12 @@ class IndexController extends Controller public function index(Request $request) { /** @var User $user */ - $user = auth()->user(); - $page = 0 === (int)$request->get('page') ? 1 : (int)$request->get('page'); - $pageSize = (int)app('preferences')->get('listPageSize', 50)->data; - $collection = $this->repository->getAll(); - $total = $collection->count(); - $collection = $collection->slice(($page - 1) * $pageSize, $pageSize); + $user = auth()->user(); + $page = 0 === (int)$request->get('page') ? 1 : (int)$request->get('page'); + $pageSize = (int)app('preferences')->get('listPageSize', 50)->data; + $collection = $this->repository->getAll(); + $total = $collection->count(); + $collection = $collection->slice(($page - 1) * $pageSize, $pageSize); // order so default is on top: $collection = $collection->sortBy( @@ -76,7 +72,7 @@ class IndexController extends Controller $currencies = new LengthAwarePaginator($collection, $total, $pageSize, $page); $currencies->setPath(route('currencies.index')); - $isOwner = true; + $isOwner = true; if (!$this->userRepository->hasRole($user, 'owner')) { $request->session()->flash('info', (string)trans('firefly.ask_site_owner', ['owner' => config('firefly.site_owner')])); $isOwner = false; diff --git a/app/Models/Account.php b/app/Models/Account.php index c44881ef38..11fc5c1aa4 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -103,6 +103,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property string $current_debt * @property int|null $user_group_id * @method static EloquentBuilder|Account whereUserGroupId($value) + * @property-read \FireflyIII\Models\UserGroup|null $userGroup * @mixin Eloquent */ class Account extends Model @@ -283,6 +284,14 @@ class Account extends Model return $this->hasMany(Transaction::class); } + /** + * @return BelongsTo + */ + public function userGroup(): BelongsTo + { + return $this->belongsTo(UserGroup::class); + } + /** * Get the virtual balance * diff --git a/app/Models/TransactionCurrency.php b/app/Models/TransactionCurrency.php index cf08a38f71..868b38f008 100644 --- a/app/Models/TransactionCurrency.php +++ b/app/Models/TransactionCurrency.php @@ -69,6 +69,10 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @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 * @mixin Eloquent */ class TransactionCurrency extends Model @@ -118,8 +122,8 @@ class TransactionCurrency extends Model */ public function refreshForUser(User $user) { - $current = $user->currencies()->where('transaction_currencies.id', $this->id)->first(); - $default = app('amount')->getDefaultCurrencyByUser($user); + $current = $user->userGroup->currencies()->where('transaction_currencies.id', $this->id)->first(); + $default = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); $this->userDefault = (int)$default->id === (int)$this->id; $this->userEnabled = null !== $current; } diff --git a/app/Models/UserGroup.php b/app/Models/UserGroup.php index e1958fa7c5..8d8a2dcbb0 100644 --- a/app/Models/UserGroup.php +++ b/app/Models/UserGroup.php @@ -86,6 +86,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @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 * @mixin Eloquent */ class UserGroup extends Model diff --git a/app/Providers/CurrencyServiceProvider.php b/app/Providers/CurrencyServiceProvider.php index a543385757..47b9070c33 100644 --- a/app/Providers/CurrencyServiceProvider.php +++ b/app/Providers/CurrencyServiceProvider.php @@ -25,6 +25,8 @@ namespace FireflyIII\Providers; use FireflyIII\Repositories\Currency\CurrencyRepository; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepository as GroupCurrencyRepository; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface as GroupCurrencyRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -55,6 +57,19 @@ class CurrencyServiceProvider extends ServiceProvider $repository->setUser(auth()->user()); } + return $repository; + } + ); + $this->app->bind( + GroupCurrencyRepositoryInterface::class, + function (Application $app) { + /** @var GroupCurrencyRepository $repository */ + $repository = app(GroupCurrencyRepository::class); + // phpstan does not get the reference to auth + if ($app->auth->check()) { // @phpstan-ignore-line + $repository->setUser(auth()->user()); + } + return $repository; } ); diff --git a/app/Repositories/Account/AccountTasker.php b/app/Repositories/Account/AccountTasker.php index bb34482089..e30e983ef8 100644 --- a/app/Repositories/Account/AccountTasker.php +++ b/app/Repositories/Account/AccountTasker.php @@ -28,7 +28,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Account; use FireflyIII\Models\TransactionType; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; diff --git a/app/Repositories/Currency/CurrencyRepository.php b/app/Repositories/Currency/CurrencyRepository.php index 6f51384b8c..4cb267b9f9 100644 --- a/app/Repositories/Currency/CurrencyRepository.php +++ b/app/Repositories/Currency/CurrencyRepository.php @@ -24,25 +24,12 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Currency; use Carbon\Carbon; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Factory\TransactionCurrencyFactory; -use FireflyIII\Models\AccountMeta; -use FireflyIII\Models\AvailableBudget; -use FireflyIII\Models\Bill; -use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\CurrencyExchangeRate; -use FireflyIII\Models\Preference; -use FireflyIII\Models\RecurrenceTransaction; -use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\User\UserRepositoryInterface; -use FireflyIII\Services\Internal\Destroy\CurrencyDestroyService; -use FireflyIII\Services\Internal\Update\CurrencyUpdateService; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; -use JsonException; /** * Class CurrencyRepository. @@ -51,358 +38,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface { private User $user; - /** - * @param TransactionCurrency $currency - * - * @return bool - * @throws FireflyException - */ - public function currencyInUse(TransactionCurrency $currency): bool - { - $result = $this->currencyInUseAt($currency); - - return null !== $result; - } - - /** - * @param TransactionCurrency $currency - * - * @return string|null - * @throws FireflyException - */ - public function currencyInUseAt(TransactionCurrency $currency): ?string - { - Log::debug(sprintf('Now in currencyInUse() for #%d ("%s")', $currency->id, $currency->code)); - $countJournals = $this->countJournals($currency); - if ($countJournals > 0) { - Log::info(sprintf('Count journals is %d, return true.', $countJournals)); - - return 'journals'; - } - - // is the only currency left - if (1 === $this->getAll()->count()) { - Log::info('Is the last currency in the system, return true. '); - - return 'last_left'; - } - - // is being used in accounts: - $meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((string)$currency->id))->count(); - if ($meta > 0) { - Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta)); - - return 'account_meta'; - } - - // is being used in bills: - $bills = Bill::where('transaction_currency_id', $currency->id)->count(); - if ($bills > 0) { - Log::info(sprintf('Used in %d bills as currency, return true. ', $bills)); - - return 'bills'; - } - - // is being used in recurring transactions - $recurringAmount = RecurrenceTransaction::where('transaction_currency_id', $currency->id)->count(); - $recurringForeign = RecurrenceTransaction::where('foreign_currency_id', $currency->id)->count(); - - if ($recurringAmount > 0 || $recurringForeign > 0) { - Log::info(sprintf('Used in %d recurring transactions as (foreign) currency id, return true. ', $recurringAmount + $recurringForeign)); - - return 'recurring'; - } - - // is being used in accounts (as integer) - $meta = AccountMeta::leftJoin('accounts', 'accounts.id', '=', 'account_meta.account_id') - ->whereNull('accounts.deleted_at') - ->where('account_meta.name', 'currency_id')->where('account_meta.data', json_encode((int)$currency->id))->count(); - if ($meta > 0) { - Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta)); - - return 'account_meta'; - } - - // is being used in available budgets - $availableBudgets = AvailableBudget::where('transaction_currency_id', $currency->id)->count(); - if ($availableBudgets > 0) { - Log::info(sprintf('Used in %d available budgets as currency, return true. ', $availableBudgets)); - - return 'available_budgets'; - } - - // is being used in budget limits - $budgetLimit = BudgetLimit::where('transaction_currency_id', $currency->id)->count(); - if ($budgetLimit > 0) { - Log::info(sprintf('Used in %d budget limits as currency, return true. ', $budgetLimit)); - - return 'budget_limits'; - } - - // is the default currency for the user or the system - $count = $this->user->currencies()->where('transaction_currencies.id', $currency->id)->wherePivot('user_default', 1)->count(); - if ($count > 0) { - Log::info('Is the default currency of the user, return true.'); - - return 'current_default'; - } - - Log::debug('Currency is not used, return false.'); - - return null; - } - - /** - * @param TransactionCurrency $currency - * - * @return int - */ - public function countJournals(TransactionCurrency $currency): int - { - $count = $currency->transactions()->whereNull('deleted_at')->count() + $currency->transactionJournals()->whereNull('deleted_at')->count(); - - // also count foreign: - return $count + Transaction::where('foreign_currency_id', $currency->id)->count(); - } - - /** - * 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(function (TransactionCurrency $current) use ($local) { - $hasId = $local->contains(function (TransactionCurrency $entry) use ($current) { - return (int)$entry->id === (int)$current->id; - }); - $isDefault = $local->contains(function (TransactionCurrency $entry) use ($current) { - return 1 === (int)$entry->pivot->user_default && (int)$entry->id === (int)$current->id; - }); - $current->userEnabled = $hasId; - $current->userDefault = $isDefault; - return $current; - }); - } - - /** - * @return Collection - */ - public function get(): Collection - { - $all = $this->user->currencies()->orderBy('code', 'ASC')->withPivot(['user_default'])->get(); - $all->map(function (TransactionCurrency $current) { - $current->userEnabled = true; - $current->userDefault = 1 === (int)$current->pivot->user_default; - return $current; - }); - return $all; - } - - /** - * Returns the complete set of transactions but needs - * no user object. - * - * @return Collection - */ - public function getCompleteSet(): Collection - { - return TransactionCurrency::orderBy('code', 'ASC')->get(); - } - - /** - * @param TransactionCurrency $currency - * - * @return bool - */ - public function destroy(TransactionCurrency $currency): bool - { - /** @var UserRepositoryInterface $repository */ - $repository = app(UserRepositoryInterface::class); - if ($repository->hasRole($this->user, 'owner')) { - /** @var CurrencyDestroyService $service */ - $service = app(CurrencyDestroyService::class); - $service->destroy($currency); - } - - return true; - } - - /** - * Disables a currency - * - * @param TransactionCurrency $currency - */ - public function disable(TransactionCurrency $currency): void - { - $this->user->currencies()->detach($currency->id); - $currency->enabled = false; - $currency->save(); - } - - /** - * @inheritDoc - */ - public function ensureMinimalEnabledCurrencies(): void - { - // if no currencies are enabled, enable the first one in the DB (usually the EUR) - if (0 === $this->user->currencies()->count()) { - $euro = app('amount')->getSystemCurrency(); - if (null === $euro) { - throw new FireflyException('No currencies found. You broke Firefly III'); - } - Log::channel('audit')->info(sprintf('Auto-enabled currency %s.', $euro->code)); - $this->enable($euro); - app('preferences')->mark(); - } - } - - /** - * @param TransactionCurrency $currency - * Enables a currency - */ - public function enable(TransactionCurrency $currency): void - { - $this->user->currencies()->syncWithoutDetaching([$currency->id]); - $currency->enabled = false; - $currency->save(); - } - - /** - * Find by currency code, return NULL if unfound. - * Used in Import Currency! - * - * @param string $currencyCode - * - * @return TransactionCurrency|null - * @deprecated - */ - public function findByCodeNull(string $currencyCode): ?TransactionCurrency - { - return TransactionCurrency::where('code', $currencyCode)->first(); - } - - /** - * Find by currency name. - * - * @param string $currencyName - * - * @return TransactionCurrency|null - */ - public function findByName(string $currencyName): ?TransactionCurrency - { - return TransactionCurrency::whereName($currencyName)->first(); - } - - /** - * Find by currency name or return null. - * Used in Import Currency! - * - * @param string $currencyName - * - * @return TransactionCurrency|null - * @deprecated - */ - public function findByNameNull(string $currencyName): ?TransactionCurrency - { - return TransactionCurrency::whereName($currencyName)->first(); - } - - /** - * Find by currency symbol. - * - * @param string $currencySymbol - * - * @return TransactionCurrency|null - */ - public function findBySymbol(string $currencySymbol): ?TransactionCurrency - { - return TransactionCurrency::whereSymbol($currencySymbol)->first(); - } - - /** - * Find by currency symbol or return NULL - * Used in Import Currency! - * - * @param string $currencySymbol - * - * @return TransactionCurrency|null - * @deprecated - */ - public function findBySymbolNull(string $currencySymbol): ?TransactionCurrency - { - return TransactionCurrency::whereSymbol($currencySymbol)->first(); - } - - /** - * 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 - { - $result = $this->findCurrencyNull($currencyId, $currencyCode); - - if (null === $result) { - Log::debug('Grabbing default currency for this user...'); - $result = app('amount')->getDefaultCurrencyByUser($this->user); - } - - if (null === $result) { - Log::debug('Grabbing EUR as fallback.'); - $result = $this->findByCode('EUR'); - } - Log::debug(sprintf('Final result: %s', $result->code)); - if (false === $result->enabled) { - Log::debug(sprintf('Also enabled currency %s', $result->code)); - $this->enable($result); - } - - return $result; - } - - /** - * 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 - { - Log::debug('Now in findCurrencyNull()'); - $result = $this->find((int)$currencyId); - if (null === $result) { - Log::debug(sprintf('Searching for currency with code %s...', $currencyCode)); - $result = $this->findByCode((string)$currencyCode); - } - if (null !== $result && false === $result->enabled) { - Log::debug(sprintf('Also enabled currency %s', $result->code)); - $this->enable($result); - } - - return $result; - } - - /** - * Find by ID, return NULL if not found. - * - * @param int $currencyId - * - * @return TransactionCurrency|null - */ - public function find(int $currencyId): ?TransactionCurrency - { - return TransactionCurrency::find($currencyId); - } /** * Find by currency code, return NULL if unfound. @@ -416,30 +51,18 @@ class CurrencyRepository implements CurrencyRepositoryInterface return TransactionCurrency::where('code', $currencyCode)->first(); } + /** - * @param array $ids + * Returns the complete set of transactions but needs + * no user object. * * @return Collection */ - public function getByIds(array $ids): Collection + public function getCompleteSet(): Collection { - return TransactionCurrency::orderBy('code', 'ASC')->whereIn('id', $ids)->get(); + return TransactionCurrency::orderBy('code', 'ASC')->get(); } - /** - * @param Preference $preference - * - * @return TransactionCurrency - */ - public function getCurrencyByPreference(Preference $preference): TransactionCurrency - { - $preferred = TransactionCurrency::where('code', $preference->data)->first(); - if (null === $preferred) { - $preferred = TransactionCurrency::first(); - } - - return $preferred; - } /** * Get currency exchange rate. @@ -473,38 +96,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface return null; } - /** - * @inheritDoc - */ - public function getUserCurrencies(User $user): Collection - { - return $user->currencies()->get(); - } - - /** - * @inheritDoc - */ - public function isFallbackCurrency(TransactionCurrency $currency): bool - { - return $currency->code === config('firefly.default_currency', 'EUR'); - } - - /** - * @param string $search - * @param int $limit - * - * @return Collection - */ - public function searchCurrency(string $search, int $limit): Collection - { - $query = TransactionCurrency::where('enabled', true); - if ('' !== $search) { - $query->where('name', 'LIKE', sprintf('%%%s%%', $search)); - } - - return $query->take($limit)->get(); - } - /** * TODO must be a factory * @@ -538,88 +129,4 @@ class CurrencyRepository implements CurrencyRepositoryInterface } } - /** - * @param array $data - * - * @return TransactionCurrency - * @throws FireflyException - */ - public function store(array $data): TransactionCurrency - { - /** @var TransactionCurrencyFactory $factory */ - $factory = app(TransactionCurrencyFactory::class); - $result = $factory->create($data); - - if (true === $data['enabled']) { - $this->user->currencies()->attach($result->id); - } - - return $result; - } - - /** - * @param TransactionCurrency $currency - * @param array $data - * - * @return TransactionCurrency - */ - public function update(TransactionCurrency $currency, array $data): TransactionCurrency - { - app('log')->debug('Now in update()'); - // can be true, false, null - $enabled = array_key_exists('enabled', $data) ? $data['enabled'] : null; - // can be true, false, but method only responds to "true". - $default = array_key_exists('default', $data) ? $data['default'] : false; - - // remove illegal combo's: - if (false === $enabled && true === $default) { - $enabled = true; - } - if (false === $default) { - app('log')->warning(sprintf('Set default=false will NOT do anything for currency %s', $currency->code)); - } - - // update currency with current user specific settings - $currency->refreshForUser($this->user); - - // currency is enabled, must be disabled. - if (false === $enabled) { - app('log')->debug(sprintf('Disabled currency %s for user #%d', $currency->code, $this->user->id)); - $this->user->currencies()->detach($currency->id); - } - // currency must be enabled - if (true === $enabled) { - app('log')->debug(sprintf('Enabled currency %s for user #%d', $currency->code, $this->user->id)); - $this->user->currencies()->detach($currency->id); - $this->user->currencies()->syncWithoutDetaching([$currency->id => ['user_default' => false]]); - } - - // currency must be made default. - if (true === $default) { - app('log')->debug(sprintf('Enabled + made default currency %s for user #%d', $currency->code, $this->user->id)); - $this->user->currencies()->detach($currency->id); - foreach ($this->user->currencies()->get() as $item) { - $this->user->currencies()->updateExistingPivot($item->id, ['user_default' => false]); - } - $this->user->currencies()->syncWithoutDetaching([$currency->id => ['user_default' => true]]); - } - - /** @var CurrencyUpdateService $service */ - $service = app(CurrencyUpdateService::class); - - return $service->update($currency, $data); - } - - /** - * @inheritDoc - */ - public function makeDefault(TransactionCurrency $currency): void - { - app('log')->debug(sprintf('Enabled + made default currency %s for user #%d', $currency->code, $this->user->id)); - $this->user->currencies()->detach($currency->id); - foreach ($this->user->currencies()->get() as $item) { - $this->user->currencies()->updateExistingPivot($item->id, ['user_default' => false]); - } - $this->user->currencies()->syncWithoutDetaching([$currency->id => ['user_default' => true]]); - } } diff --git a/app/Repositories/Currency/CurrencyRepositoryInterface.php b/app/Repositories/Currency/CurrencyRepositoryInterface.php index e1a53d16be..26475fb7aa 100644 --- a/app/Repositories/Currency/CurrencyRepositoryInterface.php +++ b/app/Repositories/Currency/CurrencyRepositoryInterface.php @@ -26,7 +26,6 @@ namespace FireflyIII\Repositories\Currency; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\CurrencyExchangeRate; -use FireflyIII\Models\Preference; use FireflyIII\Models\TransactionCurrency; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; @@ -37,75 +36,11 @@ use Illuminate\Support\Collection; */ interface CurrencyRepositoryInterface { - /** - * Returns the complete set of transactions but needs - * no user object. - * - * @return Collection - */ - public function getCompleteSet(): Collection; - - /** - * @param TransactionCurrency $currency - * - * @return int - */ - public function countJournals(TransactionCurrency $currency): int; - - /** - * @param TransactionCurrency $currency - * - * @return bool - */ - public function currencyInUse(TransactionCurrency $currency): bool; - - /** - * Currency is in use where exactly. - * - * @param TransactionCurrency $currency - * - * @return string|null - */ - public function currencyInUseAt(TransactionCurrency $currency): ?string; - - /** - * @param TransactionCurrency $currency - * - * @return bool - */ - public function destroy(TransactionCurrency $currency): bool; - - /** - * Disables a currency - * - * @param TransactionCurrency $currency - */ - public function disable(TransactionCurrency $currency): void; - - /** - * Enables a currency - * - * @param TransactionCurrency $currency - */ - public function enable(TransactionCurrency $currency): void; - - /** - * @return void - */ - public function ensureMinimalEnabledCurrencies(): void; - - /** - * Find by ID, return NULL if not found. - * - * @param int $currencyId - * - * @return TransactionCurrency|null - */ - public function find(int $currencyId): ?TransactionCurrency; - /** * 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 @@ -113,97 +48,20 @@ interface CurrencyRepositoryInterface public function findByCode(string $currencyCode): ?TransactionCurrency; /** - * Find by currency code, return NULL if unfound. + * Returns the complete set of transactions but needs + * no user object. * - * @param string $currencyCode - * - * @return TransactionCurrency|null - */ - public function findByCodeNull(string $currencyCode): ?TransactionCurrency; - - /** - * Find by currency name. - * - * @param string $currencyName - * - * @return TransactionCurrency|null - */ - public function findByName(string $currencyName): ?TransactionCurrency; - - /** - * Find by currency name. - * - * @param string $currencyName - * - * @return TransactionCurrency|null - */ - public function findByNameNull(string $currencyName): ?TransactionCurrency; - - /** - * Find by currency symbol. - * - * @param string $currencySymbol - * - * @return TransactionCurrency|null - */ - public function findBySymbol(string $currencySymbol): ?TransactionCurrency; - - /** - * Find by currency symbol. - * - * @param string $currencySymbol - * - * @return TransactionCurrency|null - */ - public function findBySymbolNull(string $currencySymbol): ?TransactionCurrency; - - /** - * Find by object, ID or code. Returns user default or system default. - * - * @param int|null $currencyId - * @param string|null $currencyCode - * - * @return TransactionCurrency - */ - public function findCurrency(?int $currencyId, ?string $currencyCode): TransactionCurrency; - - /** - * 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; - - /** - * @return Collection - */ - public function get(): Collection; - - /** - * @return Collection - */ - public function getAll(): Collection; - - /** - * @param array $ids + * Used by the download exchange rate cron job. * * @return Collection */ - public function getByIds(array $ids): Collection; - - /** - * @param Preference $preference - * - * @return TransactionCurrency - */ - public function getCurrencyByPreference(Preference $preference): TransactionCurrency; + public function getCompleteSet(): Collection; /** * 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 @@ -213,36 +71,9 @@ interface CurrencyRepositoryInterface public function getExchangeRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date): ?CurrencyExchangeRate; /** - * @param TransactionCurrency $currency + * Set currency exchange rate. * - * @return void - */ - public function makeDefault(TransactionCurrency $currency): void; - - /** - * @param User $user - * - * @return Collection - */ - public function getUserCurrencies(User $user): Collection; - - /** - * @param TransactionCurrency $currency - * - * @return bool - */ - public function isFallbackCurrency(TransactionCurrency $currency): bool; - - /** - * @param string $search - * @param int $limit - * - * @return Collection - */ - public function searchCurrency(string $search, int $limit): Collection; - - /** - * TODO must be a factory + * Used in download exchange rate cron job. Needs the user object! * * @param TransactionCurrency $fromCurrency * @param TransactionCurrency $toCurrency @@ -258,19 +89,4 @@ interface CurrencyRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void; - /** - * @param array $data - * - * @return TransactionCurrency - * @throws FireflyException - */ - public function store(array $data): TransactionCurrency; - - /** - * @param TransactionCurrency $currency - * @param array $data - * - * @return TransactionCurrency - */ - public function update(TransactionCurrency $currency, array $data): TransactionCurrency; } diff --git a/app/Repositories/UserGroups/Currency/CurrencyRepository.php b/app/Repositories/UserGroups/Currency/CurrencyRepository.php new file mode 100644 index 0000000000..b7aafdbbb1 --- /dev/null +++ b/app/Repositories/UserGroups/Currency/CurrencyRepository.php @@ -0,0 +1,418 @@ +currencyInUseAt($currency); + + return null !== $result; + } + + /** + * @param TransactionCurrency $currency + * + * @return string|null + * @throws FireflyException + */ + public function currencyInUseAt(TransactionCurrency $currency): ?string + { + app('log')->debug(sprintf('Now in currencyInUse() for #%d ("%s")', $currency->id, $currency->code)); + $countJournals = $this->countJournals($currency); + if ($countJournals > 0) { + app('log')->info(sprintf('Count journals is %d, return true.', $countJournals)); + + return 'journals'; + } + + // is the only currency left + if (1 === $this->getAll()->count()) { + app('log')->info('Is the last currency in the system, return true. '); + + return 'last_left'; + } + + // is being used in accounts: + $meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((string)$currency->id))->count(); + if ($meta > 0) { + app('log')->info(sprintf('Used in %d accounts as currency_id, return true. ', $meta)); + + return 'account_meta'; + } + + // is being used in bills: + $bills = Bill::where('transaction_currency_id', $currency->id)->count(); + if ($bills > 0) { + app('log')->info(sprintf('Used in %d bills as currency, return true. ', $bills)); + + return 'bills'; + } + + // is being used in recurring transactions + $recurringAmount = RecurrenceTransaction::where('transaction_currency_id', $currency->id)->count(); + $recurringForeign = RecurrenceTransaction::where('foreign_currency_id', $currency->id)->count(); + + if ($recurringAmount > 0 || $recurringForeign > 0) { + app('log')->info(sprintf('Used in %d recurring transactions as (foreign) currency id, return true. ', $recurringAmount + $recurringForeign)); + + return 'recurring'; + } + + // is being used in accounts (as integer) + $meta = AccountMeta::leftJoin('accounts', 'accounts.id', '=', 'account_meta.account_id') + ->whereNull('accounts.deleted_at') + ->where('account_meta.name', 'currency_id')->where('account_meta.data', json_encode((int)$currency->id))->count(); + if ($meta > 0) { + app('log')->info(sprintf('Used in %d accounts as currency_id, return true. ', $meta)); + + return 'account_meta'; + } + + // is being used in available budgets + $availableBudgets = AvailableBudget::where('transaction_currency_id', $currency->id)->count(); + if ($availableBudgets > 0) { + app('log')->info(sprintf('Used in %d available budgets as currency, return true. ', $availableBudgets)); + + return 'available_budgets'; + } + + // is being used in budget limits + $budgetLimit = BudgetLimit::where('transaction_currency_id', $currency->id)->count(); + if ($budgetLimit > 0) { + app('log')->info(sprintf('Used in %d budget limits as currency, return true. ', $budgetLimit)); + + return 'budget_limits'; + } + + // is the default currency for the user or the system + $count = $this->userGroup->currencies()->where('transaction_currencies.id', $currency->id)->wherePivot('group_default', 1)->count(); + if ($count > 0) { + app('log')->info('Is the default currency of the user, return true.'); + + return 'current_default'; + } + + // is the default currency for the user or the system + $count = $this->userGroup->currencies()->where('transaction_currencies.id', $currency->id)->wherePivot('group_default', 1)->count(); + if ($count > 0) { + app('log')->info('Is the default currency of the user group, return true.'); + + return 'current_default'; + } + + app('log')->debug('Currency is not used, return false.'); + + 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(function (TransactionCurrency $current) use ($local) { + $hasId = $local->contains(function (TransactionCurrency $entry) use ($current) { + return (int)$entry->id === (int)$current->id; + }); + $isDefault = $local->contains(function (TransactionCurrency $entry) use ($current) { + return 1 === (int)$entry->pivot->group_default && (int)$entry->id === (int)$current->id; + }); + $current->userEnabled = $hasId; + $current->userDefault = $isDefault; + return $current; + }); + } + + /** + * Get the user group's currencies. + * @return Collection + */ + public function get(): Collection + { + $all = $this->userGroup->currencies()->orderBy('code', 'ASC')->withPivot(['group_default'])->get(); + $all->map(function (TransactionCurrency $current) { + $current->userEnabled = true; + $current->userDefault = 1 === (int)$current->pivot->group_default; + return $current; + }); + return $all; + } + + /** + * @param TransactionCurrency $currency + * + * @return bool + */ + public function destroy(TransactionCurrency $currency): bool + { + /** @var UserRepositoryInterface $repository */ + $repository = app(UserRepositoryInterface::class); + if ($repository->hasRole($this->user, 'owner')) { + /** @var CurrencyDestroyService $service */ + $service = app(CurrencyDestroyService::class); + $service->destroy($currency); + } + + return true; + } + + /** + * Disables a currency + * + * @param TransactionCurrency $currency + */ + public function disable(TransactionCurrency $currency): void + { + $this->userGroup->currencies()->detach($currency->id); + $currency->enabled = false; + $currency->save(); + } + + /** + * 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 + { + $result = $this->findCurrencyNull($currencyId, $currencyCode); + + if (null === $result) { + Log::debug('Grabbing default currency for this user...'); + $result = app('amount')->getDefaultCurrencyByUser($this->user); + } + + if (null === $result) { + Log::debug('Grabbing EUR as fallback.'); + $result = $this->findByCode('EUR'); + } + Log::debug(sprintf('Final result: %s', $result->code)); + if (false === $result->enabled) { + Log::debug(sprintf('Also enabled currency %s', $result->code)); + $this->enable($result); + } + + return $result; + } + + /** + * 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 + { + Log::debug('Now in findCurrencyNull()'); + $result = $this->find((int)$currencyId); + if (null === $result) { + Log::debug(sprintf('Searching for currency with code %s...', $currencyCode)); + $result = $this->findByCode((string)$currencyCode); + } + if (null !== $result && false === $result->enabled) { + Log::debug(sprintf('Also enabled currency %s', $result->code)); + $this->enable($result); + } + + return $result; + } + + /** + * Find by ID, return NULL if not found. + * + * @param int $currencyId + * + * @return TransactionCurrency|null + */ + public function find(int $currencyId): ?TransactionCurrency + { + return TransactionCurrency::find($currencyId); + } + + /** + * Find by currency code, return NULL if unfound. + * + * @param string $currencyCode + * + * @return TransactionCurrency|null + */ + private function findByCode(string $currencyCode): ?TransactionCurrency + { + return TransactionCurrency::where('code', $currencyCode)->first(); + } + + /** + * @param TransactionCurrency $currency + * Enables a currency + */ + public function enable(TransactionCurrency $currency): void + { + $this->userGroup->currencies()->syncWithoutDetaching([$currency->id]); + $currency->enabled = false; + $currency->save(); + } + + /** + * @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)); + $this->userGroup->currencies()->detach($currency->id); + foreach ($this->userGroup->currencies()->get() as $item) { + $this->userGroup->currencies()->updateExistingPivot($item->id, ['group_default' => false]); + } + $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); + if ('' !== $search) { + $query->where('name', 'LIKE', sprintf('%%%s%%', $search)); + } + + return $query->take($limit)->get(); + } + + /** + * @param array $data + * + * @return TransactionCurrency + * @throws FireflyException + */ + public function store(array $data): TransactionCurrency + { + /** @var TransactionCurrencyFactory $factory */ + $factory = app(TransactionCurrencyFactory::class); + $result = $factory->create($data); + + if (true === $data['enabled']) { + $this->userGroup->currencies()->attach($result->id); + } + + return $result; + } + + /** + * @param TransactionCurrency $currency + * @param array $data + * + * @return TransactionCurrency + */ + public function update(TransactionCurrency $currency, array $data): TransactionCurrency + { + app('log')->debug('Now in update()'); + // can be true, false, null + $enabled = array_key_exists('enabled', $data) ? $data['enabled'] : null; + // can be true, false, but method only responds to "true". + $default = array_key_exists('default', $data) ? $data['default'] : false; + + // remove illegal combo's: + if (false === $enabled && true === $default) { + $enabled = true; + } + if (false === $default) { + app('log')->warning(sprintf('Set default=false will NOT do anything for currency %s', $currency->code)); + } + + // update currency with current user specific settings + $currency->refreshForUser($this->user); + + // currency is enabled, must be disabled. + if (false === $enabled) { + app('log')->debug(sprintf('Disabled currency %s for user #%d', $currency->code, $this->userGroup->id)); + $this->userGroup->currencies()->detach($currency->id); + } + // currency must be enabled + if (true === $enabled) { + app('log')->debug(sprintf('Enabled currency %s for user #%d', $currency->code, $this->userGroup->id)); + $this->userGroup->currencies()->detach($currency->id); + $this->userGroup->currencies()->syncWithoutDetaching([$currency->id => ['group_default' => false]]); + } + + // currency must be made default. + if (true === $default) { + app('log')->debug(sprintf('Enabled + made default currency %s for user #%d', $currency->code, $this->userGroup->id)); + $this->userGroup->currencies()->detach($currency->id); + foreach ($this->userGroup->currencies()->get() as $item) { + $this->userGroup->currencies()->updateExistingPivot($item->id, ['group_default' => false]); + } + $this->userGroup->currencies()->syncWithoutDetaching([$currency->id => ['group_default' => true]]); + } + + /** @var CurrencyUpdateService $service */ + $service = app(CurrencyUpdateService::class); + + return $service->update($currency, $data); + } +} diff --git a/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php b/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php new file mode 100644 index 0000000000..8e61214c31 --- /dev/null +++ b/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php @@ -0,0 +1,129 @@ +addProperty('getDefaultCurrencyByGroup'); + $cache->addProperty($userGroup->id); + if ($cache->has()) { + return $cache->get(); + } + $default = $userGroup->currencies()->where('group_default', true)->first(); + if(null === $default) { + $default = $this->getSystemCurrency(); + $userGroup->currencies()->sync([$default->id => ['group_default' => true]]); + } + $cache->store($default); + + return $default; + } /** * @param string $value diff --git a/app/Support/Form/CurrencyForm.php b/app/Support/Form/CurrencyForm.php index ea44ade235..0fc4487edd 100644 --- a/app/Support/Form/CurrencyForm.php +++ b/app/Support/Form/CurrencyForm.php @@ -26,7 +26,7 @@ namespace FireflyIII\Support\Form; use Amount as Amt; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Throwable; diff --git a/app/Support/Http/Controllers/ChartGeneration.php b/app/Support/Http/Controllers/ChartGeneration.php index 5b2cd7c6e6..bfb84f7937 100644 --- a/app/Support/Http/Controllers/ChartGeneration.php +++ b/app/Support/Http/Controllers/ChartGeneration.php @@ -28,7 +28,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Chart\Basic\GeneratorInterface; use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; @@ -66,8 +65,6 @@ trait ChartGeneration /** @var GeneratorInterface $generator */ $generator = app(GeneratorInterface::class); - /** @var CurrencyRepositoryInterface $repository */ - $repository = app(CurrencyRepositoryInterface::class); /** @var AccountRepositoryInterface $accountRepos */ $accountRepos = app(AccountRepositoryInterface::class); @@ -76,7 +73,7 @@ trait ChartGeneration /** @var Account $account */ foreach ($accounts as $account) { // TODO we can use getAccountCurrency instead. - $currency = $repository->find((int)$accountRepos->getMetaValue($account, 'currency_id')); + $currency = $accountRepos->getAccountCurrency($account); if (null === $currency) { $currency = $default; } diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php index b0a64829fc..664dbdff66 100644 --- a/app/Support/Search/OperatorQuerySearch.php +++ b/app/Support/Search/OperatorQuerySearch.php @@ -34,7 +34,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\ParseDateString; use FireflyIII\User; diff --git a/app/User.php b/app/User.php index cac5cbf8ca..f4575642d6 100644 --- a/app/User.php +++ b/app/User.php @@ -167,6 +167,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property-read int|null $group_memberships_count * @property-read UserGroup|null $userGroup * @method static Builder|User whereUserGroupId($value) + * @property-read \Illuminate\Database\Eloquent\Collection $currencies + * @property-read int|null $currencies_count * @mixin Eloquent */ class User extends Authenticatable diff --git a/composer.lock b/composer.lock index 5f2d0642f0..aa80ae81a9 100644 --- a/composer.lock +++ b/composer.lock @@ -3590,16 +3590,16 @@ }, { "name": "monolog/monolog", - "version": "3.4.0", + "version": "3.5.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "e2392369686d420ca32df3803de28b5d6f76867d" + "reference": "c915e2634718dbc8a4a15c61b0e62e7a44e14448" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/e2392369686d420ca32df3803de28b5d6f76867d", - "reference": "e2392369686d420ca32df3803de28b5d6f76867d", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/c915e2634718dbc8a4a15c61b0e62e7a44e14448", + "reference": "c915e2634718dbc8a4a15c61b0e62e7a44e14448", "shasum": "" }, "require": { @@ -3675,7 +3675,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.4.0" + "source": "https://github.com/Seldaek/monolog/tree/3.5.0" }, "funding": [ { @@ -3687,7 +3687,7 @@ "type": "tidelift" } ], - "time": "2023-06-21T08:46:11+00:00" + "time": "2023-10-27T15:32:31+00:00" }, { "name": "nesbot/carbon", @@ -11693,5 +11693,5 @@ "platform-overrides": { "php": "8.2" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" }