Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop

This commit is contained in:
James Cole
2025-09-25 19:17:20 +02:00
10 changed files with 332 additions and 231 deletions

View File

@@ -402,16 +402,16 @@
}, },
{ {
"name": "friendsofphp/php-cs-fixer", "name": "friendsofphp/php-cs-fixer",
"version": "v3.87.2", "version": "v3.88.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992" "reference": "f23469674ae50d40e398bfff8018911a2a2b0dbe"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992", "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/f23469674ae50d40e398bfff8018911a2a2b0dbe",
"reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992", "reference": "f23469674ae50d40e398bfff8018911a2a2b0dbe",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -438,12 +438,13 @@
"symfony/polyfill-mbstring": "^1.33", "symfony/polyfill-mbstring": "^1.33",
"symfony/polyfill-php80": "^1.33", "symfony/polyfill-php80": "^1.33",
"symfony/polyfill-php81": "^1.33", "symfony/polyfill-php81": "^1.33",
"symfony/polyfill-php84": "^1.33",
"symfony/process": "^5.4.47 || ^6.4.24 || ^7.2", "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2",
"symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0" "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0"
}, },
"require-dev": { "require-dev": {
"facile-it/paraunit": "^1.3.1 || ^2.7", "facile-it/paraunit": "^1.3.1 || ^2.7",
"infection/infection": "^0.29.14", "infection/infection": "^0.31.0",
"justinrainbow/json-schema": "^6.5", "justinrainbow/json-schema": "^6.5",
"keradus/cli-executor": "^2.2", "keradus/cli-executor": "^2.2",
"mikey179/vfsstream": "^1.6.12", "mikey179/vfsstream": "^1.6.12",
@@ -451,7 +452,6 @@
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6",
"phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34", "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34",
"symfony/polyfill-php84": "^1.33",
"symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2", "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2",
"symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2" "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2"
}, },
@@ -494,7 +494,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.87.2" "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.88.0"
}, },
"funding": [ "funding": [
{ {
@@ -502,7 +502,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-09-10T09:51:40+00:00" "time": "2025-09-24T21:31:42+00:00"
}, },
{ {
"name": "psr/container", "name": "psr/container",
@@ -2283,6 +2283,86 @@
], ],
"time": "2024-09-09T11:45:10+00:00" "time": "2024-09-09T11:45:10+00:00"
}, },
{
"name": "symfony/polyfill-php84",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php84.git",
"reference": "d8ced4d875142b6a7426000426b8abc631d6b191"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191",
"reference": "d8ced4d875142b6a7426000426b8abc631d6b191",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php84\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-06-24T13:30:11+00:00"
},
{ {
"name": "symfony/process", "name": "symfony/process",
"version": "v7.3.3", "version": "v7.3.3",

View File

@@ -160,7 +160,7 @@ class ShowController extends Controller
/** @var \FireflyIII\Models\WebhookTrigger $trigger */ /** @var \FireflyIII\Models\WebhookTrigger $trigger */
foreach($webhook->webhookTriggers as $trigger) { foreach ($webhook->webhookTriggers as $trigger) {
/** @var MessageGeneratorInterface $engine */ /** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class); $engine = app(MessageGeneratorInterface::class);
$engine->setUser(auth()->user()); $engine->setUser(auth()->user());

View File

@@ -243,7 +243,6 @@ class Account extends Model
} }
public function primaryPeriodStatistics(): MorphMany public function primaryPeriodStatistics(): MorphMany
{ {
return $this->morphMany(PeriodStatistic::class, 'primary_statable'); return $this->morphMany(PeriodStatistic::class, 'primary_statable');

View File

@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace FireflyIII\Models; namespace FireflyIII\Models;
use FireflyIII\Casts\SeparateTimezoneCaster; use FireflyIII\Casts\SeparateTimezoneCaster;
@@ -11,8 +13,8 @@ use Illuminate\Database\Eloquent\SoftDeletes;
class PeriodStatistic extends Model class PeriodStatistic extends Model
{ {
use SoftDeletes;
use ReturnsIntegerUserIdTrait; use ReturnsIntegerUserIdTrait;
use SoftDeletes;
protected function casts(): array protected function casts(): array
{ {
@@ -31,7 +33,6 @@ class PeriodStatistic extends Model
); );
} }
public function primaryStatable(): MorphTo public function primaryStatable(): MorphTo
{ {
@@ -45,6 +46,7 @@ class PeriodStatistic extends Model
return $this->morphTo(); return $this->morphTo();
} }
public function tertiaryStatable(): MorphTo public function tertiaryStatable(): MorphTo
{ {
@@ -52,5 +54,5 @@ class PeriodStatistic extends Model
} }
//
} }

View File

@@ -1,4 +1,6 @@
<?php <?php
declare(strict_types=1);
/* /*
* PeriodStatisticRepository.php * PeriodStatisticRepository.php
* Copyright (c) 2025 james@firefly-iii.org * Copyright (c) 2025 james@firefly-iii.org
@@ -28,28 +30,29 @@ use Illuminate\Support\Collection;
class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface
{ {
public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection
{ {
return $model->primaryPeriodStatistics() return $model->primaryPeriodStatistics()
->where('start', $start) ->where('start', $start)
->where('end', $end) ->where('end', $end)
->whereIn('type', $types) ->whereIn('type', $types)
->get(); ->get()
;
} }
public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection
{ {
return $model->primaryPeriodStatistics() return $model->primaryPeriodStatistics()
->where('start', $start) ->where('start', $start)
->where('end', $end) ->where('end', $end)
->where('type', $type) ->where('type', $type)
->get(); ->get()
;
} }
public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic
{ {
$stat = new PeriodStatistic; $stat = new PeriodStatistic();
$stat->primaryStatable()->associate($model); $stat->primaryStatable()->associate($model);
$stat->transaction_currency_id = $currencyId; $stat->transaction_currency_id = $currencyId;
$stat->start = $start; $stat->start = $start;
@@ -60,6 +63,7 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface
$stat->count = $count; $stat->count = $count;
$stat->type = $type; $stat->type = $type;
$stat->save(); $stat->save();
return $stat; return $stat;
} }
} }

View File

@@ -1,4 +1,6 @@
<?php <?php
declare(strict_types=1);
/* /*
* PeriodStatisticRepositoryInterface.php * PeriodStatisticRepositoryInterface.php
* Copyright (c) 2025 james@firefly-iii.org * Copyright (c) 2025 james@firefly-iii.org
@@ -28,11 +30,9 @@ use Illuminate\Support\Collection;
interface PeriodStatisticRepositoryInterface interface PeriodStatisticRepositoryInterface
{ {
public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection; public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection;
public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection; public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection;
public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic; public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic;
} }

View File

@@ -86,10 +86,11 @@ trait PeriodOverview
$this->accountRepository = app(AccountRepositoryInterface::class); $this->accountRepository = app(AccountRepositoryInterface::class);
$this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class);
$range = Navigation::getViewRange(true); $range = Navigation::getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; [$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
/** @var array $dates */ /** @var array $dates */
$dates = Navigation::blockPeriods($start, $end, $range); $dates = Navigation::blockPeriods($start, $end, $range);
$entries = []; $entries = [];
Log::debug(sprintf('Count of loops: %d', count($dates))); Log::debug(sprintf('Count of loops: %d', count($dates)));
foreach ($dates as $currentDate) { foreach ($dates as $currentDate) {
$entries[] = $this->getSingleAccountPeriod($account, $currentDate['start'], $currentDate['end']); $entries[] = $this->getSingleAccountPeriod($account, $currentDate['start'], $currentDate['end']);
@@ -109,11 +110,12 @@ trait PeriodOverview
'total_transactions' => 0, 'total_transactions' => 0,
]; ];
foreach ($types as $type) { foreach ($types as $type) {
$set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type);
$return['total_transactions'] += $set['count']; $return['total_transactions'] += $set['count'];
unset($set['count']); unset($set['count']);
$return[$type] = $set; $return[$type] = $set;
} }
return $return; return $return;
} }
@@ -125,36 +127,47 @@ trait PeriodOverview
// nothing found, regenerate them. // nothing found, regenerate them.
if (0 === $statistics->count()) { if (0 === $statistics->count()) {
$transactions = $this->accountRepository->periodCollection($account, $start, $end); $transactions = $this->accountRepository->periodCollection($account, $start, $end);
switch ($type) { switch ($type) {
default: default:
throw new FireflyException(sprintf('Cannot deal with account period type %s', $type)); throw new FireflyException(sprintf('Cannot deal with account period type %s', $type));
case 'spent': case 'spent':
$result = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $transactions, $start, $end); $result = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $transactions, $start, $end);
break; break;
case 'earned': case 'earned':
$result = $this->filterTransactionsByType(TransactionTypeEnum::DEPOSIT, $transactions, $start, $end); $result = $this->filterTransactionsByType(TransactionTypeEnum::DEPOSIT, $transactions, $start, $end);
break; break;
case 'transferred_in': case 'transferred_in':
$result = $this->filterTransfers('in', $transactions, $start, $end); $result = $this->filterTransfers('in', $transactions, $start, $end);
break; break;
case 'transferred_away': case 'transferred_away':
$result = $this->filterTransfers('away', $transactions, $start, $end); $result = $this->filterTransfers('away', $transactions, $start, $end);
break; break;
} }
// each result must be grouped by currency, then saved as period statistic. // each result must be grouped by currency, then saved as period statistic.
$grouped = $this->groupByCurrency($result); $grouped = $this->groupByCurrency($result);
// TODO save as statistic. // TODO save as statistic.
$this->saveGroupedAsStatistics($account, $start, $end, $type, $grouped); $this->saveGroupedAsStatistics($account, $start, $end, $type, $grouped);
return $grouped; return $grouped;
} }
$grouped = [ $grouped = [
'count' => 0, 'count' => 0,
]; ];
/** @var PeriodStatistic $statistic */ /** @var PeriodStatistic $statistic */
foreach($statistics as $statistic) { foreach ($statistics as $statistic) {
$id = (int) $statistic->transaction_currency_id; $id = (int) $statistic->transaction_currency_id;
$currency = Amount::getTransactionCurrencyById($id); $currency = Amount::getTransactionCurrencyById($id);
$grouped[$id] = [ $grouped[$id] = [
'amount' => (string) $statistic->amount, 'amount' => (string) $statistic->amount,
'count' => (int) $statistic->count, 'count' => (int) $statistic->count,
@@ -166,6 +179,7 @@ trait PeriodOverview
]; ];
$grouped['count'] += (int) $statistic->count; $grouped['count'] += (int) $statistic->count;
} }
return $grouped; return $grouped;
} }
@@ -182,7 +196,7 @@ trait PeriodOverview
$result = []; $result = [];
/** /**
* @var int $index * @var int $index
* @var array $item * @var array $item
*/ */
foreach ($transactions as $index => $item) { foreach ($transactions as $index => $item) {
@@ -202,7 +216,7 @@ trait PeriodOverview
$result = []; $result = [];
/** /**
* @var int $index * @var int $index
* @var array $item * @var array $item
*/ */
foreach ($transactions as $index => $item) { foreach ($transactions as $index => $item) {
@@ -237,16 +251,17 @@ trait PeriodOverview
if (!array_key_exists('currency_id', $journal)) { if (!array_key_exists('currency_id', $journal)) {
Log::debug('very strange!'); Log::debug('very strange!');
var_dump($journals); var_dump($journals);
exit; exit;
} }
$currencyId = (int)$journal['currency_id']; $currencyId = (int)$journal['currency_id'];
$currencyCode = $journal['currency_code']; $currencyCode = $journal['currency_code'];
$currencyName = $journal['currency_name']; $currencyName = $journal['currency_name'];
$currencySymbol = $journal['currency_symbol']; $currencySymbol = $journal['currency_symbol'];
$currencyDecimalPlaces = $journal['currency_decimal_places']; $currencyDecimalPlaces = $journal['currency_decimal_places'];
$foreignCurrencyId = $journal['foreign_currency_id']; $foreignCurrencyId = $journal['foreign_currency_id'];
$amount = $journal['amount'] ?? '0'; $amount = $journal['amount'] ?? '0';
if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId !== $this->primaryCurrency->id) { if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId !== $this->primaryCurrency->id) {
$amount = $journal['pc_amount'] ?? '0'; $amount = $journal['pc_amount'] ?? '0';
@@ -290,11 +305,11 @@ trait PeriodOverview
*/ */
protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array
{ {
$range = Navigation::getViewRange(true); $range = Navigation::getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; [$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for entries with their amounts. // properties for entries with their amounts.
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($range); $cache->addProperty($range);
@@ -306,32 +321,32 @@ trait PeriodOverview
} }
/** @var array $dates */ /** @var array $dates */
$dates = Navigation::blockPeriods($start, $end, $range); $dates = Navigation::blockPeriods($start, $end, $range);
$entries = []; $entries = [];
// collect all expenses in this period: // collect all expenses in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setCategory($category); $collector->setCategory($category);
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]);
$earnedSet = $collector->getExtractedJournals(); $earnedSet = $collector->getExtractedJournals();
// collect all income in this period: // collect all income in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setCategory($category); $collector->setCategory($category);
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$spentSet = $collector->getExtractedJournals(); $spentSet = $collector->getExtractedJournals();
// collect all transfers in this period: // collect all transfers in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setCategory($category); $collector->setCategory($category);
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::TRANSFER->value]); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]);
$transferSet = $collector->getExtractedJournals(); $transferSet = $collector->getExtractedJournals();
foreach ($dates as $currentDate) { foreach ($dates as $currentDate) {
$spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']);
$earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']);
@@ -339,17 +354,17 @@ trait PeriodOverview
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
$entries[] $entries[]
= [ = [
'transactions' => 0, 'transactions' => 0,
'title' => $title, 'title' => $title,
'route' => route( 'route' => route(
'categories.show', 'categories.show',
[$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]
), ),
'total_transactions' => count($spent) + count($earned) + count($transferred), 'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent), 'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned), 'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred), 'transferred' => $this->groupByCurrency($transferred),
]; ];
} }
$cache->store($entries); $cache->store($entries);
@@ -382,11 +397,11 @@ trait PeriodOverview
*/ */
protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array
{ {
$range = Navigation::getViewRange(true); $range = Navigation::getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; [$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($this->convertToPrimary); $cache->addProperty($this->convertToPrimary);
@@ -397,28 +412,28 @@ trait PeriodOverview
} }
/** @var array $dates */ /** @var array $dates */
$dates = Navigation::blockPeriods($start, $end, $range); $dates = Navigation::blockPeriods($start, $end, $range);
$entries = []; $entries = [];
// get all expenses without a budget. // get all expenses without a budget.
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
foreach ($dates as $currentDate) { foreach ($dates as $currentDate) {
$set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']); $set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']);
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
$entries[] $entries[]
= [ = [
'title' => $title, 'title' => $title,
'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
'total_transactions' => count($set), 'total_transactions' => count($set),
'spent' => $this->groupByCurrency($set), 'spent' => $this->groupByCurrency($set),
'earned' => [], 'earned' => [],
'transferred_away' => [], 'transferred_away' => [],
'transferred_in' => [], 'transferred_in' => [],
]; ];
} }
$cache->store($entries); $cache->store($entries);
@@ -435,38 +450,38 @@ trait PeriodOverview
protected function getNoCategoryPeriodOverview(Carbon $theDate): array protected function getNoCategoryPeriodOverview(Carbon $theDate): array
{ {
app('log')->debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); app('log')->debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d')));
$range = Navigation::getViewRange(true); $range = Navigation::getViewRange(true);
$first = $this->journalRepos->firstNull(); $first = $this->journalRepos->firstNull();
$start = null === $first ? new Carbon() : $first->date; $start = null === $first ? new Carbon() : $first->date;
$end = clone $theDate; $end = clone $theDate;
$end = Navigation::endOfPeriod($end, $range); $end = Navigation::endOfPeriod($end, $range);
app('log')->debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); app('log')->debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d')));
app('log')->debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); app('log')->debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d')));
// properties for cache // properties for cache
$dates = Navigation::blockPeriods($start, $end, $range); $dates = Navigation::blockPeriods($start, $end, $range);
$entries = []; $entries = [];
// collect all expenses in this period: // collect all expenses in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->withoutCategory(); $collector->withoutCategory();
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]);
$earnedSet = $collector->getExtractedJournals(); $earnedSet = $collector->getExtractedJournals();
// collect all income in this period: // collect all income in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->withoutCategory(); $collector->withoutCategory();
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$spentSet = $collector->getExtractedJournals(); $spentSet = $collector->getExtractedJournals();
// collect all transfers in this period: // collect all transfers in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->withoutCategory(); $collector->withoutCategory();
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::TRANSFER->value]); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]);
@@ -480,13 +495,13 @@ trait PeriodOverview
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
$entries[] $entries[]
= [ = [
'title' => $title, 'title' => $title,
'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
'total_transactions' => count($spent) + count($earned) + count($transferred), 'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent), 'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned), 'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred), 'transferred' => $this->groupByCurrency($transferred),
]; ];
} }
app('log')->debug('End of loops'); app('log')->debug('End of loops');
@@ -500,11 +515,11 @@ trait PeriodOverview
*/ */
protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags. protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags.
{ {
$range = Navigation::getViewRange(true); $range = Navigation::getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; [$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for cache // properties for cache
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty('tag-period-entries'); $cache->addProperty('tag-period-entries');
@@ -514,37 +529,37 @@ trait PeriodOverview
} }
/** @var array $dates */ /** @var array $dates */
$dates = Navigation::blockPeriods($start, $end, $range); $dates = Navigation::blockPeriods($start, $end, $range);
$entries = []; $entries = [];
// collect all expenses in this period: // collect all expenses in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setTag($tag); $collector->setTag($tag);
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]);
$earnedSet = $collector->getExtractedJournals(); $earnedSet = $collector->getExtractedJournals();
// collect all income in this period: // collect all income in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setTag($tag); $collector->setTag($tag);
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$spentSet = $collector->getExtractedJournals(); $spentSet = $collector->getExtractedJournals();
// collect all transfers in this period: // collect all transfers in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setTag($tag); $collector->setTag($tag);
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::TRANSFER->value]); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]);
$transferSet = $collector->getExtractedJournals(); $transferSet = $collector->getExtractedJournals();
// filer all of them: // filer all of them:
$earnedSet = $this->filterJournalsByTag($earnedSet, $tag); $earnedSet = $this->filterJournalsByTag($earnedSet, $tag);
$spentSet = $this->filterJournalsByTag($spentSet, $tag); $spentSet = $this->filterJournalsByTag($spentSet, $tag);
$transferSet = $this->filterJournalsByTag($transferSet, $tag); $transferSet = $this->filterJournalsByTag($transferSet, $tag);
foreach ($dates as $currentDate) { foreach ($dates as $currentDate) {
$spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']);
@@ -553,17 +568,17 @@ trait PeriodOverview
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
$entries[] $entries[]
= [ = [
'transactions' => 0, 'transactions' => 0,
'title' => $title, 'title' => $title,
'route' => route( 'route' => route(
'tags.show', 'tags.show',
[$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]
), ),
'total_transactions' => count($spent) + count($earned) + count($transferred), 'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent), 'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned), 'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred), 'transferred' => $this->groupByCurrency($transferred),
]; ];
} }
return $entries; return $entries;
@@ -573,7 +588,7 @@ trait PeriodOverview
{ {
$return = []; $return = [];
foreach ($set as $entry) { foreach ($set as $entry) {
$found = false; $found = false;
/** @var array $localTag */ /** @var array $localTag */
foreach ($entry['tags'] as $localTag) { foreach ($entry['tags'] as $localTag) {
@@ -595,12 +610,12 @@ trait PeriodOverview
*/ */
protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array
{ {
$range = Navigation::getViewRange(true); $range = Navigation::getViewRange(true);
$types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType));
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; [$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for cache // properties for cache
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty('transactions-period-entries'); $cache->addProperty('transactions-period-entries');
@@ -610,16 +625,16 @@ trait PeriodOverview
} }
/** @var array $dates */ /** @var array $dates */
$dates = Navigation::blockPeriods($start, $end, $range); $dates = Navigation::blockPeriods($start, $end, $range);
$entries = []; $entries = [];
$spent = []; $spent = [];
$earned = []; $earned = [];
$transferred = []; $transferred = [];
// collect all journals in this period (regardless of type) // collect all journals in this period (regardless of type)
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setTypes($types)->setRange($start, $end); $collector->setTypes($types)->setRange($start, $end);
$genericSet = $collector->getExtractedJournals(); $genericSet = $collector->getExtractedJournals();
$loops = 0; $loops = 0;
foreach ($dates as $currentDate) { foreach ($dates as $currentDate) {
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
@@ -637,14 +652,14 @@ trait PeriodOverview
} }
} }
$entries[] $entries[]
= [ = [
'title' => $title, 'title' => $title,
'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
'total_transactions' => count($spent) + count($earned) + count($transferred), 'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent), 'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned), 'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred), 'transferred' => $this->groupByCurrency($transferred),
]; ];
++$loops; ++$loops;
} }

