From 946e272d1c4a698802be6efe83cdd9e4cd218e9f Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Aug 2025 13:46:41 +0200 Subject: [PATCH 01/20] Update changelog and templates. --- .github/release-notes/alpha.md | 3 ++- .github/release-notes/beta.md | 3 ++- .github/release-notes/branch.md | 3 ++- .github/release-notes/develop.md | 3 ++- changelog.md | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) 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/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 From 87d292ca27b3e3e749bd828db50c9b80677a16c7 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Aug 2025 14:33:14 +0200 Subject: [PATCH 02/20] Rename method from default to primary. --- .../Models/TransactionCurrency/ShowController.php | 2 +- .../Models/TransactionCurrency/StoreController.php | 2 +- .../Models/TransactionCurrency/UpdateController.php | 4 ++-- app/Http/Controllers/NewUserController.php | 2 +- app/Repositories/Currency/CurrencyRepository.php | 4 ++-- app/Repositories/Currency/CurrencyRepositoryInterface.php | 2 +- app/Repositories/UserGroup/UserGroupRepository.php | 2 +- app/Repositories/UserGroups/Currency/CurrencyRepository.php | 4 ++-- .../UserGroups/Currency/CurrencyRepositoryInterface.php | 2 +- routes/api.php | 5 ++--- 10 files changed, 14 insertions(+), 15 deletions(-) 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/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/routes/api.php b/routes/api.php index 09a72d07f4..1e3bcc8933 100644 --- a/routes/api.php +++ b/routes/api.php @@ -617,15 +617,14 @@ 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('{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']); From ab773c9052c4d6395bf45801e169f1f714ae534c Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Aug 2025 19:50:12 +0200 Subject: [PATCH 03/20] Extend acceptedRoles array in AC controllers. --- .../Autocomplete/AccountController.php | 8 ++-- .../Autocomplete/BillController.php | 8 ++-- .../Autocomplete/BudgetController.php | 8 ++-- .../Autocomplete/CategoryController.php | 8 ++-- .../Autocomplete/CurrencyController.php | 8 ++-- .../Autocomplete/ObjectGroupController.php | 8 ++-- .../Autocomplete/PiggyBankController.php | 11 +++-- .../Autocomplete/RecurrenceController.php | 7 +++- .../Autocomplete/RuleController.php | 6 ++- .../Autocomplete/RuleGroupController.php | 6 ++- .../Autocomplete/TagController.php | 8 ++-- .../Autocomplete/TransactionController.php | 11 +++-- .../TransactionTypeController.php | 5 +++ app/Api/V1/Controllers/Controller.php | 42 +++++++++---------- .../Http/Api/ValidatesUserGroupTrait.php | 2 + .../Repositories/UserGroup/UserGroupTrait.php | 4 +- 16 files changed, 92 insertions(+), 58 deletions(-) diff --git a/app/Api/V1/Controllers/Autocomplete/AccountController.php b/app/Api/V1/Controllers/Autocomplete/AccountController.php index cb31c41a62..a9971fc61c 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; @@ -47,6 +48,7 @@ class AccountController extends Controller // this array only exists to test if the constructor will use it properly. protected array $accepts = ['application/json', 'application/vnd.api+json']; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; /** @var array */ private array $balanceTypes; @@ -60,10 +62,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..6bdb8b7afe 100644 --- a/app/Api/V1/Controllers/Autocomplete/BillController.php +++ b/app/Api/V1/Controllers/Autocomplete/BillController.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\Bill; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\User; @@ -37,6 +38,7 @@ use Illuminate\Http\JsonResponse; class BillController extends Controller { private BillRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::READ_SUBSCRIPTIONS]; /** * BillController constructor. @@ -46,10 +48,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..ada51c325b 100644 --- a/app/Api/V1/Controllers/Autocomplete/BudgetController.php +++ b/app/Api/V1/Controllers/Autocomplete/BudgetController.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\Budget; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\User; @@ -37,6 +38,7 @@ use Illuminate\Http\JsonResponse; class BudgetController extends Controller { private BudgetRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::READ_BUDGETS]; /** * BudgetController constructor. @@ -46,10 +48,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..8211f2dd62 100644 --- a/app/Api/V1/Controllers/Autocomplete/CategoryController.php +++ b/app/Api/V1/Controllers/Autocomplete/CategoryController.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\Category; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\User; @@ -37,6 +38,7 @@ use Illuminate\Http\JsonResponse; class CategoryController extends Controller { private CategoryRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; /** * CategoryController constructor. @@ -46,10 +48,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..bbde6f7477 100644 --- a/app/Api/V1/Controllers/Autocomplete/CurrencyController.php +++ b/app/Api/V1/Controllers/Autocomplete/CurrencyController.php @@ -27,6 +27,7 @@ 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; @@ -38,6 +39,7 @@ use Illuminate\Http\JsonResponse; class CurrencyController extends Controller { private CurrencyRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; /** * CurrencyController constructor. @@ -47,10 +49,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..d50a71aeb3 100644 --- a/app/Api/V1/Controllers/Autocomplete/ObjectGroupController.php +++ b/app/Api/V1/Controllers/Autocomplete/ObjectGroupController.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\ObjectGroup; use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface; use FireflyIII\User; @@ -37,6 +38,7 @@ use Illuminate\Http\JsonResponse; class ObjectGroupController extends Controller { private ObjectGroupRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; /** * CurrencyController constructor. @@ -46,10 +48,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..70cadd34e6 100644 --- a/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php +++ b/app/Api/V1/Controllers/Autocomplete/PiggyBankController.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\PiggyBank; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; @@ -39,6 +40,7 @@ class PiggyBankController extends Controller { private AccountRepositoryInterface $accountRepository; private PiggyBankRepositoryInterface $piggyRepository; + protected array $acceptedRoles = [UserRoleEnum::READ_PIGGY_BANKS]; /** * PiggyBankController constructor. @@ -48,12 +50,13 @@ 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); } diff --git a/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php b/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php index 2aca073dd5..aa990154f7 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,9 +47,10 @@ 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); } diff --git a/app/Api/V1/Controllers/Autocomplete/RuleController.php b/app/Api/V1/Controllers/Autocomplete/RuleController.php index bc9996dcfb..cb5f53b213 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,8 +47,10 @@ 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); } diff --git a/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php b/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php index 7315de68d8..98067b3f88 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,8 +47,10 @@ 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); } diff --git a/app/Api/V1/Controllers/Autocomplete/TagController.php b/app/Api/V1/Controllers/Autocomplete/TagController.php index dabc0a1c85..437cf94bbd 100644 --- a/app/Api/V1/Controllers/Autocomplete/TagController.php +++ b/app/Api/V1/Controllers/Autocomplete/TagController.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\Tag; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\User; @@ -37,6 +38,7 @@ use Illuminate\Http\JsonResponse; class TagController extends Controller { private TagRepositoryInterface $repository; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; /** * TagController constructor. @@ -46,10 +48,10 @@ 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); } diff --git a/app/Api/V1/Controllers/Autocomplete/TransactionController.php b/app/Api/V1/Controllers/Autocomplete/TransactionController.php index a6785dbc38..aa28415463 100644 --- a/app/Api/V1/Controllers/Autocomplete/TransactionController.php +++ b/app/Api/V1/Controllers/Autocomplete/TransactionController.php @@ -52,14 +52,13 @@ 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); } diff --git a/app/Api/V1/Controllers/Autocomplete/TransactionTypeController.php b/app/Api/V1/Controllers/Autocomplete/TransactionTypeController.php index abc0e11553..f3f7ea69d2 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,7 +47,10 @@ 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); } diff --git a/app/Api/V1/Controllers/Controller.php b/app/Api/V1/Controllers/Controller.php index ffef4503f4..57a6784a1f 100644 --- a/app/Api/V1/Controllers/Controller.php +++ b/app/Api/V1/Controllers/Controller.php @@ -62,9 +62,9 @@ abstract class Controller extends BaseController use ValidatesRequests; use ValidatesUserGroupTrait; - 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 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']; /** @var array */ protected array $allowedSort; @@ -106,8 +106,8 @@ abstract class Controller extends BaseController */ private function getParameters(): ParameterBag { - $bag = new ParameterBag(); - $page = (int) request()->get('page'); + $bag = new ParameterBag(); + $page = (int)request()->get('page'); if ($page < 1) { $page = 1; } @@ -117,7 +117,7 @@ abstract class Controller extends BaseController $bag->set('page', $page); // some date fields: - $dates = ['start', 'end', 'date']; + $dates = ['start', 'end', 'date']; foreach ($dates as $field) { $date = null; @@ -128,16 +128,16 @@ abstract class Controller extends BaseController Log::error($e->getMessage()); Log::error($e->getTraceAsString()); } - $obj = null; + $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; } @@ -173,10 +173,10 @@ abstract class Controller extends BaseController && auth()->check()) { // set default for user: /** @var User $user */ - $user = auth()->user(); + $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()); @@ -200,7 +200,7 @@ abstract class Controller extends BaseController if ('' === $param) { return $bag; } - $parts = explode(',', $param); + $parts = explode(',', $param); foreach ($parts as $part) { $part = trim($part); $direction = 'asc'; @@ -236,14 +236,14 @@ abstract class Controller extends BaseController $params[$key] = $value; } - return $return.http_build_query($params); + return $return . http_build_query($params); } final protected function getManager(): Manager { // create some objects: $manager = new Manager(); - $baseUrl = request()->getSchemeAndHttpHost().'/api/v1'; + $baseUrl = request()->getSchemeAndHttpHost() . '/api/v1'; $manager->setSerializer(new JsonApiSerializer($baseUrl)); return $manager; @@ -251,14 +251,14 @@ abstract class Controller extends BaseController final protected function jsonApiList(string $key, LengthAwarePaginator $paginator, AbstractTransformer $transformer): array { - $manager = new Manager(); - $baseUrl = sprintf('%s/api/v1/', request()->getSchemeAndHttpHost()); + $manager = new Manager(); + $baseUrl = sprintf('%s/api/v1/', request()->getSchemeAndHttpHost()); // TODO add stuff to path? $manager->setSerializer(new JsonApiSerializer($baseUrl)); - $objects = $paginator->getCollection(); + $objects = $paginator->getCollection(); // the transformer, at this point, needs to collect information that ALL items in the collection // require, like meta-data and stuff like that, and save it for later. @@ -279,8 +279,8 @@ abstract class Controller extends BaseController final protected function jsonApiObject(string $key, array|Model $object, AbstractTransformer $transformer): array { // create some objects: - $manager = new Manager(); - $baseUrl = sprintf('%s/api/v1', request()->getSchemeAndHttpHost()); + $manager = new Manager(); + $baseUrl = sprintf('%s/api/v1', request()->getSchemeAndHttpHost()); $manager->setSerializer(new JsonApiSerializer($baseUrl)); // $transformer->collectMetaData(new Collection([$object])); diff --git a/app/Support/Http/Api/ValidatesUserGroupTrait.php b/app/Support/Http/Api/ValidatesUserGroupTrait.php index 89504139ae..8e34de14af 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/Repositories/UserGroup/UserGroupTrait.php b/app/Support/Repositories/UserGroup/UserGroupTrait.php index eb30fa7f53..4bcd345e34 100644 --- a/app/Support/Repositories/UserGroup/UserGroupTrait.php +++ b/app/Support/Repositories/UserGroup/UserGroupTrait.php @@ -72,8 +72,8 @@ trait UserGroupTrait return; } - - throw new FireflyException(sprintf('Object is of class %s, not User.', $user::class)); + $class = null === $user ? 'NULL' : $user::class; + throw new FireflyException(sprintf('Object is %s, not User.', $class)); } public function getUserGroup(): ?UserGroup From 77d15f884b8e2f309e4d9ba28e2c51f475380bf5 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Aug 2025 19:57:32 +0200 Subject: [PATCH 04/20] Extend acceptedRoles to chart controllers. --- .../Controllers/Chart/AccountController.php | 33 +++++-------------- .../Controllers/Chart/BalanceController.php | 11 +++---- .../V1/Controllers/Chart/BudgetController.php | 24 +++++++------- .../Controllers/Chart/CategoryController.php | 7 ++-- 4 files changed, 29 insertions(+), 46 deletions(-) diff --git a/app/Api/V1/Controllers/Chart/AccountController.php b/app/Api/V1/Controllers/Chart/AccountController.php index 7c762aece1..9a28d923eb 100644 --- a/app/Api/V1/Controllers/Chart/AccountController.php +++ b/app/Api/V1/Controllers/Chart/AccountController.php @@ -37,6 +37,7 @@ 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; @@ -48,10 +49,11 @@ class AccountController extends Controller { use ApiSupport; use CollectsAccountsFromFilter; + use CleansChartData; protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; - private ChartData $chartData; + private array $chartData; private AccountRepositoryInterface $repository; /** @@ -62,11 +64,9 @@ 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->repository->setUserGroup($this->userGroup); + $this->repository->setUser($this->user); return $next($request); } @@ -84,7 +84,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 +93,7 @@ class AccountController extends Controller $this->renderAccountData($queryParameters, $account); } - return response()->json($this->chartData->render()); + return response()->json($this->clean($this->chartData)); } /** @@ -110,7 +110,7 @@ class AccountController extends Controller $previous = array_values($range)[0]['balance']; $pcPrevious = null; if (!$currency instanceof TransactionCurrency) { - $currency = $this->default; + $currency = $this->primaryCurrency; } $currentSet = [ 'label' => $account->name, @@ -162,21 +162,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..eb05d934e1 100644 --- a/app/Api/V1/Controllers/Chart/BalanceController.php +++ b/app/Api/V1/Controllers/Chart/BalanceController.php @@ -27,7 +27,7 @@ class BalanceController extends Controller use CollectsAccountsFromFilter; protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; - private array $chartData; + private array $chartData = []; private GroupCollectorInterface $collector; private AccountRepositoryInterface $repository; @@ -40,11 +40,10 @@ class BalanceController extends Controller function ($request, $next) { $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..4a91fc86d1 100644 --- a/app/Api/V1/Controllers/Chart/BudgetController.php +++ b/app/Api/V1/Controllers/Chart/BudgetController.php @@ -63,13 +63,17 @@ 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->validateUserGroup($request); + $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); } @@ -157,12 +161,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 +191,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..f54321ef0e 100644 --- a/app/Api/V1/Controllers/Chart/CategoryController.php +++ b/app/Api/V1/Controllers/Chart/CategoryController.php @@ -61,9 +61,10 @@ class CategoryController extends Controller function ($request, $next) { $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); } From a916cc7e782a9dcb065b2d7c8b789f7695d289ea Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Aug 2025 20:00:29 +0200 Subject: [PATCH 05/20] Rename command. --- app/Console/Commands/Tools/Cron.php | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) 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)); } } From fe9d36cd27c44f49b329ccca0fed97271488bf76 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Aug 2025 20:06:28 +0200 Subject: [PATCH 06/20] Clean up a variety of end points. --- .../Data/Bulk/TransactionController.php | 7 ++++++- app/Api/V1/Controllers/Data/DestroyController.php | 14 ++++++++++++++ .../Controllers/Data/Export/ExportController.php | 6 +++++- app/Api/V1/Controllers/Data/PurgeController.php | 9 +-------- app/Support/Export/ExportDataGenerator.php | 9 +++++++++ 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/app/Api/V1/Controllers/Data/Bulk/TransactionController.php b/app/Api/V1/Controllers/Data/Bulk/TransactionController.php index f6ae26a5e2..382df12db6 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,13 +45,17 @@ 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); } diff --git a/app/Api/V1/Controllers/Data/DestroyController.php b/app/Api/V1/Controllers/Data/DestroyController.php index b2ed17a568..af0751bf52 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,6 +56,19 @@ class DestroyController extends Controller { private bool $unused; + protected array $acceptedRoles = [UserRoleEnum::FULL]; + + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->validateUserGroup($request); + return $next($request); + } + ); + } + /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/destroyData diff --git a/app/Api/V1/Controllers/Data/Export/ExportController.php b/app/Api/V1/Controllers/Data/Export/ExportController.php index 2b847226c2..e1ad139c60 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); } diff --git a/app/Api/V1/Controllers/Data/PurgeController.php b/app/Api/V1/Controllers/Data/PurgeController.php index baba2f622a..65b0f79cc5 100644 --- a/app/Api/V1/Controllers/Data/PurgeController.php +++ b/app/Api/V1/Controllers/Data/PurgeController.php @@ -44,6 +44,7 @@ use Illuminate\Http\JsonResponse; */ class PurgeController extends Controller { + /** * TODO cleanup and use repositories. * This endpoint is documented at: @@ -66,14 +67,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/Support/Export/ExportDataGenerator.php b/app/Support/Export/ExportDataGenerator.php index 02f3f04763..d15ac20db7 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,11 @@ class ExportDataGenerator { $this->start = $start; } + + public function setUserGroup(UserGroup $userGroup): void + { + $this->userGroup = $userGroup; + } + + } From e4d443db5010039d090f8891193a8dbb6514ab23 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Aug 2025 21:10:30 +0200 Subject: [PATCH 07/20] Remove some unused comments. --- .../Autocomplete/PiggyBankController.php | 8 ------ .../Autocomplete/RecurrenceController.php | 4 --- .../Autocomplete/RuleController.php | 4 --- .../Autocomplete/RuleGroupController.php | 4 --- .../Autocomplete/TagController.php | 4 --- .../Autocomplete/TransactionController.php | 8 ------ .../TransactionTypeController.php | 4 --- .../Data/Bulk/TransactionController.php | 4 --- .../V1/Controllers/Data/DestroyController.php | 6 ----- .../Data/Export/ExportController.php | 26 ------------------- .../V1/Controllers/Data/PurgeController.php | 2 -- .../Insight/Expense/AccountController.php | 8 ------ .../Insight/Expense/BillController.php | 6 ----- .../Insight/Expense/BudgetController.php | 8 ------ .../Insight/Expense/CategoryController.php | 8 ------ .../Insight/Expense/PeriodController.php | 4 --- .../Insight/Expense/TagController.php | 6 ----- .../Insight/Income/AccountController.php | 8 ------ .../Insight/Income/CategoryController.php | 8 ------ .../Insight/Income/PeriodController.php | 4 --- .../Insight/Income/TagController.php | 6 ----- .../Insight/Transfer/AccountController.php | 4 --- .../Insight/Transfer/CategoryController.php | 8 ------ .../Insight/Transfer/PeriodController.php | 4 --- .../Insight/Transfer/TagController.php | 7 ----- .../Models/Account/DestroyController.php | 3 --- .../Models/Account/ListController.php | 9 ------- .../Models/Account/ShowController.php | 3 --- 28 files changed, 178 deletions(-) diff --git a/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php b/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php index 70cadd34e6..30737000fa 100644 --- a/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php +++ b/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php @@ -63,10 +63,6 @@ class PiggyBankController extends Controller ); } - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getPiggiesAC - */ public function piggyBanks(AutocompleteRequest $request): JsonResponse { $data = $request->getData(); @@ -93,10 +89,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 aa990154f7..f061df1443 100644 --- a/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php +++ b/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php @@ -57,10 +57,6 @@ class RecurrenceController extends Controller ); } - /** - * This endpoint is documented at: - * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getRecurringAC - */ 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 cb5f53b213..acc44effcd 100644 --- a/app/Api/V1/Controllers/Autocomplete/RuleController.php +++ b/app/Api/V1/Controllers/Autocomplete/RuleController.php @@ -57,10 +57,6 @@ class RuleController extends Controller ); } - /** - * This endpoint is documented at: - * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getRulesAC - */ 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 98067b3f88..d5e42b6733 100644 --- a/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php +++ b/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php @@ -57,10 +57,6 @@ class RuleGroupController extends Controller ); } - /** - * This endpoint is documented at: - * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getRuleGroupsAC - */ 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 437cf94bbd..027029bfb9 100644 --- a/app/Api/V1/Controllers/Autocomplete/TagController.php +++ b/app/Api/V1/Controllers/Autocomplete/TagController.php @@ -58,10 +58,6 @@ class TagController extends Controller ); } - /** - * This endpoint is documented at: - * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getTagAC - */ 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 aa28415463..8cb3d7a453 100644 --- a/app/Api/V1/Controllers/Autocomplete/TransactionController.php +++ b/app/Api/V1/Controllers/Autocomplete/TransactionController.php @@ -65,10 +65,6 @@ class TransactionController extends Controller ); } - /** - * This endpoint is documented at: - * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getTransactionsAC - */ public function transactions(AutocompleteRequest $request): JsonResponse { $data = $request->getData(); @@ -91,10 +87,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 f3f7ea69d2..ec8c723051 100644 --- a/app/Api/V1/Controllers/Autocomplete/TransactionTypeController.php +++ b/app/Api/V1/Controllers/Autocomplete/TransactionTypeController.php @@ -57,10 +57,6 @@ class TransactionTypeController extends Controller ); } - /** - * This endpoint is documented at - * * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getTransactionTypesAC - */ public function transactionTypes(AutocompleteRequest $request): JsonResponse { $data = $request->getData(); diff --git a/app/Api/V1/Controllers/Data/Bulk/TransactionController.php b/app/Api/V1/Controllers/Data/Bulk/TransactionController.php index 382df12db6..7f00225c70 100644 --- a/app/Api/V1/Controllers/Data/Bulk/TransactionController.php +++ b/app/Api/V1/Controllers/Data/Bulk/TransactionController.php @@ -62,10 +62,6 @@ class TransactionController extends Controller ); } - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/bulkUpdateTransactions - */ 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 af0751bf52..e66c562665 100644 --- a/app/Api/V1/Controllers/Data/DestroyController.php +++ b/app/Api/V1/Controllers/Data/DestroyController.php @@ -69,12 +69,6 @@ class DestroyController extends Controller ); } - /** - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/destroyData - * - * @throws FireflyException - */ 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 e1ad139c60..a4316dfeb6 100644 --- a/app/Api/V1/Controllers/Data/Export/ExportController.php +++ b/app/Api/V1/Controllers/Data/Export/ExportController.php @@ -61,8 +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 * @@ -103,9 +101,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") @@ -118,9 +113,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") @@ -133,9 +125,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") @@ -148,9 +137,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") @@ -163,9 +149,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") @@ -178,9 +161,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") @@ -193,9 +173,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") @@ -208,9 +185,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 65b0f79cc5..05b36f38e8 100644 --- a/app/Api/V1/Controllers/Data/PurgeController.php +++ b/app/Api/V1/Controllers/Data/PurgeController.php @@ -47,8 +47,6 @@ class PurgeController extends Controller /** * TODO cleanup and use repositories. - * This endpoint is documented at: - * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/purgeData */ public function purge(): JsonResponse { 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 From acd4f28ae39466312457e25058ecf79572e40480 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 15 Aug 2025 21:37:49 +0200 Subject: [PATCH 08/20] Restore "default" API endpoint so data importer does not fail. --- .ci/phpstan.neon | 2 +- app/Api/V1/Controllers/Autocomplete/AccountController.php | 1 - routes/api.php | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) 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/app/Api/V1/Controllers/Autocomplete/AccountController.php b/app/Api/V1/Controllers/Autocomplete/AccountController.php index a9971fc61c..00961cacc1 100644 --- a/app/Api/V1/Controllers/Autocomplete/AccountController.php +++ b/app/Api/V1/Controllers/Autocomplete/AccountController.php @@ -35,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; diff --git a/routes/api.php b/routes/api.php index 1e3bcc8933..1291758d67 100644 --- a/routes/api.php +++ b/routes/api.php @@ -618,6 +618,7 @@ Route::group( Route::get('', ['uses' => 'ShowController@index', 'as' => 'index']); Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']); 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']); From fb19739b54ea6343879a49b3ffd2b9fc03edaee0 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Aug 2025 05:58:58 +0200 Subject: [PATCH 09/20] Fix missing enrichment. --- .../Models/BudgetLimit/ShowController.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php b/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php index 2463533c1b..a3e9c2a7e4 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); From 8d52f83b4fe490b00442595824653bd9ced0d97f Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Aug 2025 06:02:26 +0200 Subject: [PATCH 10/20] Fix nulls. --- app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php | 4 ++-- app/Transformers/CategoryTransformer.php | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) 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/Transformers/CategoryTransformer.php b/app/Transformers/CategoryTransformer.php index dd4cd73c55..0891522887 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']); From 8f9ab150eec59937a65bfa98fa0c6d94f75e1001 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Aug 2025 06:08:01 +0200 Subject: [PATCH 11/20] Add command to reset rate limit for error emails. --- .../Commands/System/ResetsErrorMailLimit.php | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 app/Console/Commands/System/ResetsErrorMailLimit.php diff --git a/app/Console/Commands/System/ResetsErrorMailLimit.php b/app/Console/Commands/System/ResetsErrorMailLimit.php new file mode 100644 index 0000000000..4e98d40a05 --- /dev/null +++ b/app/Console/Commands/System/ResetsErrorMailLimit.php @@ -0,0 +1,57 @@ +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; + } +} From 91ce1d3b88eb67bf21bdf6088a42c00dd726151f Mon Sep 17 00:00:00 2001 From: JC5 Date: Sat, 16 Aug 2025 06:13:15 +0200 Subject: [PATCH 12/20] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-08-16?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Autocomplete/AccountController.php | 2 +- .../Autocomplete/BillController.php | 1 - .../Autocomplete/BudgetController.php | 1 - .../Autocomplete/CategoryController.php | 1 - .../Autocomplete/CurrencyController.php | 1 - .../Autocomplete/ObjectGroupController.php | 1 - .../Autocomplete/PiggyBankController.php | 1 - .../Autocomplete/TagController.php | 1 - .../Autocomplete/TransactionController.php | 1 - .../Controllers/Chart/AccountController.php | 18 ++++------- .../Controllers/Chart/BalanceController.php | 2 +- app/Api/V1/Controllers/Controller.php | 32 +++++++++---------- .../V1/Controllers/Data/DestroyController.php | 1 + .../Data/Export/ExportController.php | 1 - .../V1/Controllers/Data/PurgeController.php | 1 - .../Models/BudgetLimit/ShowController.php | 2 +- .../Commands/System/ResetsErrorMailLimit.php | 5 ++- app/Support/Export/ExportDataGenerator.php | 2 -- .../Http/Api/ValidatesUserGroupTrait.php | 4 +-- .../Repositories/UserGroup/UserGroupTrait.php | 1 + app/Transformers/CategoryTransformer.php | 2 +- composer.lock | 12 +++---- config/firefly.php | 4 +-- package-lock.json | 6 ++-- 24 files changed, 46 insertions(+), 57 deletions(-) diff --git a/app/Api/V1/Controllers/Autocomplete/AccountController.php b/app/Api/V1/Controllers/Autocomplete/AccountController.php index 00961cacc1..a9dba92ca5 100644 --- a/app/Api/V1/Controllers/Autocomplete/AccountController.php +++ b/app/Api/V1/Controllers/Autocomplete/AccountController.php @@ -46,7 +46,7 @@ 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 */ diff --git a/app/Api/V1/Controllers/Autocomplete/BillController.php b/app/Api/V1/Controllers/Autocomplete/BillController.php index 6bdb8b7afe..5fea014b5c 100644 --- a/app/Api/V1/Controllers/Autocomplete/BillController.php +++ b/app/Api/V1/Controllers/Autocomplete/BillController.php @@ -29,7 +29,6 @@ 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; /** diff --git a/app/Api/V1/Controllers/Autocomplete/BudgetController.php b/app/Api/V1/Controllers/Autocomplete/BudgetController.php index ada51c325b..3fd273d329 100644 --- a/app/Api/V1/Controllers/Autocomplete/BudgetController.php +++ b/app/Api/V1/Controllers/Autocomplete/BudgetController.php @@ -29,7 +29,6 @@ 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; /** diff --git a/app/Api/V1/Controllers/Autocomplete/CategoryController.php b/app/Api/V1/Controllers/Autocomplete/CategoryController.php index 8211f2dd62..cf24f56776 100644 --- a/app/Api/V1/Controllers/Autocomplete/CategoryController.php +++ b/app/Api/V1/Controllers/Autocomplete/CategoryController.php @@ -29,7 +29,6 @@ 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; /** diff --git a/app/Api/V1/Controllers/Autocomplete/CurrencyController.php b/app/Api/V1/Controllers/Autocomplete/CurrencyController.php index bbde6f7477..427e03d5b9 100644 --- a/app/Api/V1/Controllers/Autocomplete/CurrencyController.php +++ b/app/Api/V1/Controllers/Autocomplete/CurrencyController.php @@ -30,7 +30,6 @@ 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; /** diff --git a/app/Api/V1/Controllers/Autocomplete/ObjectGroupController.php b/app/Api/V1/Controllers/Autocomplete/ObjectGroupController.php index d50a71aeb3..0f4126f941 100644 --- a/app/Api/V1/Controllers/Autocomplete/ObjectGroupController.php +++ b/app/Api/V1/Controllers/Autocomplete/ObjectGroupController.php @@ -29,7 +29,6 @@ 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; /** diff --git a/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php b/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php index 30737000fa..de08a181b5 100644 --- a/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php +++ b/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php @@ -30,7 +30,6 @@ 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; /** diff --git a/app/Api/V1/Controllers/Autocomplete/TagController.php b/app/Api/V1/Controllers/Autocomplete/TagController.php index 027029bfb9..5ad94ea7f5 100644 --- a/app/Api/V1/Controllers/Autocomplete/TagController.php +++ b/app/Api/V1/Controllers/Autocomplete/TagController.php @@ -29,7 +29,6 @@ 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; /** diff --git a/app/Api/V1/Controllers/Autocomplete/TransactionController.php b/app/Api/V1/Controllers/Autocomplete/TransactionController.php index 8cb3d7a453..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; diff --git a/app/Api/V1/Controllers/Chart/AccountController.php b/app/Api/V1/Controllers/Chart/AccountController.php index 9a28d923eb..d617b3fd44 100644 --- a/app/Api/V1/Controllers/Chart/AccountController.php +++ b/app/Api/V1/Controllers/Chart/AccountController.php @@ -26,15 +26,11 @@ 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; @@ -48,8 +44,8 @@ use Illuminate\Support\Facades\Log; class AccountController extends Controller { use ApiSupport; - use CollectsAccountsFromFilter; use CleansChartData; + use CollectsAccountsFromFilter; protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; @@ -102,17 +98,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->primaryCurrency; } - $currentSet = [ + $currentSet = [ 'label' => $account->name, // the currency that belongs to the account. diff --git a/app/Api/V1/Controllers/Chart/BalanceController.php b/app/Api/V1/Controllers/Chart/BalanceController.php index eb05d934e1..1b5d1d610e 100644 --- a/app/Api/V1/Controllers/Chart/BalanceController.php +++ b/app/Api/V1/Controllers/Chart/BalanceController.php @@ -25,7 +25,7 @@ class BalanceController extends Controller { use CleansChartData; use CollectsAccountsFromFilter; - protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; private array $chartData = []; private GroupCollectorInterface $collector; diff --git a/app/Api/V1/Controllers/Controller.php b/app/Api/V1/Controllers/Controller.php index 57a6784a1f..196cca9290 100644 --- a/app/Api/V1/Controllers/Controller.php +++ b/app/Api/V1/Controllers/Controller.php @@ -62,9 +62,9 @@ abstract class Controller extends BaseController use ValidatesRequests; use ValidatesUserGroupTrait; - 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 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']; /** @var array */ protected array $allowedSort; @@ -106,8 +106,8 @@ abstract class Controller extends BaseController */ private function getParameters(): ParameterBag { - $bag = new ParameterBag(); - $page = (int)request()->get('page'); + $bag = new ParameterBag(); + $page = (int)request()->get('page'); if ($page < 1) { $page = 1; } @@ -117,7 +117,7 @@ abstract class Controller extends BaseController $bag->set('page', $page); // some date fields: - $dates = ['start', 'end', 'date']; + $dates = ['start', 'end', 'date']; foreach ($dates as $field) { $date = null; @@ -128,7 +128,7 @@ abstract class Controller extends BaseController Log::error($e->getMessage()); Log::error($e->getTraceAsString()); } - $obj = null; + $obj = null; if (null !== $date) { try { $obj = Carbon::parse((string)$date); @@ -173,7 +173,7 @@ abstract class Controller extends BaseController && auth()->check()) { // set default for user: /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); /** @var Preference $pageSize */ $pageSize = (int)app('preferences')->getForUser($user, 'listPageSize', 50)->data; @@ -200,7 +200,7 @@ abstract class Controller extends BaseController if ('' === $param) { return $bag; } - $parts = explode(',', $param); + $parts = explode(',', $param); foreach ($parts as $part) { $part = trim($part); $direction = 'asc'; @@ -236,14 +236,14 @@ abstract class Controller extends BaseController $params[$key] = $value; } - return $return . http_build_query($params); + return $return.http_build_query($params); } final protected function getManager(): Manager { // create some objects: $manager = new Manager(); - $baseUrl = request()->getSchemeAndHttpHost() . '/api/v1'; + $baseUrl = request()->getSchemeAndHttpHost().'/api/v1'; $manager->setSerializer(new JsonApiSerializer($baseUrl)); return $manager; @@ -251,14 +251,14 @@ abstract class Controller extends BaseController final protected function jsonApiList(string $key, LengthAwarePaginator $paginator, AbstractTransformer $transformer): array { - $manager = new Manager(); - $baseUrl = sprintf('%s/api/v1/', request()->getSchemeAndHttpHost()); + $manager = new Manager(); + $baseUrl = sprintf('%s/api/v1/', request()->getSchemeAndHttpHost()); // TODO add stuff to path? $manager->setSerializer(new JsonApiSerializer($baseUrl)); - $objects = $paginator->getCollection(); + $objects = $paginator->getCollection(); // the transformer, at this point, needs to collect information that ALL items in the collection // require, like meta-data and stuff like that, and save it for later. @@ -279,8 +279,8 @@ abstract class Controller extends BaseController final protected function jsonApiObject(string $key, array|Model $object, AbstractTransformer $transformer): array { // create some objects: - $manager = new Manager(); - $baseUrl = sprintf('%s/api/v1', request()->getSchemeAndHttpHost()); + $manager = new Manager(); + $baseUrl = sprintf('%s/api/v1', request()->getSchemeAndHttpHost()); $manager->setSerializer(new JsonApiSerializer($baseUrl)); // $transformer->collectMetaData(new Collection([$object])); diff --git a/app/Api/V1/Controllers/Data/DestroyController.php b/app/Api/V1/Controllers/Data/DestroyController.php index e66c562665..87e791be7c 100644 --- a/app/Api/V1/Controllers/Data/DestroyController.php +++ b/app/Api/V1/Controllers/Data/DestroyController.php @@ -64,6 +64,7 @@ class DestroyController extends Controller $this->middleware( function ($request, $next) { $this->validateUserGroup($request); + return $next($request); } ); diff --git a/app/Api/V1/Controllers/Data/Export/ExportController.php b/app/Api/V1/Controllers/Data/Export/ExportController.php index a4316dfeb6..f65e23dfc0 100644 --- a/app/Api/V1/Controllers/Data/Export/ExportController.php +++ b/app/Api/V1/Controllers/Data/Export/ExportController.php @@ -61,7 +61,6 @@ class ExportController extends Controller } /** - * * @throws FireflyException * * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Api/V1/Controllers/Data/PurgeController.php b/app/Api/V1/Controllers/Data/PurgeController.php index 05b36f38e8..f1c69e8ec6 100644 --- a/app/Api/V1/Controllers/Data/PurgeController.php +++ b/app/Api/V1/Controllers/Data/PurgeController.php @@ -44,7 +44,6 @@ use Illuminate\Http\JsonResponse; */ class PurgeController extends Controller { - /** * TODO cleanup and use repositories. */ diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php b/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php index a3e9c2a7e4..3b5c2c3569 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php @@ -84,7 +84,7 @@ class ShowController extends Controller $enrichment->setUser($admin); $enrichment->setStart($this->parameters->get('start')); $enrichment->setEnd($this->parameters->get('end')); - $budget = $enrichment->enrichSingle($budget); + $budget = $enrichment->enrichSingle($budget); $manager = $this->getManager(); diff --git a/app/Console/Commands/System/ResetsErrorMailLimit.php b/app/Console/Commands/System/ResetsErrorMailLimit.php index 4e98d40a05..9aba24fa79 100644 --- a/app/Console/Commands/System/ResetsErrorMailLimit.php +++ b/app/Console/Commands/System/ResetsErrorMailLimit.php @@ -1,5 +1,7 @@ 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)) { diff --git a/app/Support/Export/ExportDataGenerator.php b/app/Support/Export/ExportDataGenerator.php index d15ac20db7..ad0f0b4b71 100644 --- a/app/Support/Export/ExportDataGenerator.php +++ b/app/Support/Export/ExportDataGenerator.php @@ -913,6 +913,4 @@ class ExportDataGenerator { $this->userGroup = $userGroup; } - - } diff --git a/app/Support/Http/Api/ValidatesUserGroupTrait.php b/app/Support/Http/Api/ValidatesUserGroupTrait.php index 8e34de14af..c33be45a1e 100644 --- a/app/Support/Http/Api/ValidatesUserGroupTrait.php +++ b/app/Support/Http/Api/ValidatesUserGroupTrait.php @@ -39,7 +39,7 @@ use Illuminate\Support\Facades\Log; trait ValidatesUserGroupTrait { protected ?UserGroup $userGroup = null; - protected ?User $user = null; + protected ?User $user = null; /** * An "undocumented" filter @@ -102,7 +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; + $this->user = $user; return $group; } diff --git a/app/Support/Repositories/UserGroup/UserGroupTrait.php b/app/Support/Repositories/UserGroup/UserGroupTrait.php index 4bcd345e34..52f78553fa 100644 --- a/app/Support/Repositories/UserGroup/UserGroupTrait.php +++ b/app/Support/Repositories/UserGroup/UserGroupTrait.php @@ -73,6 +73,7 @@ trait UserGroupTrait return; } $class = null === $user ? 'NULL' : $user::class; + throw new FireflyException(sprintf('Object is %s, not User.', $class)); } diff --git a/app/Transformers/CategoryTransformer.php b/app/Transformers/CategoryTransformer.php index 0891522887..36d25b2921 100644 --- a/app/Transformers/CategoryTransformer.php +++ b/app/Transformers/CategoryTransformer.php @@ -81,7 +81,7 @@ class CategoryTransformer extends AbstractTransformer private function beautify(?array $array): ?array { - if(null === $array) { + if (null === $array) { return null; } $return = []; diff --git a/composer.lock b/composer.lock index a6297fc8ce..013ff5e6ca 100644 --- a/composer.lock +++ b/composer.lock @@ -11876,16 +11876,16 @@ }, { "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..000cb30cb6 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' => 'develop/2025-08-16', + 'build_time' => 1755317476, '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" }, From 0a235ec523d2f1d0ad22534a2bf16e83296983d3 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Aug 2025 06:34:28 +0200 Subject: [PATCH 13/20] Optimize category chart. --- .../Category/FrontpageChartGenerator.php | 79 +++++++++---------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/app/Support/Chart/Category/FrontpageChartGenerator.php b/app/Support/Chart/Category/FrontpageChartGenerator.php index dd10fd018e..14157c72d1 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()'); - $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); - } + 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]); + $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,27 @@ 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; + } } From 98db6db1ebbf362bb5e7aa7a0e9a62090ef82846 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Aug 2025 08:28:10 +0200 Subject: [PATCH 14/20] Fix references to charts. --- resources/assets/v2/src/api/v1/chart/account/dashboard.js | 2 +- resources/assets/v2/src/api/v1/chart/budget/dashboard.js | 2 +- resources/assets/v2/src/api/v1/chart/category/dashboard.js | 2 +- resources/assets/v2/src/api/v2/chart/account/dashboard.js | 2 +- resources/assets/v2/src/api/v2/chart/budget/dashboard.js | 2 +- resources/assets/v2/src/api/v2/chart/category/dashboard.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) 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}}); } } From f04ed5b8f09b985e2f80a98c66a43d4ee59cc675 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Aug 2025 14:10:24 +0200 Subject: [PATCH 15/20] Restore and fix API tests --- .../Controllers/Auth/RegisterController.php | 3 ++- app/Support/Navigation.php | 12 +++++----- .../Repositories/UserGroup/UserGroupTrait.php | 2 +- app/User.php | 2 +- phpunit.xml | 5 ++++- .../Api/Autocomplete/BillControllerTest.php | 15 ------------- .../Api/Autocomplete/BudgetControllerTest.php | 15 ------------- .../Autocomplete/CategoryControllerTest.php | 15 ++----------- .../Autocomplete/CurrencyControllerTest.php | 15 ------------- .../ObjectGroupControllerTest.php | 16 -------------- tests/integration/TestCase.php | 22 +++++++++++++++++-- 11 files changed, 36 insertions(+), 86 deletions(-) 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/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 52f78553fa..5e5eb6c59f 100644 --- a/app/Support/Repositories/UserGroup/UserGroupTrait.php +++ b/app/Support/Repositories/UserGroup/UserGroupTrait.php @@ -66,7 +66,7 @@ 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; diff --git a/app/User.php b/app/User.php index 3ebadaf347..03976c22c3 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/phpunit.xml b/phpunit.xml index 7c96bdee3e..d6b3e3e27d 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -31,12 +31,15 @@ failOnRisky="true" failOnWarning="true" processIsolation="false" + stopOnError="true" stopOnFailure="true"> + + + - diff --git a/tests/integration/Api/Autocomplete/BillControllerTest.php b/tests/integration/Api/Autocomplete/BillControllerTest.php index 7dc6b950b7..c464022132 100644 --- a/tests/integration/Api/Autocomplete/BillControllerTest.php +++ b/tests/integration/Api/Autocomplete/BillControllerTest.php @@ -45,20 +45,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 { @@ -96,7 +82,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..f4ccac21f8 100644 --- a/tests/integration/Api/Autocomplete/BudgetControllerTest.php +++ b/tests/integration/Api/Autocomplete/BudgetControllerTest.php @@ -45,21 +45,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..2fd62f9412 100644 --- a/tests/integration/Api/Autocomplete/CategoryControllerTest.php +++ b/tests/integration/Api/Autocomplete/CategoryControllerTest.php @@ -25,7 +25,9 @@ declare(strict_types=1); namespace Tests\integration\Api\Autocomplete; use FireflyIII\Models\Category; +use FireflyIII\Models\UserRole; use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Support\Facades\DB; use Tests\integration\TestCase; use FireflyIII\User; use FireflyIII\Models\UserGroup; @@ -45,20 +47,7 @@ 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 { diff --git a/tests/integration/Api/Autocomplete/CurrencyControllerTest.php b/tests/integration/Api/Autocomplete/CurrencyControllerTest.php index efdd02a866..73fc6952f9 100644 --- a/tests/integration/Api/Autocomplete/CurrencyControllerTest.php +++ b/tests/integration/Api/Autocomplete/CurrencyControllerTest.php @@ -45,21 +45,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 { diff --git a/tests/integration/Api/Autocomplete/ObjectGroupControllerTest.php b/tests/integration/Api/Autocomplete/ObjectGroupControllerTest.php index e6a089b7ec..14f3cfbc7c 100644 --- a/tests/integration/Api/Autocomplete/ObjectGroupControllerTest.php +++ b/tests/integration/Api/Autocomplete/ObjectGroupControllerTest.php @@ -45,22 +45,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/TestCase.php b/tests/integration/TestCase.php index b55259c8c7..8f88b2d372 100644 --- a/tests/integration/TestCase.php +++ b/tests/integration/TestCase.php @@ -23,10 +23,13 @@ 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; +use Illuminate\Support\Facades\DB; use Tests\integration\Traits\CollectsValues; /** @@ -57,11 +60,26 @@ abstract class TestCase extends BaseTestCase 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; } + + } From e7d9dc57d88064145caa9474a50de238fae9009a Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Aug 2025 14:52:29 +0200 Subject: [PATCH 16/20] Add some tests. --- .../Controllers/Chart/AccountController.php | 3 +- .../Controllers/Chart/BalanceController.php | 1 + .../V1/Controllers/Chart/BudgetController.php | 4 +- .../Controllers/Chart/CategoryController.php | 4 +- .../V1/Controllers/Data/PurgeController.php | 14 +++++++ phpunit.xml | 3 ++ routes/api.php | 4 +- .../Api/Chart/AccountControllerTest.php | 42 +++++++++++++++++++ .../Api/Chart/BalanceControllerTest.php | 42 +++++++++++++++++++ .../Api/Chart/BudgetControllerTest.php | 42 +++++++++++++++++++ .../Api/Chart/CategoryControllerTest.php | 42 +++++++++++++++++++ tests/integration/TestCase.php | 22 +++++----- 12 files changed, 207 insertions(+), 16 deletions(-) create mode 100644 tests/integration/Api/Chart/AccountControllerTest.php create mode 100644 tests/integration/Api/Chart/BalanceControllerTest.php create mode 100644 tests/integration/Api/Chart/BudgetControllerTest.php create mode 100644 tests/integration/Api/Chart/CategoryControllerTest.php diff --git a/app/Api/V1/Controllers/Chart/AccountController.php b/app/Api/V1/Controllers/Chart/AccountController.php index d617b3fd44..e60ac1f877 100644 --- a/app/Api/V1/Controllers/Chart/AccountController.php +++ b/app/Api/V1/Controllers/Chart/AccountController.php @@ -49,7 +49,7 @@ class AccountController extends Controller protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; - private array $chartData; + private array $chartData = []; private AccountRepositoryInterface $repository; /** @@ -61,6 +61,7 @@ class AccountController extends Controller $this->middleware( function ($request, $next) { $this->repository = app(AccountRepositoryInterface::class); + $this->validateUserGroup($request); $this->repository->setUserGroup($this->userGroup); $this->repository->setUser($this->user); diff --git a/app/Api/V1/Controllers/Chart/BalanceController.php b/app/Api/V1/Controllers/Chart/BalanceController.php index 1b5d1d610e..d589be1661 100644 --- a/app/Api/V1/Controllers/Chart/BalanceController.php +++ b/app/Api/V1/Controllers/Chart/BalanceController.php @@ -38,6 +38,7 @@ class BalanceController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { + $this->validateUserGroup($request); $this->repository = app(AccountRepositoryInterface::class); $this->collector = app(GroupCollectorInterface::class); $this->repository->setUserGroup($this->userGroup); diff --git a/app/Api/V1/Controllers/Chart/BudgetController.php b/app/Api/V1/Controllers/Chart/BudgetController.php index 4a91fc86d1..f3533231cc 100644 --- a/app/Api/V1/Controllers/Chart/BudgetController.php +++ b/app/Api/V1/Controllers/Chart/BudgetController.php @@ -27,6 +27,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; @@ -67,7 +68,6 @@ class BudgetController extends Controller $this->repository = app(BudgetRepositoryInterface::class); $this->blRepository = app(BudgetLimitRepositoryInterface::class); $this->opsRepository = app(OperationsRepositoryInterface::class); - $this->validateUserGroup($request); $this->repository->setUserGroup($this->userGroup); $this->opsRepository->setUserGroup($this->userGroup); $this->blRepository->setUserGroup($this->userGroup); @@ -85,7 +85,7 @@ class BudgetController extends Controller * * @throws FireflyException */ - public function overview(DateRequest $request): JsonResponse + public function overview(SameDateRequest $request): JsonResponse { $params = $request->getAll(); diff --git a/app/Api/V1/Controllers/Chart/CategoryController.php b/app/Api/V1/Controllers/Chart/CategoryController.php index f54321ef0e..28ca50f883 100644 --- a/app/Api/V1/Controllers/Chart/CategoryController.php +++ b/app/Api/V1/Controllers/Chart/CategoryController.php @@ -27,6 +27,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,6 +60,7 @@ class CategoryController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { + $this->validateUserGroup($request); $this->accountRepos = app(AccountRepositoryInterface::class); $this->currencyRepos = app(CurrencyRepositoryInterface::class); $this->accountRepos->setUserGroup($this->userGroup); @@ -79,7 +81,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/Data/PurgeController.php b/app/Api/V1/Controllers/Data/PurgeController.php index f1c69e8ec6..0ba815042e 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,6 +45,19 @@ 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. */ diff --git a/phpunit.xml b/phpunit.xml index d6b3e3e27d..b4ce7ee365 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -38,6 +38,9 @@ + + + diff --git a/routes/api.php b/routes/api.php index 1291758d67..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']); } ); diff --git a/tests/integration/Api/Chart/AccountControllerTest.php b/tests/integration/Api/Chart/AccountControllerTest.php new file mode 100644 index 0000000000..5049ad5414 --- /dev/null +++ b/tests/integration/Api/Chart/AccountControllerTest.php @@ -0,0 +1,42 @@ +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..ab43528f91 --- /dev/null +++ b/tests/integration/Api/Chart/BalanceControllerTest.php @@ -0,0 +1,42 @@ +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..07d5f31666 --- /dev/null +++ b/tests/integration/Api/Chart/BudgetControllerTest.php @@ -0,0 +1,42 @@ +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..aee7f2daf0 --- /dev/null +++ b/tests/integration/Api/Chart/CategoryControllerTest.php @@ -0,0 +1,42 @@ +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 8f88b2d372..ec86a0dbce 100644 --- a/tests/integration/TestCase.php +++ b/tests/integration/TestCase.php @@ -29,7 +29,6 @@ use FireflyIII\Models\UserRole; use FireflyIII\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; -use Illuminate\Support\Facades\DB; use Tests\integration\Traits\CollectsValues; /** @@ -42,7 +41,7 @@ abstract class TestCase extends BaseTestCase use RefreshDatabase; protected const MAX_ITERATIONS = 2; - protected $seed = true; + protected $seed = true; public function dateRangeProvider(): array { @@ -57,15 +56,20 @@ 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']); - $role = UserRole::where('title', 'owner')->first(); - $user = User::create([ - 'email' => 'test@email.com', - 'password' => 'password', - 'user_group_id' => $group->id, - ]); + $role = UserRole::where('title', 'owner')->first(); + $user = User::create([ + 'email' => 'test@email.com', + 'password' => 'password', + 'user_group_id' => $group->id, + ]); GroupMembership::create( [ @@ -76,8 +80,6 @@ abstract class TestCase extends BaseTestCase ); - - return $user; } From 24098f35bb955325fbdbc2cf651828245ef9ae8d Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Aug 2025 15:04:15 +0200 Subject: [PATCH 17/20] Fix some commands. --- .../ValidatesEnvironmentVariables.php | 44 ++++++++++++------- .../Commands/System/ScansAttachments.php | 27 ++++++------ app/Helpers/Collector/GroupCollector.php | 4 +- app/Support/Request/GetFilterInstructions.php | 7 ++- 4 files changed, 48 insertions(+), 34 deletions(-) diff --git a/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php b/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php index aceaa211be..197bf95a5f 100644 --- a/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php +++ b/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php @@ -42,32 +42,41 @@ class ValidatesEnvironmentVariables extends Command * * @var string */ - protected $signature = 'integrity:validates-environment-variables'; + protected $signature = 'integrity:validates-environment-variables'; /** * Execute the console 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'); - $options = array_keys(config('firefly.languages')); + $language = config('firefly.default_language'); + $locale = config('firefly.default_locale'); + $options = array_keys(config('firefly.languages')); if (!in_array($language, $options, true)) { $this->friendlyError(sprintf('DEFAULT_LANGUAGE "%s" is not a valid language for Firefly III.', $language)); $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,30 +84,31 @@ 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) { $this->friendlyError(sprintf('AUTHENTICATION_GUARD "%s" is not a valid guard for Firefly III.', $guard)); $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/ScansAttachments.php b/app/Console/Commands/System/ScansAttachments.php index 6597025bcd..dc889776ff 100644 --- a/app/Console/Commands/System/ScansAttachments.php +++ b/app/Console/Commands/System/ScansAttachments.php @@ -29,12 +29,15 @@ 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 function Safe\tempnam; +use Safe\Exceptions\FileinfoException; +use Safe\Exceptions\FilesystemException; +use Safe\Exceptions\StringsException; 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 { @@ -42,10 +45,13 @@ class ScansAttachments extends Command protected $description = 'Rescan all attachments and re-set the correct MD5 hash and mime.'; - protected $signature = 'firefly-iii:scan-attachments'; + protected $signature = 'firefly-iii:scan-attachments'; /** * Execute the console command. + * @throws FilesystemException + * @throws StringsException + * @throws FileinfoException */ public function handle(): int { @@ -57,7 +63,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 +71,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); - } + $tempFileName = tempnam(sys_get_temp_dir(), 'FireflyIII'); 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/Helpers/Collector/GroupCollector.php b/app/Helpers/Collector/GroupCollector.php index 0e05e5d7c0..b402554570 100644 --- a/app/Helpers/Collector/GroupCollector.php +++ b/app/Helpers/Collector/GroupCollector.php @@ -835,11 +835,9 @@ class GroupCollector implements GroupCollectorInterface if (count($product['transactions']) > 1) { return $product['title']; } - return 'zzz'; } - - exit('here we are 2'); + return 'zzz'; }); } 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); From eccc58e75a73bd07e0dbe9cc2836d177634b8b2b Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Aug 2025 19:31:59 +0200 Subject: [PATCH 18/20] Add piggy bank tests. --- .../Autocomplete/PiggyBankControllerTest.php | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 tests/integration/Api/Autocomplete/PiggyBankControllerTest.php diff --git a/tests/integration/Api/Autocomplete/PiggyBankControllerTest.php b/tests/integration/Api/Autocomplete/PiggyBankControllerTest.php new file mode 100644 index 0000000000..0144d0777c --- /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']); + } +} From 3409240a19c73ce331ac2de2b8ff0d37233eaf7c Mon Sep 17 00:00:00 2001 From: JC5 Date: Sat, 16 Aug 2025 19:36:45 +0200 Subject: [PATCH 19/20] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-08-16?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/Chart/AccountController.php | 2 +- .../V1/Controllers/Chart/BudgetController.php | 1 - .../Controllers/Chart/CategoryController.php | 1 - .../V1/Controllers/Data/PurgeController.php | 1 + .../ValidatesEnvironmentVariables.php | 13 ++++++--- .../Commands/System/ScansAttachments.php | 6 ++-- app/Helpers/Collector/GroupCollector.php | 2 ++ .../Category/FrontpageChartGenerator.php | 13 +++++---- app/User.php | 2 +- composer.lock | 14 +++++----- config/firefly.php | 2 +- .../Api/Autocomplete/BillControllerTest.php | 3 -- .../Api/Autocomplete/BudgetControllerTest.php | 2 -- .../Autocomplete/CategoryControllerTest.php | 6 ---- .../Autocomplete/CurrencyControllerTest.php | 3 -- .../ObjectGroupControllerTest.php | 2 -- .../Autocomplete/PiggyBankControllerTest.php | 28 +++++++++---------- .../Api/Chart/AccountControllerTest.php | 14 ++++++++-- .../Api/Chart/BalanceControllerTest.php | 14 ++++++++-- .../Api/Chart/BudgetControllerTest.php | 14 ++++++++-- .../Api/Chart/CategoryControllerTest.php | 14 ++++++++-- tests/integration/TestCase.php | 12 ++++---- 22 files changed, 96 insertions(+), 73 deletions(-) diff --git a/app/Api/V1/Controllers/Chart/AccountController.php b/app/Api/V1/Controllers/Chart/AccountController.php index e60ac1f877..47e37bb466 100644 --- a/app/Api/V1/Controllers/Chart/AccountController.php +++ b/app/Api/V1/Controllers/Chart/AccountController.php @@ -47,7 +47,7 @@ class AccountController extends Controller use CleansChartData; use CollectsAccountsFromFilter; - protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; private array $chartData = []; private AccountRepositoryInterface $repository; diff --git a/app/Api/V1/Controllers/Chart/BudgetController.php b/app/Api/V1/Controllers/Chart/BudgetController.php index f3533231cc..4102693d36 100644 --- a/app/Api/V1/Controllers/Chart/BudgetController.php +++ b/app/Api/V1/Controllers/Chart/BudgetController.php @@ -26,7 +26,6 @@ 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; diff --git a/app/Api/V1/Controllers/Chart/CategoryController.php b/app/Api/V1/Controllers/Chart/CategoryController.php index 28ca50f883..2ed6299222 100644 --- a/app/Api/V1/Controllers/Chart/CategoryController.php +++ b/app/Api/V1/Controllers/Chart/CategoryController.php @@ -26,7 +26,6 @@ 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; diff --git a/app/Api/V1/Controllers/Data/PurgeController.php b/app/Api/V1/Controllers/Data/PurgeController.php index 0ba815042e..cea88fa0e6 100644 --- a/app/Api/V1/Controllers/Data/PurgeController.php +++ b/app/Api/V1/Controllers/Data/PurgeController.php @@ -46,6 +46,7 @@ use Illuminate\Http\JsonResponse; class PurgeController extends Controller { protected array $acceptedRoles = [UserRoleEnum::FULL]; + public function __construct() { parent::__construct(); diff --git a/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php b/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php index 197bf95a5f..06ba95035a 100644 --- a/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php +++ b/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php @@ -42,7 +42,7 @@ class ValidatesEnvironmentVariables extends Command * * @var string */ - protected $signature = 'integrity:validates-environment-variables'; + protected $signature = 'integrity:validates-environment-variables'; /** * Execute the console command. @@ -67,9 +67,9 @@ class ValidatesEnvironmentVariables extends Command private function validateLanguage(): bool { - $language = config('firefly.default_language'); - $locale = config('firefly.default_locale'); - $options = array_keys(config('firefly.languages')); + $language = config('firefly.default_language'); + $locale = config('firefly.default_locale'); + $options = array_keys(config('firefly.languages')); if (!in_array($language, $options, true)) { $this->friendlyError(sprintf('DEFAULT_LANGUAGE "%s" is not a valid language for Firefly III.', $language)); @@ -86,6 +86,7 @@ class ValidatesEnvironmentVariables extends Command return false; } + return true; } @@ -96,8 +97,10 @@ class ValidatesEnvironmentVariables extends Command $this->friendlyError(sprintf('AUTHENTICATION_GUARD "%s" is not a valid guard for Firefly III.', $guard)); $this->friendlyError('Please check your .env file and make sure you use a valid setting.'); $this->friendlyError('Valid guards are: web, remote_user_guard'); + return false; } + return true; } @@ -107,8 +110,10 @@ class ValidatesEnvironmentVariables extends Command 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.'); + return false; } + return true; } } diff --git a/app/Console/Commands/System/ScansAttachments.php b/app/Console/Commands/System/ScansAttachments.php index dc889776ff..24c6f6199b 100644 --- a/app/Console/Commands/System/ScansAttachments.php +++ b/app/Console/Commands/System/ScansAttachments.php @@ -34,6 +34,7 @@ use Illuminate\Support\Facades\Storage; use Safe\Exceptions\FileinfoException; use Safe\Exceptions\FilesystemException; use Safe\Exceptions\StringsException; + use function Safe\file_put_contents; use function Safe\md5_file; use function Safe\mime_content_type; @@ -45,10 +46,11 @@ class ScansAttachments extends Command protected $description = 'Rescan all attachments and re-set the correct MD5 hash and mime.'; - protected $signature = 'firefly-iii:scan-attachments'; + protected $signature = 'firefly-iii:scan-attachments'; /** * Execute the console command. + * * @throws FilesystemException * @throws StringsException * @throws FileinfoException @@ -74,7 +76,7 @@ class ScansAttachments extends Command Log::error(sprintf('Could not decrypt data of attachment #%d: %s', $attachment->id, $e->getMessage())); $decryptedContent = $encryptedContent; } - $tempFileName = tempnam(sys_get_temp_dir(), 'FireflyIII'); + $tempFileName = tempnam(sys_get_temp_dir(), 'FireflyIII'); file_put_contents($tempFileName, $decryptedContent); $attachment->md5 = (string)md5_file($tempFileName); $attachment->mime = (string)mime_content_type($tempFileName); diff --git a/app/Helpers/Collector/GroupCollector.php b/app/Helpers/Collector/GroupCollector.php index b402554570..59b2748730 100644 --- a/app/Helpers/Collector/GroupCollector.php +++ b/app/Helpers/Collector/GroupCollector.php @@ -835,8 +835,10 @@ class GroupCollector implements GroupCollectorInterface if (count($product['transactions']) > 1) { return $product['title']; } + return 'zzz'; } + return 'zzz'; }); } diff --git a/app/Support/Chart/Category/FrontpageChartGenerator.php b/app/Support/Chart/Category/FrontpageChartGenerator.php index 14157c72d1..c27f13c686 100644 --- a/app/Support/Chart/Category/FrontpageChartGenerator.php +++ b/app/Support/Chart/Category/FrontpageChartGenerator.php @@ -66,16 +66,16 @@ class FrontpageChartGenerator public function generate(): array { 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]); - $collection = $this->collectExpensesAll($categories, $accounts); + $categories = $this->repository->getCategories(); + $accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]); + $collection = $this->collectExpensesAll($categories, $accounts); // collect for no-category: - $noCategory = $this->collectNoCatExpenses($accounts); - $collection = array_merge($collection, $noCategory); + $noCategory = $this->collectNoCatExpenses($accounts); + $collection = array_merge($collection, $noCategory); // sort temp array by amount. - $amounts = array_column($collection, 'sum_float'); + $amounts = array_column($collection, 'sum_float'); array_multisort($amounts, SORT_ASC, $collection); $currencyData = $this->createCurrencyGroups($collection); @@ -168,6 +168,7 @@ class FrontpageChartGenerator ]; } } + return $tempData; } } diff --git a/app/User.php b/app/User.php index 03976c22c3..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','user_group_id']; + protected $fillable = ['email', 'password', 'blocked', 'blocked_code', 'user_group_id']; protected $hidden = ['password', 'remember_token']; protected $table = 'users'; diff --git a/composer.lock b/composer.lock index 013ff5e6ca..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,7 +11872,7 @@ "type": "tidelift" } ], - "time": "2025-08-12T07:35:30+00:00" + "time": "2025-08-16T05:20:09+00:00" }, { "name": "rector/rector", diff --git a/config/firefly.php b/config/firefly.php index 000cb30cb6..1e3c53c73a 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -79,7 +79,7 @@ return [ // see cer.php for exchange rates feature flag. ], 'version' => 'develop/2025-08-16', - 'build_time' => 1755317476, + 'build_time' => 1755365689, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 26, diff --git a/tests/integration/Api/Autocomplete/BillControllerTest.php b/tests/integration/Api/Autocomplete/BillControllerTest.php index c464022132..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,7 +43,6 @@ final class BillControllerTest extends TestCase */ use RefreshDatabase; - private function createTestBills(int $count, User $user): void { for ($i = 1; $i <= $count; ++$i) { diff --git a/tests/integration/Api/Autocomplete/BudgetControllerTest.php b/tests/integration/Api/Autocomplete/BudgetControllerTest.php index f4ccac21f8..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 diff --git a/tests/integration/Api/Autocomplete/CategoryControllerTest.php b/tests/integration/Api/Autocomplete/CategoryControllerTest.php index 2fd62f9412..b678f24bef 100644 --- a/tests/integration/Api/Autocomplete/CategoryControllerTest.php +++ b/tests/integration/Api/Autocomplete/CategoryControllerTest.php @@ -25,13 +25,9 @@ declare(strict_types=1); namespace Tests\integration\Api\Autocomplete; use FireflyIII\Models\Category; -use FireflyIII\Models\UserRole; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Support\Facades\DB; use Tests\integration\TestCase; use FireflyIII\User; -use FireflyIII\Models\UserGroup; -use Override; /** * Class CategoryControllerTest @@ -47,8 +43,6 @@ final class CategoryControllerTest extends TestCase */ use RefreshDatabase; - - 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 73fc6952f9..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,7 +43,6 @@ final class CurrencyControllerTest extends TestCase */ use RefreshDatabase; - 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 14f3cfbc7c..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 diff --git a/tests/integration/Api/Autocomplete/PiggyBankControllerTest.php b/tests/integration/Api/Autocomplete/PiggyBankControllerTest.php index 0144d0777c..ce62738b74 100644 --- a/tests/integration/Api/Autocomplete/PiggyBankControllerTest.php +++ b/tests/integration/Api/Autocomplete/PiggyBankControllerTest.php @@ -49,7 +49,7 @@ final class PiggyBankControllerTest extends TestCase private function createTestPiggyBanks(int $count, User $user): void { - $type = AccountType::whereType(AccountTypeEnum::DEFAULT->value)->first(); + $type = AccountType::whereType(AccountTypeEnum::DEFAULT->value)->first(); if (null === $type) { $type = AccountType::create(['type' => AccountTypeEnum::DEFAULT->value]); } @@ -67,7 +67,7 @@ final class PiggyBankControllerTest extends TestCase $piggyBank = PiggyBank::create( [ 'user_id' => $user->id, - 'name' => 'Piggy bank ' . $i, + 'name' => 'Piggy bank '.$i, 'target_amount' => 1000, 'transaction_currency_id' => $currency->id, 'target_date' => now()->addDays(30), @@ -78,7 +78,7 @@ final class PiggyBankControllerTest extends TestCase $account = Account::create( [ 'user_id' => $user->id, - 'name' => 'Account ' . $i, + 'name' => 'Account '.$i, 'user_group_id' => $user->user_group_id, 'account_type_id' => $type->id, 'active' => 1, @@ -100,7 +100,7 @@ final class PiggyBankControllerTest extends TestCase public function testGivenAuthenticatedRequestWhenCallingTheBudgetsEndpointThenReturns200HttpCode(): void { // act as a user - $user = $this->createAuthenticatedUser(); + $user = $this->createAuthenticatedUser(); $this->actingAs($user); $response = $this->get(route('api.v1.autocomplete.piggy-banks'), ['Accept' => 'application/json']); @@ -111,7 +111,7 @@ final class PiggyBankControllerTest extends TestCase public function testGivenAuthenticatedRequestWhenCallingTheBudgetsEndpointThenReturnsBudgets(): void { - $user = $this->createAuthenticatedUser(); + $user = $this->createAuthenticatedUser(); $this->actingAs($user); $this->createTestPiggyBanks(5, $user); @@ -121,23 +121,23 @@ final class PiggyBankControllerTest extends TestCase $response->assertJsonCount(5); $response->assertJsonFragment(['name' => 'Piggy bank 1']); $response->assertJsonStructure([ - '*' => [ - 'id', - 'name', - ], - ]); + '*' => [ + 'id', + 'name', + ], + ]); } public function testGivenAuthenticatedRequestWhenCallingTheBudgetsEndpointWithQueryThenReturnsBudgetsWithLimit(): void { - $user = $this->createAuthenticatedUser(); + $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']); + ]), ['Accept' => 'application/json']); $response->assertStatus(200); $response->assertHeader('Content-Type', 'application/json'); @@ -146,14 +146,14 @@ final class PiggyBankControllerTest extends TestCase public function testGivenAuthenticatedRequestWhenCallingTheBudgetsEndpointWithQueryThenReturnsBudgetsThatMatchQuery(): void { - $user = $this->createAuthenticatedUser(); + $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']); + ]), ['Accept' => 'application/json']); $response->assertStatus(200); $response->assertHeader('Content-Type', 'application/json'); diff --git a/tests/integration/Api/Chart/AccountControllerTest.php b/tests/integration/Api/Chart/AccountControllerTest.php index 5049ad5414..067f347ef8 100644 --- a/tests/integration/Api/Chart/AccountControllerTest.php +++ b/tests/integration/Api/Chart/AccountControllerTest.php @@ -1,11 +1,18 @@ assertStatus(422); } + public function testGetOverviewChart(): void { $this->actingAs($this->user); - $params = [ + $params = [ 'start' => '2024-01-01', 'end' => '2024-01-31', ]; - $response = $this->getJson(route('api.v1.chart.account.overview') . '?' . http_build_query($params)); + $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 index ab43528f91..69f0fa9785 100644 --- a/tests/integration/Api/Chart/BalanceControllerTest.php +++ b/tests/integration/Api/Chart/BalanceControllerTest.php @@ -1,11 +1,18 @@ assertStatus(422); } + public function testGetOverviewChart(): void { $this->actingAs($this->user); - $params = [ + $params = [ 'start' => '2024-01-01', 'end' => '2024-01-31', ]; - $response = $this->getJson(route('api.v1.chart.balance.balance') . '?' . http_build_query($params)); + $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 index 07d5f31666..a9457e3822 100644 --- a/tests/integration/Api/Chart/BudgetControllerTest.php +++ b/tests/integration/Api/Chart/BudgetControllerTest.php @@ -1,11 +1,18 @@ assertStatus(422); } + public function testGetOverviewChart(): void { $this->actingAs($this->user); - $params = [ + $params = [ 'start' => '2024-01-01', 'end' => '2024-01-31', ]; - $response = $this->getJson(route('api.v1.chart.budget.overview') . '?' . http_build_query($params)); + $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 index aee7f2daf0..7aa7f8ce0b 100644 --- a/tests/integration/Api/Chart/CategoryControllerTest.php +++ b/tests/integration/Api/Chart/CategoryControllerTest.php @@ -1,11 +1,18 @@ assertStatus(422); } + public function testGetOverviewChart(): void { $this->actingAs($this->user); - $params = [ + $params = [ 'start' => '2024-01-01', 'end' => '2024-01-31', ]; - $response = $this->getJson(route('api.v1.chart.category.overview') . '?' . http_build_query($params)); + $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 ec86a0dbce..e1b42991b2 100644 --- a/tests/integration/TestCase.php +++ b/tests/integration/TestCase.php @@ -41,7 +41,7 @@ abstract class TestCase extends BaseTestCase use RefreshDatabase; protected const MAX_ITERATIONS = 2; - protected $seed = true; + protected $seed = true; public function dateRangeProvider(): array { @@ -66,10 +66,10 @@ abstract class TestCase extends BaseTestCase $group = UserGroup::create(['title' => 'test@email.com']); $role = UserRole::where('title', 'owner')->first(); $user = User::create([ - 'email' => 'test@email.com', - 'password' => 'password', - 'user_group_id' => $group->id, - ]); + 'email' => 'test@email.com', + 'password' => 'password', + 'user_group_id' => $group->id, + ]); GroupMembership::create( [ @@ -82,6 +82,4 @@ abstract class TestCase extends BaseTestCase return $user; } - - } From 72c31fbe6a8ef555ec18c75573d36a916123c775 Mon Sep 17 00:00:00 2001 From: JC5 Date: Sat, 16 Aug 2025 19:54:22 +0200 Subject: [PATCH 20/20] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'v6.3.0'=20on=202025-08-16?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/firefly.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/firefly.php b/config/firefly.php index 1e3c53c73a..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' => 'develop/2025-08-16', - 'build_time' => 1755365689, + 'version' => '6.3.0', + 'build_time' => 1755366750, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 26,