From f2dc0d234b0940e502f6ae6b7d0cb0df78261905 Mon Sep 17 00:00:00 2001 From: JC5 Date: Fri, 15 Aug 2025 13:37:27 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20release=20?= =?UTF-8?q?'develop'=20on=202025-08-15?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ci/php-cs-fixer/composer.lock | 32 +-- .../Controllers/Chart/AccountController.php | 12 +- .../Controllers/Chart/BalanceController.php | 5 +- .../V1/Controllers/Chart/BudgetController.php | 72 +++---- .../Controllers/Chart/CategoryController.php | 38 ++-- .../DestroyController.php | 5 +- .../CurrencyExchangeRate/ShowController.php | 8 +- .../CurrencyExchangeRate/StoreController.php | 39 ++-- .../CurrencyExchangeRate/UpdateController.php | 10 +- .../StoreByCurrenciesRequest.php | 9 +- .../StoreByDateRequest.php | 9 +- .../ExchangeRate/ExchangeRateRepository.php | 40 ++-- .../ExchangeRateRepositoryInterface.php | 1 + .../Http/Api/AccountBalanceGrouped.php | 40 ++-- app/Support/Http/Api/CleansChartData.php | 10 +- composer.lock | 38 ++-- config/firefly.php | 4 +- package-lock.json | 188 +++++++++--------- public/v1/js/.gitkeep | 0 resources/assets/v1/src/locales/fr.json | 2 +- 20 files changed, 293 insertions(+), 269 deletions(-) mode change 100755 => 100644 public/v1/js/.gitkeep diff --git a/.ci/php-cs-fixer/composer.lock b/.ci/php-cs-fixer/composer.lock index 04ea32ba02..40ac982c67 100644 --- a/.ci/php-cs-fixer/composer.lock +++ b/.ci/php-cs-fixer/composer.lock @@ -345,16 +345,16 @@ }, { "name": "fidry/cpu-core-counter", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "8520451a140d3f46ac33042715115e290cf5785f" + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", - "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", "shasum": "" }, "require": { @@ -364,10 +364,10 @@ "fidry/makefile": "^0.2.0", "fidry/php-cs-fixer-config": "^1.1.2", "phpstan/extension-installer": "^1.2.0", - "phpstan/phpstan": "^1.9.2", - "phpstan/phpstan-deprecation-rules": "^1.0.0", - "phpstan/phpstan-phpunit": "^1.2.2", - "phpstan/phpstan-strict-rules": "^1.4.4", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^8.5.31 || ^9.5.26", "webmozarts/strict-phpunit": "^7.5" }, @@ -394,7 +394,7 @@ ], "support": { "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" }, "funding": [ { @@ -402,20 +402,20 @@ "type": "github" } ], - "time": "2024-08-06T10:04:20+00:00" + "time": "2025-08-14T07:29:31+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.85.1", + "version": "v3.86.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "2fb6d7f6c3398dca5786a1635b27405d73a417ba" + "reference": "4a952bd19dc97879b0620f495552ef09b55f7d36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/2fb6d7f6c3398dca5786a1635b27405d73a417ba", - "reference": "2fb6d7f6c3398dca5786a1635b27405d73a417ba", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/4a952bd19dc97879b0620f495552ef09b55f7d36", + "reference": "4a952bd19dc97879b0620f495552ef09b55f7d36", "shasum": "" }, "require": { @@ -499,7 +499,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.85.1" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.86.0" }, "funding": [ { @@ -507,7 +507,7 @@ "type": "github" } ], - "time": "2025-07-29T22:22:50+00:00" + "time": "2025-08-13T22:36:21+00:00" }, { "name": "psr/container", diff --git a/app/Api/V1/Controllers/Chart/AccountController.php b/app/Api/V1/Controllers/Chart/AccountController.php index 62212f71e2..7c762aece1 100644 --- a/app/Api/V1/Controllers/Chart/AccountController.php +++ b/app/Api/V1/Controllers/Chart/AccountController.php @@ -65,7 +65,7 @@ class AccountController extends Controller $this->chartData = new ChartData(); $this->repository = app(AccountRepositoryInterface::class); - $userGroup = $this->validateUserGroup($request); + $userGroup = $this->validateUserGroup($request); $this->repository->setUserGroup($userGroup); return $next($request); @@ -107,12 +107,12 @@ class AccountController extends Controller $range = Steam::finalAccountBalanceInRange($account, $params['start'], clone $params['end'], $this->convertToPrimary); - $previous = array_values($range)[0]['balance']; - $pcPrevious = null; + $previous = array_values($range)[0]['balance']; + $pcPrevious = null; if (!$currency instanceof TransactionCurrency) { $currency = $this->default; } - $currentSet = [ + $currentSet = [ 'label' => $account->name, // the currency that belongs to the account. @@ -153,7 +153,7 @@ class AccountController extends Controller // do the same for the primary currency balance, if relevant: - $pcBalance = null; + $pcBalance = null; if ($this->convertToPrimary) { $pcBalance = array_key_exists($format, $range) ? $range[$format]['pc_balance'] : $pcPrevious; $pcPrevious = $pcBalance; @@ -170,7 +170,7 @@ class AccountController extends Controller $defaultSet = $this->repository->getAccountsByType([AccountTypeEnum::ASSET->value])->pluck('id')->toArray(); /** @var Preference $frontpage */ - $frontpage = Preferences::get('frontpageAccounts', $defaultSet); + $frontpage = Preferences::get('frontpageAccounts', $defaultSet); if (!(is_array($frontpage->data) && count($frontpage->data) > 0)) { $frontpage->data = $defaultSet; diff --git a/app/Api/V1/Controllers/Chart/BalanceController.php b/app/Api/V1/Controllers/Chart/BalanceController.php index 083eb0bf33..52354aa7a3 100644 --- a/app/Api/V1/Controllers/Chart/BalanceController.php +++ b/app/Api/V1/Controllers/Chart/BalanceController.php @@ -12,7 +12,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Support\Chart\ChartData; use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Http\Api\AccountBalanceGrouped; use FireflyIII\Support\Http\Api\CleansChartData; @@ -26,7 +25,7 @@ class BalanceController extends Controller { use CleansChartData; use CollectsAccountsFromFilter; - protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; private array $chartData; private GroupCollectorInterface $collector; @@ -89,7 +88,7 @@ class BalanceController extends Controller foreach ($data as $entry) { $this->chartData[] = $entry; } - $this->chartData= $this->clean($this->chartData); + $this->chartData = $this->clean($this->chartData); return response()->json($this->chartData); } diff --git a/app/Api/V1/Controllers/Chart/BudgetController.php b/app/Api/V1/Controllers/Chart/BudgetController.php index ae0fca6dab..a053ba00b7 100644 --- a/app/Api/V1/Controllers/Chart/BudgetController.php +++ b/app/Api/V1/Controllers/Chart/BudgetController.php @@ -50,7 +50,7 @@ class BudgetController extends Controller use CleansChartData; use ValidatesUserGroupTrait; - protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; + protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; protected OperationsRepositoryInterface $opsRepository; private BudgetLimitRepositoryInterface $blRepository; @@ -83,13 +83,13 @@ class BudgetController extends Controller */ public function overview(DateRequest $request): JsonResponse { - $params = $request->getAll(); + $params = $request->getAll(); /** @var Carbon $start */ - $start = $params['start']; + $start = $params['start']; /** @var Carbon $end */ - $end = $params['end']; + $end = $params['end']; // code from FrontpageChartGenerator, but not in separate class $budgets = $this->repository->getActiveBudgets(); @@ -115,19 +115,19 @@ class BudgetController extends Controller $spent = $this->opsRepository->listExpenses($start, $end, null, new Collection([$budget])); $expenses = $this->processExpenses($budget->id, $spent, $start, $end); $converter = new ExchangeRateConverter(); - $currencies = [$this->primaryCurrency->id => $this->primaryCurrency,]; + $currencies = [$this->primaryCurrency->id => $this->primaryCurrency]; /** - * @var int $currencyId + * @var int $currencyId * @var array $row */ foreach ($expenses as $currencyId => $row) { // budgeted, left and overspent are now 0. - $limit = $this->filterLimit($currencyId, $limits); + $limit = $this->filterLimit($currencyId, $limits); // primary currency entries $row['pc_budgeted'] = '0'; - $row['pc_spent'] = '0'; + $row['pc_spent'] = '0'; $row['pc_left'] = '0'; $row['pc_overspent'] = '0'; @@ -142,18 +142,18 @@ class BudgetController extends Controller // convert data if necessary. if (true === $this->convertToPrimary && $currencyId !== $this->primaryCurrency->id) { $currencies[$currencyId] ??= TransactionCurrency::find($currencyId); - $row['pc_budgeted'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['budgeted']); - $row['pc_spent'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['spent']); - $row['pc_left'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['left']); - $row['pc_overspent'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['overspent']); + $row['pc_budgeted'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['budgeted']); + $row['pc_spent'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['spent']); + $row['pc_left'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['left']); + $row['pc_overspent'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['overspent']); } if (true === $this->convertToPrimary && $currencyId === $this->primaryCurrency->id) { - $row['pc_budgeted'] = $row['budgeted']; - $row['pc_spent'] = $row['spent']; - $row['pc_left'] = $row['left']; - $row['pc_overspent'] = $row['overspent']; + $row['pc_budgeted'] = $row['budgeted']; + $row['pc_spent'] = $row['spent']; + $row['pc_left'] = $row['left']; + $row['pc_overspent'] = $row['overspent']; } - $rows[] = $row; + $rows[] = $row; } @@ -164,14 +164,14 @@ class BudgetController extends Controller // } // is always an array - $return = []; + $return = []; foreach ($rows as $row) { $current = [ - 'label' => $budget->name, - 'currency_id' => (string)$row['currency_id'], - 'currency_name' => $row['currency_name'], - 'currency_code' => $row['currency_code'], - 'currency_decimal_places' => $row['currency_decimal_places'], + 'label' => $budget->name, + 'currency_id' => (string)$row['currency_id'], + 'currency_name' => $row['currency_name'], + 'currency_code' => $row['currency_code'], + 'currency_decimal_places' => $row['currency_decimal_places'], 'primary_currency_id' => (string)$this->primaryCurrency->id, 'primary_currency_name' => $this->primaryCurrency->name, @@ -179,19 +179,19 @@ class BudgetController extends Controller 'primary_currency_symbol' => $this->primaryCurrency->symbol, 'primary_currency_decimal_places' => $this->primaryCurrency->decimal_places, - 'period' => null, - 'date' => $row['start'], - 'start_date' => $row['start'], - 'end_date' => $row['end'], - 'yAxisID' => 0, - 'type' => 'bar', - 'entries' => [ + 'period' => null, + 'date' => $row['start'], + 'start_date' => $row['start'], + 'end_date' => $row['end'], + 'yAxisID' => 0, + 'type' => 'bar', + 'entries' => [ 'budgeted' => $row['budgeted'], 'spent' => $row['spent'], 'left' => $row['left'], 'overspent' => $row['overspent'], ], - 'pc_entries' => [ + 'pc_entries' => [ 'budgeted' => $row['pc_budgeted'], 'spent' => '0', 'left' => '0', @@ -231,7 +231,7 @@ class BudgetController extends Controller * This array contains the expenses in this budget. Grouped per currency. * The grouping is on the main currency only. * - * @var int $currencyId + * @var int $currencyId * @var array $block */ foreach ($spent as $currencyId => $block) { @@ -249,7 +249,7 @@ class BudgetController extends Controller 'left' => '0', 'overspent' => '0', ]; - $currentBudgetArray = $block['budgets'][$budgetId]; + $currentBudgetArray = $block['budgets'][$budgetId]; // var_dump($return); /** @var array $journal */ @@ -290,7 +290,7 @@ class BudgetController extends Controller private function processLimit(Budget $budget, BudgetLimit $limit): array { Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__)); - $end = clone $limit->end_date; + $end = clone $limit->end_date; $end->endOfDay(); $spent = $this->opsRepository->listExpenses($limit->start_date, $end, null, new Collection([$budget])); $limitCurrencyId = $limit->transaction_currency_id; @@ -298,8 +298,8 @@ class BudgetController extends Controller /** @var array $entry */ // only spent the entry where the entry's currency matches the budget limit's currency // so $filtered will only have 1 or 0 entries - $filtered = array_filter($spent, fn($entry) => $entry['currency_id'] === $limitCurrencyId); - $result = $this->processExpenses($budget->id, $filtered, $limit->start_date, $end); + $filtered = array_filter($spent, fn ($entry) => $entry['currency_id'] === $limitCurrencyId); + $result = $this->processExpenses($budget->id, $filtered, $limit->start_date, $end); if (1 === count($result)) { $compare = bccomp($limit->amount, (string)app('steam')->positive($result[$limitCurrencyId]['spent'])); $result[$limitCurrencyId]['budgeted'] = $limit->amount; diff --git a/app/Api/V1/Controllers/Chart/CategoryController.php b/app/Api/V1/Controllers/Chart/CategoryController.php index 986f2f4042..9073627218 100644 --- a/app/Api/V1/Controllers/Chart/CategoryController.php +++ b/app/Api/V1/Controllers/Chart/CategoryController.php @@ -81,7 +81,7 @@ class CategoryController extends Controller public function overview(DateRequest $request): JsonResponse { /** @var Carbon $start */ - $start = $this->parameters->get('start'); + $start = $this->parameters->get('start'); /** @var Carbon $end */ $end = $this->parameters->get('end'); @@ -92,25 +92,25 @@ class CategoryController extends Controller // get journals for entire period: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->withAccountInformation(); $collector->setXorAccounts($accounts)->withCategoryInformation(); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::RECONCILIATION->value]); - $journals = $collector->getExtractedJournals(); + $journals = $collector->getExtractedJournals(); /** @var array $journal */ foreach ($journals as $journal) { // find journal: - $journalCurrencyId = (int)$journal['currency_id']; - $currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId); - $currencies[$journalCurrencyId] = $currency; - $currencyId = (int)$currency->id; - $currencyName = (string)$currency->name; - $currencyCode = (string)$currency->code; - $currencySymbol = (string)$currency->symbol; - $currencyDecimalPlaces = (int)$currency->decimal_places; - $amount = Steam::positive($journal['amount']); - $pcAmount = null; + $journalCurrencyId = (int)$journal['currency_id']; + $currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId); + $currencies[$journalCurrencyId] = $currency; + $currencyId = (int)$currency->id; + $currencyName = (string)$currency->name; + $currencyCode = (string)$currency->code; + $currencySymbol = (string)$currency->symbol; + $currencyDecimalPlaces = (int)$currency->decimal_places; + $amount = Steam::positive($journal['amount']); + $pcAmount = null; // overrule if necessary: if ($this->convertToPrimary && $journalCurrencyId === $this->primaryCurrency->id) { @@ -127,8 +127,8 @@ class CategoryController extends Controller } - $categoryName = $journal['category_name'] ?? (string)trans('firefly.no_category'); - $key = sprintf('%s-%s', $categoryName, $currencyCode); + $categoryName = $journal['category_name'] ?? (string)trans('firefly.no_category'); + $key = sprintf('%s-%s', $categoryName, $currencyCode); // create arrays $return[$key] ??= [ 'label' => $categoryName, @@ -148,10 +148,10 @@ class CategoryController extends Controller 'yAxisID' => 0, 'type' => 'bar', 'entries' => [ - 'spent' => '0' + 'spent' => '0', ], 'pc_entries' => [ - 'spent' => '0' + 'spent' => '0', ], ]; @@ -161,10 +161,10 @@ class CategoryController extends Controller $return[$key]['pc_entries']['spent'] = bcadd($return[$key]['pc_entries']['spent'], (string)$pcAmount); } } - $return = array_values($return); + $return = array_values($return); // order by amount - usort($return, static fn(array $a, array $b) => (float)$a['entries']['spent'] < (float)$b['entries']['spent'] ? 1 : -1); + usort($return, static fn (array $a, array $b) => (float)$a['entries']['spent'] < (float)$b['entries']['spent'] ? 1 : -1); return response()->json($this->clean($return)); } diff --git a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/DestroyController.php b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/DestroyController.php index 9ba01de025..265a72b616 100644 --- a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/DestroyController.php +++ b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/DestroyController.php @@ -28,13 +28,11 @@ use Carbon\Carbon; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate\DestroyRequest; use FireflyIII\Enums\UserRoleEnum; -use FireflyIII\Exceptions\ValidationException; use FireflyIII\Models\CurrencyExchangeRate; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\ExchangeRate\ExchangeRateRepositoryInterface; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use Illuminate\Http\JsonResponse; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class DestroyController extends Controller { @@ -70,10 +68,11 @@ class DestroyController extends Controller return response()->json([], 204); } + public function destroySingleByDate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): JsonResponse { $exchangeRate = $this->repository->getSpecificRateOnDate($from, $to, $date); - if(null !== $exchangeRate) { + if (null !== $exchangeRate) { $this->repository->deleteRate($exchangeRate); } diff --git a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/ShowController.php b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/ShowController.php index abda7e7761..3b2f8c6e4d 100644 --- a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/ShowController.php +++ b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/ShowController.php @@ -91,17 +91,17 @@ class ShowController extends Controller public function showSingleByDate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): JsonResponse { - $transformer = new ExchangeRateTransformer(); + $transformer = new ExchangeRateTransformer(); $transformer->setParameters($this->parameters); - $exchangeRate = $this->repository->getSpecificRateOnDate($from, $to, $date); - if(null === $exchangeRate) { + $exchangeRate = $this->repository->getSpecificRateOnDate($from, $to, $date); + if (null === $exchangeRate) { throw new NotFoundHttpException(); } return response() ->api($this->jsonApiObject(self::RESOURCE_KEY, $exchangeRate, $transformer)) ->header('Content-Type', self::CONTENT_TYPE) - ; + ; } } diff --git a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/StoreController.php b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/StoreController.php index 676b621485..0e0f0fa8a1 100644 --- a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/StoreController.php +++ b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/StoreController.php @@ -43,7 +43,7 @@ class StoreController extends Controller { use ValidatesUserGroupTrait; - public const string RESOURCE_KEY = 'exchange-rates'; + public const string RESOURCE_KEY = 'exchange-rates'; protected array $acceptedRoles = [UserRoleEnum::OWNER]; private ExchangeRateRepositoryInterface $repository; @@ -63,8 +63,8 @@ class StoreController extends Controller public function storeByCurrencies(StoreByCurrenciesRequest $request, TransactionCurrency $from, TransactionCurrency $to): JsonResponse { - $data = $request->getAll(); - $collection = new Collection(); + $data = $request->getAll(); + $collection = new Collection(); foreach ($data as $date => $rate) { $date = Carbon::createFromFormat('Y-m-d', $date); @@ -73,9 +73,10 @@ class StoreController extends Controller // update existing rate. $existing = $this->repository->updateExchangeRate($existing, $rate); $collection->push($existing); + continue; } - $new = $this->repository->storeExchangeRate($from, $to, $rate, $date); + $new = $this->repository->storeExchangeRate($from, $to, $rate, $date); $collection->push($new); } @@ -86,17 +87,18 @@ class StoreController extends Controller return response() ->json($this->jsonApiList(self::RESOURCE_KEY, $paginator, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } public function storeByDate(StoreByDateRequest $request, Carbon $date): JsonResponse { - $data = $request->getAll(); - $from = $request->getFromCurrency(); - $collection = new Collection(); + $data = $request->getAll(); + $from = $request->getFromCurrency(); + $collection = new Collection(); foreach ($data['rates'] as $key => $rate) { - $to = TransactionCurrency::where('code', $key)->first(); + $to = TransactionCurrency::where('code', $key)->first(); if (null === $to) { continue; // should not happen. } @@ -105,9 +107,10 @@ class StoreController extends Controller // update existing rate. $existing = $this->repository->updateExchangeRate($existing, $rate); $collection->push($existing); + continue; } - $new = $this->repository->storeExchangeRate($from, $to, $rate, $date); + $new = $this->repository->storeExchangeRate($from, $to, $rate, $date); $collection->push($new); } @@ -118,18 +121,19 @@ class StoreController extends Controller return response() ->json($this->jsonApiList(self::RESOURCE_KEY, $paginator, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } public function store(StoreRequest $request): JsonResponse { - $date = $request->getDate(); - $rate = $request->getRate(); - $from = $request->getFromCurrency(); - $to = $request->getToCurrency(); + $date = $request->getDate(); + $rate = $request->getRate(); + $from = $request->getFromCurrency(); + $to = $request->getToCurrency(); // already has rate? - $object = $this->repository->getSpecificRateOnDate($from, $to, $date); + $object = $this->repository->getSpecificRateOnDate($from, $to, $date); if ($object instanceof CurrencyExchangeRate) { // just update it, no matter. $rate = $this->repository->updateExchangeRate($object, $rate, $date); @@ -144,6 +148,7 @@ class StoreController extends Controller return response() ->api($this->jsonApiObject(self::RESOURCE_KEY, $rate, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } } diff --git a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/UpdateController.php b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/UpdateController.php index 99b4d04bbc..8844407f7d 100644 --- a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/UpdateController.php +++ b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/UpdateController.php @@ -40,7 +40,7 @@ class UpdateController extends Controller { use ValidatesUserGroupTrait; - public const string RESOURCE_KEY = 'exchange-rates'; + public const string RESOURCE_KEY = 'exchange-rates'; protected array $acceptedRoles = [UserRoleEnum::OWNER]; private ExchangeRateRepositoryInterface $repository; @@ -67,7 +67,8 @@ class UpdateController extends Controller return response() ->api($this->jsonApiObject(self::RESOURCE_KEY, $exchangeRate, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } public function updateByDate(UpdateRequest $request, TransactionCurrency $from, TransactionCurrency $to, Carbon $date): JsonResponse @@ -80,11 +81,12 @@ class UpdateController extends Controller $rate = $request->getRate(); $exchangeRate = $this->repository->updateExchangeRate($exchangeRate, $rate, $date); - $transformer = new ExchangeRateTransformer(); + $transformer = new ExchangeRateTransformer(); $transformer->setParameters($this->parameters); return response() ->api($this->jsonApiObject(self::RESOURCE_KEY, $exchangeRate, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + ->header('Content-Type', self::CONTENT_TYPE) + ; } } diff --git a/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByCurrenciesRequest.php b/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByCurrenciesRequest.php index 58e2adbfb3..67706bdc69 100644 --- a/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByCurrenciesRequest.php +++ b/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByCurrenciesRequest.php @@ -60,14 +60,17 @@ class StoreByCurrenciesRequest extends FormRequest try { $date = Carbon::createFromFormat('Y-m-d', $date); } catch (InvalidFormatException $e) { - $validator->errors()->add('date', trans('validation.date',['attribute' => 'date'])); + $validator->errors()->add('date', trans('validation.date', ['attribute' => 'date'])); + return; } if (!is_numeric($rate)) { - $validator->errors()->add('rate', trans('validation.number',['attribute' => 'rate'])); + $validator->errors()->add('rate', trans('validation.number', ['attribute' => 'rate'])); + return; } } - }); + } + ); } } diff --git a/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByDateRequest.php b/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByDateRequest.php index 19dab5dbdd..433e6ac90b 100644 --- a/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByDateRequest.php +++ b/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByDateRequest.php @@ -38,7 +38,7 @@ class StoreByDateRequest extends FormRequest public function getAll(): array { return [ - 'from' => $this->get('from'), + 'from' => $this->get('from'), 'rates' => $this->get('rates', []), ]; } @@ -62,7 +62,7 @@ class StoreByDateRequest extends FormRequest public function withValidator(Validator $validator): void { - $from = $this->getFromCurrency(); + $from = $this->getFromCurrency(); $validator->after( static function (Validator $validator) use ($from): void { @@ -70,11 +70,13 @@ class StoreByDateRequest extends FormRequest $rates = $data['rates'] ?? []; if (0 === count($rates)) { $validator->errors()->add('rates', 'No rates given.'); + return; } foreach ($rates as $key => $entry) { if ($key === $from->code) { $validator->errors()->add(sprintf('rates.%s', $key), trans('validation.convert_to_itself', ['code' => $key])); + continue; } $to = TransactionCurrency::where('code', $key)->first(); @@ -82,6 +84,7 @@ class StoreByDateRequest extends FormRequest $validator->errors()->add(sprintf('rates.%s', $key), trans('validation.invalid_currency_code', ['code' => $key])); } } - }); + } + ); } } diff --git a/app/Repositories/ExchangeRate/ExchangeRateRepository.php b/app/Repositories/ExchangeRate/ExchangeRateRepository.php index bcaf547cf8..d2f8544a4a 100644 --- a/app/Repositories/ExchangeRate/ExchangeRateRepository.php +++ b/app/Repositories/ExchangeRate/ExchangeRateRepository.php @@ -55,17 +55,20 @@ class ExchangeRateRepository implements ExchangeRateRepositoryInterface, UserGro // orderBy('date', 'DESC')->toRawSql(); return $this->userGroup->currencyExchangeRates() - ->where(function (Builder $q1) use ($from, $to): void { - $q1->where(function (Builder $q) use ($from, $to): void { - $q->where('from_currency_id', $from->id) - ->where('to_currency_id', $to->id); - })->orWhere(function (Builder $q) use ($from, $to): void { - $q->where('from_currency_id', $to->id) - ->where('to_currency_id', $from->id); - }); - }) - ->orderBy('date', 'DESC') - ->get(['currency_exchange_rates.*']); + ->where(function (Builder $q1) use ($from, $to): void { + $q1->where(function (Builder $q) use ($from, $to): void { + $q->where('from_currency_id', $from->id) + ->where('to_currency_id', $to->id) + ; + })->orWhere(function (Builder $q) use ($from, $to): void { + $q->where('from_currency_id', $to->id) + ->where('to_currency_id', $from->id) + ; + }); + }) + ->orderBy('date', 'DESC') + ->get(['currency_exchange_rates.*']) + ; } @@ -75,10 +78,11 @@ class ExchangeRateRepository implements ExchangeRateRepositoryInterface, UserGro /** @var null|CurrencyExchangeRate */ return $this->userGroup->currencyExchangeRates() - ->where('from_currency_id', $from->id) - ->where('to_currency_id', $to->id) - ->where('date', $date->format('Y-m-d')) - ->first(); + ->where('from_currency_id', $from->id) + ->where('to_currency_id', $to->id) + ->where('date', $date->format('Y-m-d')) + ->first() + ; } #[Override] @@ -112,8 +116,8 @@ class ExchangeRateRepository implements ExchangeRateRepositoryInterface, UserGro public function deleteRates(TransactionCurrency $from, TransactionCurrency $to): void { $this->userGroup->currencyExchangeRates() - ->where('from_currency_id', $from->id) - ->where('to_currency_id', $to->id) - ->delete(); + ->where('from_currency_id', $from->id) + ->where('to_currency_id', $to->id) + ->delete(); } } diff --git a/app/Repositories/ExchangeRate/ExchangeRateRepositoryInterface.php b/app/Repositories/ExchangeRate/ExchangeRateRepositoryInterface.php index 956c6a0787..04f8ac1072 100644 --- a/app/Repositories/ExchangeRate/ExchangeRateRepositoryInterface.php +++ b/app/Repositories/ExchangeRate/ExchangeRateRepositoryInterface.php @@ -46,6 +46,7 @@ use Illuminate\Support\Collection; interface ExchangeRateRepositoryInterface { public function deleteRate(CurrencyExchangeRate $rate): void; + public function deleteRates(TransactionCurrency $from, TransactionCurrency $to): void; public function getAll(): Collection; diff --git a/app/Support/Http/Api/AccountBalanceGrouped.php b/app/Support/Http/Api/AccountBalanceGrouped.php index 6d258df38c..397146be61 100644 --- a/app/Support/Http/Api/AccountBalanceGrouped.php +++ b/app/Support/Http/Api/AccountBalanceGrouped.php @@ -66,7 +66,7 @@ class AccountBalanceGrouped /** @var array $currency */ foreach ($this->data as $currency) { // income and expense array prepped: - $income = [ + $income = [ 'label' => 'earned', 'currency_id' => (string)$currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], @@ -85,7 +85,7 @@ class AccountBalanceGrouped 'entries' => [], 'pc_entries' => [], ]; - $expense = [ + $expense = [ 'label' => 'spent', 'currency_id' => (string)$currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], @@ -107,22 +107,22 @@ class AccountBalanceGrouped // loop all possible periods between $start and $end, and add them to the correct dataset. $currentStart = clone $this->start; while ($currentStart <= $this->end) { - $key = $currentStart->format($this->carbonFormat); - $label = $currentStart->toAtomString(); + $key = $currentStart->format($this->carbonFormat); + $label = $currentStart->toAtomString(); // normal entries - $income['entries'][$label] = Steam::bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); - $expense['entries'][$label] = Steam::bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']); + $income['entries'][$label] = Steam::bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); + $expense['entries'][$label] = Steam::bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']); // converted entries $income['pc_entries'][$label] = Steam::bcround($currency[$key]['pc_earned'] ?? '0', $currency['primary_currency_decimal_places']); $expense['pc_entries'][$label] = Steam::bcround($currency[$key]['pc_spent'] ?? '0', $currency['primary_currency_decimal_places']); // next loop - $currentStart = Navigation::addPeriod($currentStart, $this->preferredRange, 0); + $currentStart = Navigation::addPeriod($currentStart, $this->preferredRange, 0); } - $chartData[] = $income; - $chartData[] = $expense; + $chartData[] = $income; + $chartData[] = $expense; } return $chartData; @@ -148,9 +148,9 @@ class AccountBalanceGrouped private function processJournal(array $journal): void { // format the date according to the period - $period = $journal['date']->format($this->carbonFormat); - $currencyId = (int)$journal['currency_id']; - $currency = $this->findCurrency($currencyId); + $period = $journal['date']->format($this->carbonFormat); + $currencyId = (int)$journal['currency_id']; + $currency = $this->findCurrency($currencyId); // set the array with monetary info, if it does not exist. $this->createDefaultDataEntry($journal); @@ -158,12 +158,12 @@ class AccountBalanceGrouped $this->createDefaultPeriodEntry($journal); // is this journal's amount in- our outgoing? - $key = $this->getDataKey($journal); - $amount = 'spent' === $key ? Steam::negative($journal['amount']) : Steam::positive($journal['amount']); + $key = $this->getDataKey($journal); + $amount = 'spent' === $key ? Steam::negative($journal['amount']) : Steam::positive($journal['amount']); // get conversion rate - $rate = $this->getRate($currency, $journal['date']); - $amountConverted = bcmul((string)$amount, $rate); + $rate = $this->getRate($currency, $journal['date']); + $amountConverted = bcmul((string)$amount, $rate); // perhaps transaction already has the foreign amount in the primary currency. if ((int)$journal['foreign_currency_id'] === $this->primary->id) { @@ -172,7 +172,7 @@ class AccountBalanceGrouped } // add normal entry - $this->data[$currencyId][$period][$key] = bcadd((string)$this->data[$currencyId][$period][$key], (string)$amount); + $this->data[$currencyId][$period][$key] = bcadd((string)$this->data[$currencyId][$period][$key], (string)$amount); // add converted entry $convertedKey = sprintf('pc_%s', $key); @@ -191,7 +191,7 @@ class AccountBalanceGrouped private function createDefaultDataEntry(array $journal): void { - $currencyId = (int)$journal['currency_id']; + $currencyId = (int)$journal['currency_id']; $this->data[$currencyId] ??= [ 'currency_id' => (string)$currencyId, 'currency_symbol' => $journal['currency_symbol'], @@ -208,8 +208,8 @@ class AccountBalanceGrouped private function createDefaultPeriodEntry(array $journal): void { - $currencyId = (int)$journal['currency_id']; - $period = $journal['date']->format($this->carbonFormat); + $currencyId = (int)$journal['currency_id']; + $period = $journal['date']->format($this->carbonFormat); $this->data[$currencyId][$period] ??= [ 'period' => $period, 'spent' => '0', diff --git a/app/Support/Http/Api/CleansChartData.php b/app/Support/Http/Api/CleansChartData.php index 702926d35c..4b02726c1b 100644 --- a/app/Support/Http/Api/CleansChartData.php +++ b/app/Support/Http/Api/CleansChartData.php @@ -47,14 +47,15 @@ trait CleansChartData * @var array $array */ foreach ($data as $index => $array) { - $array = $this->cleanSingleArray($index, $array); + $array = $this->cleanSingleArray($index, $array); $return[] = $array; } return $return; } - private function cleanSingleArray(mixed $index, array $array): array { + private function cleanSingleArray(mixed $index, array $array): array + { if (array_key_exists('currency_id', $array)) { $array['currency_id'] = (string)$array['currency_id']; } @@ -62,14 +63,15 @@ trait CleansChartData $array['primary_currency_id'] = (string)$array['primary_currency_id']; } $required = [ - 'start_date', 'end_date', 'period', 'yAxisID','type','entries','pc_entries', - 'currency_id', 'primary_currency_id' + 'start_date', 'end_date', 'period', 'yAxisID', 'type', 'entries', 'pc_entries', + 'currency_id', 'primary_currency_id', ]; foreach ($required as $field) { if (!array_key_exists($field, $array)) { throw new FireflyException(sprintf('Data-set "%s" is missing the "%s"-variable.', $index, $field)); } } + return $array; } } diff --git a/composer.lock b/composer.lock index b502f9f472..a6297fc8ce 100644 --- a/composer.lock +++ b/composer.lock @@ -1878,16 +1878,16 @@ }, { "name": "laravel/framework", - "version": "v12.23.1", + "version": "v12.24.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "2a0e9331a0db904236143fe915c281ff4be274a3" + "reference": "6dcf2c46da23d159f35d6246234953a74b740d83" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/2a0e9331a0db904236143fe915c281ff4be274a3", - "reference": "2a0e9331a0db904236143fe915c281ff4be274a3", + "url": "https://api.github.com/repos/laravel/framework/zipball/6dcf2c46da23d159f35d6246234953a74b740d83", + "reference": "6dcf2c46da23d159f35d6246234953a74b740d83", "shasum": "" }, "require": { @@ -1997,7 +1997,7 @@ "league/flysystem-read-only": "^3.25.1", "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", - "orchestra/testbench-core": "^10.0.0", + "orchestra/testbench-core": "^10.6.0", "pda/pheanstalk": "^5.0.6|^7.0.0", "php-http/discovery": "^1.15", "phpstan/phpstan": "^2.0", @@ -2091,7 +2091,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-08-12T17:35:05+00:00" + "time": "2025-08-13T20:30:36+00:00" }, { "name": "laravel/passport", @@ -10987,16 +10987,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.6.0", + "version": "v5.6.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56" + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/221b0d0fdf1369c71047ad1d18bb5880017bbc56", - "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", "shasum": "" }, "require": { @@ -11015,7 +11015,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -11039,9 +11039,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" }, - "time": "2025-07-27T20:03:57+00:00" + "time": "2025-08-13T20:13:15+00:00" }, { "name": "phar-io/manifest", @@ -11876,16 +11876,16 @@ }, { "name": "rector/rector", - "version": "2.1.2", + "version": "2.1.3", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "40a71441dd73fa150a66102f5ca1364c44fc8fff" + "reference": "dd430c869fddf4965049c8fd6f5ee49f155cfddf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/40a71441dd73fa150a66102f5ca1364c44fc8fff", - "reference": "40a71441dd73fa150a66102f5ca1364c44fc8fff", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/dd430c869fddf4965049c8fd6f5ee49f155cfddf", + "reference": "dd430c869fddf4965049c8fd6f5ee49f155cfddf", "shasum": "" }, "require": { @@ -11924,7 +11924,7 @@ ], "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/2.1.2" + "source": "https://github.com/rectorphp/rector/tree/2.1.3" }, "funding": [ { @@ -11932,7 +11932,7 @@ "type": "github" } ], - "time": "2025-07-17T19:30:06+00:00" + "time": "2025-08-13T11:43:04+00:00" }, { "name": "sebastian/cli-parser", diff --git a/config/firefly.php b/config/firefly.php index 8e7e49fabd..c6f9d33788 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => 'develop/2025-08-13', - 'build_time' => 1755064254, + 'version' => 'develop/2025-08-15', + 'build_time' => 1755257739, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 26, diff --git a/package-lock.json b/package-lock.json index 563fe2e3a4..336c6d0d6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,22 +53,22 @@ } }, "node_modules/@babel/core": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", - "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", + "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", + "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.28.0", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.3", + "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.0", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -94,14 +94,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -151,18 +151,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", - "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.27.1", + "@babel/traverse": "^7.28.3", "semver": "^6.3.1" }, "engines": { @@ -266,15 +266,15 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -387,24 +387,24 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", - "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz", + "integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", - "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", + "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", "dev": true, "license": "MIT", "dependencies": { @@ -416,13 +416,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.0" + "@babel/types": "^7.28.2" }, "bin": { "parser": "bin/babel-parser.js" @@ -499,14 +499,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz", - "integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz", + "integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -726,13 +726,13 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz", - "integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz", + "integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { @@ -743,9 +743,9 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz", - "integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.3.tgz", + "integrity": "sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==", "dev": true, "license": "MIT", "dependencies": { @@ -754,7 +754,7 @@ "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.0" + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -1284,9 +1284,9 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.28.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz", - "integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.3.tgz", + "integrity": "sha512-K3/M/a4+ESb5LEldjQb+XSrpY0nF+ZBFlTCbSnKaYAMfD8v33O6PMs4uYnOk19HlcsI8WMu3McdFPTiQHF/1/A==", "dev": true, "license": "MIT", "dependencies": { @@ -1333,9 +1333,9 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.0.tgz", - "integrity": "sha512-dGopk9nZrtCs2+nfIem25UuHyt5moSJamArzIoh9/vezUQPmYDOzjaHDCkAzuGJibCIkPup8rMT2+wYB6S73cA==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.3.tgz", + "integrity": "sha512-Y6ab1kGqZ0u42Zv/4a7l0l72n9DKP/MKoKWaUSBylrhNZO2prYuqFOLbn5aW5SIFXwSH93yfjbgllL8lxuGKLg==", "dev": true, "license": "MIT", "dependencies": { @@ -1512,9 +1512,9 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.0.tgz", - "integrity": "sha512-VmaxeGOwuDqzLl5JUkIRM1X2Qu2uKGxHEQWh+cvvbl7JuJRgKGJSfsEF/bUaxFhJl/XAyxBe7q7qSuTbKFuCyg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.3.tgz", + "integrity": "sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==", "dev": true, "license": "MIT", "dependencies": { @@ -1526,7 +1526,7 @@ "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.27.1", "@babel/plugin-syntax-import-attributes": "^7.27.1", @@ -1537,8 +1537,8 @@ "@babel/plugin-transform-block-scoped-functions": "^7.27.1", "@babel/plugin-transform-block-scoping": "^7.28.0", "@babel/plugin-transform-class-properties": "^7.27.1", - "@babel/plugin-transform-class-static-block": "^7.27.1", - "@babel/plugin-transform-classes": "^7.28.0", + "@babel/plugin-transform-class-static-block": "^7.28.3", + "@babel/plugin-transform-classes": "^7.28.3", "@babel/plugin-transform-computed-properties": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.0", "@babel/plugin-transform-dotall-regex": "^7.27.1", @@ -1570,7 +1570,7 @@ "@babel/plugin-transform-private-methods": "^7.27.1", "@babel/plugin-transform-private-property-in-object": "^7.27.1", "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.28.0", + "@babel/plugin-transform-regenerator": "^7.28.3", "@babel/plugin-transform-regexp-modifiers": "^7.27.1", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", @@ -1622,9 +1622,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", - "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", + "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1646,18 +1646,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", + "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", + "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", + "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", + "@babel/types": "^7.28.2", "debug": "^4.3.1" }, "engines": { @@ -3148,9 +3148,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.2.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz", - "integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==", + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", + "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", "dev": true, "license": "MIT", "dependencies": { @@ -4486,9 +4486,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001734", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001734.tgz", - "integrity": "sha512-uhE1Ye5vgqju6OI71HTQqcBCZrvHugk0MjLak7Q+HfoBgoq5Bi+5YnwjP4fjDgrtYr/l8MVRBvzz9dPD4KyK0A==", + "version": "1.0.30001735", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz", + "integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==", "dev": true, "funding": [ { @@ -5700,9 +5700,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.200", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.200.tgz", - "integrity": "sha512-rFCxROw7aOe4uPTfIAx+rXv9cEcGx+buAF4npnhtTqCJk5KDFRnh3+KYj7rdVh6lsFt5/aPs+Irj9rZ33WMA7w==", + "version": "1.5.202", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.202.tgz", + "integrity": "sha512-NxbYjRmiHcHXV1Ws3fWUW+SLb62isauajk45LUJ/HgIOkUA7jLZu/X2Iif+X9FBNK8QkF9Zb4Q2mcwXCcY30mg==", "dev": true, "license": "ISC" }, @@ -7052,9 +7052,9 @@ } }, "node_modules/i18next": { - "version": "25.3.4", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.3.4.tgz", - "integrity": "sha512-AHklEYFLiRRxW1Cb6zE9lfnEtYvsydRC8nRS3RSKGX3zCqZ8nLZwMaUsrb80YuccPNv2RNokDL8LkTNnp+6mDw==", + "version": "25.3.6", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.3.6.tgz", + "integrity": "sha512-dThZ0CTCM3sUG/qS0ZtQYZQcUI6DtBN8yBHK+SKEqihPcEYmjVWh/YJ4luic73Iq6Uxhp6q7LJJntRK5+1t7jQ==", "funding": [ { "type": "individual", @@ -11170,11 +11170,14 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -11617,11 +11620,14 @@ "license": "MIT" }, "node_modules/vite/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -11844,9 +11850,9 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.101.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.1.tgz", - "integrity": "sha512-rHY3vHXRbkSfhG6fH8zYQdth/BtDgXXuR2pHF++1f/EBkI8zkgM5XWfsC3BvOoW9pr1CvZ1qQCxhCEsbNgT50g==", + "version": "5.101.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.2.tgz", + "integrity": "sha512-4JLXU0tD6OZNVqlwzm3HGEhAHufSiyv+skb7q0d2367VDMzrU1Q/ZeepvkcHH0rZie6uqEtTQQe0OEOOluH3Mg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/public/v1/js/.gitkeep b/public/v1/js/.gitkeep old mode 100755 new mode 100644 diff --git a/resources/assets/v1/src/locales/fr.json b/resources/assets/v1/src/locales/fr.json index 462e02a54e..630079dacb 100644 --- a/resources/assets/v1/src/locales/fr.json +++ b/resources/assets/v1/src/locales/fr.json @@ -102,7 +102,7 @@ "profile_oauth_client_secret_title": "Secret du client", "profile_oauth_client_secret_expl": "Voici votre nouveau secret de client. C'est la seule fois qu'il sera affich\u00e9, donc ne le perdez pas ! Vous pouvez maintenant utiliser ce secret pour faire des requ\u00eates d'API.", "profile_oauth_confidential": "Confidentiel", - "profile_oauth_confidential_help": "Require the client to authenticate with a secret. Confidential clients can hold credentials in a secure way without exposing them to unauthorized parties. Public applications, such as native desktop or JavaScript SPA applications, are unable to hold secrets securely.", + "profile_oauth_confidential_help": "Exiger que le client s'authentifie avec un secret. Les clients confidentiels peuvent d\u00e9tenir des informations d'identification de mani\u00e8re s\u00e9curis\u00e9e sans les exposer \u00e0 des tiers non autoris\u00e9s. Les applications publiques, telles que les applications de bureau natif ou les SPA JavaScript, ne peuvent pas tenir des secrets en toute s\u00e9curit\u00e9.", "multi_account_warning_unknown": "Selon le type d'op\u00e9ration que vous cr\u00e9ez, le(s) compte(s) source et\/ou de destination des s\u00e9parations suivantes peuvent \u00eatre remplac\u00e9s par celui de la premi\u00e8re s\u00e9paration de l'op\u00e9ration.", "multi_account_warning_withdrawal": "Gardez en t\u00eate que le compte source des s\u00e9parations suivantes peut \u00eatre remplac\u00e9 par celui de la premi\u00e8re s\u00e9paration de la d\u00e9pense.", "multi_account_warning_deposit": "Gardez en t\u00eate que le compte de destination des s\u00e9parations suivantes peut \u00eatre remplac\u00e9 par celui de la premi\u00e8re s\u00e9paration du d\u00e9p\u00f4t.",