62
composer.lock generated
View File

@@ -4235,16 +4235,16 @@
}, },
{ {
"name": "paragonie/constant_time_encoding", "name": "paragonie/constant_time_encoding",
"version": "v3.1.1", "version": "v3.1.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/paragonie/constant_time_encoding.git", "url": "https://github.com/paragonie/constant_time_encoding.git",
"reference": "5e9b582660b997de205a84c02a3aac7c060900c9" "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/5e9b582660b997de205a84c02a3aac7c060900c9", "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77",
"reference": "5e9b582660b997de205a84c02a3aac7c060900c9", "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -4300,7 +4300,7 @@
"issues": "https://github.com/paragonie/constant_time_encoding/issues", "issues": "https://github.com/paragonie/constant_time_encoding/issues",
"source": "https://github.com/paragonie/constant_time_encoding" "source": "https://github.com/paragonie/constant_time_encoding"
}, },
"time": "2025-09-22T21:00:33+00:00" "time": "2025-09-24T15:06:41+00:00"
}, },
{ {
"name": "paragonie/random_compat", "name": "paragonie/random_compat",
@@ -11406,16 +11406,16 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "2.1.28", "version": "2.1.29",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan-phar-composer-source.git",
"reference": "578fa296a166605d97b94091f724f1257185d278" "reference": "git"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/578fa296a166605d97b94091f724f1257185d278", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d618573eed4a1b6b75e37b2e0b65ac65c885d88e",
"reference": "578fa296a166605d97b94091f724f1257185d278", "reference": "d618573eed4a1b6b75e37b2e0b65ac65c885d88e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -11460,7 +11460,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-09-19T08:58:49+00:00" "time": "2025-09-25T06:58:18+00:00"
}, },
{ {
"name": "phpstan/phpstan-deprecation-rules", "name": "phpstan/phpstan-deprecation-rules",
@@ -11559,16 +11559,16 @@
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "12.3.8", "version": "12.4.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "99e692c6a84708211f7536ba322bbbaef57ac7fc" "reference": "67e8aed88f93d0e6e1cb7effe1a2dfc2fee6022c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/99e692c6a84708211f7536ba322bbbaef57ac7fc", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/67e8aed88f93d0e6e1cb7effe1a2dfc2fee6022c",
"reference": "99e692c6a84708211f7536ba322bbbaef57ac7fc", "reference": "67e8aed88f93d0e6e1cb7effe1a2dfc2fee6022c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -11595,7 +11595,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "12.3.x-dev" "dev-main": "12.4.x-dev"
} }
}, },
"autoload": { "autoload": {
@@ -11624,7 +11624,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.8" "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.4.0"
}, },
"funding": [ "funding": [
{ {
@@ -11644,7 +11644,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-09-17T11:31:43+00:00" "time": "2025-09-24T13:44:41+00:00"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@@ -11893,16 +11893,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "12.3.13", "version": "12.3.14",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "44f15312c4968fa8102e491fc6f3746410819c16" "reference": "13e9b2bea9327b094176147250d2c10319a10f5b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/44f15312c4968fa8102e491fc6f3746410819c16", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/13e9b2bea9327b094176147250d2c10319a10f5b",
"reference": "44f15312c4968fa8102e491fc6f3746410819c16", "reference": "13e9b2bea9327b094176147250d2c10319a10f5b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -11925,7 +11925,7 @@
"sebastian/comparator": "^7.1.3", "sebastian/comparator": "^7.1.3",
"sebastian/diff": "^7.0.0", "sebastian/diff": "^7.0.0",
"sebastian/environment": "^8.0.3", "sebastian/environment": "^8.0.3",
"sebastian/exporter": "^7.0.1", "sebastian/exporter": "^7.0.2",
"sebastian/global-state": "^8.0.2", "sebastian/global-state": "^8.0.2",
"sebastian/object-enumerator": "^7.0.0", "sebastian/object-enumerator": "^7.0.0",
"sebastian/type": "^6.0.3", "sebastian/type": "^6.0.3",
@@ -11970,7 +11970,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues", "issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy", "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.13" "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.14"
}, },
"funding": [ "funding": [
{ {
@@ -11994,7 +11994,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-09-23T06:25:02+00:00" "time": "2025-09-24T06:34:27+00:00"
}, },
{ {
"name": "rector/rector", "name": "rector/rector",
@@ -12420,16 +12420,16 @@
}, },
{ {
"name": "sebastian/exporter", "name": "sebastian/exporter",
"version": "7.0.1", "version": "7.0.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git", "url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "b759164a8e02263784b662889cc6cbb686077af6" "reference": "016951ae10980765e4e7aee491eb288c64e505b7"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/b759164a8e02263784b662889cc6cbb686077af6", "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/016951ae10980765e4e7aee491eb288c64e505b7",
"reference": "b759164a8e02263784b662889cc6cbb686077af6", "reference": "016951ae10980765e4e7aee491eb288c64e505b7",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -12486,7 +12486,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues", "issues": "https://github.com/sebastianbergmann/exporter/issues",
"security": "https://github.com/sebastianbergmann/exporter/security/policy", "security": "https://github.com/sebastianbergmann/exporter/security/policy",
"source": "https://github.com/sebastianbergmann/exporter/tree/7.0.1" "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.2"
}, },
"funding": [ "funding": [
{ {
@@ -12506,7 +12506,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-09-22T05:39:29+00:00" "time": "2025-09-24T06:16:11+00:00"
}, },
{ {
"name": "sebastian/global-state", "name": "sebastian/global-state",

View File

@@ -78,8 +78,8 @@ return [
'running_balance_column' => env('USE_RUNNING_BALANCE', false), 'running_balance_column' => env('USE_RUNNING_BALANCE', false),
// see cer.php for exchange rates feature flag. // see cer.php for exchange rates feature flag.
], ],
'version' => 'develop/2025-09-23', 'version' => 'develop/2025-09-25',
'build_time' => 1758652788, 'build_time' => 1758820163,
'api_version' => '2.1.0', // field is no longer used. 'api_version' => '2.1.0', // field is no longer used.
'db_version' => 27, 'db_version' => 27,

115
package-lock.json generated
View File

@@ -3281,57 +3281,57 @@
} }
}, },
"node_modules/@vue/compiler-core": { "node_modules/@vue/compiler-core": {
"version": "3.5.21", "version": "3.5.22",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.22.tgz",
"integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==", "integrity": "sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/parser": "^7.28.3", "@babel/parser": "^7.28.4",
"@vue/shared": "3.5.21", "@vue/shared": "3.5.22",
"entities": "^4.5.0", "entities": "^4.5.0",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map-js": "^1.2.1" "source-map-js": "^1.2.1"
} }
}, },
"node_modules/@vue/compiler-dom": { "node_modules/@vue/compiler-dom": {
"version": "3.5.21", "version": "3.5.22",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.22.tgz",
"integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==", "integrity": "sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/compiler-core": "3.5.21", "@vue/compiler-core": "3.5.22",
"@vue/shared": "3.5.21" "@vue/shared": "3.5.22"
} }
}, },
"node_modules/@vue/compiler-sfc": { "node_modules/@vue/compiler-sfc": {
"version": "3.5.21", "version": "3.5.22",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.22.tgz",
"integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==", "integrity": "sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/parser": "^7.28.3", "@babel/parser": "^7.28.4",
"@vue/compiler-core": "3.5.21", "@vue/compiler-core": "3.5.22",
"@vue/compiler-dom": "3.5.21", "@vue/compiler-dom": "3.5.22",
"@vue/compiler-ssr": "3.5.21", "@vue/compiler-ssr": "3.5.22",
"@vue/shared": "3.5.21", "@vue/shared": "3.5.22",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"magic-string": "^0.30.18", "magic-string": "^0.30.19",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"source-map-js": "^1.2.1" "source-map-js": "^1.2.1"
} }
}, },
"node_modules/@vue/compiler-ssr": { "node_modules/@vue/compiler-ssr": {
"version": "3.5.21", "version": "3.5.22",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.22.tgz",
"integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==", "integrity": "sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.5.21", "@vue/compiler-dom": "3.5.22",
"@vue/shared": "3.5.21" "@vue/shared": "3.5.22"
} }
}, },
"node_modules/@vue/component-compiler-utils": { "node_modules/@vue/component-compiler-utils": {
@@ -3413,9 +3413,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@vue/shared": { "node_modules/@vue/shared": {
"version": "3.5.21", "version": "3.5.22",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.22.tgz",
"integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==", "integrity": "sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@@ -4075,9 +4075,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/baseline-browser-mapping": { "node_modules/baseline-browser-mapping": {
"version": "2.8.6", "version": "2.8.7",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.6.tgz", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.7.tgz",
"integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==", "integrity": "sha512-bxxN2M3a4d1CRoQC//IqsR5XrLh0IJ8TCv2x6Y9N0nckNz/rTjZB3//GGscZziZOxmjP55rzxg/ze7usFI9FqQ==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
@@ -4329,9 +4329,9 @@
} }
}, },
"node_modules/browserify-sign": { "node_modules/browserify-sign": {
"version": "4.2.4", "version": "4.2.5",
"resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.4.tgz", "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.5.tgz",
"integrity": "sha512-pbZw0FHibrwXcpLQlXwHp21A5undDBo+RaGNL0K3KOm8nK8uP6PThhS301VDzoMgURZPiVRWRrVHlo6NyU57kA==", "integrity": "sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
@@ -4341,12 +4341,12 @@
"create-hmac": "^1.1.7", "create-hmac": "^1.1.7",
"elliptic": "^6.6.1", "elliptic": "^6.6.1",
"inherits": "^2.0.4", "inherits": "^2.0.4",
"parse-asn1": "^5.1.7", "parse-asn1": "^5.1.9",
"readable-stream": "^2.3.8", "readable-stream": "^2.3.8",
"safe-buffer": "^5.2.1" "safe-buffer": "^5.2.1"
}, },
"engines": { "engines": {
"node": ">= 0.12" "node": ">= 0.10"
} }
}, },
"node_modules/browserify-zlib": { "node_modules/browserify-zlib": {
@@ -4521,9 +4521,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001743", "version": "1.0.30001745",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001745.tgz",
"integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==", "integrity": "sha512-ywt6i8FzvdgrrrGbr1jZVObnVv6adj+0if2/omv9cmR2oiZs30zL4DIyaptKcbOrBdOIc74QTMoJvSE2QHh5UQ==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -4651,14 +4651,15 @@
} }
}, },
"node_modules/cipher-base": { "node_modules/cipher-base": {
"version": "1.0.6", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.7.tgz",
"integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==", "integrity": "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"inherits": "^2.0.4", "inherits": "^2.0.4",
"safe-buffer": "^5.2.1" "safe-buffer": "^5.2.1",
"to-buffer": "^1.2.2"
}, },
"engines": { "engines": {
"node": ">= 0.10" "node": ">= 0.10"
@@ -8760,20 +8761,20 @@
} }
}, },
"node_modules/parse-asn1": { "node_modules/parse-asn1": {
"version": "5.1.8", "version": "5.1.9",
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.8.tgz", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.9.tgz",
"integrity": "sha512-e90aVPe/1q/g7BrNeYvbJy++5tln4ShE+I3qZ5LxFpUbu+uavfKMuzH2R3SH141O7Pvruwif0BZRwKoVf6vW6w==", "integrity": "sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"asn1.js": "^4.10.1", "asn1.js": "^4.10.1",
"browserify-aes": "^1.2.0", "browserify-aes": "^1.2.0",
"evp_bytestokey": "^1.0.3", "evp_bytestokey": "^1.0.3",
"pbkdf2": "^3.1.3", "pbkdf2": "^3.1.5",
"safe-buffer": "^5.2.1" "safe-buffer": "^5.2.1"
}, },
"engines": { "engines": {
"node": ">= 0.12" "node": ">= 0.10"
} }
}, },
"node_modules/parse-json": { "node_modules/parse-json": {
@@ -8935,9 +8936,9 @@
} }
}, },
"node_modules/pbkdf2": { "node_modules/pbkdf2": {
"version": "3.1.4", "version": "3.1.5",
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.4.tgz", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz",
"integrity": "sha512-0yPXNT01PxSUdkIxL85Fd+yPdeCcvGwFPAAHbR3Z2ukMERcRrJFfLUKK3oglbQ9eUPeX6qDY3QiELqiDarZYUQ==", "integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -8949,7 +8950,7 @@
"to-buffer": "^1.2.1" "to-buffer": "^1.2.1"
}, },
"engines": { "engines": {
"node": ">=0.12" "node": ">= 0.10"
} }
}, },
"node_modules/picocolors": { "node_modules/picocolors": {
@@ -10222,9 +10223,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/sass": { "node_modules/sass": {
"version": "1.93.1", "version": "1.93.2",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.93.1.tgz", "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.2.tgz",
"integrity": "sha512-wLAeLB7IksO2u+cCfhHqcy7/2ZUMPp/X2oV6+LjmweTqgjhOKrkaE/Q1wljxtco5EcOcupZ4c981X0gpk5Tiag==", "integrity": "sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -11233,9 +11234,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/to-buffer": { "node_modules/to-buffer": {
"version": "1.2.1", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz",
"integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==", "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {