diff --git a/.ci/phpstan.neon b/.ci/phpstan.neon index be6038a047..c07d5db7e7 100644 --- a/.ci/phpstan.neon +++ b/.ci/phpstan.neon @@ -1,6 +1,6 @@ parameters: scanFiles: - - ../_ide_helper + - ../_ide_helper.php paths: - ../app - ../database diff --git a/.github/release-notes/alpha.md b/.github/release-notes/alpha.md index a9dca0594a..888a4093fc 100644 --- a/.github/release-notes/alpha.md +++ b/.github/release-notes/alpha.md @@ -1,6 +1,7 @@ Welcome to release %version of Firefly III. This **alpha** release contains the latest fixes, translations and features. It is probably buggy and may not work as expected. You can download the release below, and adventurous Docker users can find this release under the `alpha` tag. -:warning: Please be careful with this alpha release, as it may not work as expected. +> [!WARNING] +> Please be careful with this alpha release, as it may not work as expected. Alpha releases are created to test new features and fixes before they are included in a stable release. They are not recommended for production use. This release was created on %date and may contain unexpected bugs. Data loss is rare but possible. diff --git a/.github/release-notes/beta.md b/.github/release-notes/beta.md index 300aceee44..5940d83f67 100644 --- a/.github/release-notes/beta.md +++ b/.github/release-notes/beta.md @@ -1,6 +1,7 @@ Welcome to release %version of Firefly III. This **beta** release contains the latest fixes, translations and features. It may be buggy, nor work as expected. You can download the release below, and adventurous Docker users can find this release under the `beta` tag. -:warning: Please be careful with this beta release, as it may not work as expected. +> [!WARNING] +> Please be careful with this beta release, as it may not work as expected. Alpha releases are created to test new features and fixes before they are included in a stable release. They are not recommended for production use. This release was created on %date and may contain unexpected bugs. Data loss is rare but possible. diff --git a/.github/release-notes/branch.md b/.github/release-notes/branch.md index f5fe6ac5a3..22e1c3cc3e 100644 --- a/.github/release-notes/branch.md +++ b/.github/release-notes/branch.md @@ -1,6 +1,7 @@ Welcome to release %version of Firefly III. This branch-related release contains the latest fixes, translations and features. It is probably buggy and may not work as expected. You can download the release below, and adventurous Docker users can find this release under the `branch-*` tag. -:warning: Please be careful with this branch release, as it may not work as expected. +> [!WARNING] +> Please be careful with this branch release, as it may not work as expected. Branch releases are created to test large new features that are developed alongside the normal release flow. They are not recommended for production use. This release was created on %date and may contain unexpected bugs. Data loss is rare but possible. diff --git a/.github/release-notes/develop.md b/.github/release-notes/develop.md index 8b5271af05..04774b7dc1 100644 --- a/.github/release-notes/develop.md +++ b/.github/release-notes/develop.md @@ -1,6 +1,7 @@ Welcome to the latest development release of Firefly III. This test release contains the absolute latest fixes, translations and features. It is probably buggy and may not work as expected. You can download the release below, and adventurous Docker users can find this release under the `develop` tag. -:warning: Please be careful with this pre-release, as it may not work as expected. +> [!WARNING] +> Please be careful with this pre-release, as it may not work as expected. This release was created on %date and may contain unexpected bugs. Data loss is rare but possible. diff --git a/app/Api/V1/Controllers/Autocomplete/AccountController.php b/app/Api/V1/Controllers/Autocomplete/AccountController.php index cb31c41a62..a9dba92ca5 100644 --- a/app/Api/V1/Controllers/Autocomplete/AccountController.php +++ b/app/Api/V1/Controllers/Autocomplete/AccountController.php @@ -27,6 +27,7 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; use FireflyIII\Enums\AccountTypeEnum; +use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; @@ -34,7 +35,6 @@ use FireflyIII\Support\Debug\Timer; use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Http\Api\AccountFilter; -use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Log; @@ -46,7 +46,8 @@ class AccountController extends Controller use AccountFilter; // this array only exists to test if the constructor will use it properly. - protected array $accepts = ['application/json', 'application/vnd.api+json']; + protected array $accepts = ['application/json', 'application/vnd.api+json']; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; /** @var array */ private array $balanceTypes; @@ -60,10 +61,10 @@ class AccountController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); + $this->validateUserGroup($request); $this->repository = app(AccountRepositoryInterface::class); - $this->repository->setUser($user); + $this->repository->setUser($this->user); + $this->repository->setUserGroup($this->userGroup); return $next($request); } diff --git a/app/Api/V1/Controllers/Autocomplete/BillController.php b/app/Api/V1/Controllers/Autocomplete/BillController.php index d95e1ee108..5fea014b5c 100644 --- a/app/Api/V1/Controllers/Autocomplete/BillController.php +++ b/app/Api/V1/Controllers/Autocomplete/BillController.php @@ -26,9 +26,9 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; +use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\Bill; use FireflyIII\Repositories\Bill\BillRepositoryInterface; -use FireflyIII\User; use Illuminate\Http\JsonResponse; /** @@ -37,6 +37,7 @@ use Illuminate\Http\JsonResponse; class BillController extends Controller { private BillRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::READ_SUBSCRIPTIONS]; /** * BillController constructor. @@ -46,10 +47,10 @@ class BillController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); + $this->validateUserGroup($request); $this->repository = app(BillRepositoryInterface::class); - $this->repository->setUser($user); + $this->repository->setUser($this->user); + $this->repository->setUserGroup($this->userGroup); return $next($request); } diff --git a/app/Api/V1/Controllers/Autocomplete/BudgetController.php b/app/Api/V1/Controllers/Autocomplete/BudgetController.php index dec3e5961c..3fd273d329 100644 --- a/app/Api/V1/Controllers/Autocomplete/BudgetController.php +++ b/app/Api/V1/Controllers/Autocomplete/BudgetController.php @@ -26,9 +26,9 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; +use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\Budget; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use FireflyIII\User; use Illuminate\Http\JsonResponse; /** @@ -37,6 +37,7 @@ use Illuminate\Http\JsonResponse; class BudgetController extends Controller { private BudgetRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::READ_BUDGETS]; /** * BudgetController constructor. @@ -46,10 +47,10 @@ class BudgetController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); + $this->validateUserGroup($request); $this->repository = app(BudgetRepositoryInterface::class); - $this->repository->setUser($user); + $this->repository->setUser($this->user); + $this->repository->setUserGroup($this->userGroup); return $next($request); } diff --git a/app/Api/V1/Controllers/Autocomplete/CategoryController.php b/app/Api/V1/Controllers/Autocomplete/CategoryController.php index ea4032bc6f..cf24f56776 100644 --- a/app/Api/V1/Controllers/Autocomplete/CategoryController.php +++ b/app/Api/V1/Controllers/Autocomplete/CategoryController.php @@ -26,9 +26,9 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; +use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\Category; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; -use FireflyIII\User; use Illuminate\Http\JsonResponse; /** @@ -37,6 +37,7 @@ use Illuminate\Http\JsonResponse; class CategoryController extends Controller { private CategoryRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; /** * CategoryController constructor. @@ -46,10 +47,10 @@ class CategoryController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); + $this->validateUserGroup($request); $this->repository = app(CategoryRepositoryInterface::class); - $this->repository->setUser($user); + $this->repository->setUser($this->user); + $this->repository->setUserGroup($this->userGroup); return $next($request); } diff --git a/app/Api/V1/Controllers/Autocomplete/CurrencyController.php b/app/Api/V1/Controllers/Autocomplete/CurrencyController.php index 5b49ed3a06..427e03d5b9 100644 --- a/app/Api/V1/Controllers/Autocomplete/CurrencyController.php +++ b/app/Api/V1/Controllers/Autocomplete/CurrencyController.php @@ -27,9 +27,9 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete; use Deprecated; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; +use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; -use FireflyIII\User; use Illuminate\Http\JsonResponse; /** @@ -38,6 +38,7 @@ use Illuminate\Http\JsonResponse; class CurrencyController extends Controller { private CurrencyRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; /** * CurrencyController constructor. @@ -47,10 +48,10 @@ class CurrencyController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); + $this->validateUserGroup($request); $this->repository = app(CurrencyRepositoryInterface::class); - $this->repository->setUser($user); + $this->repository->setUser($this->user); + $this->repository->setUserGroup($this->userGroup); return $next($request); } diff --git a/app/Api/V1/Controllers/Autocomplete/ObjectGroupController.php b/app/Api/V1/Controllers/Autocomplete/ObjectGroupController.php index 3b41ebcecf..0f4126f941 100644 --- a/app/Api/V1/Controllers/Autocomplete/ObjectGroupController.php +++ b/app/Api/V1/Controllers/Autocomplete/ObjectGroupController.php @@ -26,9 +26,9 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; +use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\ObjectGroup; use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface; -use FireflyIII\User; use Illuminate\Http\JsonResponse; /** @@ -37,6 +37,7 @@ use Illuminate\Http\JsonResponse; class ObjectGroupController extends Controller { private ObjectGroupRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; /** * CurrencyController constructor. @@ -46,10 +47,10 @@ class ObjectGroupController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); + $this->validateUserGroup($request); $this->repository = app(ObjectGroupRepositoryInterface::class); - $this->repository->setUser($user); + $this->repository->setUser($this->user); + $this->repository->setUserGroup($this->userGroup); return $next($request); } diff --git a/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php b/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php index f077d3df00..de08a181b5 100644 --- a/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php +++ b/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php @@ -26,10 +26,10 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; +use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\PiggyBank; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; -use FireflyIII\User; use Illuminate\Http\JsonResponse; /** @@ -39,6 +39,7 @@ class PiggyBankController extends Controller { private AccountRepositoryInterface $accountRepository; private PiggyBankRepositoryInterface $piggyRepository; + protected array $acceptedRoles = [UserRoleEnum::READ_PIGGY_BANKS]; /** * PiggyBankController constructor. @@ -48,22 +49,19 @@ class PiggyBankController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); + $this->validateUserGroup($request); $this->piggyRepository = app(PiggyBankRepositoryInterface::class); $this->accountRepository = app(AccountRepositoryInterface::class); - $this->piggyRepository->setUser($user); - $this->accountRepository->setUser($user); + $this->piggyRepository->setUser($this->user); + $this->piggyRepository->setUserGroup($this->userGroup); + $this->accountRepository->setUser($this->user); + $this->accountRepository->setUserGroup($this->userGroup); return $next($request); } ); } - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getPiggiesAC - */ public function piggyBanks(AutocompleteRequest $request): JsonResponse { $data = $request->getData(); @@ -90,10 +88,6 @@ class PiggyBankController extends Controller return response()->api($response); } - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getPiggiesBalanceAC - */ public function piggyBanksWithBalance(AutocompleteRequest $request): JsonResponse { $data = $request->getData(); diff --git a/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php b/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php index 2aca073dd5..f061df1443 100644 --- a/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php +++ b/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php @@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; +use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\Recurrence; use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; use Illuminate\Http\JsonResponse; @@ -36,6 +37,7 @@ use Illuminate\Http\JsonResponse; class RecurrenceController extends Controller { private RecurringRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::READ_RECURRING]; /** * RecurrenceController constructor. @@ -45,19 +47,16 @@ class RecurrenceController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { + $this->validateUserGroup($request); $this->repository = app(RecurringRepositoryInterface::class); - - $this->repository->setUser(auth()->user()); + $this->repository->setUser($this->user); + $this->repository->setUserGroup($this->userGroup); return $next($request); } ); } - /** - * This endpoint is documented at: - * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getRecurringAC - */ public function recurring(AutocompleteRequest $request): JsonResponse { $data = $request->getData(); diff --git a/app/Api/V1/Controllers/Autocomplete/RuleController.php b/app/Api/V1/Controllers/Autocomplete/RuleController.php index bc9996dcfb..acc44effcd 100644 --- a/app/Api/V1/Controllers/Autocomplete/RuleController.php +++ b/app/Api/V1/Controllers/Autocomplete/RuleController.php @@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; +use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\Rule; use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use Illuminate\Http\JsonResponse; @@ -36,6 +37,7 @@ use Illuminate\Http\JsonResponse; class RuleController extends Controller { private RuleRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::READ_RULES]; /** * RuleController constructor. @@ -45,18 +47,16 @@ class RuleController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { + $this->validateUserGroup($request); $this->repository = app(RuleRepositoryInterface::class); - $this->repository->setUser(auth()->user()); + $this->repository->setUser($this->user); + $this->repository->setUserGroup($this->userGroup); return $next($request); } ); } - /** - * This endpoint is documented at: - * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getRulesAC - */ public function rules(AutocompleteRequest $request): JsonResponse { $data = $request->getData(); diff --git a/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php b/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php index 7315de68d8..d5e42b6733 100644 --- a/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php +++ b/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php @@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; +use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\RuleGroup; use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use Illuminate\Http\JsonResponse; @@ -36,6 +37,7 @@ use Illuminate\Http\JsonResponse; class RuleGroupController extends Controller { private RuleGroupRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::READ_RULES]; /** * RuleGroupController constructor. @@ -45,18 +47,16 @@ class RuleGroupController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { + $this->validateUserGroup($request); $this->repository = app(RuleGroupRepositoryInterface::class); - $this->repository->setUser(auth()->user()); + $this->repository->setUser($this->user); + $this->repository->setUserGroup($this->userGroup); return $next($request); } ); } - /** - * This endpoint is documented at: - * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getRuleGroupsAC - */ public function ruleGroups(AutocompleteRequest $request): JsonResponse { $data = $request->getData(); diff --git a/app/Api/V1/Controllers/Autocomplete/TagController.php b/app/Api/V1/Controllers/Autocomplete/TagController.php index dabc0a1c85..5ad94ea7f5 100644 --- a/app/Api/V1/Controllers/Autocomplete/TagController.php +++ b/app/Api/V1/Controllers/Autocomplete/TagController.php @@ -26,9 +26,9 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; +use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Tag\TagRepositoryInterface; -use FireflyIII\User; use Illuminate\Http\JsonResponse; /** @@ -37,6 +37,7 @@ use Illuminate\Http\JsonResponse; class TagController extends Controller { private TagRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; /** * TagController constructor. @@ -46,20 +47,16 @@ class TagController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); + $this->validateUserGroup($request); $this->repository = app(TagRepositoryInterface::class); - $this->repository->setUser($user); + $this->repository->setUser($this->user); + $this->repository->setUserGroup($this->userGroup); return $next($request); } ); } - /** - * This endpoint is documented at: - * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getTagAC - */ public function tags(AutocompleteRequest $request): JsonResponse { $data = $request->getData(); diff --git a/app/Api/V1/Controllers/Autocomplete/TransactionController.php b/app/Api/V1/Controllers/Autocomplete/TransactionController.php index a6785dbc38..4321479d26 100644 --- a/app/Api/V1/Controllers/Autocomplete/TransactionController.php +++ b/app/Api/V1/Controllers/Autocomplete/TransactionController.php @@ -31,7 +31,6 @@ use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; -use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; @@ -52,24 +51,19 @@ class TransactionController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); - $userGroup = $this->validateUserGroup($request); + $this->validateUserGroup($request); $this->repository = app(JournalRepositoryInterface::class); $this->groupRepository = app(TransactionGroupRepositoryInterface::class); - $this->repository->setUser($user); - $this->groupRepository->setUser($user); - $this->groupRepository->setUserGroup($userGroup); + $this->repository->setUser($this->user); + $this->repository->setUserGroup($this->userGroup); + $this->groupRepository->setUser($this->user); + $this->groupRepository->setUserGroup($this->userGroup); return $next($request); } ); } - /** - * This endpoint is documented at: - * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getTransactionsAC - */ public function transactions(AutocompleteRequest $request): JsonResponse { $data = $request->getData(); @@ -92,10 +86,6 @@ class TransactionController extends Controller return response()->api($array); } - /** - * This endpoint is documented at: - * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getTransactionsIDAC - */ public function transactionsWithID(AutocompleteRequest $request): JsonResponse { $data = $request->getData(); diff --git a/app/Api/V1/Controllers/Autocomplete/TransactionTypeController.php b/app/Api/V1/Controllers/Autocomplete/TransactionTypeController.php index abc0e11553..ec8c723051 100644 --- a/app/Api/V1/Controllers/Autocomplete/TransactionTypeController.php +++ b/app/Api/V1/Controllers/Autocomplete/TransactionTypeController.php @@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; +use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\TransactionType\TransactionTypeRepositoryInterface; use Illuminate\Http\JsonResponse; @@ -36,6 +37,7 @@ use Illuminate\Http\JsonResponse; class TransactionTypeController extends Controller { private TransactionTypeRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; /** * TransactionTypeController constructor. @@ -45,17 +47,16 @@ class TransactionTypeController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { + $this->validateUserGroup($request); $this->repository = app(TransactionTypeRepositoryInterface::class); + $this->repository->setUser($this->user); + $this->repository->setUserGroup($this->userGroup); return $next($request); } ); } - /** - * This endpoint is documented at - * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getTransactionTypesAC - */ public function transactionTypes(AutocompleteRequest $request): JsonResponse { $data = $request->getData(); diff --git a/app/Api/V1/Controllers/Chart/AccountController.php b/app/Api/V1/Controllers/Chart/AccountController.php index 7c762aece1..47e37bb466 100644 --- a/app/Api/V1/Controllers/Chart/AccountController.php +++ b/app/Api/V1/Controllers/Chart/AccountController.php @@ -26,17 +26,14 @@ namespace FireflyIII\Api\V1\Controllers\Chart; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Chart\ChartRequest; -use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; -use FireflyIII\Models\Preference; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Support\Chart\ChartData; -use FireflyIII\Support\Facades\Preferences; use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Http\Api\ApiSupport; +use FireflyIII\Support\Http\Api\CleansChartData; use FireflyIII\Support\Http\Api\CollectsAccountsFromFilter; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Log; @@ -47,11 +44,12 @@ use Illuminate\Support\Facades\Log; class AccountController extends Controller { use ApiSupport; + use CleansChartData; use CollectsAccountsFromFilter; - protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; - private ChartData $chartData; + private array $chartData = []; private AccountRepositoryInterface $repository; /** @@ -62,11 +60,10 @@ class AccountController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { - $this->chartData = new ChartData(); $this->repository = app(AccountRepositoryInterface::class); - - $userGroup = $this->validateUserGroup($request); - $this->repository->setUserGroup($userGroup); + $this->validateUserGroup($request); + $this->repository->setUserGroup($this->userGroup); + $this->repository->setUser($this->user); return $next($request); } @@ -84,7 +81,7 @@ class AccountController extends Controller // move date to end of day $queryParameters['start']->startOfDay(); $queryParameters['end']->endOfDay(); - Log::debug(sprintf('dashboard(), convert to primary: %s', var_export($this->convertToPrimary, true))); + // Log::debug(sprintf('dashboard(), convert to primary: %s', var_export($this->convertToPrimary, true))); // loop each account, and collect info: /** @var Account $account */ @@ -93,7 +90,7 @@ class AccountController extends Controller $this->renderAccountData($queryParameters, $account); } - return response()->json($this->chartData->render()); + return response()->json($this->clean($this->chartData)); } /** @@ -102,17 +99,17 @@ class AccountController extends Controller private function renderAccountData(array $params, Account $account): void { Log::debug(sprintf('Now in %s(array, #%d)', __METHOD__, $account->id)); - $currency = $this->repository->getAccountCurrency($account); - $currentStart = clone $params['start']; - $range = Steam::finalAccountBalanceInRange($account, $params['start'], clone $params['end'], $this->convertToPrimary); + $currency = $this->repository->getAccountCurrency($account); + $currentStart = clone $params['start']; + $range = Steam::finalAccountBalanceInRange($account, $params['start'], clone $params['end'], $this->convertToPrimary); - $previous = array_values($range)[0]['balance']; - $pcPrevious = null; + $previous = array_values($range)[0]['balance']; + $pcPrevious = null; if (!$currency instanceof TransactionCurrency) { - $currency = $this->default; + $currency = $this->primaryCurrency; } - $currentSet = [ + $currentSet = [ 'label' => $account->name, // the currency that belongs to the account. @@ -162,21 +159,6 @@ class AccountController extends Controller $currentStart->addDay(); } - $this->chartData->add($currentSet); - } - - private function getFrontPageAccountIds(): array - { - $defaultSet = $this->repository->getAccountsByType([AccountTypeEnum::ASSET->value])->pluck('id')->toArray(); - - /** @var Preference $frontpage */ - $frontpage = Preferences::get('frontpageAccounts', $defaultSet); - - if (!(is_array($frontpage->data) && count($frontpage->data) > 0)) { - $frontpage->data = $defaultSet; - $frontpage->save(); - } - - return $frontpage->data ?? $defaultSet; + $this->chartData[] = $currentSet; } } diff --git a/app/Api/V1/Controllers/Chart/BalanceController.php b/app/Api/V1/Controllers/Chart/BalanceController.php index 52354aa7a3..d589be1661 100644 --- a/app/Api/V1/Controllers/Chart/BalanceController.php +++ b/app/Api/V1/Controllers/Chart/BalanceController.php @@ -25,9 +25,9 @@ class BalanceController extends Controller { use CleansChartData; use CollectsAccountsFromFilter; - protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; - private array $chartData; + private array $chartData = []; private GroupCollectorInterface $collector; private AccountRepositoryInterface $repository; @@ -38,13 +38,13 @@ class BalanceController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { + $this->validateUserGroup($request); $this->repository = app(AccountRepositoryInterface::class); $this->collector = app(GroupCollectorInterface::class); - $userGroup = $this->validateUserGroup($request); - $this->repository->setUserGroup($userGroup); - $this->collector->setUserGroup($userGroup); - $this->chartData = []; - // $this->default = app('amount')->getPrimaryCurrency(); + $this->repository->setUserGroup($this->userGroup); + $this->collector->setUserGroup($this->userGroup); + $this->repository->setUser($this->user); + $this->collector->setUser($this->user); return $next($request); } diff --git a/app/Api/V1/Controllers/Chart/BudgetController.php b/app/Api/V1/Controllers/Chart/BudgetController.php index a053ba00b7..4102693d36 100644 --- a/app/Api/V1/Controllers/Chart/BudgetController.php +++ b/app/Api/V1/Controllers/Chart/BudgetController.php @@ -26,7 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Api\V1\Requests\Data\DateRequest; +use FireflyIII\Api\V1\Requests\Data\SameDateRequest; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Budget; @@ -63,13 +63,16 @@ class BudgetController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { + $this->validateUserGroup($request); $this->repository = app(BudgetRepositoryInterface::class); $this->blRepository = app(BudgetLimitRepositoryInterface::class); $this->opsRepository = app(OperationsRepositoryInterface::class); - $userGroup = $this->validateUserGroup($request); - $this->repository->setUserGroup($userGroup); - $this->opsRepository->setUserGroup($userGroup); - $this->blRepository->setUserGroup($userGroup); + $this->repository->setUserGroup($this->userGroup); + $this->opsRepository->setUserGroup($this->userGroup); + $this->blRepository->setUserGroup($this->userGroup); + $this->repository->setUser($this->user); + $this->opsRepository->setUser($this->user); + $this->blRepository->setUser($this->user); return $next($request); } @@ -81,7 +84,7 @@ class BudgetController extends Controller * * @throws FireflyException */ - public function overview(DateRequest $request): JsonResponse + public function overview(SameDateRequest $request): JsonResponse { $params = $request->getAll(); @@ -157,12 +160,6 @@ class BudgetController extends Controller } - // if no limits - // if (0 === $limits->count()) { - // return as a single item in an array - // $rows = $this->noBudgetLimits($budget, $start, $end); - // } - // is always an array $return = []; foreach ($rows as $row) { @@ -193,9 +190,9 @@ class BudgetController extends Controller ], 'pc_entries' => [ 'budgeted' => $row['pc_budgeted'], - 'spent' => '0', - 'left' => '0', - 'overspent' => '0', + 'spent' => $row['pc_spent'], + 'left' => $row['pc_left'], + 'overspent' => $row['pc_overspent'], ], ]; $return[] = $current; diff --git a/app/Api/V1/Controllers/Chart/CategoryController.php b/app/Api/V1/Controllers/Chart/CategoryController.php index 9073627218..2ed6299222 100644 --- a/app/Api/V1/Controllers/Chart/CategoryController.php +++ b/app/Api/V1/Controllers/Chart/CategoryController.php @@ -26,7 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Api\V1\Requests\Data\DateRequest; +use FireflyIII\Api\V1\Requests\Data\SameDateRequest; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\UserRoleEnum; @@ -59,11 +59,13 @@ class CategoryController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { + $this->validateUserGroup($request); $this->accountRepos = app(AccountRepositoryInterface::class); $this->currencyRepos = app(CurrencyRepositoryInterface::class); - $userGroup = $this->validateUserGroup($request); - $this->accountRepos->setUserGroup($userGroup); - $this->currencyRepos->setUserGroup($userGroup); + $this->accountRepos->setUserGroup($this->userGroup); + $this->currencyRepos->setUserGroup($this->userGroup); + $this->accountRepos->setUser($this->user); + $this->currencyRepos->setUser($this->user); return $next($request); } @@ -78,7 +80,7 @@ class CategoryController extends Controller * * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ - public function overview(DateRequest $request): JsonResponse + public function overview(SameDateRequest $request): JsonResponse { /** @var Carbon $start */ $start = $this->parameters->get('start'); diff --git a/app/Api/V1/Controllers/Controller.php b/app/Api/V1/Controllers/Controller.php index ffef4503f4..196cca9290 100644 --- a/app/Api/V1/Controllers/Controller.php +++ b/app/Api/V1/Controllers/Controller.php @@ -64,7 +64,7 @@ abstract class Controller extends BaseController protected const string CONTENT_TYPE = 'application/vnd.api+json'; protected const string JSON_CONTENT_TYPE = 'application/json'; - protected array $accepts = ['application/json', 'application/vnd.api+json']; + protected array $accepts = ['application/json', 'application/vnd.api+json']; /** @var array */ protected array $allowedSort; @@ -107,7 +107,7 @@ abstract class Controller extends BaseController private function getParameters(): ParameterBag { $bag = new ParameterBag(); - $page = (int) request()->get('page'); + $page = (int)request()->get('page'); if ($page < 1) { $page = 1; } @@ -131,13 +131,13 @@ abstract class Controller extends BaseController $obj = null; if (null !== $date) { try { - $obj = Carbon::parse((string) $date); + $obj = Carbon::parse((string)$date); } catch (InvalidFormatException $e) { // don't care Log::warning( sprintf( 'Ignored invalid date "%s" in API controller parameter check: %s', - substr((string) $date, 0, 20), + substr((string)$date, 0, 20), $e->getMessage() ) ); @@ -158,7 +158,7 @@ abstract class Controller extends BaseController $value = null; } if (null !== $value) { - $value = (int) $value; + $value = (int)$value; if ($value < 1) { $value = 1; } @@ -176,7 +176,7 @@ abstract class Controller extends BaseController $user = auth()->user(); /** @var Preference $pageSize */ - $pageSize = (int) app('preferences')->getForUser($user, 'listPageSize', 50)->data; + $pageSize = (int)app('preferences')->getForUser($user, 'listPageSize', 50)->data; $bag->set($integer, $pageSize); } } @@ -190,7 +190,7 @@ abstract class Controller extends BaseController $sortParameters = []; try { - $param = (string) request()->query->get('sort'); + $param = (string)request()->query->get('sort'); } catch (BadRequestException $e) { Log::error('Request field "sort" contains a non-scalar value. Value set to NULL.'); Log::error($e->getMessage()); diff --git a/app/Api/V1/Controllers/Data/Bulk/TransactionController.php b/app/Api/V1/Controllers/Data/Bulk/TransactionController.php index f6ae26a5e2..7f00225c70 100644 --- a/app/Api/V1/Controllers/Data/Bulk/TransactionController.php +++ b/app/Api/V1/Controllers/Data/Bulk/TransactionController.php @@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Data\Bulk; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Data\Bulk\TransactionRequest; +use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Services\Internal\Destroy\AccountDestroyService; use Illuminate\Http\JsonResponse; @@ -44,23 +45,23 @@ class TransactionController extends Controller { private AccountRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::MANAGE_TRANSACTIONS]; + public function __construct() { parent::__construct(); $this->middleware( function ($request, $next) { + $this->validateUserGroup($request); $this->repository = app(AccountRepositoryInterface::class); - $this->repository->setUser(auth()->user()); + $this->repository->setUserGroup($this->userGroup); + $this->repository->setUser($this->user); return $next($request); } ); } - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/bulkUpdateTransactions - */ public function update(TransactionRequest $request): JsonResponse { $query = $request->getAll(); diff --git a/app/Api/V1/Controllers/Data/DestroyController.php b/app/Api/V1/Controllers/Data/DestroyController.php index b2ed17a568..87e791be7c 100644 --- a/app/Api/V1/Controllers/Data/DestroyController.php +++ b/app/Api/V1/Controllers/Data/DestroyController.php @@ -28,6 +28,7 @@ use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Data\DestroyRequest; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\TransactionTypeEnum; +use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\TransactionJournal; @@ -55,12 +56,20 @@ class DestroyController extends Controller { private bool $unused; - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/destroyData - * - * @throws FireflyException - */ + protected array $acceptedRoles = [UserRoleEnum::FULL]; + + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->validateUserGroup($request); + + return $next($request); + } + ); + } + public function destroy(DestroyRequest $request): JsonResponse { $objects = $request->getObjects(); diff --git a/app/Api/V1/Controllers/Data/Export/ExportController.php b/app/Api/V1/Controllers/Data/Export/ExportController.php index 2b847226c2..f65e23dfc0 100644 --- a/app/Api/V1/Controllers/Data/Export/ExportController.php +++ b/app/Api/V1/Controllers/Data/Export/ExportController.php @@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Data\Export; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Data\Export\ExportRequest; +use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Support\Export\ExportDataGenerator; use Illuminate\Http\Response as LaravelResponse; @@ -39,6 +40,7 @@ use function Safe\date; class ExportController extends Controller { private ExportDataGenerator $exporter; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; /** * ExportController constructor. @@ -48,8 +50,10 @@ class ExportController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { + $this->validateUserGroup($request); $this->exporter = app(ExportDataGenerator::class); - $this->exporter->setUser(auth()->user()); + $this->exporter->setUserGroup($this->userGroup); + $this->exporter->setUser($this->user); return $next($request); } @@ -57,9 +61,6 @@ class ExportController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportAccounts - * * @throws FireflyException * * @SuppressWarnings("PHPMD.UnusedFormalParameter") @@ -99,9 +100,6 @@ class ExportController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportBills - * * @throws FireflyException * * @SuppressWarnings("PHPMD.UnusedFormalParameter") @@ -114,9 +112,6 @@ class ExportController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportBudgets - * * @throws FireflyException * * @SuppressWarnings("PHPMD.UnusedFormalParameter") @@ -129,9 +124,6 @@ class ExportController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportCategories - * * @throws FireflyException * * @SuppressWarnings("PHPMD.UnusedFormalParameter") @@ -144,9 +136,6 @@ class ExportController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportPiggies - * * @throws FireflyException * * @SuppressWarnings("PHPMD.UnusedFormalParameter") @@ -159,9 +148,6 @@ class ExportController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportRecurring - * * @throws FireflyException * * @SuppressWarnings("PHPMD.UnusedFormalParameter") @@ -174,9 +160,6 @@ class ExportController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportRules - * * @throws FireflyException * * @SuppressWarnings("PHPMD.UnusedFormalParameter") @@ -189,9 +172,6 @@ class ExportController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportTags - * * @throws FireflyException * * @SuppressWarnings("PHPMD.UnusedFormalParameter") @@ -204,9 +184,6 @@ class ExportController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportTransactions - * * @throws FireflyException */ public function transactions(ExportRequest $request): LaravelResponse diff --git a/app/Api/V1/Controllers/Data/PurgeController.php b/app/Api/V1/Controllers/Data/PurgeController.php index baba2f622a..cea88fa0e6 100644 --- a/app/Api/V1/Controllers/Data/PurgeController.php +++ b/app/Api/V1/Controllers/Data/PurgeController.php @@ -25,6 +25,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Data; use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; use FireflyIII\Models\Budget; @@ -44,10 +45,22 @@ use Illuminate\Http\JsonResponse; */ class PurgeController extends Controller { + protected array $acceptedRoles = [UserRoleEnum::FULL]; + + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->validateUserGroup($request); + + return $next($request); + } + ); + } + /** * 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 */ public function purge(): JsonResponse { @@ -66,14 +79,6 @@ class PurgeController extends Controller $repository = app(PiggyBankRepositoryInterface::class); $repository->setUser($user); $repository->purgeAll(); - // $set = PiggyBank::leftJoin('accounts', 'accounts.id', 'piggy_banks.account_id') - // ->where('accounts.user_id', $user->id)->onlyTrashed()->get(['piggy_banks.*']) - // ; - // - // /** @var PiggyBank $piggy */ - // foreach ($set as $piggy) { - // $piggy->forceDelete(); - // } // rule group RuleGroup::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 323afc1e0f..889fdce65d 100644 --- a/app/Api/V1/Controllers/Insight/Expense/AccountController.php +++ b/app/Api/V1/Controllers/Insight/Expense/AccountController.php @@ -64,10 +64,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 - */ public function asset(GenericRequest $request): JsonResponse { $start = $request->getStart(); @@ -91,10 +87,6 @@ class AccountController extends Controller return response()->json($result); } - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseExpense - */ public function expense(GenericRequest $request): JsonResponse { $start = $request->getStart(); diff --git a/app/Api/V1/Controllers/Insight/Expense/BillController.php b/app/Api/V1/Controllers/Insight/Expense/BillController.php index 938c4d5e31..55cd1dc77c 100644 --- a/app/Api/V1/Controllers/Insight/Expense/BillController.php +++ b/app/Api/V1/Controllers/Insight/Expense/BillController.php @@ -58,9 +58,6 @@ class BillController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseBill - * * Expenses per bill, possibly filtered by bill and account. */ public function bill(GenericRequest $request): JsonResponse @@ -122,9 +119,6 @@ class BillController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseNoBill - * * Expenses for no bill filtered by account. */ 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 5795cb114e..ed91b96a16 100644 --- a/app/Api/V1/Controllers/Insight/Expense/BudgetController.php +++ b/app/Api/V1/Controllers/Insight/Expense/BudgetController.php @@ -63,10 +63,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 - */ public function budget(GenericRequest $request): JsonResponse { $start = $request->getStart(); @@ -98,10 +94,6 @@ class BudgetController extends Controller return response()->json($result); } - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseNoBudget - */ public function noBudget(GenericRequest $request): JsonResponse { $start = $request->getStart(); diff --git a/app/Api/V1/Controllers/Insight/Expense/CategoryController.php b/app/Api/V1/Controllers/Insight/Expense/CategoryController.php index fd96e47ed7..d5d092b461 100644 --- a/app/Api/V1/Controllers/Insight/Expense/CategoryController.php +++ b/app/Api/V1/Controllers/Insight/Expense/CategoryController.php @@ -63,10 +63,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 - */ public function category(GenericRequest $request): JsonResponse { $start = $request->getStart(); @@ -98,10 +94,6 @@ class CategoryController extends Controller return response()->json($result); } - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferNoCategory - */ public function noCategory(GenericRequest $request): JsonResponse { $start = $request->getStart(); diff --git a/app/Api/V1/Controllers/Insight/Expense/PeriodController.php b/app/Api/V1/Controllers/Insight/Expense/PeriodController.php index b50c3a0384..3e03ec287b 100644 --- a/app/Api/V1/Controllers/Insight/Expense/PeriodController.php +++ b/app/Api/V1/Controllers/Insight/Expense/PeriodController.php @@ -37,10 +37,6 @@ use Illuminate\Support\Facades\Log; */ class PeriodController extends Controller { - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseTotal - */ public function total(GenericRequest $request): JsonResponse { $accounts = $request->getAssetAccounts(); diff --git a/app/Api/V1/Controllers/Insight/Expense/TagController.php b/app/Api/V1/Controllers/Insight/Expense/TagController.php index e48bb1df25..ff98df82fe 100644 --- a/app/Api/V1/Controllers/Insight/Expense/TagController.php +++ b/app/Api/V1/Controllers/Insight/Expense/TagController.php @@ -57,9 +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/insightExpenseNoTag - * * Expenses for no tag filtered by account. */ public function noTag(GenericRequest $request): JsonResponse @@ -115,9 +112,6 @@ class TagController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseTag - * * Expenses per tag, possibly filtered by tag and account. */ public function tag(GenericRequest $request): JsonResponse diff --git a/app/Api/V1/Controllers/Insight/Income/AccountController.php b/app/Api/V1/Controllers/Insight/Income/AccountController.php index 87cff7be5c..d167092261 100644 --- a/app/Api/V1/Controllers/Insight/Income/AccountController.php +++ b/app/Api/V1/Controllers/Insight/Income/AccountController.php @@ -64,10 +64,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 - */ public function asset(GenericRequest $request): JsonResponse { $start = $request->getStart(); @@ -92,10 +88,6 @@ class AccountController extends Controller return response()->json($result); } - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeRevenue - */ public function revenue(GenericRequest $request): JsonResponse { $start = $request->getStart(); diff --git a/app/Api/V1/Controllers/Insight/Income/CategoryController.php b/app/Api/V1/Controllers/Insight/Income/CategoryController.php index d2a1fa9744..1547090337 100644 --- a/app/Api/V1/Controllers/Insight/Income/CategoryController.php +++ b/app/Api/V1/Controllers/Insight/Income/CategoryController.php @@ -63,10 +63,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 - */ public function category(GenericRequest $request): JsonResponse { $start = $request->getStart(); @@ -98,10 +94,6 @@ class CategoryController extends Controller return response()->json($result); } - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeNoCategory - */ public function noCategory(GenericRequest $request): JsonResponse { $start = $request->getStart(); diff --git a/app/Api/V1/Controllers/Insight/Income/PeriodController.php b/app/Api/V1/Controllers/Insight/Income/PeriodController.php index 8f535c8bbf..c66a19eab8 100644 --- a/app/Api/V1/Controllers/Insight/Income/PeriodController.php +++ b/app/Api/V1/Controllers/Insight/Income/PeriodController.php @@ -36,10 +36,6 @@ use Illuminate\Http\JsonResponse; */ class PeriodController extends Controller { - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeTotal - */ public function total(GenericRequest $request): JsonResponse { $accounts = $request->getAssetAccounts(); diff --git a/app/Api/V1/Controllers/Insight/Income/TagController.php b/app/Api/V1/Controllers/Insight/Income/TagController.php index 72c869711a..fe45270654 100644 --- a/app/Api/V1/Controllers/Insight/Income/TagController.php +++ b/app/Api/V1/Controllers/Insight/Income/TagController.php @@ -57,9 +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/insightIncomeTag - * * Expenses for no tag filtered by account. */ public function noTag(GenericRequest $request): JsonResponse @@ -109,9 +106,6 @@ class TagController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeNoTag - * * Expenses per tag, possibly filtered by tag and account. */ public function tag(GenericRequest $request): JsonResponse diff --git a/app/Api/V1/Controllers/Insight/Transfer/AccountController.php b/app/Api/V1/Controllers/Insight/Transfer/AccountController.php index 6fc08e056b..ce9e1635aa 100644 --- a/app/Api/V1/Controllers/Insight/Transfer/AccountController.php +++ b/app/Api/V1/Controllers/Insight/Transfer/AccountController.php @@ -56,10 +56,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 - */ public function asset(GenericRequest $request): JsonResponse { $start = $request->getStart(); diff --git a/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php b/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php index 6ad2c269df..63cabef200 100644 --- a/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php +++ b/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php @@ -63,10 +63,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 - */ public function category(GenericRequest $request): JsonResponse { $start = $request->getStart(); @@ -98,10 +94,6 @@ class CategoryController extends Controller return response()->json($result); } - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferNoCategory - */ public function noCategory(GenericRequest $request): JsonResponse { $start = $request->getStart(); diff --git a/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php b/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php index ef3230bac5..e2dc396d19 100644 --- a/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php +++ b/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php @@ -36,10 +36,6 @@ use Illuminate\Http\JsonResponse; */ class PeriodController extends Controller { - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferTotal - */ public function total(GenericRequest $request): JsonResponse { $accounts = $request->getAssetAccounts(); diff --git a/app/Api/V1/Controllers/Insight/Transfer/TagController.php b/app/Api/V1/Controllers/Insight/Transfer/TagController.php index be3f15ef59..a03d623cfd 100644 --- a/app/Api/V1/Controllers/Insight/Transfer/TagController.php +++ b/app/Api/V1/Controllers/Insight/Transfer/TagController.php @@ -56,10 +56,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 - */ public function noTag(GenericRequest $request): JsonResponse { $accounts = $request->getAssetAccounts(); @@ -108,9 +104,6 @@ class TagController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferTag - * * Transfers per tag, possibly filtered by tag and account. */ public function tag(GenericRequest $request): JsonResponse diff --git a/app/Api/V1/Controllers/Models/Account/DestroyController.php b/app/Api/V1/Controllers/Models/Account/DestroyController.php index c48b612296..fc23429f76 100644 --- a/app/Api/V1/Controllers/Models/Account/DestroyController.php +++ b/app/Api/V1/Controllers/Models/Account/DestroyController.php @@ -55,9 +55,6 @@ class DestroyController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/accounts/deleteAccount - * * Remove the specified resource from storage. */ 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 aa36e26380..15f8ac7c04 100644 --- a/app/Api/V1/Controllers/Models/Account/ListController.php +++ b/app/Api/V1/Controllers/Models/Account/ListController.php @@ -71,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 - * * @throws FireflyException */ public function attachments(Account $account): JsonResponse @@ -100,9 +97,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 - * * @throws FireflyException */ public function piggyBanks(Account $account): JsonResponse @@ -140,9 +134,6 @@ class ListController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/accounts/listTransactionByAccount - * * Show all transaction groups related to the account. * * @throws FireflyException diff --git a/app/Api/V1/Controllers/Models/Account/ShowController.php b/app/Api/V1/Controllers/Models/Account/ShowController.php index 602a557724..5cdb73d10d 100644 --- a/app/Api/V1/Controllers/Models/Account/ShowController.php +++ b/app/Api/V1/Controllers/Models/Account/ShowController.php @@ -67,9 +67,6 @@ class ShowController extends Controller } /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/accounts/listAccount - * * Display a listing of the resource. * * @throws FireflyException diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php b/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php index 2463533c1b..3b5c2c3569 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php @@ -31,6 +31,7 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Support\JsonApi\Enrichments\BudgetEnrichment; use FireflyIII\Support\JsonApi\Enrichments\BudgetLimitEnrichment; use FireflyIII\Transformers\BudgetLimitTransformer; use FireflyIII\User; @@ -76,6 +77,16 @@ class ShowController extends Controller */ public function index(Budget $budget): JsonResponse { + /** @var User $admin */ + $admin = auth()->user(); + // enrich budget: + $enrichment = new BudgetEnrichment(); + $enrichment->setUser($admin); + $enrichment->setStart($this->parameters->get('start')); + $enrichment->setEnd($this->parameters->get('end')); + $budget = $enrichment->enrichSingle($budget); + + $manager = $this->getManager(); $manager->parseIncludes('budget'); $pageSize = $this->parameters->get('limit'); @@ -87,8 +98,6 @@ class ShowController extends Controller // enrich - /** @var User $admin */ - $admin = auth()->user(); $enrichment = new BudgetLimitEnrichment(); $enrichment->setUser($admin); $budgetLimits = $enrichment->enrich($budgetLimits); diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php index 73b6b1de3e..481896505c 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php @@ -126,7 +126,7 @@ class ShowController extends Controller * * @throws FireflyException */ - public function showDefault(): JsonResponse + public function showPrimary(): JsonResponse { /** @var User $user */ $user = auth()->user(); diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php index d3ef22fb29..5e4364043f 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php @@ -73,7 +73,7 @@ class StoreController extends Controller { $currency = $this->repository->store($request->getAll()); if (true === $request->boolean('default')) { - $this->repository->makeDefault($currency); + $this->repository->makePrimary($currency); app('preferences')->mark(); } $manager = $this->getManager(); diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php index 2516a34c67..f9a45ffdee 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php @@ -101,12 +101,12 @@ class UpdateController extends Controller /** * @throws FireflyException */ - public function makeDefault(TransactionCurrency $currency): JsonResponse + public function makePrimary(TransactionCurrency $currency): JsonResponse { /** @var User $user */ $user = auth()->user(); $this->repository->enable($currency); - $this->repository->makeDefault($currency); + $this->repository->makePrimary($currency); app('preferences')->mark(); diff --git a/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php b/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php index aceaa211be..06ba95035a 100644 --- a/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php +++ b/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php @@ -49,14 +49,23 @@ class ValidatesEnvironmentVariables extends Command */ public function handle(): int { - $this->validateLanguage(); - $this->validateGuard(); - $this->validateStaticToken(); + $result = $this->validateLanguage(); + if (false === $result) { + return Command::FAILURE; + } + $result = $this->validateGuard(); + if (false === $result) { + return Command::FAILURE; + } + $result = $this->validateStaticToken(); + if (false === $result) { + return Command::FAILURE; + } return Command::SUCCESS; } - private function validateLanguage(): void + private function validateLanguage(): bool { $language = config('firefly.default_language'); $locale = config('firefly.default_locale'); @@ -67,7 +76,7 @@ class ValidatesEnvironmentVariables extends Command $this->friendlyError('Please check your .env file and make sure you use a valid setting.'); $this->friendlyError(sprintf('Valid languages are: %s', implode(', ', $options))); - exit(1); + return false; } $options[] = 'equal'; if (!in_array($locale, $options, true)) { @@ -75,11 +84,13 @@ class ValidatesEnvironmentVariables extends Command $this->friendlyError('Please check your .env file and make sure you use a valid setting.'); $this->friendlyError(sprintf('Valid locales are: %s', implode(', ', $options))); - exit(1); + return false; } + + return true; } - private function validateGuard(): void + private function validateGuard(): bool { $guard = config('auth.defaults.guard'); if ('web' !== $guard && 'remote_user_guard' !== $guard) { @@ -87,18 +98,22 @@ class ValidatesEnvironmentVariables extends Command $this->friendlyError('Please check your .env file and make sure you use a valid setting.'); $this->friendlyError('Valid guards are: web, remote_user_guard'); - exit(1); + return false; } + + return true; } - private function validateStaticToken(): void + private function validateStaticToken(): bool { - $token = (string) config('firefly.static_cron_token'); + $token = (string)config('firefly.static_cron_token'); if ('' !== $token && 32 !== strlen($token)) { $this->friendlyError('STATIC_CRON_TOKEN must be empty or a 32-character string.'); $this->friendlyError('Please check your .env file and make sure you use a valid setting.'); - exit(1); + return false; } + + return true; } } diff --git a/app/Console/Commands/System/ResetsErrorMailLimit.php b/app/Console/Commands/System/ResetsErrorMailLimit.php new file mode 100644 index 0000000000..9aba24fa79 --- /dev/null +++ b/app/Console/Commands/System/ResetsErrorMailLimit.php @@ -0,0 +1,60 @@ +friendlyError(sprintf('Cannot write to directory "%s", cannot rate limit errors.', $directory)); + + return CommandAlias::FAILURE; + } + if (!file_exists($file)) { + $this->friendlyInfo(sprintf('Created new limits file at "%s"', $file)); + file_put_contents($file, json_encode($limits, JSON_PRETTY_PRINT)); + + return CommandAlias::SUCCESS; + } + if (!is_writable($file)) { + $this->friendlyError(sprintf('Cannot write to "%s", cannot rate limit errors.', $file)); + + return CommandAlias::FAILURE; + } + + $this->friendlyInfo(sprintf('Successfully reset the error rate-limits file located at "%s"', $file)); + file_put_contents($file, json_encode($limits, JSON_PRETTY_PRINT)); + + return CommandAlias::SUCCESS; + } +} diff --git a/app/Console/Commands/System/ScansAttachments.php b/app/Console/Commands/System/ScansAttachments.php index 6597025bcd..24c6f6199b 100644 --- a/app/Console/Commands/System/ScansAttachments.php +++ b/app/Console/Commands/System/ScansAttachments.php @@ -29,12 +29,16 @@ use FireflyIII\Models\Attachment; use Illuminate\Console\Command; use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Support\Facades\Crypt; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; +use Safe\Exceptions\FileinfoException; +use Safe\Exceptions\FilesystemException; +use Safe\Exceptions\StringsException; -use function Safe\tempnam; use function Safe\file_put_contents; use function Safe\md5_file; use function Safe\mime_content_type; +use function Safe\tempnam; class ScansAttachments extends Command { @@ -46,6 +50,10 @@ class ScansAttachments extends Command /** * Execute the console command. + * + * @throws FilesystemException + * @throws StringsException + * @throws FileinfoException */ public function handle(): int { @@ -57,7 +65,7 @@ class ScansAttachments extends Command $fileName = $attachment->fileName(); $encryptedContent = $disk->get($fileName); if (null === $encryptedContent) { - app('log')->error(sprintf('No content for attachment #%d under filename "%s"', $attachment->id, $fileName)); + Log::error(sprintf('No content for attachment #%d under filename "%s"', $attachment->id, $fileName)); continue; } @@ -65,18 +73,13 @@ class ScansAttachments extends Command try { $decryptedContent = Crypt::decrypt($encryptedContent); // verified } catch (DecryptException $e) { - app('log')->error(sprintf('Could not decrypt data of attachment #%d: %s', $attachment->id, $e->getMessage())); + Log::error(sprintf('Could not decrypt data of attachment #%d: %s', $attachment->id, $e->getMessage())); $decryptedContent = $encryptedContent; } $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); - $attachment->md5 = (string) md5_file($tempFileName); - $attachment->mime = (string) mime_content_type($tempFileName); + $attachment->md5 = (string)md5_file($tempFileName); + $attachment->mime = (string)mime_content_type($tempFileName); $attachment->save(); $this->friendlyInfo(sprintf('Fixed attachment #%d', $attachment->id)); } diff --git a/app/Console/Commands/Tools/Cron.php b/app/Console/Commands/Tools/Cron.php index 09ab24c6b4..27a4e66a64 100644 --- a/app/Console/Commands/Tools/Cron.php +++ b/app/Console/Commands/Tools/Cron.php @@ -50,7 +50,7 @@ class Cron extends Command {--download-cer : Download exchange rates. Other tasks will be skipped unless also requested.} {--create-recurring : Create recurring transactions. Other tasks will be skipped unless also requested.} {--create-auto-budgets : Create auto budgets. Other tasks will be skipped unless also requested.} - {--send-bill-warnings : Send bill warnings. Other tasks will be skipped unless also requested.} + {--send-subscription-warnings : Send subscription warnings. Other tasks will be skipped unless also requested.} {--send-webhook-messages : Sends any stray webhook messages (with a maximum of 5).} '; @@ -59,7 +59,7 @@ class Cron extends Command $doAll = !$this->option('download-cer') && !$this->option('create-recurring') && !$this->option('create-auto-budgets') - && !$this->option('send-bill-warnings') + && !$this->option('send-subscription-warnings') && !$this->option('check-version') && !$this->option('send-webhook-messages'); $date = null; @@ -116,9 +116,9 @@ class Cron extends Command } // Fire bill warning cron job - if ($doAll || $this->option('send-bill-warnings')) { + if ($doAll || $this->option('send-subscription-warnings')) { try { - $this->billWarningCronJob($force, $date); + $this->subscriptionWarningCronJob($force, $date); } catch (FireflyException $e) { app('log')->error($e->getMessage()); app('log')->error($e->getTraceAsString()); @@ -231,25 +231,25 @@ class Cron extends Command /** * @throws FireflyException */ - private function billWarningCronJob(bool $force, ?Carbon $date): void + private function subscriptionWarningCronJob(bool $force, ?Carbon $date): void { - $autoBudget = new BillWarningCronjob(); - $autoBudget->setForce($force); + $subscriptionWarningJob = new BillWarningCronjob(); + $subscriptionWarningJob->setForce($force); // set date in cron job: if ($date instanceof Carbon) { - $autoBudget->setDate($date); + $subscriptionWarningJob->setDate($date); } - $autoBudget->fire(); + $subscriptionWarningJob->fire(); - if ($autoBudget->jobErrored) { - $this->friendlyError(sprintf('Error in "bill warnings" cron: %s', $autoBudget->message)); + if ($subscriptionWarningJob->jobErrored) { + $this->friendlyError(sprintf('Error in "subscription warnings" cron: %s', $subscriptionWarningJob->message)); } - if ($autoBudget->jobFired) { - $this->friendlyInfo(sprintf('"Send bill warnings" cron fired: %s', $autoBudget->message)); + if ($subscriptionWarningJob->jobFired) { + $this->friendlyInfo(sprintf('"Send subscription warnings" cron fired: %s', $subscriptionWarningJob->message)); } - if ($autoBudget->jobSucceeded) { - $this->friendlyPositive(sprintf('"Send bill warnings" cron ran with success: %s', $autoBudget->message)); + if ($subscriptionWarningJob->jobSucceeded) { + $this->friendlyPositive(sprintf('"Send subscription warnings" cron ran with success: %s', $subscriptionWarningJob->message)); } } diff --git a/app/Helpers/Collector/GroupCollector.php b/app/Helpers/Collector/GroupCollector.php index 0e05e5d7c0..59b2748730 100644 --- a/app/Helpers/Collector/GroupCollector.php +++ b/app/Helpers/Collector/GroupCollector.php @@ -839,7 +839,7 @@ class GroupCollector implements GroupCollectorInterface return 'zzz'; } - exit('here we are 2'); + return 'zzz'; }); } diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index 5d7f731206..79701fe3f7 100644 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -36,6 +36,7 @@ use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Routing\Redirector; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\ValidationException; use Illuminate\View\View; use Psr\Container\ContainerExceptionInterface; @@ -94,7 +95,7 @@ class RegisterController extends Controller $this->validator($request->all())->validate(); $user = $this->createUser($request->all()); - app('log')->info(sprintf('Registered new user %s', $user->email)); + Log::info(sprintf('Registered new user %s', $user->email)); $owner = new OwnerNotifiable(); event(new RegisteredUser($owner, $user)); diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php index ab53751cce..271c70e692 100644 --- a/app/Http/Controllers/NewUserController.php +++ b/app/Http/Controllers/NewUserController.php @@ -112,7 +112,7 @@ class NewUserController extends Controller $this->createCashWalletAccount($currency, $language); // create cash wallet account // store currency preference: - $currencyRepository->makeDefault($currency); + $currencyRepository->makePrimary($currency); // store frontpage preferences: $accounts = $this->repository->getAccountsByType([AccountTypeEnum::ASSET->value])->pluck('id')->toArray(); diff --git a/app/Repositories/Currency/CurrencyRepository.php b/app/Repositories/Currency/CurrencyRepository.php index 3acdfc5593..ce52a5f503 100644 --- a/app/Repositories/Currency/CurrencyRepository.php +++ b/app/Repositories/Currency/CurrencyRepository.php @@ -417,7 +417,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf // currency must be made default. if (true === $default) { - $this->makeDefault($currency); + $this->makePrimary($currency); } /** @var CurrencyUpdateService $service */ @@ -426,7 +426,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf return $service->update($currency, $data); } - public function makeDefault(TransactionCurrency $currency): void + public function makePrimary(TransactionCurrency $currency): void { $current = app('amount')->getPrimaryCurrencyByUserGroup($this->userGroup); Log::debug(sprintf('Enabled + made default currency %s for user #%d', $currency->code, $this->userGroup->id)); diff --git a/app/Repositories/Currency/CurrencyRepositoryInterface.php b/app/Repositories/Currency/CurrencyRepositoryInterface.php index f45f5642d2..820b57c73c 100644 --- a/app/Repositories/Currency/CurrencyRepositoryInterface.php +++ b/app/Repositories/Currency/CurrencyRepositoryInterface.php @@ -102,7 +102,7 @@ interface CurrencyRepositoryInterface public function isFallbackCurrency(TransactionCurrency $currency): bool; - public function makeDefault(TransactionCurrency $currency): void; + public function makePrimary(TransactionCurrency $currency): void; public function searchCurrency(string $search, int $limit): Collection; diff --git a/app/Repositories/UserGroup/UserGroupRepository.php b/app/Repositories/UserGroup/UserGroupRepository.php index 5301b87f9f..f8c9e3a488 100644 --- a/app/Repositories/UserGroup/UserGroupRepository.php +++ b/app/Repositories/UserGroup/UserGroupRepository.php @@ -209,7 +209,7 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte $currency = $repository->find((int) $data['primary_currency_id']); } if (null !== $currency) { - $repository->makeDefault($currency); + $repository->makePrimary($currency); } diff --git a/app/Repositories/UserGroups/Currency/CurrencyRepository.php b/app/Repositories/UserGroups/Currency/CurrencyRepository.php index 409ebeef5d..7928ec74e6 100644 --- a/app/Repositories/UserGroups/Currency/CurrencyRepository.php +++ b/app/Repositories/UserGroups/Currency/CurrencyRepository.php @@ -365,7 +365,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface // currency must be made default. if (true === $default) { - $this->makeDefault($currency); + $this->makePrimary($currency); } /** @var CurrencyUpdateService $service */ @@ -374,7 +374,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface return $service->update($currency, $data); } - public function makeDefault(TransactionCurrency $currency): void + public function makePrimary(TransactionCurrency $currency): void { $current = app('amount')->getPrimaryCurrencyByUserGroup($this->userGroup); Log::debug(sprintf('Enabled + made default currency %s for user #%d', $currency->code, $this->userGroup->id)); diff --git a/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php b/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php index f876c65e84..8c34e22708 100644 --- a/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php +++ b/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php @@ -89,7 +89,7 @@ interface CurrencyRepositoryInterface public function isFallbackCurrency(TransactionCurrency $currency): bool; - public function makeDefault(TransactionCurrency $currency): void; + public function makePrimary(TransactionCurrency $currency): void; public function searchCurrency(string $search, int $limit): Collection; diff --git a/app/Support/Chart/Category/FrontpageChartGenerator.php b/app/Support/Chart/Category/FrontpageChartGenerator.php index dd10fd018e..c27f13c686 100644 --- a/app/Support/Chart/Category/FrontpageChartGenerator.php +++ b/app/Support/Chart/Category/FrontpageChartGenerator.php @@ -65,55 +65,27 @@ class FrontpageChartGenerator public function generate(): array { - Log::debug('Now in generate()'); + Log::debug(sprintf('Now in %s', __METHOD__)); $categories = $this->repository->getCategories(); $accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]); - - // get expenses + income per category: - $collection = []; - - /** @var Category $category */ - foreach ($categories as $category) { - // get expenses - $collection[] = $this->collectExpenses($category, $accounts); - } + $collection = $this->collectExpensesAll($categories, $accounts); // collect for no-category: - $collection[] = $this->collectNoCatExpenses($accounts); - - $tempData = array_merge(...$collection); + $noCategory = $this->collectNoCatExpenses($accounts); + $collection = array_merge($collection, $noCategory); // sort temp array by amount. - $amounts = array_column($tempData, 'sum_float'); - array_multisort($amounts, SORT_ASC, $tempData); + $amounts = array_column($collection, 'sum_float'); + array_multisort($amounts, SORT_ASC, $collection); - $currencyData = $this->createCurrencyGroups($tempData); + $currencyData = $this->createCurrencyGroups($collection); - return $this->insertValues($currencyData, $tempData); - } - - private function collectExpenses(Category $category, Collection $accounts): array - { - Log::debug(sprintf('Collect expenses for category #%d ("%s")', $category->id, $category->name)); - $spent = $this->opsRepos->sumExpenses($this->start, $this->end, $accounts, new Collection([$category])); - $tempData = []; - foreach ($spent as $currency) { - Log::debug(sprintf('Spent %s %s', $currency['currency_code'], $currency['sum'])); - $this->addCurrency($currency); - $tempData[] = [ - 'name' => $category->name, - 'sum' => $currency['sum'], - 'sum_float' => round((float) $currency['sum'], $currency['currency_decimal_places']), - 'currency_id' => (int) $currency['currency_id'], - ]; - } - - return $tempData; + return $this->insertValues($currencyData, $collection); } private function addCurrency(array $currency): void { - $currencyId = (int) $currency['currency_id']; + $currencyId = (int)$currency['currency_id']; $this->currencies[$currencyId] ??= [ 'currency_id' => $currencyId, @@ -133,8 +105,8 @@ class FrontpageChartGenerator $tempData[] = [ 'name' => trans('firefly.no_category'), 'sum' => $currency['sum'], - 'sum_float' => round((float) $currency['sum'], $currency['currency_decimal_places'] ?? 2), // intentional float - 'currency_id' => (int) $currency['currency_id'], + 'sum_float' => round((float)$currency['sum'], $currency['currency_decimal_places'] ?? 2), // intentional float + 'currency_id' => (int)$currency['currency_id'], ]; } @@ -152,7 +124,7 @@ class FrontpageChartGenerator foreach ($this->currencies as $currencyId => $currency) { $key = sprintf('spent-%d', $currencyId); $return[$key] = [ - 'label' => sprintf('%s (%s)', (string) trans('firefly.spent'), $currency['currency_name']), + 'label' => sprintf('%s (%s)', (string)trans('firefly.spent'), $currency['currency_name']), 'type' => 'bar', 'currency_symbol' => $currency['currency_symbol'], 'entries' => $names, @@ -175,4 +147,28 @@ class FrontpageChartGenerator return $currencyData; } + + private function collectExpensesAll(Collection $categories, Collection $accounts): array + { + Log::debug(sprintf('Collect expenses for %d category(ies).', count($categories))); + $spent = $this->opsRepos->collectExpenses($this->start, $this->end, $accounts, $categories); + $tempData = []; + foreach ($categories as $category) { + $sums = $this->opsRepos->sumCollectedTransactionsByCategory($spent, $category, 'negative', $this->convertToPrimary); + if (0 === count($sums)) { + continue; + } + foreach ($sums as $currency) { + $this->addCurrency($currency); + $tempData[] = [ + 'name' => $category->name, + 'sum' => $currency['sum'], + 'sum_float' => round((float)$currency['sum'], $currency['currency_decimal_places']), + 'currency_id' => (int)$currency['currency_id'], + ]; + } + } + + return $tempData; + } } diff --git a/app/Support/Export/ExportDataGenerator.php b/app/Support/Export/ExportDataGenerator.php index 02f3f04763..ad0f0b4b71 100644 --- a/app/Support/Export/ExportDataGenerator.php +++ b/app/Support/Export/ExportDataGenerator.php @@ -42,6 +42,7 @@ use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleTrigger; use FireflyIII\Models\Tag; +use FireflyIII\Models\UserGroup; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; @@ -84,6 +85,7 @@ class ExportDataGenerator private bool $exportTransactions; private Carbon $start; private User $user; + private UserGroup $userGroup; public function __construct() { @@ -906,4 +908,9 @@ class ExportDataGenerator { $this->start = $start; } + + public function setUserGroup(UserGroup $userGroup): void + { + $this->userGroup = $userGroup; + } } diff --git a/app/Support/Http/Api/ValidatesUserGroupTrait.php b/app/Support/Http/Api/ValidatesUserGroupTrait.php index 89504139ae..c33be45a1e 100644 --- a/app/Support/Http/Api/ValidatesUserGroupTrait.php +++ b/app/Support/Http/Api/ValidatesUserGroupTrait.php @@ -39,6 +39,7 @@ use Illuminate\Support\Facades\Log; trait ValidatesUserGroupTrait { protected ?UserGroup $userGroup = null; + protected ?User $user = null; /** * An "undocumented" filter @@ -101,6 +102,7 @@ trait ValidatesUserGroupTrait if ($user->hasRoleInGroupOrOwner($group, $role)) { Log::debug(sprintf('validateUserGroup: User has role "%s" in group #%d, return the group.', $role->value, $groupId)); $this->userGroup = $group; + $this->user = $user; return $group; } diff --git a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php index f4697394db..d94c91cb40 100644 --- a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php +++ b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php @@ -74,8 +74,8 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function collectIds(): void { - $this->start = $this->collection->min('start_date'); - $this->end = $this->collection->max('end_date'); + $this->start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth(); + $this->end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth(); /** @var BudgetLimit $limit */ foreach ($this->collection as $limit) { diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index e3edfa9b7f..d88b01ba38 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -464,7 +464,7 @@ class Navigation $displayFormat = (string) trans('config.month_and_day_js', [], $locale); $diff = $start->diffInMonths($end, true); // increment by month (for year) - if ($diff >= 1.0001) { + if ($diff >= 1.0001 && $diff < 12.001) { $increment = 'addMonth'; $displayFormat = (string) trans('config.month_js'); } @@ -495,7 +495,7 @@ class Navigation $format = 'Y-m-d'; $diff = $start->diffInMonths($end, true); // Log::debug(sprintf('preferredCarbonFormat(%s, %s) = %f', $start->format('Y-m-d'), $end->format('Y-m-d'), $diff)); - if ($diff >= 1.001) { + if ($diff >= 1.001 && $diff < 12.001) { // Log::debug(sprintf('Return Y-m because %s', $diff)); $format = 'Y-m'; } @@ -566,7 +566,7 @@ class Navigation { $locale = app('steam')->getLocale(); $diff = $start->diffInMonths($end, true); - if ($diff >= 1.001) { + if ($diff >= 1.001 && $diff < 12.001) { return (string) trans('config.month_js', [], $locale); } @@ -584,7 +584,7 @@ class Navigation public function preferredEndOfPeriod(Carbon $start, Carbon $end): string { $diff = $start->diffInMonths($end, true); - if ($diff >= 1.001) { + if ($diff >= 1.001 && $diff < 12.001) { return 'endOfMonth'; } @@ -602,7 +602,7 @@ class Navigation public function preferredRangeFormat(Carbon $start, Carbon $end): string { $diff = $start->diffInMonths($end, true); - if ($diff >= 1.001) { + if ($diff >= 1.001 && $diff < 12.001) { return '1M'; } @@ -620,7 +620,7 @@ class Navigation public function preferredSqlFormat(Carbon $start, Carbon $end): string { $diff = $start->diffInMonths($end, true); - if ($diff >= 1.001) { + if ($diff >= 1.001 && $diff < 12.001) { return '%Y-%m'; } diff --git a/app/Support/Repositories/UserGroup/UserGroupTrait.php b/app/Support/Repositories/UserGroup/UserGroupTrait.php index eb30fa7f53..5e5eb6c59f 100644 --- a/app/Support/Repositories/UserGroup/UserGroupTrait.php +++ b/app/Support/Repositories/UserGroup/UserGroupTrait.php @@ -66,14 +66,15 @@ trait UserGroupTrait if ($user instanceof User) { $this->user = $user; if (null === $user->userGroup) { - throw new FireflyException(sprintf('User #%d has no user group.', $user->id)); + throw new FireflyException(sprintf('User #%d ("%s") has no user group.', $user->id, $user->email)); } $this->userGroup = $user->userGroup; return; } + $class = null === $user ? 'NULL' : $user::class; - throw new FireflyException(sprintf('Object is of class %s, not User.', $user::class)); + throw new FireflyException(sprintf('Object is %s, not User.', $class)); } public function getUserGroup(): ?UserGroup diff --git a/app/Support/Request/GetFilterInstructions.php b/app/Support/Request/GetFilterInstructions.php index a8c19dccc4..cd8c68b568 100644 --- a/app/Support/Request/GetFilterInstructions.php +++ b/app/Support/Request/GetFilterInstructions.php @@ -24,10 +24,15 @@ declare(strict_types=1); namespace FireflyIII\Support\Request; +use FireflyIII\Exceptions\FireflyException; + trait GetFilterInstructions { private const string INVALID_FILTER = '%INVALID_JAMES_%'; + /** + * @throws FireflyException + */ final public function getFilterInstructions(string $key): array { $config = config(sprintf('firefly.filters.allowed.%s', $key)); @@ -48,7 +53,7 @@ trait GetFilterInstructions switch ($filterType) { default: - exit(sprintf('Do not support filter type "%s"', $filterType)); + throw new FireflyException(sprintf('Do not support filter type "%s"', $filterType)); case 'boolean': $filterValue = $this->booleanInstruction($filterValue); diff --git a/app/Transformers/CategoryTransformer.php b/app/Transformers/CategoryTransformer.php index dd4cd73c55..36d25b2921 100644 --- a/app/Transformers/CategoryTransformer.php +++ b/app/Transformers/CategoryTransformer.php @@ -79,8 +79,11 @@ class CategoryTransformer extends AbstractTransformer ]; } - private function beautify(array $array): array + private function beautify(?array $array): ?array { + if (null === $array) { + return null; + } $return = []; foreach ($array as $data) { $data['sum'] = Steam::bcround($data['sum'], (int)$data['currency_decimal_places']); diff --git a/app/User.php b/app/User.php index 3ebadaf347..3e2eccddf0 100644 --- a/app/User.php +++ b/app/User.php @@ -74,7 +74,7 @@ class User extends Authenticatable use HasApiTokens; use Notifiable; use ReturnsIntegerIdTrait; - protected $fillable = ['email', 'password', 'blocked', 'blocked_code']; + protected $fillable = ['email', 'password', 'blocked', 'blocked_code', 'user_group_id']; protected $hidden = ['password', 'remember_token']; protected $table = 'users'; diff --git a/changelog.md b/changelog.md index c7e1f28467..475d54d9be 100644 --- a/changelog.md +++ b/changelog.md @@ -5,7 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## 6.3.0 - 2025-08-xx -> ⚠️ Firefly III v6.3.0 introduces a lot of API changes that deal with multi-currency support. Make sure your beloved apps are updated to support this. +> [!WARNING] +> Firefly III v6.3.0 introduces a lot of API changes that deal with multi-currency support. Make sure your beloved apps are updated to support this. ### Added diff --git a/composer.lock b/composer.lock index a6297fc8ce..f1409d757e 100644 --- a/composer.lock +++ b/composer.lock @@ -11771,16 +11771,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.3.4", + "version": "12.3.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "429095031bd38cb5070ca44166bd9dd5a9245dd6" + "reference": "f10ba5f12a256026ad3c7ee4894ffe47f60d7dc7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/429095031bd38cb5070ca44166bd9dd5a9245dd6", - "reference": "429095031bd38cb5070ca44166bd9dd5a9245dd6", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f10ba5f12a256026ad3c7ee4894ffe47f60d7dc7", + "reference": "f10ba5f12a256026ad3c7ee4894ffe47f60d7dc7", "shasum": "" }, "require": { @@ -11802,7 +11802,7 @@ "sebastian/cli-parser": "^4.0.0", "sebastian/comparator": "^7.1.2", "sebastian/diff": "^7.0.0", - "sebastian/environment": "^8.0.2", + "sebastian/environment": "^8.0.3", "sebastian/exporter": "^7.0.0", "sebastian/global-state": "^8.0.0", "sebastian/object-enumerator": "^7.0.0", @@ -11848,7 +11848,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.4" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.5" }, "funding": [ { @@ -11872,20 +11872,20 @@ "type": "tidelift" } ], - "time": "2025-08-12T07:35:30+00:00" + "time": "2025-08-16T05:20:09+00:00" }, { "name": "rector/rector", - "version": "2.1.3", + "version": "2.1.4", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "dd430c869fddf4965049c8fd6f5ee49f155cfddf" + "reference": "fe613c528819222f8686a9a037a315ef9d4915b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/dd430c869fddf4965049c8fd6f5ee49f155cfddf", - "reference": "dd430c869fddf4965049c8fd6f5ee49f155cfddf", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/fe613c528819222f8686a9a037a315ef9d4915b3", + "reference": "fe613c528819222f8686a9a037a315ef9d4915b3", "shasum": "" }, "require": { @@ -11924,7 +11924,7 @@ ], "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/2.1.3" + "source": "https://github.com/rectorphp/rector/tree/2.1.4" }, "funding": [ { @@ -11932,7 +11932,7 @@ "type": "github" } ], - "time": "2025-08-13T11:43:04+00:00" + "time": "2025-08-15T14:41:36+00:00" }, { "name": "sebastian/cli-parser", diff --git a/config/firefly.php b/config/firefly.php index 998250e370..0f60a6bd75 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => '6.3.0-beta.2', - 'build_time' => 1755258109, + 'version' => '6.3.0', + 'build_time' => 1755366750, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 26, diff --git a/package-lock.json b/package-lock.json index 336c6d0d6d..396653c719 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5700,9 +5700,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.202", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.202.tgz", - "integrity": "sha512-NxbYjRmiHcHXV1Ws3fWUW+SLb62isauajk45LUJ/HgIOkUA7jLZu/X2Iif+X9FBNK8QkF9Zb4Q2mcwXCcY30mg==", + "version": "1.5.203", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.203.tgz", + "integrity": "sha512-uz4i0vLhfm6dLZWbz/iH88KNDV+ivj5+2SA+utpgjKaj9Q0iDLuwk6Idhe9BTxciHudyx6IvTvijhkPvFGUQ0g==", "dev": true, "license": "ISC" }, diff --git a/phpunit.xml b/phpunit.xml index 7c96bdee3e..b4ce7ee365 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -31,12 +31,18 @@ failOnRisky="true" failOnWarning="true" processIsolation="false" + stopOnError="true" stopOnFailure="true"> + + + + + + - diff --git a/resources/assets/v2/src/api/v1/chart/account/dashboard.js b/resources/assets/v2/src/api/v1/chart/account/dashboard.js index 90cf920ee2..028c31c9a3 100644 --- a/resources/assets/v2/src/api/v1/chart/account/dashboard.js +++ b/resources/assets/v2/src/api/v1/chart/account/dashboard.js @@ -25,7 +25,7 @@ export default class Dashboard { dashboard(start, end) { let startStr = format(start, 'y-MM-dd'); let endStr = format(end, 'y-MM-dd'); - return api.get('/api/v1/chart/account/dashboard', {params: {start: startStr, end: endStr}}); + return api.get('/api/v1/chart/account/overview', {params: {start: startStr, end: endStr}}); } expense(start, end) { diff --git a/resources/assets/v2/src/api/v1/chart/budget/dashboard.js b/resources/assets/v2/src/api/v1/chart/budget/dashboard.js index 6e94a2cc7c..5218703ee4 100644 --- a/resources/assets/v2/src/api/v1/chart/budget/dashboard.js +++ b/resources/assets/v2/src/api/v1/chart/budget/dashboard.js @@ -25,6 +25,6 @@ export default class Dashboard { dashboard(start, end) { let startStr = format(start, 'y-MM-dd'); let endStr = format(end, 'y-MM-dd'); - return api.get('/api/v1/chart/budget/dashboard', {params: {start: startStr, end: endStr}}); + return api.get('/api/v1/chart/budget/overview', {params: {start: startStr, end: endStr}}); } } diff --git a/resources/assets/v2/src/api/v1/chart/category/dashboard.js b/resources/assets/v2/src/api/v1/chart/category/dashboard.js index 46afdc01b0..039a104c8e 100644 --- a/resources/assets/v2/src/api/v1/chart/category/dashboard.js +++ b/resources/assets/v2/src/api/v1/chart/category/dashboard.js @@ -25,6 +25,6 @@ export default class Dashboard { dashboard(start, end) { let startStr = format(start, 'y-MM-dd'); let endStr = format(end, 'y-MM-dd'); - return api.get('/api/v1/chart/category/dashboard', {params: {start: startStr, end: endStr}}); + return api.get('/api/v1/chart/category/overview', {params: {start: startStr, end: endStr}}); } } diff --git a/resources/assets/v2/src/api/v2/chart/account/dashboard.js b/resources/assets/v2/src/api/v2/chart/account/dashboard.js index 90cf920ee2..028c31c9a3 100644 --- a/resources/assets/v2/src/api/v2/chart/account/dashboard.js +++ b/resources/assets/v2/src/api/v2/chart/account/dashboard.js @@ -25,7 +25,7 @@ export default class Dashboard { dashboard(start, end) { let startStr = format(start, 'y-MM-dd'); let endStr = format(end, 'y-MM-dd'); - return api.get('/api/v1/chart/account/dashboard', {params: {start: startStr, end: endStr}}); + return api.get('/api/v1/chart/account/overview', {params: {start: startStr, end: endStr}}); } expense(start, end) { diff --git a/resources/assets/v2/src/api/v2/chart/budget/dashboard.js b/resources/assets/v2/src/api/v2/chart/budget/dashboard.js index 6e94a2cc7c..5218703ee4 100644 --- a/resources/assets/v2/src/api/v2/chart/budget/dashboard.js +++ b/resources/assets/v2/src/api/v2/chart/budget/dashboard.js @@ -25,6 +25,6 @@ export default class Dashboard { dashboard(start, end) { let startStr = format(start, 'y-MM-dd'); let endStr = format(end, 'y-MM-dd'); - return api.get('/api/v1/chart/budget/dashboard', {params: {start: startStr, end: endStr}}); + return api.get('/api/v1/chart/budget/overview', {params: {start: startStr, end: endStr}}); } } diff --git a/resources/assets/v2/src/api/v2/chart/category/dashboard.js b/resources/assets/v2/src/api/v2/chart/category/dashboard.js index 46afdc01b0..039a104c8e 100644 --- a/resources/assets/v2/src/api/v2/chart/category/dashboard.js +++ b/resources/assets/v2/src/api/v2/chart/category/dashboard.js @@ -25,6 +25,6 @@ export default class Dashboard { dashboard(start, end) { let startStr = format(start, 'y-MM-dd'); let endStr = format(end, 'y-MM-dd'); - return api.get('/api/v1/chart/category/dashboard', {params: {start: startStr, end: endStr}}); + return api.get('/api/v1/chart/category/overview', {params: {start: startStr, end: endStr}}); } } diff --git a/routes/api.php b/routes/api.php index 09a72d07f4..be21f9248c 100644 --- a/routes/api.php +++ b/routes/api.php @@ -102,10 +102,10 @@ Route::group( [ 'namespace' => 'FireflyIII\Api\V1\Controllers\Chart', 'prefix' => 'v1/chart/balance', - 'as' => 'api.v1.chart.balance', + 'as' => 'api.v1.chart.balance.', ], static function (): void { - Route::get('balance', ['uses' => 'BalanceController@balance', 'as' => 'balance.balance']); + Route::get('balance', ['uses' => 'BalanceController@balance', 'as' => 'balance']); } ); @@ -617,15 +617,15 @@ Route::group( static function (): void { Route::get('', ['uses' => 'ShowController@index', 'as' => 'index']); Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']); - Route::get('default', ['uses' => 'ShowController@showDefault', 'as' => 'show.default']); - Route::get('native', ['uses' => 'ShowController@showDefault', 'as' => 'show.native']); + Route::get('primary', ['uses' => 'ShowController@showPrimary', 'as' => 'show.primary']); + Route::get('default', ['uses' => 'ShowController@showPrimary', 'as' => 'show.default']); Route::get('{currency_code}', ['uses' => 'ShowController@show', 'as' => 'show']); Route::put('{currency_code?}', ['uses' => 'UpdateController@update', 'as' => 'update']); Route::delete('{currency_code}', ['uses' => 'DestroyController@destroy', 'as' => 'delete']); Route::post('{currency_code}/enable', ['uses' => 'UpdateController@enable', 'as' => 'enable']); Route::post('{currency_code}/disable', ['uses' => 'UpdateController@disable', 'as' => 'disable']); - Route::post('{currency_code}/default', ['uses' => 'UpdateController@makeDefault', 'as' => 'default']); + Route::post('{currency_code}/primary', ['uses' => 'UpdateController@makePrimary', 'as' => 'update.primary']); Route::get('{currency_code}/accounts', ['uses' => 'ListController@accounts', 'as' => 'accounts']); Route::get('{currency_code}/available-budgets', ['uses' => 'ListController@availableBudgets', 'as' => 'available-budgets']); diff --git a/tests/integration/Api/Autocomplete/BillControllerTest.php b/tests/integration/Api/Autocomplete/BillControllerTest.php index 7dc6b950b7..a0ae364922 100644 --- a/tests/integration/Api/Autocomplete/BillControllerTest.php +++ b/tests/integration/Api/Autocomplete/BillControllerTest.php @@ -28,8 +28,6 @@ use FireflyIII\Models\Bill; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\integration\TestCase; use FireflyIII\User; -use FireflyIII\Models\UserGroup; -use Override; /** * Class BillControllerTest @@ -45,21 +43,6 @@ final class BillControllerTest extends TestCase */ use RefreshDatabase; - #[Override] - protected function createAuthenticatedUser(): User - { - $userGroup = UserGroup::create(['title' => 'Test Group']); - - $user = User::create([ - 'email' => 'test@email.com', - 'password' => 'password', - ]); - $user->user_group_id = $userGroup->id; - $user->save(); - - return $user; - } - private function createTestBills(int $count, User $user): void { for ($i = 1; $i <= $count; ++$i) { @@ -96,7 +79,6 @@ final class BillControllerTest extends TestCase $response = $this->get(route('api.v1.autocomplete.bills'), ['Accept' => 'application/json']); $response->assertStatus(200); $response->assertHeader('Content-Type', 'application/json'); - } public function testGivenAuthenticatedRequestWhenCallingTheBillsEndpointThenReturnsBills(): void diff --git a/tests/integration/Api/Autocomplete/BudgetControllerTest.php b/tests/integration/Api/Autocomplete/BudgetControllerTest.php index e137f5d262..23bc8ba048 100644 --- a/tests/integration/Api/Autocomplete/BudgetControllerTest.php +++ b/tests/integration/Api/Autocomplete/BudgetControllerTest.php @@ -25,11 +25,9 @@ declare(strict_types=1); namespace Tests\integration\Api\Autocomplete; use FireflyIII\Models\Budget; -use FireflyIII\Models\UserGroup; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\integration\TestCase; use FireflyIII\User; -use Override; /** * Class BudgetControllerTest @@ -45,21 +43,6 @@ final class BudgetControllerTest extends TestCase */ use RefreshDatabase; - #[Override] - protected function createAuthenticatedUser(): User - { - $userGroup = UserGroup::create(['title' => 'Test Group']); - - $user = User::create([ - 'email' => 'test@email.com', - 'password' => 'password', - ]); - $user->user_group_id = $userGroup->id; - $user->save(); - - return $user; - } - private function createTestBudgets(int $count, User $user): void { for ($i = 1; $i <= $count; ++$i) { diff --git a/tests/integration/Api/Autocomplete/CategoryControllerTest.php b/tests/integration/Api/Autocomplete/CategoryControllerTest.php index 9456cf4001..b678f24bef 100644 --- a/tests/integration/Api/Autocomplete/CategoryControllerTest.php +++ b/tests/integration/Api/Autocomplete/CategoryControllerTest.php @@ -28,8 +28,6 @@ use FireflyIII\Models\Category; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\integration\TestCase; use FireflyIII\User; -use FireflyIII\Models\UserGroup; -use Override; /** * Class CategoryControllerTest @@ -45,21 +43,6 @@ final class CategoryControllerTest extends TestCase */ use RefreshDatabase; - #[Override] - protected function createAuthenticatedUser(): User - { - $userGroup = UserGroup::create(['title' => 'Test Group']); - - $user = User::create([ - 'email' => 'test@email.com', - 'password' => 'password', - ]); - $user->user_group_id = $userGroup->id; - $user->save(); - - return $user; - } - private function createTestCategories(int $count, User $user): void { for ($i = 1; $i <= $count; ++$i) { diff --git a/tests/integration/Api/Autocomplete/CurrencyControllerTest.php b/tests/integration/Api/Autocomplete/CurrencyControllerTest.php index efdd02a866..b517ec6445 100644 --- a/tests/integration/Api/Autocomplete/CurrencyControllerTest.php +++ b/tests/integration/Api/Autocomplete/CurrencyControllerTest.php @@ -28,8 +28,6 @@ use FireflyIII\Models\TransactionCurrency; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\integration\TestCase; use FireflyIII\User; -use FireflyIII\Models\UserGroup; -use Override; /** * Class CurrencyControllerTest @@ -45,22 +43,6 @@ final class CurrencyControllerTest extends TestCase */ use RefreshDatabase; - #[Override] - protected function createAuthenticatedUser(): User - { - $userGroup = UserGroup::create(['title' => 'Test Group']); - - - $user = User::create([ - 'email' => 'test@email.com', - 'password' => 'password', - ]); - $user->user_group_id = $userGroup->id; - $user->save(); - - return $user; - } - private function createTestCurrencies(int $count, bool $enabled): void { for ($i = 1; $i <= $count; ++$i) { diff --git a/tests/integration/Api/Autocomplete/ObjectGroupControllerTest.php b/tests/integration/Api/Autocomplete/ObjectGroupControllerTest.php index e6a089b7ec..e714115793 100644 --- a/tests/integration/Api/Autocomplete/ObjectGroupControllerTest.php +++ b/tests/integration/Api/Autocomplete/ObjectGroupControllerTest.php @@ -28,8 +28,6 @@ use FireflyIII\Models\ObjectGroup; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\integration\TestCase; use FireflyIII\User; -use FireflyIII\Models\UserGroup; -use Override; /** * Class ObjectGroupControllerTest @@ -45,22 +43,6 @@ final class ObjectGroupControllerTest extends TestCase */ use RefreshDatabase; - #[Override] - protected function createAuthenticatedUser(): User - { - $userGroup = UserGroup::create(['title' => 'Test Group']); - - - $user = User::create([ - 'email' => 'test@email.com', - 'password' => 'password', - ]); - $user->user_group_id = $userGroup->id; - $user->save(); - - return $user; - } - private function createTestObjectGroups(int $count, User $user): void { for ($i = 1; $i <= $count; ++$i) { diff --git a/tests/integration/Api/Autocomplete/PiggyBankControllerTest.php b/tests/integration/Api/Autocomplete/PiggyBankControllerTest.php new file mode 100644 index 0000000000..ce62738b74 --- /dev/null +++ b/tests/integration/Api/Autocomplete/PiggyBankControllerTest.php @@ -0,0 +1,164 @@ +. + */ + +declare(strict_types=1); + +namespace Tests\integration\Api\Autocomplete; + +use FireflyIII\Enums\AccountTypeEnum; +use FireflyIII\Models\Account; +use FireflyIII\Models\AccountType; +use FireflyIII\Models\PiggyBank; +use FireflyIII\Models\TransactionCurrency; +use FireflyIII\User; +use Illuminate\Foundation\Testing\RefreshDatabase; +use Tests\integration\TestCase; + +/** + * Class BudgetControllerTest + * + * @internal + * + * @coversNothing + */ +final class PiggyBankControllerTest extends TestCase +{ + /** + * @covers \FireflyIII\Api\V1\Controllers\Autocomplete\BudgetController + */ + use RefreshDatabase; + + private function createTestPiggyBanks(int $count, User $user): void + { + $type = AccountType::whereType(AccountTypeEnum::DEFAULT->value)->first(); + if (null === $type) { + $type = AccountType::create(['type' => AccountTypeEnum::DEFAULT->value]); + } + $currency = TransactionCurrency::whereCode('EUR')->first(); + if (null === $currency) { + $currency = TransactionCurrency::create( + [ + 'code' => 'EUR', + 'name' => 'Euro', + 'symbol' => '€', + ] + ); + } + for ($i = 1; $i <= $count; ++$i) { + $piggyBank = PiggyBank::create( + [ + 'user_id' => $user->id, + 'name' => 'Piggy bank '.$i, + 'target_amount' => 1000, + 'transaction_currency_id' => $currency->id, + 'target_date' => now()->addDays(30), + 'user_group_id' => $user->user_group_id, + 'active' => 1, + ] + ); + $account = Account::create( + [ + 'user_id' => $user->id, + 'name' => 'Account '.$i, + 'user_group_id' => $user->user_group_id, + 'account_type_id' => $type->id, + 'active' => 1, + ] + ); + $piggyBank->accounts()->save($account); + } + } + + public function testGivenAnUnauthenticatedRequestWhenCallingTheBudgetsEndpointThenReturns401HttpCode(): void + { + // test API + $response = $this->get(route('api.v1.autocomplete.piggy-banks'), ['Accept' => 'application/json']); + $response->assertStatus(401); + $response->assertHeader('Content-Type', 'application/json'); + $response->assertContent('{"message":"Unauthenticated.","exception":"AuthenticationException"}'); + } + + public function testGivenAuthenticatedRequestWhenCallingTheBudgetsEndpointThenReturns200HttpCode(): void + { + // act as a user + $user = $this->createAuthenticatedUser(); + $this->actingAs($user); + + $response = $this->get(route('api.v1.autocomplete.piggy-banks'), ['Accept' => 'application/json']); + $response->assertStatus(200); + $response->assertHeader('Content-Type', 'application/json'); + + } + + public function testGivenAuthenticatedRequestWhenCallingTheBudgetsEndpointThenReturnsBudgets(): void + { + $user = $this->createAuthenticatedUser(); + $this->actingAs($user); + + $this->createTestPiggyBanks(5, $user); + $response = $this->get(route('api.v1.autocomplete.piggy-banks'), ['Accept' => 'application/json']); + $response->assertStatus(200); + $response->assertHeader('Content-Type', 'application/json'); + $response->assertJsonCount(5); + $response->assertJsonFragment(['name' => 'Piggy bank 1']); + $response->assertJsonStructure([ + '*' => [ + 'id', + 'name', + ], + ]); + } + + public function testGivenAuthenticatedRequestWhenCallingTheBudgetsEndpointWithQueryThenReturnsBudgetsWithLimit(): void + { + $user = $this->createAuthenticatedUser(); + $this->actingAs($user); + + $this->createTestPiggyBanks(5, $user); + $response = $this->get(route('api.v1.autocomplete.piggy-banks', [ + 'query' => 'Piggy', + 'limit' => 3, + ]), ['Accept' => 'application/json']); + + $response->assertStatus(200); + $response->assertHeader('Content-Type', 'application/json'); + $response->assertJsonCount(3); + } + + public function testGivenAuthenticatedRequestWhenCallingTheBudgetsEndpointWithQueryThenReturnsBudgetsThatMatchQuery(): void + { + $user = $this->createAuthenticatedUser(); + $this->actingAs($user); + + $this->createTestPiggyBanks(20, $user); + $response = $this->get(route('api.v1.autocomplete.piggy-banks', [ + 'query' => 'Piggy bank 1', + 'limit' => 20, + ]), ['Accept' => 'application/json']); + + $response->assertStatus(200); + $response->assertHeader('Content-Type', 'application/json'); + // Budget 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 (11) + $response->assertJsonCount(11); + $response->assertJsonMissing(['name' => 'Piggy bank 2']); + } +} diff --git a/tests/integration/Api/Chart/AccountControllerTest.php b/tests/integration/Api/Chart/AccountControllerTest.php new file mode 100644 index 0000000000..067f347ef8 --- /dev/null +++ b/tests/integration/Api/Chart/AccountControllerTest.php @@ -0,0 +1,50 @@ +user)) { + $this->user = $this->createAuthenticatedUser(); + } + $this->actingAs($this->user); + } + + public function testGetOverviewChartFails(): void + { + $this->actingAs($this->user); + $response = $this->getJson(route('api.v1.chart.account.overview')); + $response->assertStatus(422); + + } + + public function testGetOverviewChart(): void + { + $this->actingAs($this->user); + $params = [ + 'start' => '2024-01-01', + 'end' => '2024-01-31', + ]; + $response = $this->getJson(route('api.v1.chart.account.overview').'?'.http_build_query($params)); + $response->assertStatus(200); + + } +} diff --git a/tests/integration/Api/Chart/BalanceControllerTest.php b/tests/integration/Api/Chart/BalanceControllerTest.php new file mode 100644 index 0000000000..69f0fa9785 --- /dev/null +++ b/tests/integration/Api/Chart/BalanceControllerTest.php @@ -0,0 +1,50 @@ +user)) { + $this->user = $this->createAuthenticatedUser(); + } + $this->actingAs($this->user); + } + + public function testGetOverviewChartFails(): void + { + $this->actingAs($this->user); + $response = $this->getJson(route('api.v1.chart.balance.balance')); + $response->assertStatus(422); + + } + + public function testGetOverviewChart(): void + { + $this->actingAs($this->user); + $params = [ + 'start' => '2024-01-01', + 'end' => '2024-01-31', + ]; + $response = $this->getJson(route('api.v1.chart.balance.balance').'?'.http_build_query($params)); + $response->assertStatus(200); + + } +} diff --git a/tests/integration/Api/Chart/BudgetControllerTest.php b/tests/integration/Api/Chart/BudgetControllerTest.php new file mode 100644 index 0000000000..a9457e3822 --- /dev/null +++ b/tests/integration/Api/Chart/BudgetControllerTest.php @@ -0,0 +1,50 @@ +user)) { + $this->user = $this->createAuthenticatedUser(); + } + $this->actingAs($this->user); + } + + public function testGetOverviewChartFails(): void + { + $this->actingAs($this->user); + $response = $this->getJson(route('api.v1.chart.budget.overview')); + $response->assertStatus(422); + + } + + public function testGetOverviewChart(): void + { + $this->actingAs($this->user); + $params = [ + 'start' => '2024-01-01', + 'end' => '2024-01-31', + ]; + $response = $this->getJson(route('api.v1.chart.budget.overview').'?'.http_build_query($params)); + $response->assertStatus(200); + + } +} diff --git a/tests/integration/Api/Chart/CategoryControllerTest.php b/tests/integration/Api/Chart/CategoryControllerTest.php new file mode 100644 index 0000000000..7aa7f8ce0b --- /dev/null +++ b/tests/integration/Api/Chart/CategoryControllerTest.php @@ -0,0 +1,50 @@ +user)) { + $this->user = $this->createAuthenticatedUser(); + } + $this->actingAs($this->user); + } + + public function testGetOverviewChartFails(): void + { + $this->actingAs($this->user); + $response = $this->getJson(route('api.v1.chart.category.overview')); + $response->assertStatus(422); + + } + + public function testGetOverviewChart(): void + { + $this->actingAs($this->user); + $params = [ + 'start' => '2024-01-01', + 'end' => '2024-01-31', + ]; + $response = $this->getJson(route('api.v1.chart.category.overview').'?'.http_build_query($params)); + $response->assertStatus(200); + + } +} diff --git a/tests/integration/TestCase.php b/tests/integration/TestCase.php index b55259c8c7..e1b42991b2 100644 --- a/tests/integration/TestCase.php +++ b/tests/integration/TestCase.php @@ -23,7 +23,9 @@ declare(strict_types=1); namespace Tests\integration; +use FireflyIII\Models\GroupMembership; use FireflyIII\Models\UserGroup; +use FireflyIII\Models\UserRole; use FireflyIII\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; @@ -54,14 +56,30 @@ abstract class TestCase extends BaseTestCase ]; } + protected function getAuthenticatedUser(): User + { + return User::where('email', 'james@firefly')->first(); + } + protected function createAuthenticatedUser(): User { $group = UserGroup::create(['title' => 'test@email.com']); - - return User::create([ + $role = UserRole::where('title', 'owner')->first(); + $user = User::create([ 'email' => 'test@email.com', 'password' => 'password', 'user_group_id' => $group->id, ]); + + GroupMembership::create( + [ + 'user_id' => $user->id, + 'user_group_id' => $group->id, + 'user_role_id' => $role->id, + ] + ); + + + return $user; } }