Compare commits

..

57 Commits

Author SHA1 Message Date
James Cole
0c598cb034 Fix changelog header. 2026-03-21 11:57:08 +01:00
github-actions[bot]
f5dea9ac09 Merge pull request #11999 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-03-21 11:54:49 +01:00
github-actions[bot]
4ef7944147 Merge pull request #11998 from firefly-iii/release-1774090477
🤖 Automatically merge the PR into the develop branch.
2026-03-21 11:54:45 +01:00
JC5
4e1c84944c 🤖 Auto commit for release 'v6.5.8' on 2026-03-21 2026-03-21 11:54:37 +01:00
github-actions[bot]
f36da26cc3 Merge pull request #11997 from firefly-iii/release-1774090121
🤖 Automatically merge the PR into the develop branch.
2026-03-21 11:48:48 +01:00
JC5
5983a8eb6d 🤖 Auto commit for release 'develop' on 2026-03-21 2026-03-21 11:48:41 +01:00
James Cole
b4a8a219ff Fix https://github.com/firefly-iii/firefly-iii/issues/11995 2026-03-21 11:42:55 +01:00
github-actions[bot]
4190c4d243 Merge pull request #11994 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-03-21 07:44:44 +01:00
github-actions[bot]
70cbbc1523 Merge pull request #11993 from firefly-iii/release-1774075474
🤖 Automatically merge the PR into the develop branch.
2026-03-21 07:44:40 +01:00
JC5
c724f13501 🤖 Auto commit for release 'v6.5.7' on 2026-03-21 2026-03-21 07:44:34 +01:00
James Cole
5f01a83b43 Fix phpstan issues. 2026-03-21 07:36:52 +01:00
James Cole
53c13d221d Clean up API routes. 2026-03-21 07:27:10 +01:00
James Cole
266cd7d8d0 Update changelog and html rendering. 2026-03-21 07:01:42 +01:00
github-actions[bot]
7c09278c8e Merge pull request #11992 from firefly-iii/release-1774047683
🤖 Automatically merge the PR into the develop branch.
2026-03-21 00:01:32 +01:00
JC5
21af34c65a 🤖 Auto commit for release 'develop' on 2026-03-21 2026-03-21 00:01:23 +01:00
James Cole
594c04b121 Add date. 2026-03-20 23:55:46 +01:00
James Cole
c50408249b Update changelog for the next release. 2026-03-20 23:52:53 +01:00
James Cole
b05a38c0e2 So let's make this absolutely clear. 2026-03-20 23:48:42 +01:00
James Cole
0bb1afdf6c Fuck these AI agents. 2026-03-20 23:36:57 +01:00
James Cole
547b83b36e Call dedicated method. 2026-03-20 15:43:04 +01:00
James Cole
134d8c8cf6 Merge branch 'main' into develop 2026-03-20 10:42:05 +01:00
James Cole
94144a407d Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop
# Conflicts:
#	app/Services/Internal/Recalculate/PrimaryAmountRecalculationService.php
2026-03-20 08:49:29 +01:00
James Cole
15e29d133a Expand the pull request template 2026-03-20 08:48:18 +01:00
github-actions[bot]
889ba9f3e6 Merge pull request #11990 from firefly-iii/release-1773991497
🤖 Automatically merge the PR into the develop branch.
2026-03-20 08:25:08 +01:00
JC5
32fe62df03 🤖 Auto commit for release 'develop' on 2026-03-20 2026-03-20 08:24:57 +01:00
James Cole
21f9be6504 Rewrite calculation service for #11982
- moved reset methods to this class
- Add method `recalculateForGroupAndCurrency` that accepts a $limitCurrency as third argument.
- Add `recalculateAccountsForCurrency` and `calculateTransactionsForCurrency`

`recalculateForGroupAndCurrency` can recalculate any foreign amount back to the primary currency (just like the class was already capable of) BUT limits the action to foreign amounts in $limitCurrency. This greatly reduces the rework necessary. This means the method can be safely called when changing currency exchange rates.

Not all methods in `recalculateForGroupAndCurrency` actually care about the given $limitCurrency but the methods that don't aren't doing much anyway, so it's OK to recalculate everything even though its not necessary.
2026-03-20 07:17:33 +01:00
James Cole
d514792f4d Add new handler for currency exchange rate events #11982 2026-03-20 07:14:34 +01:00
James Cole
5894695ad6 Can also collect budgets for user groups #11982 2026-03-20 07:14:19 +01:00
James Cole
7004c9aaf5 Add missing link to user group in currency exchange rate #11982 2026-03-20 07:14:10 +01:00
James Cole
1893a33d84 Move methods to calculation service for #11982 2026-03-20 07:13:54 +01:00
James Cole
d345b31cd4 Catch missing keys in old data for #11982 2026-03-20 07:13:38 +01:00
James Cole
a27642024d Add events for #11982 2026-03-20 07:13:26 +01:00
James Cole
c23ad831d0 New events for https://github.com/firefly-iii/firefly-iii/issues/11982 2026-03-20 06:35:39 +01:00
James Cole
d50c283973 Merge pull request #11988 from firefly-iii/dependabot/composer/composer-bc2e42b409
Bump league/commonmark from 2.8.1 to 2.8.2 in the composer group across 1 directory
2026-03-20 06:04:05 +01:00
dependabot[bot]
9ea3519585 Bump league/commonmark in the composer group across 1 directory
Bumps the composer group with 1 update in the / directory: [league/commonmark](https://github.com/thephpleague/commonmark).


Updates `league/commonmark` from 2.8.1 to 2.8.2
- [Release notes](https://github.com/thephpleague/commonmark/releases)
- [Changelog](https://github.com/thephpleague/commonmark/blob/2.8/CHANGELOG.md)
- [Commits](https://github.com/thephpleague/commonmark/compare/2.8.1...2.8.2)

---
updated-dependencies:
- dependency-name: league/commonmark
  dependency-version: 2.8.2
  dependency-type: direct:production
  dependency-group: composer
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-20 05:02:47 +00:00
James Cole
ddb5bc6038 Merge pull request #11985 from firefly-iii/dependabot/composer/composer-9e6dd9d4c4
Bump phpseclib/phpseclib from 3.0.49 to 3.0.50 in the composer group across 1 directory
2026-03-20 06:02:02 +01:00
James Cole
b3d048eb67 Fix https://github.com/orgs/firefly-iii/discussions/11977 2026-03-20 06:01:23 +01:00
dependabot[bot]
caadef7c64 Bump phpseclib/phpseclib in the composer group across 1 directory
Bumps the composer group with 1 update in the / directory: [phpseclib/phpseclib](https://github.com/phpseclib/phpseclib).


Updates `phpseclib/phpseclib` from 3.0.49 to 3.0.50
- [Release notes](https://github.com/phpseclib/phpseclib/releases)
- [Changelog](https://github.com/phpseclib/phpseclib/blob/master/CHANGELOG.md)
- [Commits](https://github.com/phpseclib/phpseclib/compare/3.0.49...3.0.50)

---
updated-dependencies:
- dependency-name: phpseclib/phpseclib
  dependency-version: 3.0.50
  dependency-type: indirect
  dependency-group: composer
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 17:14:44 +00:00
James Cole
f7c01e6821 Restore v2 layout in dev. 2026-03-18 06:10:21 +01:00
James Cole
3a971d738c Fix https://github.com/firefly-iii/firefly-iii/issues/11976 2026-03-18 05:33:42 +01:00
James Cole
1eb4ae3a2c Fix a variety of Mago issues. 2026-03-18 05:26:50 +01:00
James Cole
0f30eb59a4 Fix https://github.com/firefly-iii/firefly-iii/issues/11978 2026-03-18 05:10:25 +01:00
James Cole
9c10b01e8b Small code fixes. 2026-03-17 20:43:32 +01:00
James Cole
7c4f80a360 Small changes based on Mago rule 2026-03-17 17:29:24 +01:00
James Cole
e5c19f6088 Fix https://github.com/firefly-iii/firefly-iii/issues/11969 2026-03-17 16:26:57 +01:00
James Cole
b067215ba8 Merge pull request #11974 from NorskNoobing/patch-1 2026-03-17 10:57:44 +01:00
Daniel Holøien
a17d10b064 Fix typo in SMTP server comment in .env.example
Signed-off-by: Daniel Holøien <39239702+NorskNoobing@users.noreply.github.com>
2026-03-17 07:52:36 +01:00
github-actions[bot]
859fea532d Merge pull request #11967 from firefly-iii/release-1773691728
🤖 Automatically merge the PR into the develop branch.
2026-03-16 21:10:01 +01:00
JC5
75261a46d9 🤖 Auto commit for release 'develop' on 2026-03-16 2026-03-16 21:08:48 +01:00
James Cole
a4a99310ea Fix bad comparison. 2026-03-16 21:02:52 +01:00
James Cole
8de0844e55 Remove old fields for tag 2026-03-16 20:54:57 +01:00
James Cole
e7a6dd792f Another fix for https://github.com/firefly-iii/firefly-iii/issues/11964 2026-03-16 20:43:05 +01:00
James Cole
395ccf8f75 Update changelog. 2026-03-16 20:27:57 +01:00
James Cole
dfbfdb6aa2 Fix https://github.com/firefly-iii/firefly-iii/issues/11964 2026-03-16 20:26:57 +01:00
James Cole
a367ee96bd Merge branch 'main' into develop 2026-03-16 20:18:25 +01:00
James Cole
b6b1261df5 Fix https://github.com/firefly-iii/firefly-iii/issues/11966 2026-03-16 20:17:52 +01:00
github-actions[bot]
490c421ae5 Merge pull request #11957 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-03-15 17:36:55 +01:00
77 changed files with 1159 additions and 694 deletions

View File

@@ -173,7 +173,7 @@ MAIL_ENCRYPTION=null
MAIL_SENDMAIL_COMMAND=
#
# If you use self-signed certificates for your STMP server, you can use the following settings.
# If you use self-signed certificates for your SMTP server, you can use the following settings.
#
MAIL_ALLOW_SELF_SIGNED=false
MAIL_VERIFY_PEER=true

View File

@@ -1,25 +1,50 @@
<!--
Please TALK TO ME FIRST before you open a PR.
🙌 Thanks for contributing a pull request. Before you continue:
1. If you fix a problem that has no ticket, talk to me FIRST.
2. If you introduce new financial solutions or concepts, talk to me FIRST.
3. If your PR is more than 25 lines, talk to me FIRST.
4. If you used AI to write your PR, talk to me FIRST.
5. If you fix spelling or code comments, talk to me FIRST.
1. If you introduce new financial solutions or concepts, talk to me FIRST.
2. If your PR is more than 25 lines, talk to me FIRST.
3. If you fix spelling or code comments, talk to me FIRST.
Wanna talk to me? Open a GitHub Issue, Discussion, or send me an email: james@firefly-iii.org
Wanna talk to me? Open a GitHub Issue, Discussion, or email me: james@firefly-iii.org
See also: https://docs.firefly-iii.org/explanation/support/#contributing-code
👀 Please ensure you have taken a look at the contribution guidelines:
https://docs.firefly-iii.org/explanation/support/#contributing-code
Remember that your PR may be CLOSED:
1. If you do not refer to an existing issue, your PR will be CLOSED.
2. If you open a PR on the main branch, your PR will be CLOSED.
3. If you only fix a spelling error or code comment, your PR will be CLOSED.
Thanks again, and happy developing!
-->
@JC5
This PR fixes issue # <!-- mandatory field! -->.
#### Reference issues and PRs
<!--
Example: Fixes #1234. See also #3456.
-->
Changes in this pull request:
#### What does this implement/fix? Explain your changes.
-
-
-
#### AI usage disclosure
<!--
If AI tools were involved in creating this PR, please check all boxes that apply
below and make sure that you adhere to our Automated Contributions Policy:
https://docs.firefly-iii.org/explanation/support/#automated-contributions-policy
-->
I used AI assistance for:
- [ ] Code generation (e.g., when writing an implementation or fixing a bug)
- [ ] Test/benchmark generation
- [ ] Documentation (including examples)
- [ ] Research and understanding
#### Any other comments?
<!--
Thanks for contributing!
-->

2
.github/security.md vendored
View File

@@ -106,6 +106,8 @@ found with the full or partial support of AI coding agents, large language model
2. explain how the vulnerability can actually be abused by a nefarious third party, and
3. try to limit the verbosity of your report.
At the discretion of the maintainer of the developer, your report may be closed without resolve.
## Credits
This security policy is based on [Harbor](https://github.com/goharbor/harbor)'s security policy.

View File

@@ -4,6 +4,7 @@ Over time, many people have contributed to Firefly III. Their efforts are not al
Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution.
## 2026
- Daniel Holøien
- Matthew Grove
- Cinnamon Pyro
- R1DEN

View File

@@ -96,7 +96,7 @@ final class AccountController extends Controller
$nameWithBalance = $account->name;
$currency = $this->repository->getAccountCurrency($account) ?? $this->primaryCurrency;
$useCurrency = $currency;
if (in_array($account->accountType->type, $this->balanceTypes, true)) {
if (in_array($account->accountType->type, $this->balanceTypes, strict: true)) {
// this one is correct.
Log::debug(sprintf('accounts: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
$balance = $allBalances[$account->id] ?? [];

View File

@@ -28,6 +28,7 @@ use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate\DestroyRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Events\Model\CurrencyExchangeRate\DestroyedCurrencyExchangeRate;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency;
@@ -59,11 +60,12 @@ final class DestroyController extends Controller
public function destroy(DestroyRequest $request, TransactionCurrency $from, TransactionCurrency $to): JsonResponse
{
$this->repository->deleteRates($from, $to);
event(new DestroyedCurrencyExchangeRate($from, $to, $this->validateUserGroup($request)));
return response()->json([], 204);
}
public function destroySingleByDate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): JsonResponse
public function destroySingleByDate(Request $request, TransactionCurrency $from, TransactionCurrency $to, Carbon $date): JsonResponse
{
$exchangeRate = $this->repository->getSpecificRateOnDate($from, $to, $date);
if ($exchangeRate instanceof CurrencyExchangeRate) {
@@ -72,14 +74,19 @@ final class DestroyController extends Controller
if (!$exchangeRate instanceof CurrencyExchangeRate) {
throw new FireflyException('Bla');
}
event(new DestroyedCurrencyExchangeRate($from, $to, $this->validateUserGroup($request)));
return response()->json([], 204);
}
public function destroySingleById(CurrencyExchangeRate $exchangeRate): JsonResponse
public function destroySingleById(Request $request, CurrencyExchangeRate $exchangeRate): JsonResponse
{
$from = $exchangeRate->fromCurrency;
$to = $exchangeRate->toCurrency;
$this->repository->deleteRate($exchangeRate);
event(new DestroyedCurrencyExchangeRate($from, $to, $this->validateUserGroup($request)));
return response()->json([], 204);
}
}

View File

@@ -30,6 +30,8 @@ use FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate\StoreByCurrenciesRequ
use FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate\StoreByDateRequest;
use FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate\StoreRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Events\Model\CurrencyExchangeRate\CreatedCurrencyExchangeRate;
use FireflyIII\Events\Model\CurrencyExchangeRate\UpdatedCurrencyExchangeRate;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\ExchangeRate\ExchangeRateRepositoryInterface;
@@ -73,10 +75,12 @@ final class StoreController extends Controller
if ($object instanceof CurrencyExchangeRate) {
// just update it, no matter.
$rate = $this->repository->updateExchangeRate($object, $rate, $date);
event(new UpdatedCurrencyExchangeRate($rate));
}
if (!$object instanceof CurrencyExchangeRate) {
// store new
$rate = $this->repository->storeExchangeRate($from, $to, $rate, $date);
event(new CreatedCurrencyExchangeRate($rate));
}
$transformer = new ExchangeRateTransformer();
@@ -97,10 +101,12 @@ final class StoreController extends Controller
// update existing rate.
$existing = $this->repository->updateExchangeRate($existing, $rate);
$collection->push($existing);
event(new UpdatedCurrencyExchangeRate($existing));
continue;
}
$new = $this->repository->storeExchangeRate($from, $to, $rate, $date);
event(new CreatedCurrencyExchangeRate($new));
$collection->push($new);
}
@@ -124,11 +130,13 @@ final class StoreController extends Controller
// update existing rate.
$existing = $this->repository->updateExchangeRate($existing, $rate);
$collection->push($existing);
event(new UpdatedCurrencyExchangeRate($existing));
continue;
}
$new = $this->repository->storeExchangeRate($from, $to, $rate, $date);
$collection->push($new);
event(new CreatedCurrencyExchangeRate($new));
}
$count = $collection->count();

View File

@@ -28,6 +28,7 @@ use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate\UpdateRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Events\Model\CurrencyExchangeRate\UpdatedCurrencyExchangeRate;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\ExchangeRate\ExchangeRateRepositoryInterface;
@@ -66,7 +67,7 @@ final class UpdateController extends Controller
$date = $request->getDate();
$rate = $request->getRate();
$exchangeRate = $this->repository->updateExchangeRate($exchangeRate, $rate, $date);
event(new UpdatedCurrencyExchangeRate($exchangeRate));
$transformer = new ExchangeRateTransformer();
return response()->api($this->jsonApiObject(self::RESOURCE_KEY, $exchangeRate, $transformer))->header('Content-Type', self::CONTENT_TYPE);
@@ -77,6 +78,7 @@ final class UpdateController extends Controller
$date = $request->getDate();
$rate = $request->getRate();
$exchangeRate = $this->repository->updateExchangeRate($exchangeRate, $rate, $date);
event(new UpdatedCurrencyExchangeRate($exchangeRate));
$transformer = new ExchangeRateTransformer();
$transformer->setParameters($this->parameters);

View File

@@ -83,6 +83,9 @@ final class ListController extends Controller
// use new group collector:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
if (0 === count($journalIds)) {
$collector->findNothing();
}
$collector
->setUser($admin)
// filter on journal IDs.

View File

@@ -28,9 +28,7 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
@@ -41,7 +39,6 @@ use Illuminate\Validation\ValidationException;
final class DestroyController extends Controller
{
private CurrencyRepositoryInterface $repository;
private UserRepositoryInterface $userRepository;
/**
* CurrencyRepository constructor.
@@ -50,8 +47,7 @@ final class DestroyController extends Controller
{
parent::__construct();
$this->middleware(function ($request, $next) {
$this->repository = app(CurrencyRepositoryInterface::class);
$this->userRepository = app(UserRepositoryInterface::class);
$this->repository = app(CurrencyRepositoryInterface::class);
$this->repository->setUser(auth()->user());
return $next($request);
@@ -69,15 +65,8 @@ final class DestroyController extends Controller
*/
public function destroy(TransactionCurrency $currency): JsonResponse
{
/** @var User $admin */
$admin = auth()->user();
$rules = ['currency_code' => 'required'];
if (!$this->userRepository->hasRole($admin, 'owner')) {
// access denied:
$messages = ['currency_code' => '200005: You need the "owner" role to do this.'];
Validator::make([], $rules, $messages)->validate();
}
if ($this->repository->currencyInUse($currency)) {
$messages = ['currency_code' => '200006: Currency in use.'];
Validator::make([], $rules, $messages)->validate();

View File

@@ -32,7 +32,6 @@ use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
/**
* Class DestroyController
@@ -72,11 +71,6 @@ final class DestroyController extends Controller
if (false === $linkType->editable) {
throw new FireflyException('200020: Link type cannot be changed.');
}
if (false === auth()->user()->hasRole('owner')) {
Log::channel('audit')->warning('Non-owner user tries to delete a link type.');
response()->json([], 401);
}
$this->repository->destroy($linkType);
Preferences::mark();

View File

@@ -84,6 +84,9 @@ final class ListController extends Controller
// use new group collector:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
if (0 === count($journalIds)) {
$collector->findNothing();
}
$collector
->setUser($admin)
// filter on journal IDs.

View File

@@ -27,12 +27,10 @@ namespace FireflyIII\Api\V1\Controllers\Models\TransactionLinkType;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\TransactionLinkType\StoreRequest;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\LinkTypeTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use League\Fractal\Resource\Item;
@@ -44,7 +42,6 @@ final class StoreController extends Controller
use TransactionFilter;
private LinkTypeRepositoryInterface $repository;
private UserRepositoryInterface $userRepository;
/**
* LinkTypeController constructor.
@@ -54,9 +51,8 @@ final class StoreController extends Controller
parent::__construct();
$this->middleware(function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->repository = app(LinkTypeRepositoryInterface::class);
$this->userRepository = app(UserRepositoryInterface::class);
$user = auth()->user();
$this->repository = app(LinkTypeRepositoryInterface::class);
$this->repository->setUser($user);
return $next($request);
@@ -73,15 +69,6 @@ final class StoreController extends Controller
*/
public function store(StoreRequest $request): JsonResponse
{
/** @var User $admin */
$admin = auth()->user();
$rules = ['name' => 'required'];
if (!$this->userRepository->hasRole($admin, 'owner')) {
// access denied:
$messages = ['name' => '200005: You need the "owner" role to do this.'];
Validator::make([], $rules, $messages)->validate();
}
$data = $request->getAll();
// if currency ID is 0, find the currency by the code:
$linkType = $this->repository->store($data);

View File

@@ -29,12 +29,10 @@ use FireflyIII\Api\V1\Requests\Models\TransactionLinkType\UpdateRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\LinkType;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\LinkTypeTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use League\Fractal\Resource\Item;
@@ -46,7 +44,6 @@ final class UpdateController extends Controller
use TransactionFilter;
private LinkTypeRepositoryInterface $repository;
private UserRepositoryInterface $userRepository;
/**
* LinkTypeController constructor.
@@ -56,9 +53,8 @@ final class UpdateController extends Controller
parent::__construct();
$this->middleware(function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->repository = app(LinkTypeRepositoryInterface::class);
$this->userRepository = app(UserRepositoryInterface::class);
$user = auth()->user();
$this->repository = app(LinkTypeRepositoryInterface::class);
$this->repository->setUser($user);
return $next($request);
@@ -80,15 +76,6 @@ final class UpdateController extends Controller
throw new FireflyException('200020: Link type cannot be changed.');
}
/** @var User $admin */
$admin = auth()->user();
$rules = ['name' => 'required'];
if (!$this->userRepository->hasRole($admin, 'owner')) {
$messages = ['name' => '200005: You need the "owner" role to do this.'];
Validator::make([], $rules, $messages)->validate();
}
$data = $request->getAll();
$this->repository->update($linkType, $data);
$manager = $this->getManager();

View File

@@ -63,7 +63,7 @@ final class AccountController extends Controller
$query = trim((string) $request->get('query'));
$field = trim((string) $request->get('field'));
$type = $request->get('type') ?? 'all';
if ('' === $query || !in_array($field, $this->validFields, true)) {
if ('' === $query || !in_array($field, $this->validFields, strict: true)) {
return response(null, 422);
}
Log::debug(sprintf('Now in account search("%s", "%s")', $field, $query));

View File

@@ -66,7 +66,7 @@ final class TransactionController extends Controller
$internalRef = (string) $request->attributes->get('internal_reference');
$notes = (string) $request->attributes->get('notes');
$description = (string) $request->attributes->get('description');
Log::debug(sprintf('Include deleted? %s', var_export($includeDeleted, true)));
Log::debug(sprintf('Include deleted? %s', var_export(value: $includeDeleted, return: true)));
if ('' !== $externalId) {
$count += $this->repository->countByMeta('external_id', $externalId, $includeDeleted);
Log::debug(sprintf('Search for transactions with external_identifier "%s", count is now %d', $externalId, $count));

View File

@@ -323,7 +323,7 @@ final class BasicController extends Controller
$today = today(config('app.timezone'));
$available = $this->abRepository->getAvailableBudgetWithCurrency($start, $end);
$budgets = $this->budgetRepository->getActiveBudgets();
$spent = $this->opsRepository->sumExpenses($start, $end, null, $budgets);
$spent = $this->opsRepository->sumExpenses($start, $end, null, $budgets, null, true);
$days = (int) $today->diffInDays($end, true) + 1;
$currencies = [];

View File

@@ -30,12 +30,10 @@ use FireflyIII\Enums\WebhookDelivery;
use FireflyIII\Enums\WebhookResponse;
use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Binder\EitherConfigKey;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
/**
@@ -43,21 +41,6 @@ use Illuminate\Validation\ValidationException;
*/
final class ConfigurationController extends Controller
{
private UserRepositoryInterface $repository;
/**
* ConfigurationController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(function ($request, $next) {
$this->repository = app(UserRepositoryInterface::class);
return $next($request);
});
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/configuration/getConfiguration
@@ -142,11 +125,6 @@ final class ConfigurationController extends Controller
*/
public function update(UpdateRequest $request, string $name): JsonResponse
{
$rules = ['value' => 'required'];
if (!$this->repository->hasRole(auth()->user(), 'owner')) {
$messages = ['value' => '200005: You need the "owner" role to do this.'];
Validator::make([], $rules, $messages)->validate();
}
$data = $request->getAll();
$shortName = str_replace('configuration.', '', $name);

View File

@@ -74,13 +74,9 @@ final class UserController extends Controller
return response()->json([], 500);
}
if ($this->repository->hasRole($admin, 'owner')) {
$this->repository->destroy($user);
$this->repository->destroy($user);
return response()->json([], 204);
}
throw new FireflyException('200025: No access to function.');
return response()->json([], 204);
}
/**

View File

@@ -38,7 +38,7 @@ class ApiRequest extends FormRequest
public function handleConfig(array $config): void
{
if (in_array('required', $config, true)) {
if (in_array('required', $config, strict: true)) {
$this->required = 'required';
}
}

View File

@@ -72,7 +72,7 @@ class GenericRequest extends FormRequest
if (in_array(
$type,
[AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value],
true
strict: true
)) {
$return->push($account);
}

View File

@@ -116,7 +116,7 @@ class StoreRequest extends FormRequest
$validator->errors()->add(sprintf('accounts.%d', $index), trans('validation.invalid_account_currency'));
}
$type = $account->accountType->type;
if (!in_array($type, $types, true)) {
if (!in_array($type, $types, strict: true)) {
$validator->errors()->add(sprintf('accounts.%d', $index), trans('validation.invalid_account_type'));
}
}

View File

@@ -51,7 +51,7 @@ class StoreRequest extends FormRequest
{
$fields = [
'title' => ['title', 'convertString'],
'description' => ['description', 'convertString'],
'description' => ['description', 'stringWithNewlines'],
'rule_group_id' => ['rule_group_id', 'convertInteger'],
'order' => ['order', 'convertInteger'],
'rule_group_title' => ['rule_group_title', 'convertString'],

View File

@@ -52,7 +52,7 @@ class CreateRequest extends FormRequest
$responses = $this->get('responses', []);
$deliveries = $this->get('deliveries', []);
if (in_array(0, [count($triggers), count($responses), count($deliveries)], true)) {
if (in_array(0, [count($triggers), count($responses), count($deliveries)], strict: true)) {
throw new FireflyException('Unexpectedly got no responses, triggers or deliveries.');
}

View File

@@ -53,7 +53,7 @@ class UpdateRequest extends FormRequest
$responses = $this->get('responses', []);
$deliveries = $this->get('deliveries', []);
if (in_array(0, [count($triggers), count($responses), count($deliveries)], true)) {
if (in_array(0, [count($triggers), count($responses), count($deliveries)], strict: true)) {
throw new FireflyException('Unexpectedly got no responses, triggers or deliveries.');
}

View File

@@ -56,10 +56,10 @@ class UpdateRequest extends FormRequest
public function getAll(): array
{
$name = $this->route()->parameter('dynamicConfigKey');
if (in_array($name, $this->booleans, true)) {
if (in_array($name, $this->booleans, strict: true)) {
return ['value' => $this->boolean('value')];
}
if (in_array($name, $this->integers, true)) {
if (in_array($name, $this->integers, strict: true)) {
return ['value' => $this->convertInteger('value')];
}
@@ -73,13 +73,13 @@ class UpdateRequest extends FormRequest
{
$name = $this->route()->parameter('configName');
if (in_array($name, $this->booleans, true)) {
if (in_array($name, $this->booleans, strict: true)) {
return ['value' => ['required', new IsBoolean()]];
}
if ('configuration.permission_update_check' === $name) {
return ['value' => 'required|numeric|min:-1|max:1'];
}
if (in_array($name, $this->integers, true)) {
if (in_array($name, $this->integers, strict: true)) {
return ['value' => 'required|numeric|min:464272080'];
}

View File

@@ -125,7 +125,7 @@ class CorrectsAccountTypes extends Command
private function canCreateDestination(array $validDestinations): bool
{
return in_array(AccountTypeEnum::EXPENSE->value, $validDestinations, true);
return in_array(AccountTypeEnum::EXPENSE->value, $validDestinations, strict: true);
}
/**
@@ -133,7 +133,7 @@ class CorrectsAccountTypes extends Command
*/
private function canCreateSource(array $validSources): bool
{
return in_array(AccountTypeEnum::REVENUE->value, $validSources, true);
return in_array(AccountTypeEnum::REVENUE->value, $validSources, strict: true);
}
private function fixJournal(TransactionJournal $journal, string $transactionType, Transaction $source, Transaction $dest): void
@@ -308,7 +308,7 @@ class CorrectsAccountTypes extends Command
private function hasValidAccountType(array $validTypes, string $accountType): bool
{
return in_array($accountType, $validTypes, true);
return in_array($accountType, $validTypes, strict: true);
}
private function inspectJournal(TransactionJournal $journal): void
@@ -342,7 +342,7 @@ class CorrectsAccountTypes extends Command
return;
}
$expectedTypes = $this->expected[$type][$sourceAccountType];
if (!in_array($destAccountType, $expectedTypes, true)) {
if (!in_array($destAccountType, $expectedTypes, strict: true)) {
Log::debug(sprintf('[b] Going to fix journal #%d', $journal->id));
$this->fixJournal($journal, $type, $sourceTransaction, $destTransaction);
}

View File

@@ -86,10 +86,10 @@ class UpgradesToGroups extends Command
private function findOpposingTransaction(TransactionJournal $journal, Transaction $transaction): ?Transaction
{
$set = $journal->transactions->filter(static function (Transaction $subject) use ($transaction): bool {
$amount = ((float) $transaction->amount * -1) === (float) $subject->amount; // intentional float
$amount = -(float) $transaction->amount === (float) $subject->amount; // intentional float
$identifier = $transaction->identifier === $subject->identifier;
Log::debug(sprintf('Amount the same? %s', var_export($amount, true)));
Log::debug(sprintf('ID the same? %s', var_export($identifier, true)));
Log::debug(sprintf('Amount the same? %s', var_export($amount, return: true)));
Log::debug(sprintf('ID the same? %s', var_export($identifier, return: true)));
return $amount && $identifier;
});

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/*
* CreatedCurrencyExchangeRate.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Events\Model\CurrencyExchangeRate;
use FireflyIII\Events\Event;
use FireflyIII\Models\CurrencyExchangeRate;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class CreatedCurrencyExchangeRate extends Event
{
use SerializesModels;
public function __construct(
public CurrencyExchangeRate $rate
) {
Log::debug(sprintf('CreatedCurrencyExchangeRate(#%d) Event', $rate->id));
}
}

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
/*
* DestroyedCurrencyExchangeRate.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Events\Model\CurrencyExchangeRate;
use FireflyIII\Events\Event;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\UserGroup;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class DestroyedCurrencyExchangeRate extends Event
{
use SerializesModels;
public function __construct(
public TransactionCurrency $from,
public TransactionCurrency $to,
public UserGroup $userGroup
) {
Log::debug(sprintf('DestroyedCurrencyExchangeRate(%s, %s) Event', $from->code, $to->code));
}
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/*
* UpdatedCurrencyExchangeRate.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Events\Model\CurrencyExchangeRate;
use FireflyIII\Events\Event;
use FireflyIII\Models\CurrencyExchangeRate;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class UpdatedCurrencyExchangeRate extends Event
{
use SerializesModels;
public function __construct(
public CurrencyExchangeRate $rate
) {
Log::debug(sprintf('UpdatedCurrencyExchangeRate(#%d) Event', $rate->id));
}
}

View File

@@ -250,6 +250,7 @@ class Handler extends ExceptionHandler
'json' => request()->acceptsJson(),
'method' => request()->method(),
'headers' => $headers,
// @mago-expect lint:no-request-all
'post' => 'POST' === request()->method() ? json_encode(request()->all()) : '',
];

View File

@@ -56,11 +56,12 @@ class YearReportGenerator implements ReportGeneratorInterface
$reportType = 'default';
try {
$result = view('reports.default.year', ['accountIds' => $accountIds, 'reportType' => $reportType])
->with('start', $this->start)
->with('end', $this->end)
->render()
;
$result = view('reports.default.year', [
'accountIds' => $accountIds,
'reportType' => $reportType,
'start' => $this->start,
'end' => $this->end,
])->render();
} catch (Throwable $e) {
Log::error(sprintf('Cannot render reports.account.report: %s', $e->getMessage()));
Log::error($e->getTraceAsString());

View File

@@ -63,13 +63,15 @@ class MonthReportGenerator implements ReportGeneratorInterface
// render!
try {
$result = view('reports.tag.month', ['accountIds' => $accountIds, 'reportType' => $reportType, 'tagIds' => $tagIds])
->with('start', $this->start)
->with('end', $this->end)
->with('tags', $this->tags)
->with('accounts', $this->accounts)
->render()
;
$result = view('reports.tag.month', [
'accountIds' => $accountIds,
'reportType' => $reportType,
'tagIds' => $tagIds,
'start' => $this->start,
'end' => $this->end,
'tags' => $this->tags,
'accounts' => $this->accounts,
])->render();
} catch (Throwable $e) {
Log::error(sprintf('Cannot render reports.tag.month: %s', $e->getMessage()));
Log::error($e->getTraceAsString());

View File

@@ -955,9 +955,9 @@ trait MetaCollection
$this->fields[] = 'tags.tag as tag_name';
$this->fields[] = 'tags.date as tag_date';
$this->fields[] = 'tags.description as tag_description';
$this->fields[] = 'tags.latitude as tag_latitude';
$this->fields[] = 'tags.longitude as tag_longitude';
$this->fields[] = 'tags.zoomLevel as tag_zoom_level';
// $this->fields[] = 'tags.latitude as tag_latitude';
// $this->fields[] = 'tags.longitude as tag_longitude';
// $this->fields[] = 'tags.zoomLevel as tag_zoom_level';
$this->joinTagTables();

View File

@@ -132,7 +132,7 @@ final class NotificationController extends Controller
return redirect(route('settings.notification.index'));
}
$all = $request->all();
$all = $request->only(['channel']);
$channel = $all['test_submit'] ?? '';
switch ($channel) {

View File

@@ -83,8 +83,8 @@ final class RegisterController extends Controller
throw new FireflyException('Registration is currently not available :(');
}
$this->validator($request->all())->validate();
$user = $this->createUser($request->all());
$this->validator($request->only(['email', 'password', 'password_confirmation']))->validate();
$user = $this->createUser($request->only(['email', 'password']));
Log::info(sprintf('Registered new user %s', $user->email));
$owner = new OwnerNotifiable();
event(new NewUserRegistered($owner, $user));

View File

@@ -132,7 +132,7 @@ final class ResetPasswordController extends Controller
$allowRegistration = false;
}
return view('auth.passwords.reset')->with([
return view('auth.passwords.reset', [
'token' => $token,
'email' => $request->email,
'allowRegistration' => $allowRegistration,

View File

@@ -137,15 +137,15 @@ final class BudgetLimitController extends Controller
*/
public function store(Request $request): JsonResponse|RedirectResponse
{
Log::debug('Going to store new budget-limit.', $request->all());
Log::debug('Going to store new budget-limit.');
// first search for existing one and update it if necessary.
$currency = $this->currencyRepos->find((int) $request->get('transaction_currency_id'));
$budget = $this->repository->find((int) $request->get('budget_id'));
$currency = $this->currencyRepos->find((int) $request->input('transaction_currency_id'));
$budget = $this->repository->find((int) $request->input('budget_id'));
if (!$currency instanceof TransactionCurrency || !$budget instanceof Budget) {
throw new FireflyException('No valid currency or budget.');
}
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
$start = Carbon::createFromFormat('Y-m-d', $request->input('start'));
$end = Carbon::createFromFormat('Y-m-d', $request->input('end'));
if (!$start instanceof Carbon || !$end instanceof Carbon) {
return response()->json();

View File

@@ -44,6 +44,7 @@ use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
@@ -108,7 +109,7 @@ final class DebugController extends Controller
Preferences::mark();
$request->session()->forget(['start', 'end', '_previous', 'viewRange', 'range', 'is_custom_range', 'temp-mfa-secret', 'temp-mfa-codes']);
Artisan::call('cache:clear');
Cache::clear();
Artisan::call('config:clear');
Artisan::call('route:clear');
Artisan::call('view:clear');
@@ -302,7 +303,7 @@ final class DebugController extends Controller
}
return [
'debug' => var_export(config('app.debug'), true),
'debug' => var_export(config('app.debug'), return: true),
'audit_log_channel' => implode(', ', config('logging.channels.audit.channels')),
'default_language' => (string) config('firefly.default_language'),
'default_locale' => (string) config('firefly.default_locale'),

View File

@@ -106,7 +106,7 @@ final class HomeController extends Controller
}
$request->session()->put('is_custom_range', $isCustomRange);
Log::debug(sprintf('Set is_custom_range to %s', var_export($isCustomRange, true)));
Log::debug(sprintf('Set is_custom_range to %s', var_export($isCustomRange, return: true)));
$request->session()->put('start', $start);
Log::debug(sprintf('Set start to %s', $start->format('Y-m-d H:i:s')));
$request->session()->put('end', $end);

View File

@@ -116,7 +116,7 @@ final class JavascriptController extends Controller
'currencyCode' => $currency->code,
'currencySymbol' => $currency->symbol,
'accountingLocaleInfo' => $accounting,
'anonymous' => var_export(Steam::anonymous(), true),
'anonymous' => var_export(Steam::anonymous(), return: true),
'language' => $lang,
'dateRangeTitle' => $dateRange['title'],
'locale' => $locale,

View File

@@ -82,7 +82,7 @@ final class IntroController extends Controller
Log::debug('Elements is array', $elements);
Log::debug('Keys is', array_keys($elements));
Log::debug(sprintf('Keys has "outro": %s', var_export($hasStep, true)));
Log::debug(sprintf('Keys has "outro": %s', var_export($hasStep, return: true)));
return $hasStep;
}

View File

@@ -135,7 +135,7 @@ final class AmountController extends Controller
*/
public function postAdd(Request $request, PiggyBank $piggyBank): RedirectResponse
{
$data = $request->all();
$data = $request->only(['amount']);
$amounts = $data['amount'] ?? [];
$total = '0';
Log::debug('Start with loop.');

View File

@@ -231,8 +231,8 @@ final class PreferencesController extends Controller
Log::debug('postIndex for preferences.');
// front page accounts
$frontpageAccounts = [];
if (is_array($request->get('frontpageAccounts')) && count($request->get('frontpageAccounts')) > 0) {
foreach ($request->get('frontpageAccounts') as $id) {
if (is_array($request->input('frontpageAccounts')) && count($request->input('frontpageAccounts')) > 0) {
foreach ($request->input('frontpageAccounts') as $id) {
$frontpageAccounts[] = (int) $id;
}
Log::debug('Update frontpageAccounts', $frontpageAccounts);
@@ -240,7 +240,10 @@ final class PreferencesController extends Controller
}
// extract notifications:
$all = $request->all();
$keys = array_map(function (string $value): string {
return sprintf('notification_%s', $value);
}, array_keys(config('notifications.notifications.user')));
$all = $request->only($keys);
foreach (config('notifications.notifications.user') as $key => $info) {
$key = sprintf('notification_%s', $key);
if (array_key_exists($key, $all)) {
@@ -252,10 +255,11 @@ final class PreferencesController extends Controller
Preferences::set($key, false);
}
}
unset($all);
// view range:
Log::debug(sprintf('Let viewRange to "%s"', $request->get('viewRange')));
Preferences::set('viewRange', $request->get('viewRange'));
Log::debug(sprintf('Let viewRange to "%s"', $request->input('viewRange')));
Preferences::set('viewRange', $request->input('viewRange'));
// forget session values:
session()->forget('start');
session()->forget('end');
@@ -264,6 +268,7 @@ final class PreferencesController extends Controller
// notification settings, cannot be set by the demo user.
if (!auth()->user()->hasRole('demo')) {
$variables = ['slack_webhook_url', 'pushover_app_token', 'pushover_user_token', 'ntfy_server', 'ntfy_topic', 'ntfy_user', 'ntfy_pass'];
$all = $request->only($variables);
foreach ($variables as $variable) {
if ('' === $all[$variable]) {
Preferences::delete($variable);
@@ -274,9 +279,10 @@ final class PreferencesController extends Controller
}
Preferences::set('ntfy_auth', $all['ntfy_auth'] ?? false);
}
unset($all);
// convert primary
$convertToPrimary = 1 === (int) $request->get('convertToPrimary');
$convertToPrimary = 1 === (int) $request->input('convertToPrimary');
if ($convertToPrimary && !$this->convertToPrimary) {
// set to true!
Log::debug('User sets convertToPrimary to true.');
@@ -288,9 +294,9 @@ final class PreferencesController extends Controller
Preferences::set('convert_to_primary', $convertToPrimary);
// custom fiscal year
$customFiscalYear = 1 === (int) $request->get('customFiscalYear');
$customFiscalYear = 1 === (int) $request->input('customFiscalYear');
Preferences::set('customFiscalYear', $customFiscalYear);
$fiscalYearString = (string) $request->get('fiscalYearStart');
$fiscalYearString = (string) $request->input('fiscalYearStart');
if ('' !== $fiscalYearString) {
$fiscalYearStart = Carbon::parse($fiscalYearString, config('app.timezone'))->format('m-d');
Preferences::set('fiscalYearStart', $fiscalYearStart);
@@ -298,7 +304,7 @@ final class PreferencesController extends Controller
// save page size:
Preferences::set('listPageSize', 50);
$listPageSize = (int) $request->get('listPageSize');
$listPageSize = (int) $request->input('listPageSize');
if ($listPageSize > 0 && $listPageSize < 1337) {
Preferences::set('listPageSize', $listPageSize);
}
@@ -306,7 +312,7 @@ final class PreferencesController extends Controller
// language:
/** @var Preference $currentLang */
$currentLang = Preferences::get('language', 'en_US');
$lang = $request->get('language');
$lang = $request->input('language');
if (array_key_exists($lang, config('firefly.languages'))) {
Preferences::set('language', $lang);
}
@@ -317,13 +323,13 @@ final class PreferencesController extends Controller
// same for locale:
if (!auth()->user()->hasRole('demo')) {
$locale = (string) $request->get('locale');
$locale = (string) $request->input('locale');
$locale = '' === $locale ? null : $locale;
Preferences::set('locale', $locale);
}
// optional fields for transactions:
$setOptions = $request->get('tj') ?? [];
$setOptions = $request->input('tj') ?? [];
$optionalTj = [
'interest_date' => array_key_exists('interest_date', $setOptions),
'book_date' => array_key_exists('book_date', $setOptions),
@@ -341,13 +347,13 @@ final class PreferencesController extends Controller
Preferences::set('transaction_journal_optional_fields', $optionalTj);
// dark mode
$darkMode = $request->get('darkMode') ?? 'browser';
$darkMode = $request->input('darkMode') ?? 'browser';
if (in_array($darkMode, config('firefly.available_dark_modes'), true)) {
Preferences::set('darkMode', $darkMode);
}
// anonymous amounts?
$anonymous = '1' === $request->get('anonymous');
$anonymous = '1' === $request->input('anonymous');
Preferences::set('anonymous', $anonymous);
// save and continue
@@ -360,7 +366,7 @@ final class PreferencesController extends Controller
public function testNotification(Request $request): mixed
{
$all = $request->all();
$all = $request->only(['channel']);
$channel = $all['channel'] ?? '';
switch ($channel) {

View File

@@ -225,7 +225,7 @@ final class TagController extends Controller
{
// default values:
$subTitleIcon = 'fa-tag';
$page = (int) $request->get('page');
$page = (int) $request->input('page');
$pageSize = (int) Preferences::get('listPageSize', 50)->data;
$start ??= session('start');
$end ??= session('end');
@@ -249,18 +249,20 @@ final class TagController extends Controller
// collect transaction journal IDs in repository,
// this makes the collector faster and more accurate.
$journalIds = $this->repository->getJournalIds($tag);
if (0 === count($journalIds)) {
$collector->findNothing();
}
$collector
->setRange($start, $end)
->setLimit($pageSize)
->setPage($page)
->setJournalIds($journalIds)
->withAccountInformation()
// ->setTag($tag)
->withBudgetInformation()
->withCategoryInformation()
->withAttachmentInformation()
;
$groups = $collector->getPaginatedGroups();
$groups->setPath($path);
$sums = $this->repository->sumsOfTag($tag, $start, $end);
@@ -303,10 +305,13 @@ final class TagController extends Controller
// collect transaction journal IDs in repository,
// this makes the collector faster and more accurate.
$journalIds = $this->repository->getJournalIds($tag);
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$journalIds = $this->repository->getJournalIds($tag);
if (0 === count($journalIds)) {
$collector->findNothing();
}
$collector
->setRange($start, $end)
->setLimit($pageSize)

View File

@@ -153,7 +153,7 @@ final class ConvertController extends Controller
foreach ($group->transactionJournals as $journal) {
// catch FF exception.
try {
$this->convertJournal($journal, $destinationType, $request->all());
$this->convertJournal($journal, $destinationType, $request->only(['source_id', 'source_name', 'destination_id', 'destination_name']));
} catch (FireflyException $e) {
session()->flash('error', $e->getMessage());

View File

@@ -115,7 +115,7 @@ final class EditController extends Controller
];
$optionalFields['external_url'] ??= false;
$optionalFields['location'] ??= false;
$optionalFields['location'] = $optionalFields['location']
$optionalFields['location'] = true === $optionalFields['location']
&& true === FireflyConfig::get('enable_external_map', config('firefly.enable_external_map'))->data;
// map info voor v2:

View File

@@ -43,6 +43,6 @@ final class EditController extends Controller
$mainTitleIcon = 'fa-book';
Log::debug(sprintf('Now at %s', __METHOD__));
return view('administrations.edit')->with(['title' => $title, 'subTitle' => $subTitle, 'mainTitleIcon' => $mainTitleIcon]);
return view('administrations.edit', ['title' => $title, 'subTitle' => $subTitle, 'mainTitleIcon' => $mainTitleIcon]);
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* IsAdmin.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Middleware;
use Closure;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
/**
* Class IsAdmin.
*/
class IsAdminApi
{
/**
* Handle an incoming request. Must be admin.
*
* @param null|string $guard
*
* @return mixed
*/
public function handle(Request $request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
}
return response()->redirectTo(route('login'));
}
/** @var User $user */
$user = auth()->user();
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
if (!$repository->hasRole($user, 'owner')) {
throw new AuthorizationException();
}
return $next($request);
}
}

View File

@@ -61,7 +61,7 @@ class SecureHeaders
// sprintf("style-src 'self' 'nonce-%1s'", $nonce), // safe variant
"style-src 'self' 'unsafe-inline'", // unsafe variant
"base-uri 'self'",
"form-action 'self'",
// "form-action 'self'", // safe
"font-src 'self' data:",
sprintf("connect-src 'self' %s", $trackingScriptSrc),
sprintf("img-src 'self' data: 'nonce-%1s' ", $nonce),
@@ -70,16 +70,17 @@ class SecureHeaders
// overrule in development mode
if (true === config('firefly.is_local_dev')) {
$ip = '192.168.96.165';
$csp = [
"default-src 'none'",
"object-src 'none'",
sprintf("script-src 'unsafe-eval' 'strict-dynamic' 'nonce-%1s'", $nonce),
// sprintf("style-src 'self' 'nonce-%1s' https://10.0.0.15:5173/", $nonce), // safe variant
"style-src 'self' 'unsafe-inline' https://10.0.0.15:5173/", // unsafe variant
sprintf("style-src 'self' 'unsafe-inline' https://%s:5173/", $ip), // unsafe variant
"base-uri 'self'",
"form-action 'self'",
"font-src 'self' data: https://10.0.0.15:5173/",
sprintf("connect-src 'self' %s https://10.0.0.15:5173/ wss://10.0.0.15:5173/", $trackingScriptSrc),
sprintf("font-src 'self' data: https://%s:5173/", $ip),
sprintf('connect-src \'self\' %1$s https://%2$s:5173/ wss://%2$s:5173/', $trackingScriptSrc, $ip),
sprintf("img-src 'self' data: 'nonce-%1s'", $nonce),
"manifest-src 'self'",
];

View File

@@ -59,7 +59,7 @@ class UpdatesAccountInformation implements ShouldQueue
/** @var RuleAction $action */
foreach ($rule->ruleActions as $action) {
// fix name:
if ($oldData['name'] === $action->action_value && in_array($action->action_type, $fields, true)) {
if (array_key_exists('name', $oldData) && $oldData['name'] === $action->action_value && in_array($action->action_type, $fields, true)) {
Log::debug(sprintf('Rule action #%d "%s" has old account name, replace with new.', $action->id, $action->action_type));
$action->action_value = $account->name;
$action->save();
@@ -105,21 +105,25 @@ class UpdatesAccountInformation implements ShouldQueue
/** @var RuleTrigger $trigger */
foreach ($rule->ruleTriggers as $trigger) {
// fix name:
if ($oldData['name'] === $trigger->trigger_value && in_array($trigger->trigger_type, $nameFields, true)) {
if (array_key_exists('name', $oldData) && $oldData['name'] === $trigger->trigger_value && in_array($trigger->trigger_type, $nameFields, true)) {
Log::debug(sprintf('Rule trigger #%d "%s" has old account name, replace with new.', $trigger->id, $trigger->trigger_type));
$trigger->trigger_value = $account->name;
$trigger->save();
++$fixed;
}
// fix IBAN:
if ($oldData['iban'] === $trigger->trigger_value && in_array($trigger->trigger_type, $numberFields, true)) {
if (array_key_exists('iban', $oldData) && $oldData['iban'] === $trigger->trigger_value && in_array($trigger->trigger_type, $numberFields, true)) {
Log::debug(sprintf('Rule trigger #%d "%s" has old account IBAN, replace with new.', $trigger->id, $trigger->trigger_type));
$trigger->trigger_value = $account->iban;
$trigger->save();
++$fixed;
}
// fix account number: // account_number
if ($oldData['account_number'] === $trigger->trigger_value && in_array($trigger->trigger_type, $numberFields, true)) {
if (
array_key_exists('account_number', $oldData)
&& $oldData['account_number'] === $trigger->trigger_value
&& in_array($trigger->trigger_type, $numberFields, true)
) {
Log::debug(sprintf('Rule trigger #%d "%s" has old account account_number, replace with new.', $trigger->id, $trigger->trigger_type));
$trigger->trigger_value = $account->iban;
$trigger->save();

View File

@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
/*
* ProcessesExchangeRates.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Listeners\Model\CurrencyExchangeRate;
use FireflyIII\Events\Model\CurrencyExchangeRate\CreatedCurrencyExchangeRate;
use FireflyIII\Events\Model\CurrencyExchangeRate\DestroyedCurrencyExchangeRate;
use FireflyIII\Events\Model\CurrencyExchangeRate\UpdatedCurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\UserGroup;
use FireflyIII\Services\Internal\Recalculate\PrimaryAmountRecalculationService;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Preferences;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
class ProcessesExchangeRates
{
public function handle(CreatedCurrencyExchangeRate|DestroyedCurrencyExchangeRate|UpdatedCurrencyExchangeRate $event): void
{
Preferences::mark();
Cache::clear();
if ($event instanceof DestroyedCurrencyExchangeRate) {
$this->handleCurrency($event->userGroup, $event->from);
$this->handleCurrency($event->userGroup, $event->to);
return;
}
$this->handleCurrency($event->rate->userGroup, $event->rate->fromCurrency);
$this->handleCurrency($event->rate->userGroup, $event->rate->toCurrency);
}
private function handleCurrency(UserGroup $userGroup, TransactionCurrency $currency): void
{
$calculator = new PrimaryAmountRecalculationService();
if (Amount::convertToPrimary()) {
Log::debug(sprintf('Will now convert amounts to primary currency for currency %s.', $currency->code));
$calculator->recalculateForGroupAndCurrency($userGroup, $currency);
// $calculator->recalculateForGroup($userGroup);
return;
}
Log::debug('Will NOT convert to primary currency.');
}
}

View File

@@ -25,46 +25,17 @@ declare(strict_types=1);
namespace FireflyIII\Listeners\System;
use FireflyIII\Events\Preferences\UserGroupChangedPrimaryCurrency;
use FireflyIII\Models\Budget;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\UserGroup;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Services\Internal\Recalculate\PrimaryAmountRecalculationService;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class RecalculatesPrimaryCurrencyAmounts
{
public function handle(UserGroupChangedPrimaryCurrency $event): void
{
// Reset the primary currency amounts for all objects that have it.
Log::debug('Resetting primary currency amounts for all objects.');
$tables = [
// !!! this array is also in the migration
'accounts' => ['native_virtual_balance'],
'available_budgets' => ['native_amount'],
'bills' => ['native_amount_min', 'native_amount_max'],
];
foreach ($tables as $table => $columns) {
Log::debug(sprintf('Now processing table "%s"', $table));
foreach ($columns as $column) {
Log::debug(sprintf('Resetting column "%s" in table "%s".', $column, $table));
DB::table($table)->where('user_group_id', $event->userGroup->id)->update([$column => null]);
}
}
$this->resetPiggyBanks($event->userGroup);
$this->resetBudgets($event->userGroup);
$this->resetTransactions($event->userGroup);
Log::debug('Have now reset all primary amounts to NULL.');
// fire laravel command to recalculate them all.
if (Amount::convertToPrimary()) {
Log::debug('Will now convert amounts to primary currency.');
$calculator = new PrimaryAmountRecalculationService();
$calculator->recalculate();
@@ -72,87 +43,4 @@ class RecalculatesPrimaryCurrencyAmounts
}
Log::debug('Will NOT convert to primary currency.');
}
private function resetBudget(Budget $budget): void
{
foreach ($budget->autoBudgets as $autoBudget) {
if ('' === (string) $autoBudget->native_amount) {
continue;
}
Log::debug(sprintf('Resetting native_amount for budget #%d and auto budget #%d.', $budget->id, $autoBudget->id));
$autoBudget->native_amount = null;
$autoBudget->saveQuietly();
}
foreach ($budget->budgetlimits as $limit) {
if ('' !== (string) $limit->native_amount) {
Log::debug(sprintf('Resetting native_amount for budget #%d and budget limit #%d.', $budget->id, $limit->id));
$limit->native_amount = null;
$limit->saveQuietly();
}
}
}
private function resetBudgets(UserGroup $userGroup): void
{
$repository = app(BudgetRepositoryInterface::class);
$repository->setUserGroup($userGroup);
$set = $repository->getBudgets();
Log::debug(sprintf('Reset primary currency of %d budget(s).', $set->count()));
/** @var Budget $budget */
foreach ($set as $budget) {
$this->resetBudget($budget);
}
}
private function resetPiggyBank(PiggyBank $piggyBank): void
{
if ('' !== (string) $piggyBank->native_target_amount) {
Log::debug(sprintf('Resetting native_target_amount for piggy bank #%d.', $piggyBank->id));
$piggyBank->native_target_amount = null;
$piggyBank->saveQuietly();
}
foreach ($piggyBank->accounts as $account) {
if ('' !== (string) $account->pivot->native_current_amount) {
Log::debug(sprintf('Resetting native_current_amount for piggy bank #%d and account #%d.', $piggyBank->id, $account->id));
$account->pivot->native_current_amount = null;
$account->pivot->save();
}
}
foreach ($piggyBank->piggyBankEvents as $event) {
if ('' !== (string) $event->native_amount) {
Log::debug(sprintf('Resetting native_amount for piggy bank #%d and event #%d.', $piggyBank->id, $event->id));
$event->native_amount = null;
$event->saveQuietly();
}
}
}
private function resetPiggyBanks(UserGroup $userGroup): void
{
$repository = app(PiggyBankRepositoryInterface::class);
$repository->setUserGroup($userGroup);
$piggyBanks = $repository->getPiggyBanks();
Log::debug(sprintf('Reset primary currency of %d piggy bank(s).', $piggyBanks->count()));
/** @var PiggyBank $piggyBank */
foreach ($piggyBanks as $piggyBank) {
$this->resetPiggyBank($piggyBank);
}
}
private function resetTransactions(UserGroup $userGroup): void
{
// custom query because of the potential size of this update.
$success = DB::table('transactions')
->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.user_group_id', $userGroup->id)
->where(static function (Builder $q): void {
$q->whereNotNull('native_amount')->orWhereNotNull('native_foreign_amount');
})
->update(['native_amount' => null, 'native_foreign_amount' => null])
;
Log::debug(sprintf('Reset %d transactions.', $success));
}
}

View File

@@ -59,6 +59,11 @@ class CurrencyExchangeRate extends Model
return $this->belongsTo(User::class);
}
public function userGroup(): BelongsTo
{
return $this->belongsTo(UserGroup::class);
}
protected function casts(): array
{
return [

View File

@@ -638,9 +638,10 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
/** @var Bill $bill */
foreach ($bills as $bill) {
// Log::debug(sprintf('Bill #%d ("%s")', $bill->id, $bill->name));
/** @var Collection $set */
$set = $bill->transactionJournals()->after($start)->before($end)->get(['transaction_journals.*']);
$currency = $convertToPrimary && $bill->transactionCurrency->id !== $primary->id ? $primary : $bill->transactionCurrency;
$set = $bill->transactionJournals()->after($start)->before($end)->get(['transaction_journals.*']);
$currency = $convertToPrimary && $bill->transactionCurrency->id !== $primary->id ? $primary : $bill->transactionCurrency;
$return[(int) $currency->id] ??= [
'id' => (string) $currency->id,
'name' => $currency->name,
@@ -649,13 +650,14 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
'decimal_places' => $currency->decimal_places,
'sum' => '0',
];
$setAmount = '0';
// Log::debug(sprintf('Created a new array for currency #%d', $currency->id));
/** @var TransactionJournal $transactionJournal */
foreach ($set as $transactionJournal) {
// grab currency from transaction.
$transactionCurrency = $transactionJournal->transactionCurrency;
$return[(int) $transactionCurrency->id] ??= [
// grab currency from journal.
$transactionCurrency = $transactionJournal->transactionCurrency;
$currencyId = (int) $transactionCurrency->id;
$return[$currencyId] ??= [
'id' => (string) $transactionCurrency->id,
'name' => $transactionCurrency->name,
'symbol' => $transactionCurrency->symbol,
@@ -663,19 +665,12 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
'decimal_places' => $transactionCurrency->decimal_places,
'sum' => '0',
];
$amountFromJournal = Amount::getAmountFromJournalObject($transactionJournal);
// Log::debug(sprintf('Created a (new) array for currency #%d', $currencyId));
// Log::debug(sprintf('Amount to add is %s', $amountFromJournal));
// get currency from transaction as well.
$return[(int) $transactionCurrency->id]['sum'] = bcadd(
$return[(int) $transactionCurrency->id]['sum'],
Amount::getAmountFromJournalObject($transactionJournal)
);
// $setAmount = bcadd($setAmount, Amount::getAmountFromJournalObject($transactionJournal));
$return[$currencyId]['sum'] = bcadd($return[$currencyId]['sum'], $amountFromJournal);
}
// Log::debug(sprintf('Bill #%d ("%s") with %d transaction(s) and sum %s %s', $bill->id, $bill->name, $set->count(), $currency->code, $setAmount));
// $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $setAmount);
// Log::debug(sprintf('Total sum is now %s', $return[$currency->id]['sum']));
}
// remove empty sets
$final = [];

View File

@@ -368,6 +368,15 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
public function getBudgets(): Collection
{
if (null === $this->user) {
return $this->userGroup
->budgets()
->orderBy('order', 'ASC')
->orderBy('name', 'ASC')
->get()
;
}
return $this->user
->budgets()
->orderBy('order', 'ASC')

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Services\Internal\Recalculate;
use FireflyIII\Events\Model\Account\UpdatedExistingAccount;
use FireflyIII\Handlers\Observer\TransactionObserver;
use FireflyIII\Models\Account;
use FireflyIII\Models\AutoBudget;
@@ -36,14 +37,16 @@ use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\UserGroup;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\FireflyConfig;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\Builder as DatabaseBuilder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
@@ -57,14 +60,52 @@ class PrimaryAmountRecalculationService
/** @var UserGroupRepositoryInterface $repository */
$repository = app(UserGroupRepositoryInterface::class);
Preferences::mark();
/** @var UserGroup $userGroup */
foreach ($repository->getAll() as $userGroup) {
Log::debug('Resetting primary currency amounts for all objects.');
$this->resetGenericTables($userGroup);
$this->resetPiggyBanks($userGroup);
$this->resetBudgets($userGroup);
$this->resetTransactions($userGroup);
Log::debug('Have now reset all primary amounts to NULL.');
$this->recalculateForGroup($userGroup);
}
}
public function recalculateForGroup(UserGroup $userGroup): void
{
Log::debug(sprintf('Now recalculating primary amounts for user group #%d', $userGroup->id));
// do a check with the group's currency so we can skip some stuff.
$currency = Amount::getPrimaryCurrencyByUserGroup($userGroup);
$this->recalculateAccounts($userGroup, $currency);
$this->recalculatePiggyBanks($userGroup, $currency);
$this->recalculateBudgets($userGroup, $currency);
$this->recalculateAvailableBudgets($userGroup, $currency);
$this->recalculateBills($userGroup, $currency);
$this->calculateTransactions($userGroup, $currency);
}
public function recalculateForGroupAndCurrency(UserGroup $userGroup, TransactionCurrency $limitCurrency): void
{
// do a check with the group's currency so we can skip some stuff.
$currency = Amount::getPrimaryCurrencyByUserGroup($userGroup);
if ($limitCurrency->id === $currency->id) {
Log::debug(sprintf('Can skip recalculation because user requested the same currencies (%s).', $limitCurrency->code));
return;
}
$this->recalculateAccountsForCurrency($userGroup, $currency, $limitCurrency);
$this->recalculatePiggyBanks($userGroup, $currency);
$this->recalculateBudgets($userGroup, $currency);
$this->recalculateAvailableBudgets($userGroup, $currency);
$this->recalculateBills($userGroup, $currency);
$this->calculateTransactionsForCurrency($userGroup, $currency, $limitCurrency);
}
private function calculateTransactions(UserGroup $userGroup, TransactionCurrency $currency): void
{
// custom query because of the potential size of this update.
@@ -86,7 +127,10 @@ class PrimaryAmountRecalculationService
->get(['transactions.id'])
;
TransactionObserver::$recalculate = false;
Log::debug(sprintf('Count of set is %d', $set->count()));
foreach ($set as $item) {
Log::debug(sprintf('Touch transaction #%d', $item->id));
// here we are.
/** @var null|Transaction $transaction */
$transaction = Transaction::find($item->id);
@@ -96,9 +140,42 @@ class PrimaryAmountRecalculationService
Log::debug(sprintf('Recalculated %d transactions.', $set->count()));
}
private function recalculateAccounts(UserGroup $userGroup): void
private function calculateTransactionsForCurrency(UserGroup $userGroup, TransactionCurrency $currency, TransactionCurrency $limitCurrency): void
{
$set = $userGroup
Log::debug(sprintf('Now in calculateTransactionsForCurrency(#%d, %s, %s)', $userGroup->id, $currency->code, $limitCurrency->code));
// custom query because of the potential size of this update.
$set = DB::table('transactions')
->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.user_group_id', $userGroup->id)
->where(static function (DatabaseBuilder $q1) use ($currency): void {
$q1->where(static function (DatabaseBuilder $q2) use ($currency): void {
$q2->whereNot('transactions.transaction_currency_id', $currency->id)->whereNull('transactions.foreign_currency_id');
})->orWhere(static function (DatabaseBuilder $q3) use ($currency): void {
$q3->whereNot('transactions.transaction_currency_id', $currency->id)->whereNot('transactions.foreign_currency_id', $currency->id);
});
})
// must be in the limit currency.
->where('transactions.transaction_currency_id', $limitCurrency->id)
->orWhere('transactions.foreign_currency_id', $limitCurrency->id)
->get(['transactions.id'])
;
TransactionObserver::$recalculate = false;
Log::debug(sprintf('Count of set is %d', $set->count()));
foreach ($set as $item) {
Log::debug(sprintf('Touch transaction #%d', $item->id));
// here we are.
/** @var null|Transaction $transaction */
$transaction = Transaction::find($item->id);
$transaction?->touch();
}
TransactionObserver::$recalculate = true;
Log::debug(sprintf('Recalculated %d transactions.', $set->count()));
}
private function collectAccounts(UserGroup $userGroup): Collection
{
return $userGroup
->accounts()
->where(static function (EloquentBuilder $q): void {
$q->whereNotNull('virtual_balance');
@@ -113,14 +190,59 @@ class PrimaryAmountRecalculationService
})
->get()
;
}
/**
* Only recalculate accounts that have a virtual balance.
*/
private function recalculateAccounts(UserGroup $userGroup, TransactionCurrency $groupCurrency): void
{
Log::debug(sprintf('recalculateAccounts(#%d, %s)', $userGroup->id, $groupCurrency->code));
$set = $this->collectAccounts($userGroup);
/** @var Account $account */
foreach ($set as $account) {
$currencyId = (int) $account->accountMeta()->where('name', 'currency_id')->first()->data;
if ($groupCurrency->id === $currencyId) {
Log::debug(sprintf('Account "%s" is in group currency %s. Skip.', $account->name, $groupCurrency->code));
continue;
}
Log::debug(sprintf('Account "%s" is NOT in group currency %s, so do it.', $account->name, $groupCurrency->code));
$account->touch();
}
Log::debug(sprintf('Recalculated %d accounts for user group #%d.', $set->count(), $userGroup->id));
}
/**
* Only recalculate accounts that have a virtual balance.
*/
private function recalculateAccountsForCurrency(UserGroup $userGroup, TransactionCurrency $groupCurrency, TransactionCurrency $limitCurrency): void
{
Log::debug(sprintf('recalculateAccountsForCurrency(#%d, %s, %s)', $userGroup->id, $groupCurrency->code, $limitCurrency->code));
$set = $this->collectAccounts($userGroup);
/** @var Account $account */
foreach ($set as $account) {
$currencyId = (int) $account->accountMeta()->where('name', 'currency_id')->first()->data;
if ($groupCurrency->id === $currencyId) {
Log::debug(sprintf('Account "%s" is in group currency %s. Skip.', $account->name, $groupCurrency->code));
continue;
}
if ($limitCurrency->id !== $currencyId) {
Log::debug(sprintf('Account "%s" is NOT in limit currency %s, skip.', $account->name, $limitCurrency->code));
continue;
}
Log::debug(sprintf('Account "%s" is NOT in group currency %s, so do it.', $account->name, $groupCurrency->code));
// TODO it is bad form to call an event from an event but OK.
event(new UpdatedExistingAccount($account, []));
}
Log::debug(sprintf('Recalculated %d accounts for user group #%d.', $set->count(), $userGroup->id));
}
private function recalculateAutoBudgets(Budget $budget, TransactionCurrency $currency): void
{
$set = $budget->autoBudgets()->where('transaction_currency_id', '!=', $currency->id)->get();
@@ -180,21 +302,6 @@ class PrimaryAmountRecalculationService
Log::debug(sprintf('Recalculated %d budgets.', $set->count()));
}
private function recalculateForGroup(UserGroup $userGroup): void
{
Log::debug(sprintf('Now recalculating primary amounts for user group #%d', $userGroup->id));
$this->recalculateAccounts($userGroup);
// do a check with the group's currency so we can skip some stuff.
$currency = Amount::getPrimaryCurrencyByUserGroup($userGroup);
$this->recalculatePiggyBanks($userGroup, $currency);
$this->recalculateBudgets($userGroup, $currency);
$this->recalculateAvailableBudgets($userGroup, $currency);
$this->recalculateBills($userGroup, $currency);
$this->calculateTransactions($userGroup, $currency);
}
private function recalculatePiggyBankEvents(PiggyBank $piggyBank): void
{
$set = $piggyBank->piggyBankEvents()->get();
@@ -204,6 +311,9 @@ class PrimaryAmountRecalculationService
Log::debug(sprintf('Recalculated %d piggy bank events.', $set->count()));
}
/**
* This method collects ALL piggy banks, but only processes those that do not have the userGroup's primary currency.
*/
private function recalculatePiggyBanks(UserGroup $userGroup, TransactionCurrency $currency): void
{
$converter = new ExchangeRateConverter();
@@ -233,4 +343,104 @@ class PrimaryAmountRecalculationService
}
Log::debug(sprintf('Recalculated %d piggy banks for user group #%d.', $set->count(), $userGroup->id));
}
private function resetBudget(Budget $budget): void
{
foreach ($budget->autoBudgets as $autoBudget) {
if ('' === (string) $autoBudget->native_amount) {
continue;
}
Log::debug(sprintf('Resetting native_amount for budget #%d and auto budget #%d.', $budget->id, $autoBudget->id));
$autoBudget->native_amount = null;
$autoBudget->saveQuietly();
}
foreach ($budget->budgetlimits as $limit) {
if ('' !== (string) $limit->native_amount) {
Log::debug(sprintf('Resetting native_amount for budget #%d and budget limit #%d.', $budget->id, $limit->id));
$limit->native_amount = null;
$limit->saveQuietly();
}
}
}
private function resetBudgets(UserGroup $userGroup): void
{
$repository = app(BudgetRepositoryInterface::class);
$repository->setUserGroup($userGroup);
$set = $repository->getBudgets();
Log::debug(sprintf('Reset primary currency of %d budget(s).', $set->count()));
/** @var Budget $budget */
foreach ($set as $budget) {
$this->resetBudget($budget);
}
}
private function resetGenericTables(UserGroup $userGroup): void
{
$tables = [
// !!! this array is also in the migration
'accounts' => ['native_virtual_balance'],
'available_budgets' => ['native_amount'],
'bills' => ['native_amount_min', 'native_amount_max'],
];
foreach ($tables as $table => $columns) {
Log::debug(sprintf('Now processing table "%s"', $table));
foreach ($columns as $column) {
Log::debug(sprintf('Resetting column "%s" in table "%s".', $column, $table));
DB::table($table)->where('user_group_id', $userGroup->id)->update([$column => null]);
}
}
}
private function resetPiggyBank(PiggyBank $piggyBank): void
{
if ('' !== (string) $piggyBank->native_target_amount) {
Log::debug(sprintf('Resetting native_target_amount for piggy bank #%d.', $piggyBank->id));
$piggyBank->native_target_amount = null;
$piggyBank->saveQuietly();
}
foreach ($piggyBank->accounts as $account) {
if ('' !== (string) $account->pivot->native_current_amount) {
Log::debug(sprintf('Resetting native_current_amount for piggy bank #%d and account #%d.', $piggyBank->id, $account->id));
$account->pivot->native_current_amount = null;
$account->pivot->save();
}
}
foreach ($piggyBank->piggyBankEvents as $event) {
if ('' !== (string) $event->native_amount) {
Log::debug(sprintf('Resetting native_amount for piggy bank #%d and event #%d.', $piggyBank->id, $event->id));
$event->native_amount = null;
$event->saveQuietly();
}
}
}
private function resetPiggyBanks(UserGroup $userGroup): void
{
$repository = app(PiggyBankRepositoryInterface::class);
$repository->setUserGroup($userGroup);
$piggyBanks = $repository->getPiggyBanks();
Log::debug(sprintf('Reset primary currency of %d piggy bank(s).', $piggyBanks->count()));
/** @var PiggyBank $piggyBank */
foreach ($piggyBanks as $piggyBank) {
$this->resetPiggyBank($piggyBank);
}
}
private function resetTransactions(UserGroup $userGroup): void
{
// custom query because of the potential size of this update.
$success = DB::table('transactions')
->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.user_group_id', $userGroup->id)
->where(static function (Builder $q): void {
$q->whereNotNull('native_amount')->orWhereNotNull('native_foreign_amount');
})
->update(['native_amount' => null, 'native_foreign_amount' => null])
;
Log::debug(sprintf('Reset %d transactions.', $success));
}
}

View File

@@ -695,6 +695,7 @@ class JournalUpdateService
$source = $this->getSourceTransaction();
$dest = $this->getDestinationTransaction();
$foreignCurrency = $source->foreignCurrency;
$oldForeignCurrency = $foreignCurrency;
$originalSourceAmount = $source->foreign_amount;
// find currency in data array
@@ -773,11 +774,15 @@ class JournalUpdateService
$this->transactionJournal,
'update_foreign_amount',
[
'currency_symbol' => $recordCurrency->symbol,
'decimal_places' => $recordCurrency->decimal_places,
'currency_symbol' => $oldForeignCurrency->symbol,
'decimal_places' => $oldForeignCurrency->decimal_places,
'amount' => $originalSourceAmount,
],
['currency_symbol' => $recordCurrency->symbol, 'decimal_places' => $recordCurrency->decimal_places, 'amount' => $value]
[
'currency_symbol' => $recordCurrency->symbol,
'decimal_places' => $recordCurrency->decimal_places,
'amount' => $value,
]
)
);
}

View File

@@ -225,21 +225,27 @@ class Amount
*/
public function getAmountFromJournalObject(TransactionJournal $journal): string
{
// Log::debug(sprintf('Get amount from journal #%d', $journal->id));
$convertToPrimary = $this->convertToPrimary();
$currency = $this->getPrimaryCurrency();
$field = $convertToPrimary && $currency->id !== $journal->transaction_currency_id ? 'pc_amount' : 'amount';
$field = $convertToPrimary && $currency->id !== $journal->transaction_currency_id ? 'native_amount' : 'amount';
/** @var null|Transaction $sourceTransaction */
$sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first();
if (null === $sourceTransaction) {
// Log::debug('Return zero!');
return '0';
}
$amount = $sourceTransaction->{$field} ?? '0';
// Log::debug(sprintf('Amount is %s', $amount));
if ((int) $sourceTransaction->foreign_currency_id === $currency->id) {
// use foreign amount instead!
$amount = (string) $sourceTransaction->foreign_amount; // hard coded to be foreign amount.
// Log::debug(sprintf('Amount is now %s', $amount));
}
// Log::debug(sprintf('Final return is %s', $amount));
return $amount;
}

View File

@@ -171,7 +171,7 @@ class FrontpageChartGenerator
$direction = $array['sum_float'] < 0 ? 'spent' : 'earned';
$key = sprintf('%s-%d', $direction, $array['currency_id']);
$category = $array['name'];
$amount = $array['sum_float'] < 0 ? $array['sum_float'] * -1 : $array['sum_float'];
$amount = $array['sum_float'] < 0 ? -$array['sum_float'] : $array['sum_float'];
$currencyData[$key]['entries'][$category] = $amount;
}

View File

@@ -59,9 +59,9 @@ use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use League\Csv\AbstractCsv;
use League\Csv\CannotInsertRecord;
use League\Csv\Exception;
use League\Csv\Writer;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
@@ -284,7 +284,7 @@ class ExportDataGenerator
}
// load the CSV document from a string
$csv = AbstractCsv::fromString();
$csv = Writer::fromString();
// insert the header
try {
@@ -353,7 +353,7 @@ class ExportDataGenerator
}
// load the CSV document from a string
$csv = AbstractCsv::fromString();
$csv = Writer::fromString();
// insert the header
try {
@@ -412,7 +412,7 @@ class ExportDataGenerator
}
// load the CSV document from a string
$csv = AbstractCsv::fromString();
$csv = Writer::fromString();
// insert the header
try {
@@ -457,7 +457,7 @@ class ExportDataGenerator
}
// load the CSV document from a string
$csv = AbstractCsv::fromString();
$csv = Writer::fromString();
// insert the header
try {
@@ -537,7 +537,7 @@ class ExportDataGenerator
}
// load the CSV document from a string
$csv = AbstractCsv::fromString();
$csv = Writer::fromString();
// insert the header
try {
@@ -704,7 +704,7 @@ class ExportDataGenerator
}
}
// load the CSV document from a string
$csv = AbstractCsv::fromString();
$csv = Writer::fromString();
// insert the header
try {
@@ -851,7 +851,7 @@ class ExportDataGenerator
}
// load the CSV document from a string
$csv = AbstractCsv::fromString();
$csv = Writer::fromString();
// insert the header
try {
@@ -883,7 +883,7 @@ class ExportDataGenerator
*/
private function exportTags(): string
{
$header = ['user_id', 'tag_id', 'created_at', 'updated_at', 'tag', 'date', 'description', 'latitude', 'longitude', 'zoom_level'];
$header = ['user_id', 'tag_id', 'created_at', 'updated_at', 'tag', 'date', 'description']; // 'latitude', 'longitude', 'zoom_level'
$tagRepos = app(TagRepositoryInterface::class);
$tagRepos->setUser($this->user);
@@ -900,14 +900,14 @@ class ExportDataGenerator
$tag->tag,
$tag->date?->format('Y-m-d'),
$tag->description,
$tag->latitude,
$tag->longitude,
$tag->zoomLevel,
// $tag->latitude,
// $tag->longitude,
// $tag->zoomLevel,
];
}
// load the CSV document from a string
$csv = AbstractCsv::fromString();
$csv = Writer::fromString();
// insert the header
try {
@@ -1103,7 +1103,7 @@ class ExportDataGenerator
}
// load the CSV document from a string
$csv = AbstractCsv::fromString();
$csv = Writer::fromString();
// insert the header
try {

View File

@@ -235,7 +235,12 @@ trait ConvertsDataTypes
*/
public function stringWithNewlines(string $field): string
{
return (string) $this->clearStringKeepNewlines((string) ($this->get($field) ?? ''));
$entry = $this->get($field);
if (!is_scalar($entry)) {
return '';
}
return (string) $this->clearStringKeepNewlines((string) $entry);
}
/**

View File

@@ -81,7 +81,7 @@ class AccountTransformer extends AbstractTransformer
}
// no order for some accounts:
if (!in_array(strtolower($accountType), ['liability', 'liabilities', 'asset'], true)) {
if (!in_array(strtolower($accountType), ['liability', 'liabilities', 'asset'], strict: true)) {
$order = null;
}

View File

@@ -524,7 +524,7 @@ class User extends Authenticatable
$userGroup->id,
$userGroup->title
));
if (in_array($membership->userRole->title, $dbRolesTitles, true)) {
if (in_array($membership->userRole->title, $dbRolesTitles, strict: true)) {
Log::debug(sprintf('Return true, found role "%s"', $membership->userRole->title));
return true;

View File

@@ -29,6 +29,7 @@ use FireflyIII\Http\Middleware\EncryptCookies;
use FireflyIII\Http\Middleware\Installer;
use FireflyIII\Http\Middleware\InterestingMessage;
use FireflyIII\Http\Middleware\IsAdmin;
use FireflyIII\Http\Middleware\IsAdminApi;
use FireflyIII\Http\Middleware\Range;
use FireflyIII\Http\Middleware\RedirectIfAuthenticated;
use FireflyIII\Http\Middleware\SecureHeaders;
@@ -157,7 +158,7 @@ $app = Application::configure(basePath: dirname(__DIR__))
// This middleware is added to ensure that the user is not only logged in and
// authenticated (with MFA and everything), but also admin.
$middleware->appendToGroup('api-admin', [
IsAdmin::class,
IsAdminApi::class,
]);
$middleware->appendToGroup('admin', [
IsAdmin::class,

View File

@@ -3,6 +3,33 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## v6.5.8 - 2026-03-22
<!-- summary: This release fixes a regression bug in user registration. -->
### Fixed
- [Issue 11995](https://github.com/firefly-iii/firefly-iii/issues/11995) (User registration breaks on password validation) reported by @mikaelhm
## v6.5.7 - 2026-03-21
<!-- summary: There is a new security policy for AI-generated security advisories and of course, some interesting but annoying bugs fixed. -->
### Fixed
- [Issue 11964](https://github.com/firefly-iii/firefly-iii/issues/11964) ("Left to spend" is not taking into account non-main currency withdrawals (when displaying in primary currency)) reported by @absdjfh
- [Issue 11966](https://github.com/firefly-iii/firefly-iii/issues/11966) (Error when trying to export data in CSV (Export data via front end)) reported by @jgmm81
- [Issue 11969](https://github.com/firefly-iii/firefly-iii/issues/11969) (Problem found when editing a multi-currency record, as well as details in the "Audit log entries") reported by @jgmm81
- [PR 11974](https://github.com/firefly-iii/firefly-iii/pull/11974) (Fix typo in SMTP server comment in .env.example) reported by @NorskNoobing
- [Discussion 11977](https://github.com/orgs/firefly-iii/discussions/11977) (CSP header `form-action 'self'` prevents form submission because it's a redirect) started by @superrio0187
- [Issue 11978](https://github.com/firefly-iii/firefly-iii/issues/11978) (Tags not associated with any record display incorrect information) reported by @jgmm81
- [Issue 11982](https://github.com/firefly-iii/firefly-iii/issues/11982) (Foreign currency account value in primary currency does not update after changing exchange rates) reported by @gattacus
- Remove old `zoomLevel` / `zoom_level` database references for tags, since they are no longer queries anyway.
### API
- [Issue 11976](https://github.com/firefly-iii/firefly-iii/issues/11976) (New lines are removed from rule description when created using API POST) reported by @AlexRNL
## v6.5.6 - 2026-03-16
<!-- summary: This release takes note of some security issues, and fixes interesting bugs. -->

299
composer.lock generated
View File

@@ -1879,16 +1879,16 @@
},
{
"name": "laravel/framework",
"version": "v12.54.1",
"version": "v12.55.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "325497463e7599cd14224c422c6e5dd2fe832868"
"reference": "6d9185a248d101b07eecaf8fd60b18129545fd33"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/325497463e7599cd14224c422c6e5dd2fe832868",
"reference": "325497463e7599cd14224c422c6e5dd2fe832868",
"url": "https://api.github.com/repos/laravel/framework/zipball/6d9185a248d101b07eecaf8fd60b18129545fd33",
"reference": "6d9185a248d101b07eecaf8fd60b18129545fd33",
"shasum": ""
},
"require": {
@@ -2004,7 +2004,7 @@
"orchestra/testbench-core": "^10.9.0",
"pda/pheanstalk": "^5.0.6|^7.0.0",
"php-http/discovery": "^1.15",
"phpstan/phpstan": "^2.0",
"phpstan/phpstan": "^2.1.41",
"phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1",
"predis/predis": "^2.3|^3.0",
"resend/resend-php": "^0.10.0|^1.0",
@@ -2097,7 +2097,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2026-03-10T20:25:56+00:00"
"time": "2026-03-18T14:28:59+00:00"
},
{
"name": "laravel/passport",
@@ -2177,16 +2177,16 @@
},
{
"name": "laravel/prompts",
"version": "v0.3.14",
"version": "v0.3.15",
"source": {
"type": "git",
"url": "https://github.com/laravel/prompts.git",
"reference": "9f0e371244eedfe2ebeaa72c79c54bb5df6e0176"
"reference": "4bb8107ec97651fd3f17f897d6489dbc4d8fb999"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/prompts/zipball/9f0e371244eedfe2ebeaa72c79c54bb5df6e0176",
"reference": "9f0e371244eedfe2ebeaa72c79c54bb5df6e0176",
"url": "https://api.github.com/repos/laravel/prompts/zipball/4bb8107ec97651fd3f17f897d6489dbc4d8fb999",
"reference": "4bb8107ec97651fd3f17f897d6489dbc4d8fb999",
"shasum": ""
},
"require": {
@@ -2230,9 +2230,9 @@
"description": "Add beautiful and user-friendly forms to your command-line applications.",
"support": {
"issues": "https://github.com/laravel/prompts/issues",
"source": "https://github.com/laravel/prompts/tree/v0.3.14"
"source": "https://github.com/laravel/prompts/tree/v0.3.15"
},
"time": "2026-03-01T09:02:38+00:00"
"time": "2026-03-17T13:45:17+00:00"
},
{
"name": "laravel/serializable-closure",
@@ -2297,30 +2297,30 @@
},
{
"name": "laravel/slack-notification-channel",
"version": "v3.7.0",
"version": "v3.8.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/slack-notification-channel.git",
"reference": "414aec57b487bfbac7f90fc30f50a2f0a2df4caa"
"reference": "3c27f6e851570274255809f4d3939ff5c2ace82b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/slack-notification-channel/zipball/414aec57b487bfbac7f90fc30f50a2f0a2df4caa",
"reference": "414aec57b487bfbac7f90fc30f50a2f0a2df4caa",
"url": "https://api.github.com/repos/laravel/slack-notification-channel/zipball/3c27f6e851570274255809f4d3939ff5c2ace82b",
"reference": "3c27f6e851570274255809f4d3939ff5c2ace82b",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^7.0",
"illuminate/http": "^9.0|^10.0|^11.0|^12.0",
"illuminate/notifications": "^9.0|^10.0|^11.0|^12.0",
"illuminate/support": "^9.0|^10.0|^11.0|^12.0",
"illuminate/http": "^9.0|^10.0|^11.0|^12.0|^13.0",
"illuminate/notifications": "^9.0|^10.0|^11.0|^12.0|^13.0",
"illuminate/support": "^9.0|^10.0|^11.0|^12.0|^13.0",
"php": "^8.0"
},
"require-dev": {
"mockery/mockery": "^1.0",
"orchestra/testbench": "^7.0|^8.0|^9.0|^10.0",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.0|^10.4|^11.5"
"orchestra/testbench": "^7.0|^8.0|^9.0|^10.0|^11.0",
"phpstan/phpstan": "^1.10|^2.0",
"phpunit/phpunit": "^9.0|^10.4|^11.5|^12.0|^13.0"
},
"type": "library",
"extra": {
@@ -2356,35 +2356,35 @@
],
"support": {
"issues": "https://github.com/laravel/slack-notification-channel/issues",
"source": "https://github.com/laravel/slack-notification-channel/tree/v3.7.0"
"source": "https://github.com/laravel/slack-notification-channel/tree/v3.8.0"
},
"time": "2025-11-20T17:26:07+00:00"
"time": "2026-03-17T16:40:49+00:00"
},
{
"name": "laravel/ui",
"version": "v4.6.2",
"version": "v4.6.3",
"source": {
"type": "git",
"url": "https://github.com/laravel/ui.git",
"reference": "4acfa331aa073f169a22d87851dc51eb2f7ac6be"
"reference": "ff27db15416c1ed8ad9848f5692e47595dd5de27"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/ui/zipball/4acfa331aa073f169a22d87851dc51eb2f7ac6be",
"reference": "4acfa331aa073f169a22d87851dc51eb2f7ac6be",
"url": "https://api.github.com/repos/laravel/ui/zipball/ff27db15416c1ed8ad9848f5692e47595dd5de27",
"reference": "ff27db15416c1ed8ad9848f5692e47595dd5de27",
"shasum": ""
},
"require": {
"illuminate/console": "^9.21|^10.0|^11.0|^12.0",
"illuminate/filesystem": "^9.21|^10.0|^11.0|^12.0",
"illuminate/support": "^9.21|^10.0|^11.0|^12.0",
"illuminate/validation": "^9.21|^10.0|^11.0|^12.0",
"illuminate/console": "^9.21|^10.0|^11.0|^12.0|^13.0",
"illuminate/filesystem": "^9.21|^10.0|^11.0|^12.0|^13.0",
"illuminate/support": "^9.21|^10.0|^11.0|^12.0|^13.0",
"illuminate/validation": "^9.21|^10.0|^11.0|^12.0|^13.0",
"php": "^8.0",
"symfony/console": "^6.0|^7.0"
"symfony/console": "^6.0|^7.0|^8.0"
},
"require-dev": {
"orchestra/testbench": "^7.35|^8.15|^9.0|^10.0",
"phpunit/phpunit": "^9.3|^10.4|^11.5"
"orchestra/testbench": "^7.35|^8.15|^9.0|^10.0|^11.0",
"phpunit/phpunit": "^9.3|^10.4|^11.5|^12.5|^13.0"
},
"type": "library",
"extra": {
@@ -2419,9 +2419,9 @@
"ui"
],
"support": {
"source": "https://github.com/laravel/ui/tree/v4.6.2"
"source": "https://github.com/laravel/ui/tree/v4.6.3"
},
"time": "2026-03-10T20:00:50+00:00"
"time": "2026-03-17T13:41:52+00:00"
},
{
"name": "lcobucci/clock",
@@ -2562,16 +2562,16 @@
},
{
"name": "league/commonmark",
"version": "2.8.1",
"version": "2.8.2",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
"reference": "84b1ca48347efdbe775426f108622a42735a6579"
"reference": "59fb075d2101740c337c7216e3f32b36c204218b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/84b1ca48347efdbe775426f108622a42735a6579",
"reference": "84b1ca48347efdbe775426f108622a42735a6579",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/59fb075d2101740c337c7216e3f32b36c204218b",
"reference": "59fb075d2101740c337c7216e3f32b36c204218b",
"shasum": ""
},
"require": {
@@ -2665,7 +2665,7 @@
"type": "tidelift"
}
],
"time": "2026-03-05T21:37:03+00:00"
"time": "2026-03-19T13:16:38+00:00"
},
{
"name": "league/config",
@@ -3424,29 +3424,29 @@
},
{
"name": "mailersend/laravel-driver",
"version": "v3.0.0",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/mailersend/mailersend-laravel-driver.git",
"reference": "cbda1bdcbd35e54f27a329df3c011389636eb783"
"reference": "9452f4f7e3881494bc55f3521b960b1c233a4be1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mailersend/mailersend-laravel-driver/zipball/cbda1bdcbd35e54f27a329df3c011389636eb783",
"reference": "cbda1bdcbd35e54f27a329df3c011389636eb783",
"url": "https://api.github.com/repos/mailersend/mailersend-laravel-driver/zipball/9452f4f7e3881494bc55f3521b960b1c233a4be1",
"reference": "9452f4f7e3881494bc55f3521b960b1c233a4be1",
"shasum": ""
},
"require": {
"ext-json": "*",
"illuminate/support": "^10.0 || ^11.0 || ^12.0",
"illuminate/support": "^10.0 || ^11.0 || ^12.0 || ^13.0",
"mailersend/mailersend": "^0.36.0",
"nyholm/psr7": "^1.5",
"php": ">=8.2",
"php-http/guzzle7-adapter": "^1.0",
"symfony/mailer": "^6.0 || ^7.0"
"symfony/mailer": "^6.0 || ^7.0 || ^8.0"
},
"require-dev": {
"orchestra/testbench": "^9.0 || ^10.0",
"orchestra/testbench": "^9.0 || ^10.0 || ^11.0",
"phpunit/phpunit": "^9.0 || ^10.5 || ^12.0"
},
"type": "library",
@@ -3487,9 +3487,9 @@
],
"support": {
"issues": "https://github.com/mailersend/mailersend-laravel-driver/issues",
"source": "https://github.com/mailersend/mailersend-laravel-driver/tree/v3.0.0"
"source": "https://github.com/mailersend/mailersend-laravel-driver/tree/v3.1.0"
},
"time": "2026-02-05T12:32:37+00:00"
"time": "2026-03-18T15:44:07+00:00"
},
{
"name": "mailersend/mailersend",
@@ -4763,16 +4763,16 @@
},
{
"name": "phpseclib/phpseclib",
"version": "3.0.49",
"version": "3.0.50",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "6233a1e12584754e6b5daa69fe1289b47775c1b9"
"reference": "aa6ad8321ed103dc3624fb600a25b66ebf78ec7b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/6233a1e12584754e6b5daa69fe1289b47775c1b9",
"reference": "6233a1e12584754e6b5daa69fe1289b47775c1b9",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/aa6ad8321ed103dc3624fb600a25b66ebf78ec7b",
"reference": "aa6ad8321ed103dc3624fb600a25b66ebf78ec7b",
"shasum": ""
},
"require": {
@@ -4853,7 +4853,7 @@
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.49"
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.50"
},
"funding": [
{
@@ -4869,7 +4869,7 @@
"type": "tidelift"
}
],
"time": "2026-01-27T09:17:28+00:00"
"time": "2026-03-19T02:57:58+00:00"
},
{
"name": "pragmarx/google2fa",
@@ -5781,27 +5781,27 @@
},
{
"name": "rcrowe/twigbridge",
"version": "v0.14.6",
"version": "v0.14.7",
"source": {
"type": "git",
"url": "https://github.com/rcrowe/TwigBridge.git",
"reference": "0798ee4b5e5b943d0200850acaa87ccd82e2fe45"
"reference": "03a767c8d5c1d74d5f14e9fc754619a271822663"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/0798ee4b5e5b943d0200850acaa87ccd82e2fe45",
"reference": "0798ee4b5e5b943d0200850acaa87ccd82e2fe45",
"url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/03a767c8d5c1d74d5f14e9fc754619a271822663",
"reference": "03a767c8d5c1d74d5f14e9fc754619a271822663",
"shasum": ""
},
"require": {
"illuminate/support": "^9|^10|^11|^12",
"illuminate/view": "^9|^10|^11|^12",
"illuminate/support": "^9|^10|^11|^12|^13",
"illuminate/view": "^9|^10|^11|^12|^13",
"php": "^8.1",
"twig/twig": "~3.21"
},
"require-dev": {
"ext-json": "*",
"laravel/framework": "^9|^10|^11|^12",
"laravel/framework": "^9|^10|^11|^12|^13",
"mockery/mockery": "^1.3.1",
"phpunit/phpunit": "^8.5.8 || ^9.3.7 || ^10.0 || ^11.0 || ^12.0",
"squizlabs/php_codesniffer": "^3.6"
@@ -5847,9 +5847,9 @@
],
"support": {
"issues": "https://github.com/rcrowe/TwigBridge/issues",
"source": "https://github.com/rcrowe/TwigBridge/tree/v0.14.6"
"source": "https://github.com/rcrowe/TwigBridge/tree/v0.14.7"
},
"time": "2025-08-20T11:25:49+00:00"
"time": "2026-03-20T16:59:18+00:00"
},
{
"name": "spatie/backtrace",
@@ -5991,26 +5991,26 @@
},
{
"name": "spatie/flare-client-php",
"version": "1.10.1",
"version": "1.11.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/flare-client-php.git",
"reference": "bf1716eb98bd689451b071548ae9e70738dce62f"
"reference": "fb3ffb946675dba811fbde9122224db2f84daca9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/flare-client-php/zipball/bf1716eb98bd689451b071548ae9e70738dce62f",
"reference": "bf1716eb98bd689451b071548ae9e70738dce62f",
"url": "https://api.github.com/repos/spatie/flare-client-php/zipball/fb3ffb946675dba811fbde9122224db2f84daca9",
"reference": "fb3ffb946675dba811fbde9122224db2f84daca9",
"shasum": ""
},
"require": {
"illuminate/pipeline": "^8.0|^9.0|^10.0|^11.0|^12.0",
"illuminate/pipeline": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
"php": "^8.0",
"spatie/backtrace": "^1.6.1",
"symfony/http-foundation": "^5.2|^6.0|^7.0",
"symfony/mime": "^5.2|^6.0|^7.0",
"symfony/process": "^5.2|^6.0|^7.0",
"symfony/var-dumper": "^5.2|^6.0|^7.0"
"symfony/http-foundation": "^5.2|^6.0|^7.0|^8.0",
"symfony/mime": "^5.2|^6.0|^7.0|^8.0",
"symfony/process": "^5.2|^6.0|^7.0|^8.0",
"symfony/var-dumper": "^5.2|^6.0|^7.0|^8.0"
},
"require-dev": {
"dms/phpunit-arraysubset-asserts": "^0.5.0",
@@ -6048,7 +6048,7 @@
],
"support": {
"issues": "https://github.com/spatie/flare-client-php/issues",
"source": "https://github.com/spatie/flare-client-php/tree/1.10.1"
"source": "https://github.com/spatie/flare-client-php/tree/1.11.0"
},
"funding": [
{
@@ -6056,41 +6056,44 @@
"type": "github"
}
],
"time": "2025-02-14T13:42:06+00:00"
"time": "2026-03-17T08:06:16+00:00"
},
{
"name": "spatie/ignition",
"version": "1.15.1",
"version": "1.16.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/ignition.git",
"reference": "31f314153020aee5af3537e507fef892ffbf8c85"
"reference": "b59385bb7aa24dae81bcc15850ebecfda7b40838"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/ignition/zipball/31f314153020aee5af3537e507fef892ffbf8c85",
"reference": "31f314153020aee5af3537e507fef892ffbf8c85",
"url": "https://api.github.com/repos/spatie/ignition/zipball/b59385bb7aa24dae81bcc15850ebecfda7b40838",
"reference": "b59385bb7aa24dae81bcc15850ebecfda7b40838",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-mbstring": "*",
"php": "^8.0",
"spatie/error-solutions": "^1.0",
"spatie/flare-client-php": "^1.7",
"symfony/console": "^5.4|^6.0|^7.0",
"symfony/var-dumper": "^5.4|^6.0|^7.0"
"spatie/backtrace": "^1.7.1",
"spatie/error-solutions": "^1.1.2",
"spatie/flare-client-php": "^1.9",
"symfony/console": "^5.4.42|^6.0|^7.0|^8.0",
"symfony/http-foundation": "^5.4.42|^6.0|^7.0|^8.0",
"symfony/mime": "^5.4.42|^6.0|^7.0|^8.0",
"symfony/var-dumper": "^5.4.42|^6.0|^7.0|^8.0"
},
"require-dev": {
"illuminate/cache": "^9.52|^10.0|^11.0|^12.0",
"illuminate/cache": "^9.52|^10.0|^11.0|^12.0|^13.0",
"mockery/mockery": "^1.4",
"pestphp/pest": "^1.20|^2.0",
"pestphp/pest": "^1.20|^2.0|^3.0",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan-deprecation-rules": "^1.0",
"phpstan/phpstan-phpunit": "^1.0",
"psr/simple-cache-implementation": "*",
"symfony/cache": "^5.4|^6.0|^7.0",
"symfony/process": "^5.4|^6.0|^7.0",
"symfony/cache": "^5.4.38|^6.0|^7.0|^8.0",
"symfony/process": "^5.4.35|^6.0|^7.0|^8.0",
"vlucas/phpdotenv": "^5.5"
},
"suggest": {
@@ -6139,7 +6142,7 @@
"type": "github"
}
],
"time": "2025-02-21T14:31:39+00:00"
"time": "2026-03-17T10:51:08+00:00"
},
{
"name": "spatie/laravel-html",
@@ -6221,16 +6224,16 @@
},
{
"name": "spatie/laravel-ignition",
"version": "2.11.0",
"version": "2.12.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-ignition.git",
"reference": "11f38d1ff7abc583a61c96bf3c1b03610a69cccd"
"reference": "45b3b6e1e73fc161cba2149972698644b99594ee"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/11f38d1ff7abc583a61c96bf3c1b03610a69cccd",
"reference": "11f38d1ff7abc583a61c96bf3c1b03610a69cccd",
"url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/45b3b6e1e73fc161cba2149972698644b99594ee",
"reference": "45b3b6e1e73fc161cba2149972698644b99594ee",
"shasum": ""
},
"require": {
@@ -6240,7 +6243,7 @@
"illuminate/support": "^11.0|^12.0|^13.0",
"nesbot/carbon": "^2.72|^3.0",
"php": "^8.2",
"spatie/ignition": "^1.15.1",
"spatie/ignition": "^1.16",
"symfony/console": "^7.4|^8.0",
"symfony/var-dumper": "^7.4|^8.0"
},
@@ -6309,7 +6312,7 @@
"type": "github"
}
],
"time": "2026-02-22T19:14:05+00:00"
"time": "2026-03-17T12:20:04+00:00"
},
{
"name": "spatie/period",
@@ -9783,16 +9786,16 @@
},
{
"name": "twig/twig",
"version": "v3.23.0",
"version": "v3.24.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9"
"reference": "a6769aefb305efef849dc25c9fd1653358c148f0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9",
"reference": "a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/a6769aefb305efef849dc25c9fd1653358c148f0",
"reference": "a6769aefb305efef849dc25c9fd1653358c148f0",
"shasum": ""
},
"require": {
@@ -9802,7 +9805,8 @@
"symfony/polyfill-mbstring": "^1.3"
},
"require-dev": {
"phpstan/phpstan": "^2.0",
"php-cs-fixer/shim": "^3.0@stable",
"phpstan/phpstan": "^2.0@stable",
"psr/container": "^1.0|^2.0",
"symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0"
},
@@ -9846,7 +9850,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.23.0"
"source": "https://github.com/twigphp/Twig/tree/v3.24.0"
},
"funding": [
{
@@ -9858,7 +9862,7 @@
"type": "tidelift"
}
],
"time": "2026-01-23T21:00:41+00:00"
"time": "2026-03-17T21:31:11+00:00"
},
{
"name": "vlucas/phpdotenv",
@@ -10022,38 +10026,39 @@
"packages-dev": [
{
"name": "barryvdh/laravel-ide-helper",
"version": "v3.6.1",
"version": "v3.7.0",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-ide-helper.git",
"reference": "b106f7ee85f263c4f103eca49e7bf3862c2e5e75"
"reference": "ad7e37676f1ff985d55ef1b6b96a0c0a40f2609a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/b106f7ee85f263c4f103eca49e7bf3862c2e5e75",
"reference": "b106f7ee85f263c4f103eca49e7bf3862c2e5e75",
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/ad7e37676f1ff985d55ef1b6b96a0c0a40f2609a",
"reference": "ad7e37676f1ff985d55ef1b6b96a0c0a40f2609a",
"shasum": ""
},
"require": {
"barryvdh/reflection-docblock": "^2.4",
"composer/class-map-generator": "^1.0",
"ext-json": "*",
"illuminate/console": "^11.15 || ^12",
"illuminate/database": "^11.15 || ^12",
"illuminate/filesystem": "^11.15 || ^12",
"illuminate/support": "^11.15 || ^12",
"illuminate/console": "^11.15 || ^12 || ^13.0",
"illuminate/database": "^11.15 || ^12 || ^13.0",
"illuminate/filesystem": "^11.15 || ^12 || ^13.0",
"illuminate/support": "^11.15 || ^12 || ^13.0",
"php": "^8.2"
},
"require-dev": {
"ext-pdo_sqlite": "*",
"friendsofphp/php-cs-fixer": "^3",
"illuminate/config": "^11.15 || ^12",
"illuminate/view": "^11.15 || ^12",
"illuminate/config": "^11.15 || ^12 || ^13.0",
"illuminate/view": "^11.15 || ^12 || ^13.0",
"larastan/larastan": "^3.1",
"mockery/mockery": "^1.4",
"orchestra/testbench": "^9.2 || ^10",
"phpunit/phpunit": "^10.5 || ^11.5.3",
"orchestra/testbench": "^9.2 || ^10 || ^11.0",
"phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^10.5 || ^11.5.3 || ^12.5.12",
"spatie/phpunit-snapshot-assertions": "^4 || ^5",
"vimeo/psalm": "^5.4",
"vlucas/phpdotenv": "^5"
},
"suggest": {
@@ -10067,7 +10072,7 @@
]
},
"branch-alias": {
"dev-master": "3.5-dev"
"dev-master": "3.6-dev"
}
},
"autoload": {
@@ -10100,7 +10105,7 @@
],
"support": {
"issues": "https://github.com/barryvdh/laravel-ide-helper/issues",
"source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.6.1"
"source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.7.0"
},
"funding": [
{
@@ -10112,7 +10117,7 @@
"type": "github"
}
],
"time": "2025-12-10T09:11:07+00:00"
"time": "2026-03-17T14:12:51+00:00"
},
{
"name": "barryvdh/reflection-docblock",
@@ -10374,22 +10379,22 @@
},
{
"name": "driftingly/rector-laravel",
"version": "2.1.12",
"version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/driftingly/rector-laravel.git",
"reference": "2a2175eefabca6d15c247d55de17c75dc2f787a9"
"reference": "807840ceb09de6764cbfcce0719108d044a459a9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/2a2175eefabca6d15c247d55de17c75dc2f787a9",
"reference": "2a2175eefabca6d15c247d55de17c75dc2f787a9",
"url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/807840ceb09de6764cbfcce0719108d044a459a9",
"reference": "807840ceb09de6764cbfcce0719108d044a459a9",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0",
"rector/rector": "^2.2.7",
"webmozart/assert": "^1.11"
"webmozart/assert": "^1.11 || ^2.0"
},
"type": "rector-extension",
"autoload": {
@@ -10404,9 +10409,9 @@
"description": "Rector upgrades rules for Laravel Framework",
"support": {
"issues": "https://github.com/driftingly/rector-laravel/issues",
"source": "https://github.com/driftingly/rector-laravel/tree/2.1.12"
"source": "https://github.com/driftingly/rector-laravel/tree/2.2.0"
},
"time": "2026-03-06T19:59:21+00:00"
"time": "2026-03-19T17:24:38+00:00"
},
{
"name": "ergebnis/phpstan-rules",
@@ -11430,11 +11435,11 @@
},
{
"name": "phpstan/phpstan",
"version": "2.1.40",
"version": "2.1.42",
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b",
"reference": "9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/1279e1ce86ba768f0780c9d889852b4e02ff40d0",
"reference": "1279e1ce86ba768f0780c9d889852b4e02ff40d0",
"shasum": ""
},
"require": {
@@ -11479,7 +11484,7 @@
"type": "github"
}
],
"time": "2026-02-23T15:04:35+00:00"
"time": "2026-03-17T14:58:32+00:00"
},
{
"name": "phpstan/phpstan-deprecation-rules",
@@ -12036,21 +12041,21 @@
},
{
"name": "rector/rector",
"version": "2.3.8",
"version": "2.3.9",
"source": {
"type": "git",
"url": "https://github.com/rectorphp/rector.git",
"reference": "bbd37aedd8df749916cffa2a947cfc4714d1ba2c"
"reference": "917842143fd9f5331a2adefc214b8d7143bd32c4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/bbd37aedd8df749916cffa2a947cfc4714d1ba2c",
"reference": "bbd37aedd8df749916cffa2a947cfc4714d1ba2c",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/917842143fd9f5331a2adefc214b8d7143bd32c4",
"reference": "917842143fd9f5331a2adefc214b8d7143bd32c4",
"shasum": ""
},
"require": {
"php": "^7.4|^8.0",
"phpstan/phpstan": "^2.1.38"
"phpstan/phpstan": "^2.1.40"
},
"conflict": {
"rector/rector-doctrine": "*",
@@ -12084,7 +12089,7 @@
],
"support": {
"issues": "https://github.com/rectorphp/rector/issues",
"source": "https://github.com/rectorphp/rector/tree/2.3.8"
"source": "https://github.com/rectorphp/rector/tree/2.3.9"
},
"funding": [
{
@@ -12092,7 +12097,7 @@
"type": "github"
}
],
"time": "2026-02-22T09:45:50+00:00"
"time": "2026-03-16T09:43:55+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -13153,23 +13158,23 @@
},
{
"name": "webmozart/assert",
"version": "1.12.1",
"version": "2.1.6",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
"reference": "9be6926d8b485f55b9229203f962b51ed377ba68"
"reference": "ff31ad6efc62e66e518fbab1cde3453d389bcdc8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68",
"reference": "9be6926d8b485f55b9229203f962b51ed377ba68",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/ff31ad6efc62e66e518fbab1cde3453d389bcdc8",
"reference": "ff31ad6efc62e66e518fbab1cde3453d389bcdc8",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"ext-date": "*",
"ext-filter": "*",
"php": "^7.2 || ^8.0"
"php": "^8.2"
},
"suggest": {
"ext-intl": "",
@@ -13179,7 +13184,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.10-dev"
"dev-feature/2-0": "2.0-dev"
}
},
"autoload": {
@@ -13195,6 +13200,10 @@
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
},
{
"name": "Woody Gilk",
"email": "woody.gilk@gmail.com"
}
],
"description": "Assertions to validate method input/output with nice error messages.",
@@ -13205,9 +13214,9 @@
],
"support": {
"issues": "https://github.com/webmozarts/assert/issues",
"source": "https://github.com/webmozarts/assert/tree/1.12.1"
"source": "https://github.com/webmozarts/assert/tree/2.1.6"
},
"time": "2025-10-29T15:56:20+00:00"
"time": "2026-02-27T10:28:38+00:00"
}
],
"aliases": [],

View File

@@ -78,8 +78,8 @@ return [
'running_balance_column' => (bool)envDefaultWhenEmpty(env('USE_RUNNING_BALANCE'), true), // this is only the default value, is not used.
// see cer.php for exchange rates feature flag.
],
'version' => 'develop/2026-03-16',
'build_time' => 1773633747,
'version' => '6.5.8',
'build_time' => 1774090288,
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 28, // field is no longer used.

View File

@@ -24,18 +24,12 @@ integrations = ["symfony", "laravel", "phpunit"]
excludes = ["app/Providers/AppServiceProvider.php"] # Additionally excluded from linter only
[linter.rules]
ambiguous-function-call = { enabled = false }
literal-named-argument = { enabled = false }
halstead = { effort-threshold = 7000, enabled = false }
prefer-early-continue = { enabled = false }
cyclomatic-complexity = { enabled = false }
middleware-in-routes = { enabled = false }
too-many-methods = { enabled = false }
kan-defect = { enabled = false }
no-request-all = { enabled = false }
tagged-todo = { enabled = false }
too-many-properties = { enabled = false }
prefer-view-array = { enabled = false }
halstead = { effort-threshold = 250000, volume-threshold = 200000, difficulty-threshold = 1000, enabled = true } # way too high
cyclomatic-complexity = { enabled = true, threshold = 255 } # way too high
kan-defect = { enabled = true, threshold = 20 } # way too high
too-many-properties = { enabled = true, threshold = 25 } # way too high
no-request-all = { enabled = true }
prefer-view-array = { enabled = false } # to be continued.
assertion-style = { enabled = false }
no-boolean-flag-parameter = { enabled = false }
prefer-static-closure = { enabled = false }
@@ -43,9 +37,14 @@ no-literal-password = { enabled = false }
too-many-enum-cases = { enabled = false }
tagged-fixme = { enabled = false }
excessive-parameter-list = { enabled = false }
no-redundant-math={ enabled = false }
prefer-first-class-callable={ enabled = false }
prefer-arrow-function = { enabled = false }
ambiguous-function-call = { enabled = false } # very specific rule.
literal-named-argument = { enabled = false } # insanely specific.
prefer-early-continue = { enabled = false } # does NOT improve readability.
middleware-in-routes = { enabled = false } # I like to do this.
too-many-methods = { enabled = false, threshold = 100 } # arbitrary nonsense.
tagged-todo = { enabled = false } # weird rule

334
package-lock.json generated
View File

@@ -200,9 +200,9 @@
}
},
"node_modules/@babel/helper-define-polyfill-provider": {
"version": "0.6.7",
"resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.7.tgz",
"integrity": "sha512-6Fqi8MtQ/PweQ9xvux65emkLQ83uB+qAVtfHkC9UodyHMIZdxNI01HjLCLUtybElp2KY2XNE0nOgyP1E1vXw9w==",
"version": "0.6.8",
"resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz",
"integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -391,23 +391,23 @@
}
},
"node_modules/@babel/helpers": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz",
"integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==",
"version": "7.29.2",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz",
"integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.28.6",
"@babel/types": "^7.28.6"
"@babel/types": "^7.29.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz",
"integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==",
"version": "7.29.2",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
"integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1501,9 +1501,9 @@
}
},
"node_modules/@babel/preset-env": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.0.tgz",
"integrity": "sha512-fNEdfc0yi16lt6IZo2Qxk3knHVdfMYX33czNb4v8yWhemoBhibCpQK/uYHtSKIiO+p/zd3+8fYVXhQdOVV608w==",
"version": "7.29.2",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.2.tgz",
"integrity": "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1586,13 +1586,13 @@
}
},
"node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.1.tgz",
"integrity": "sha512-ENp89vM9Pw4kv/koBb5N2f9bDZsR0hpf3BdPMOg/pkS3pwO4dzNnQZVXtBbeyAadgm865DmQG2jMMLqmZXvuCw==",
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz",
"integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-define-polyfill-provider": "^0.6.7",
"@babel/helper-define-polyfill-provider": "^0.6.8",
"core-js-compat": "^3.48.0"
},
"peerDependencies": {
@@ -1625,9 +1625,9 @@
}
},
"node_modules/@babel/runtime": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
"integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
"version": "7.29.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
"integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -2620,9 +2620,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
"integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.1.tgz",
"integrity": "sha512-xB0b51TB7IfDEzAojXahmr+gfA00uYVInJGgNNkeQG6RPnCPGr7udsylFLTubuIUSRE6FkcI1NElyRt83PP5oQ==",
"cpu": [
"arm"
],
@@ -2634,9 +2634,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
"integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.1.tgz",
"integrity": "sha512-XOjPId0qwSDKHaIsdzHJtKCxX0+nH8MhBwvrNsT7tVyKmdTx1jJ4XzN5RZXCdTzMpufLb+B8llTC0D8uCrLhcw==",
"cpu": [
"arm64"
],
@@ -2648,9 +2648,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
"integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.1.tgz",
"integrity": "sha512-vQuRd28p0gQpPrS6kppd8IrWmFo42U8Pz1XLRjSZXq5zCqyMDYFABT7/sywL11mO1EL10Qhh7MVPEwkG8GiBeg==",
"cpu": [
"arm64"
],
@@ -2662,9 +2662,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
"integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.1.tgz",
"integrity": "sha512-x6VG6U29+Ivlnajrg1IHdzXeAwSoEHBFVO+CtC9Brugx6de712CUJobRUxsIA0KYrQvCmzNrMPFTT1A4CCqNTg==",
"cpu": [
"x64"
],
@@ -2676,9 +2676,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
"integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.1.tgz",
"integrity": "sha512-Sgi0Uo6t1YCHJMNO3Y8+bm+SvOanUGkoZKn/VJPwYUe2kp31X5KnXmzKd/NjW8iA3gFcfNZ64zh14uOGrIllCQ==",
"cpu": [
"arm64"
],
@@ -2690,9 +2690,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
"integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.1.tgz",
"integrity": "sha512-AM4xnwEZwukdhk7laMWfzWu9JGSVnJd+Fowt6Fd7QW1nrf3h0Hp7Qx5881M4aqrUlKBCybOxz0jofvIIfl7C5g==",
"cpu": [
"x64"
],
@@ -2704,9 +2704,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
"integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.1.tgz",
"integrity": "sha512-KUizqxpwaR2AZdAUsMWfL/C94pUu7TKpoPd88c8yFVixJ+l9hejkrwoK5Zj3wiNh65UeyryKnJyxL1b7yNqFQA==",
"cpu": [
"arm"
],
@@ -2718,9 +2718,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
"integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.1.tgz",
"integrity": "sha512-MZoQ/am77ckJtZGFAtPucgUuJWiop3m2R3lw7tC0QCcbfl4DRhQUBUkHWCkcrT3pqy5Mzv5QQgY6Dmlba6iTWg==",
"cpu": [
"arm"
],
@@ -2732,9 +2732,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
"integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.1.tgz",
"integrity": "sha512-Sez95TP6xGjkWB1608EfhCX1gdGrO5wzyN99VqzRtC17x/1bhw5VU1V0GfKUwbW/Xr1J8mSasoFoJa6Y7aGGSA==",
"cpu": [
"arm64"
],
@@ -2746,9 +2746,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
"integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.1.tgz",
"integrity": "sha512-9Cs2Seq98LWNOJzR89EGTZoiP8EkZ9UbQhBlDgfAkM6asVna1xJ04W2CLYWDN/RpUgOjtQvcv8wQVi1t5oQazA==",
"cpu": [
"arm64"
],
@@ -2760,9 +2760,9 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
"integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.1.tgz",
"integrity": "sha512-n9yqttftgFy7IrNEnHy1bOp6B4OSe8mJDiPkT7EqlM9FnKOwUMnCK62ixW0Kd9Clw0/wgvh8+SqaDXMFvw3KqQ==",
"cpu": [
"loong64"
],
@@ -2774,9 +2774,9 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-musl": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
"integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.1.tgz",
"integrity": "sha512-SfpNXDzVTqs/riak4xXcLpq5gIQWsqGWMhN1AGRQKB4qGSs4r0sEs3ervXPcE1O9RsQ5bm8Muz6zmQpQnPss1g==",
"cpu": [
"loong64"
],
@@ -2788,9 +2788,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
"integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.1.tgz",
"integrity": "sha512-LjaChED0wQnjKZU+tsmGbN+9nN1XhaWUkAlSbTdhpEseCS4a15f/Q8xC2BN4GDKRzhhLZpYtJBZr2NZhR0jvNw==",
"cpu": [
"ppc64"
],
@@ -2802,9 +2802,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-musl": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
"integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.1.tgz",
"integrity": "sha512-ojW7iTJSIs4pwB2xV6QXGwNyDctvXOivYllttuPbXguuKDX5vwpqYJsHc6D2LZzjDGHML414Tuj3LvVPe1CT1A==",
"cpu": [
"ppc64"
],
@@ -2816,9 +2816,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
"integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.1.tgz",
"integrity": "sha512-FP+Q6WTcxxvsr0wQczhSE+tOZvFPV8A/mUE6mhZYFW9/eea/y/XqAgRoLLMuE9Cz0hfX5bi7p116IWoB+P237A==",
"cpu": [
"riscv64"
],
@@ -2830,9 +2830,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
"integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.1.tgz",
"integrity": "sha512-L1uD9b/Ig8Z+rn1KttCJjwhN1FgjRMBKsPaBsDKkfUl7GfFq71pU4vWCnpOsGljycFEbkHWARZLf4lMYg3WOLw==",
"cpu": [
"riscv64"
],
@@ -2844,9 +2844,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
"integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.1.tgz",
"integrity": "sha512-EZc9NGTk/oSUzzOD4nYY4gIjteo2M3CiozX6t1IXGCOdgxJTlVu/7EdPeiqeHPSIrxkLhavqpBAUCfvC6vBOug==",
"cpu": [
"s390x"
],
@@ -2858,9 +2858,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
"integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.1.tgz",
"integrity": "sha512-NQ9KyU1Anuy59L8+HHOKM++CoUxrQWrZWXRik4BJFm+7i5NP6q/SW43xIBr80zzt+PDBJ7LeNmloQGfa0JGk0w==",
"cpu": [
"x64"
],
@@ -2872,9 +2872,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
"integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.1.tgz",
"integrity": "sha512-GZkLk2t6naywsveSFBsEb0PLU+JC9ggVjbndsbG20VPhar6D1gkMfCx4NfP9owpovBXTN+eRdqGSkDGIxPHhmQ==",
"cpu": [
"x64"
],
@@ -2886,9 +2886,9 @@
]
},
"node_modules/@rollup/rollup-openbsd-x64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
"integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.1.tgz",
"integrity": "sha512-1hjG9Jpl2KDOetr64iQd8AZAEjkDUUK5RbDkYWsViYLC1op1oNzdjMJeFiofcGhqbNTaY2kfgqowE7DILifsrA==",
"cpu": [
"x64"
],
@@ -2900,9 +2900,9 @@
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
"integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.1.tgz",
"integrity": "sha512-ARoKfflk0SiiYm3r1fmF73K/yB+PThmOwfWCk1sr7x/k9dc3uGLWuEE9if+Pw21el8MSpp3TMnG5vLNsJ/MMGQ==",
"cpu": [
"arm64"
],
@@ -2914,9 +2914,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
"integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.1.tgz",
"integrity": "sha512-oOST61G6VM45Mz2vdzWMr1s2slI7y9LqxEV5fCoWi2MDONmMvgsJVHSXxce/I2xOSZPTZ47nDPOl1tkwKWSHcw==",
"cpu": [
"arm64"
],
@@ -2928,9 +2928,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
"integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.1.tgz",
"integrity": "sha512-x5WgLi5dWpRz7WclKBGEF15LcWTh0ewrHM6Cq4A+WUbkysUMZNeqt05bwPonOQ3ihPS/WMhAZV5zB1DfnI4Sxg==",
"cpu": [
"ia32"
],
@@ -2942,9 +2942,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
"integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.1.tgz",
"integrity": "sha512-wS+zHAJRVP5zOL0e+a3V3E/NTEwM2HEvvNKoDy5Xcfs0o8lljxn+EAFPkUsxihBdmDq1JWzXmmB9cbssCPdxxw==",
"cpu": [
"x64"
],
@@ -2956,9 +2956,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
"integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.1.tgz",
"integrity": "sha512-rhHyrMeLpErT/C7BxcEsU4COHQUzHyrPYW5tOZUeUhziNtRuYxmDWvqQqzpuUt8xpOgmbKa1btGXfnA/ANVO+g==",
"cpu": [
"x64"
],
@@ -4055,14 +4055,14 @@
}
},
"node_modules/babel-plugin-polyfill-corejs2": {
"version": "0.4.16",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.16.tgz",
"integrity": "sha512-xaVwwSfebXf0ooE11BJovZYKhFjIvQo7TsyVpETuIeH2JHv0k/T6Y5j22pPTvqYqmpkxdlPAJlyJ0tfOJAoMxw==",
"version": "0.4.17",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz",
"integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.28.6",
"@babel/helper-define-polyfill-provider": "^0.6.7",
"@babel/helper-define-polyfill-provider": "^0.6.8",
"semver": "^6.3.1"
},
"peerDependencies": {
@@ -4094,13 +4094,13 @@
}
},
"node_modules/babel-plugin-polyfill-regenerator": {
"version": "0.6.7",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.7.tgz",
"integrity": "sha512-OTYbUlSwXhNgr4g6efMZgsO8//jA61P7ZbRX3iTT53VON8l+WQS8IAUEVo4a4cWknrg2W8Cj4gQhRYNCJ8GkAA==",
"version": "0.6.8",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz",
"integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-define-polyfill-provider": "^0.6.7"
"@babel/helper-define-polyfill-provider": "^0.6.8"
},
"peerDependencies": {
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
@@ -4135,9 +4135,9 @@
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
"version": "2.10.8",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz",
"integrity": "sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==",
"version": "2.10.9",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.9.tgz",
"integrity": "sha512-OZd0e2mU11ClX8+IdXe3r0dbqMEznRiT4TfbhYIbcRPZkqJ7Qwer8ij3GZAmLsRKa+II9V1v5czCkvmHH3XZBg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -4587,9 +4587,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001779",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001779.tgz",
"integrity": "sha512-U5og2PN7V4DMgF50YPNtnZJGWVLFjjsN3zb6uMT5VGYIewieDj1upwfuVNXf4Kor+89c3iCRJnSzMD5LmTvsfA==",
"version": "1.0.30001780",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz",
"integrity": "sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==",
"dev": true,
"funding": [
{
@@ -5028,9 +5028,9 @@
"license": "MIT"
},
"node_modules/core-js-compat": {
"version": "3.48.0",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.48.0.tgz",
"integrity": "sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==",
"version": "3.49.0",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz",
"integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5066,9 +5066,9 @@
}
},
"node_modules/cosmiconfig/node_modules/yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
"integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
"dev": true,
"license": "ISC",
"engines": {
@@ -5403,9 +5403,9 @@
}
},
"node_modules/cssnano/node_modules/yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
"integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
"dev": true,
"license": "ISC",
"engines": {
@@ -5792,9 +5792,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
"version": "1.5.313",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.313.tgz",
"integrity": "sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==",
"version": "1.5.321",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.321.tgz",
"integrity": "sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ==",
"dev": true,
"license": "ISC"
},
@@ -5849,9 +5849,9 @@
}
},
"node_modules/enhanced-resolve": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz",
"integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==",
"version": "5.20.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz",
"integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7143,9 +7143,9 @@
}
},
"node_modules/i18next": {
"version": "25.8.18",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.18.tgz",
"integrity": "sha512-lzY5X83BiL5AP77+9DydbrqkQHFN9hUzWGjqjLpPcp5ZOzuu1aSoKaU3xbBLSjWx9dAzW431y+d+aogxOZaKRA==",
"version": "25.9.0",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.9.0.tgz",
"integrity": "sha512-mJ4rVRNWOTkqh5xnaGR6iMFT5vEw3Y2MTJhcjinR/7u8cRv6dAfC0ofuePh5fVPxoh395p6JdrJTStCcNW66gg==",
"funding": [
{
"type": "individual",
@@ -9156,9 +9156,9 @@
}
},
"node_modules/postcss-load-config/node_modules/yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
"integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
"dev": true,
"license": "ISC",
"engines": {
@@ -10128,9 +10128,9 @@
}
},
"node_modules/rollup": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
"integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.1.tgz",
"integrity": "sha512-iZKH8BeoCwTCBTZBZWQQMreekd4mdomwdjIQ40GC1oZm6o+8PnNMIxFOiCsGMWeS8iDJ7KZcl7KwmKk/0HOQpA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10144,31 +10144,31 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.59.0",
"@rollup/rollup-android-arm64": "4.59.0",
"@rollup/rollup-darwin-arm64": "4.59.0",
"@rollup/rollup-darwin-x64": "4.59.0",
"@rollup/rollup-freebsd-arm64": "4.59.0",
"@rollup/rollup-freebsd-x64": "4.59.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
"@rollup/rollup-linux-arm-musleabihf": "4.59.0",
"@rollup/rollup-linux-arm64-gnu": "4.59.0",
"@rollup/rollup-linux-arm64-musl": "4.59.0",
"@rollup/rollup-linux-loong64-gnu": "4.59.0",
"@rollup/rollup-linux-loong64-musl": "4.59.0",
"@rollup/rollup-linux-ppc64-gnu": "4.59.0",
"@rollup/rollup-linux-ppc64-musl": "4.59.0",
"@rollup/rollup-linux-riscv64-gnu": "4.59.0",
"@rollup/rollup-linux-riscv64-musl": "4.59.0",
"@rollup/rollup-linux-s390x-gnu": "4.59.0",
"@rollup/rollup-linux-x64-gnu": "4.59.0",
"@rollup/rollup-linux-x64-musl": "4.59.0",
"@rollup/rollup-openbsd-x64": "4.59.0",
"@rollup/rollup-openharmony-arm64": "4.59.0",
"@rollup/rollup-win32-arm64-msvc": "4.59.0",
"@rollup/rollup-win32-ia32-msvc": "4.59.0",
"@rollup/rollup-win32-x64-gnu": "4.59.0",
"@rollup/rollup-win32-x64-msvc": "4.59.0",
"@rollup/rollup-android-arm-eabi": "4.59.1",
"@rollup/rollup-android-arm64": "4.59.1",
"@rollup/rollup-darwin-arm64": "4.59.1",
"@rollup/rollup-darwin-x64": "4.59.1",
"@rollup/rollup-freebsd-arm64": "4.59.1",
"@rollup/rollup-freebsd-x64": "4.59.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.59.1",
"@rollup/rollup-linux-arm-musleabihf": "4.59.1",
"@rollup/rollup-linux-arm64-gnu": "4.59.1",
"@rollup/rollup-linux-arm64-musl": "4.59.1",
"@rollup/rollup-linux-loong64-gnu": "4.59.1",
"@rollup/rollup-linux-loong64-musl": "4.59.1",
"@rollup/rollup-linux-ppc64-gnu": "4.59.1",
"@rollup/rollup-linux-ppc64-musl": "4.59.1",
"@rollup/rollup-linux-riscv64-gnu": "4.59.1",
"@rollup/rollup-linux-riscv64-musl": "4.59.1",
"@rollup/rollup-linux-s390x-gnu": "4.59.1",
"@rollup/rollup-linux-x64-gnu": "4.59.1",
"@rollup/rollup-linux-x64-musl": "4.59.1",
"@rollup/rollup-openbsd-x64": "4.59.1",
"@rollup/rollup-openharmony-arm64": "4.59.1",
"@rollup/rollup-win32-arm64-msvc": "4.59.1",
"@rollup/rollup-win32-ia32-msvc": "4.59.1",
"@rollup/rollup-win32-x64-gnu": "4.59.1",
"@rollup/rollup-win32-x64-msvc": "4.59.1",
"fsevents": "~2.3.2"
}
},
@@ -10275,9 +10275,9 @@
}
},
"node_modules/sax": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.5.0.tgz",
"integrity": "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
"integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
"dev": true,
"license": "BlueOak-1.0.0",
"engines": {
@@ -11004,9 +11004,9 @@
}
},
"node_modules/terser": {
"version": "5.46.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz",
"integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==",
"version": "5.46.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz",
"integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -12422,9 +12422,9 @@
"license": "ISC"
},
"node_modules/yaml": {
"version": "2.8.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
"integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
"integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
"license": "ISC",
"bin": {
"yaml": "bin.mjs"

View File

@@ -54,7 +54,7 @@
"update_transaction": "\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044e",
"after_update_create_another": "\u041f\u043e\u0441\u043b\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u0435\u0440\u043d\u0438\u0442\u0435\u0441\u044c \u0441\u044e\u0434\u0430, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435.",
"store_as_new": "\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u0430\u043a \u043d\u043e\u0432\u0443\u044e \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044e \u0432\u043c\u0435\u0441\u0442\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f.",
"split_title_help": "\u0415\u0441\u043b\u0438 \u0432\u044b \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0451\u043d\u043d\u0443\u044e \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044e, \u0442\u043e \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043e\u0431\u0449\u0435\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0434\u043b\u0435 \u0432\u0441\u0435\u0445 \u0435\u0451 \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0445.",
"split_title_help": "\u0415\u0441\u043b\u0438 \u0432\u044b \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0451\u043d\u043d\u0443\u044e \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044e, \u0442\u043e \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043e\u0431\u0449\u0435\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0435\u0451 \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0445.",
"none_in_select_list": "(\u043d\u0435\u0442)",
"no_piggy_bank": "(\u043d\u0435\u0442 \u043a\u043e\u043f\u0438\u043b\u043a\u0438)",
"description": "\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435",
@@ -82,9 +82,9 @@
"profile_oauth_create_client": "\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0430",
"profile_oauth_edit_client": "\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0430",
"profile_oauth_name_help": "\u0427\u0442\u043e-\u0442\u043e, \u0447\u0442\u043e \u0432\u0430\u0448\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0437\u043d\u0430\u044e\u0442, \u0438 \u0447\u0435\u043c\u0443 \u0434\u043e\u0432\u0435\u0440\u044f\u044e\u0442.",
"profile_oauth_redirect_url": "URL \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442\u0430",
"profile_oauth_redirect_url": "\u0421\u0441\u044b\u043b\u043a\u0430 \u043f\u0435\u0440\u0435\u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u0438",
"profile_oauth_clients_external_auth": "\u0415\u0441\u043b\u0438 \u0432\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u0432\u043d\u0435\u0448\u043d\u0438\u0439 \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 Authelia, \u043a\u043b\u0438\u0435\u043d\u0442\u044b OAuth \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c. \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0442\u043e\u043a\u0435\u043d\u044b \u0434\u043e\u0441\u0442\u0443\u043f\u0430.",
"profile_oauth_redirect_url_help": "URL \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430 \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.",
"profile_oauth_redirect_url_help": "\u0421\u0441\u044b\u043b\u043a\u0430 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430 \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.",
"profile_authorized_apps": "\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f",
"profile_authorized_clients": "\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u044b",
"profile_scopes": "\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f",
@@ -116,9 +116,9 @@
"webhook_trigger_DESTROY_BUDGET": "\u041f\u043e\u0441\u043b\u0435 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0431\u044e\u0434\u0436\u0435\u0442\u0430",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "\u0414\u0435\u0442\u0430\u043b\u0438 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438",
"webhook_response_RELEVANT": "Relevant details",
"webhook_response_RELEVANT": "\u0420\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u044b\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0435 \u0441\u0432\u0435\u0434\u0435\u043d\u0438\u044f",
"webhook_response_ACCOUNTS": "\u0421\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u043e\u0431 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438",
"webhook_response_NONE": "No details",
"webhook_response_NONE": "\u041d\u0435\u0442 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0445 \u0441\u0432\u0435\u0434\u0435\u043d\u0438\u0439",
"webhook_delivery_JSON": "JSON",
"actions": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044f",
"meta_data": "\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435",
@@ -128,7 +128,7 @@
"inspect": "\u041f\u0440\u043e\u0438\u043d\u0441\u043f\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c",
"create_new_webhook": "\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0432\u0435\u0431\u0445\u0443\u043a",
"webhooks": "\u0412\u0435\u0431-\u0445\u0443\u043a\u0438",
"webhook_trigger_form_help": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435, \u043d\u0430 \u043a\u0430\u043a\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u0445 \u0431\u0443\u0434\u0435\u0442 \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0432\u0435\u0431-\u0445\u0443\u043a",
"webhook_trigger_form_help": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435, \u043d\u0430 \u043a\u0430\u043a\u0438\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0432\u0435\u0431-\u0445\u0443\u043a",
"webhook_response_form_help": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435, \u0447\u0442\u043e \u0432\u0435\u0431-\u0445\u0443\u043a \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043d\u0430 URL.",
"webhook_delivery_form_help": "\u0412 \u043a\u0430\u043a\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u044b \u0434\u0430\u043d\u043d\u044b\u0435 \u0432\u0435\u0431-\u0445\u0443\u043a\u0430.",
"webhook_active_form_help": "\u0412\u0435\u0431-\u0445\u0443\u043a \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u043c \u0438\u043b\u0438 \u043e\u043d \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d.",
@@ -160,7 +160,7 @@
"url": "\u0421\u0441\u044b\u043b\u043a\u0430",
"active": "\u0410\u043a\u0442\u0438\u0432\u043d\u044b\u0439",
"interest_date": "\u0414\u0430\u0442\u0430 \u043d\u0430\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u043e\u0432",
"administration_currency": "Primary currency",
"administration_currency": "\u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0432\u0430\u043b\u044e\u0442\u0430",
"title": "\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a",
"date": "\u0414\u0430\u0442\u0430",
"book_date": "\u0414\u0430\u0442\u0430 \u0431\u0440\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f",
@@ -180,7 +180,7 @@
"list": {
"title": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435",
"active": "\u0410\u043a\u0442\u0438\u0432\u0435\u043d?",
"primary_currency": "Primary currency",
"primary_currency": "\u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0432\u0430\u043b\u044e\u0442\u0430",
"trigger": "\u0421\u043e\u0431\u044b\u0442\u0438\u0435",
"response": "\u041e\u0442\u0432\u0435\u0442",
"delivery": "\u0414\u043e\u0441\u0442\u0430\u0432\u043a\u0430",

View File

@@ -118,7 +118,7 @@
"webhook_response_TRANSACTIONS": "\u0414\u0435\u0442\u0430\u043b\u0456 \u043e\u043f\u0435\u0440\u0430\u0446\u0456\u0457",
"webhook_response_RELEVANT": "Relevant details",
"webhook_response_ACCOUNTS": "\u0414\u0430\u043d\u0456 \u0440\u0430\u0445\u0443\u043d\u043a\u0443",
"webhook_response_NONE": "No details",
"webhook_response_NONE": "\u041d\u0435\u043c\u0430\u0454 \u0434\u0435\u0442\u0430\u043b\u0435\u0439",
"webhook_delivery_JSON": "JSON",
"actions": "\u0414\u0456\u0457",
"meta_data": "\u041c\u0435\u0442\u0430-\u0434\u0430\u043d\u0456",

View File

@@ -20,6 +20,14 @@
export default class GenericObjectRenderer {
renderUrl(url, title, text) {
return `<a href="${url}" title="${title}">${text}</a>`;
return `<a href="${url}" title="${this.escapeHtml(title)}">${this.escapeHtml(text)}</a>`;
}
escapeHtml(unsafe) {
return unsafe
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#039;");
};
}

View File

@@ -23,8 +23,6 @@ import laravel from 'laravel-vite-plugin';
import manifestSRI from 'vite-plugin-manifest-sri';
import * as fs from "fs";
const host = '127.0.0.1';
function manualChunks(id) {
if (id.includes('node_modules')) {
return 'vendor';
@@ -84,7 +82,7 @@ export default defineConfig(({command, mode, isSsrBuild, isPreview}) => {
server: {
cors: true,
// make sure this IP matches the IP of the dev machine.
origin: 'https://192.168.96.162:5173',
origin: 'https://192.168.96.165:5173',
watch: {
usePolling: true,
},

View File

@@ -636,12 +636,9 @@ Route::group(
],
static function (): void {
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']);
Route::post('{currency_code}/enable', ['uses' => 'UpdateController@enable', 'as' => 'enable']);
Route::post('{currency_code}/disable', ['uses' => 'UpdateController@disable', 'as' => 'disable']);
@@ -658,6 +655,22 @@ Route::group(
}
);
// Transaction currency API routes that require admin rights:
Route::group(
[
'namespace' => 'FireflyIII\Api\V1\Controllers\Models\TransactionCurrency',
'prefix' => 'v1/currencies',
'as' => 'api.v1.currencies.',
'middleware' => ['api-admin'],
],
static function (): void {
Route::delete('{currency_code}', ['uses' => 'DestroyController@destroy', 'as' => 'delete']);
Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']);
Route::put('{currency_code?}', ['uses' => 'UpdateController@update', 'as' => 'update']);
}
);
// Transaction Links API routes:
Route::group(
[
@@ -683,11 +696,23 @@ Route::group(
],
static function (): void {
Route::get('', ['uses' => 'ShowController@index', 'as' => 'index']);
Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']);
Route::get('{linkType}', ['uses' => 'ShowController@show', 'as' => 'show']);
Route::get('{linkType}/transactions', ['uses' => 'ListController@transactions', 'as' => 'transactions']);
}
);
// Transaction Link Type API routes that need admin rights.
Route::group(
[
'namespace' => 'FireflyIII\Api\V1\Controllers\Models\TransactionLinkType',
'prefix' => 'v1/link-types',
'as' => 'api.v1.link-types.',
'middleware' => ['api-admin'],
],
static function (): void {
Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']);
Route::put('{linkType}', ['uses' => 'UpdateController@update', 'as' => 'update']);
Route::delete('{linkType}', ['uses' => 'DestroyController@destroy', 'as' => 'delete']);
Route::get('{linkType}/transactions', ['uses' => 'ListController@transactions', 'as' => 'transactions']);
}
);
@@ -727,10 +752,23 @@ Route::group(
],
static function (): void {
Route::get('', ['uses' => 'ConfigurationController@index', 'as' => 'index']);
Route::put('{dynamicConfigKey}', ['uses' => 'ConfigurationController@update', 'as' => 'update']);
Route::get('{eitherConfigKey}', ['uses' => 'ConfigurationController@show', 'as' => 'show']);
}
);
// Configuration API routes that need admin rights
Route::group(
[
'namespace' => 'FireflyIII\Api\V1\Controllers\System',
'prefix' => 'v1/configuration',
'as' => 'api.v1.configuration.',
'middleware' => ['api-admin'],
],
static function (): void {
Route::put('{dynamicConfigKey}', ['uses' => 'ConfigurationController@update', 'as' => 'update']);
}
);
// Users API routes:
Route::group(
[