mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-11-03 12:49:58 -06:00
Merge pull request #12930 from firefly-iii/release-1790710740
🤖 Automatically merge the PR into the develop branch.
This commit is contained in:
36 files changed
+237
-322
No files matched your search
Generated
+12
-12
@@ -1331,16 +1331,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v8.1.7",
|
||||
"version": "v8.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "29afb89f4e941f68a6e90f28e3f52ff1f6793a7d"
|
||||
"reference": "4b81146e3ee248ea969186f85499b54fbc78db08"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/29afb89f4e941f68a6e90f28e3f52ff1f6793a7d",
|
||||
"reference": "29afb89f4e941f68a6e90f28e3f52ff1f6793a7d",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/4b81146e3ee248ea969186f85499b54fbc78db08",
|
||||
"reference": "4b81146e3ee248ea969186f85499b54fbc78db08",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1407,7 +1407,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v8.1.7"
|
||||
"source": "https://github.com/symfony/console/tree/v8.1.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1427,7 +1427,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-09-13T10:55:57+00:00"
|
||||
"time": "2026-09-18T16:00:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
@@ -1739,16 +1739,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v8.1.7",
|
||||
"version": "v8.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "4fbe46a3eb64abf8a57f0364075b91f4a233e062"
|
||||
"reference": "bd4fed193a3132c91c5c93595cbfc78988915822"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/4fbe46a3eb64abf8a57f0364075b91f4a233e062",
|
||||
"reference": "4fbe46a3eb64abf8a57f0364075b91f4a233e062",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/bd4fed193a3132c91c5c93595cbfc78988915822",
|
||||
"reference": "bd4fed193a3132c91c5c93595cbfc78988915822",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1783,7 +1783,7 @@
|
||||
"description": "Finds files and directories via an intuitive fluent interface",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/finder/tree/v8.1.7"
|
||||
"source": "https://github.com/symfony/finder/tree/v8.1.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1803,7 +1803,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-09-10T19:14:39+00:00"
|
||||
"time": "2026-09-26T06:33:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
|
||||
@@ -158,10 +158,7 @@ final class TagController extends Controller
|
||||
'currency_id' => (string) $foreignCurrencyId,
|
||||
'currency_code' => $journal['foreign_currency_code'],
|
||||
];
|
||||
$response[$foreignKey]['difference'] = bcadd(
|
||||
(string) $response[$foreignKey]['difference'],
|
||||
Steam::positive($journal['foreign_amount'])
|
||||
);
|
||||
$response[$foreignKey]['difference'] = bcadd((string) $response[$foreignKey]['difference'], Steam::positive($journal['foreign_amount']));
|
||||
$response[$foreignKey]['difference_float'] = (float) $response[$foreignKey]['difference'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,10 +155,7 @@ final class TagController extends Controller
|
||||
'currency_id' => (string) $foreignCurrencyId,
|
||||
'currency_code' => $journal['foreign_currency_code'],
|
||||
];
|
||||
$response[$foreignKey]['difference'] = bcadd(
|
||||
(string) $response[$foreignKey]['difference'],
|
||||
Steam::positive($journal['foreign_amount'])
|
||||
);
|
||||
$response[$foreignKey]['difference'] = bcadd((string) $response[$foreignKey]['difference'], Steam::positive($journal['foreign_amount']));
|
||||
$response[$foreignKey]['difference_float'] = (float) $response[$foreignKey]['difference']; // intentional float
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ abstract class AggregateFormRequest extends ApiRequest
|
||||
parent::initialize($query, $request, $attributes, $cookies, $files, $server, $content);
|
||||
|
||||
// instantiate all subrequests and share current requests' bags with them
|
||||
Log::debug('Initializing AggregateFormRequest.');
|
||||
Log::debug('Initializing AggregateFormRequest.');
|
||||
|
||||
/** @var array|string $config */
|
||||
foreach ($this->getRequests() as $config) {
|
||||
@@ -62,7 +62,7 @@ abstract class AggregateFormRequest extends ApiRequest
|
||||
if (!is_a($requestClass, Request::class, true)) {
|
||||
throw new RuntimeException('getRequests() must return class-strings of subclasses of Request');
|
||||
}
|
||||
Log::debug(sprintf('Initializing subrequest %s', $requestClass));
|
||||
Log::debug(sprintf('Initializing subrequest %s', $requestClass));
|
||||
|
||||
$instance = new $requestClass();
|
||||
$this->requests[] = $instance;
|
||||
@@ -97,7 +97,7 @@ abstract class AggregateFormRequest extends ApiRequest
|
||||
// register all subrequests' validators
|
||||
foreach ($this->requests as $request) {
|
||||
if (method_exists($request, 'withValidator')) {
|
||||
Log::debug(sprintf('Process withValidator from class %s', $request::class));
|
||||
Log::debug(sprintf('Process withValidator from class %s', $request::class));
|
||||
$request->withValidator($validator);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace FireflyIII\Api\V1\Requests\Generic;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\ApiRequest;
|
||||
use FireflyIII\Rules\IsValidFilterInstruction;
|
||||
use FireflyIII\Rules\IsValidSortInstruction;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
@@ -36,6 +35,7 @@ class FilterRequest extends ApiRequest
|
||||
{
|
||||
use ChecksLogin;
|
||||
use ConvertsDataTypes;
|
||||
|
||||
private ?string $filterClass = null;
|
||||
|
||||
public function handleConfig(array $config): void
|
||||
@@ -45,7 +45,7 @@ class FilterRequest extends ApiRequest
|
||||
$this->filterClass = $config['filter_class'] ?? null;
|
||||
|
||||
if (null === $this->filterClass) {
|
||||
//throw new RuntimeException('FilterRequest requires a filter_class config');
|
||||
// throw new RuntimeException('FilterRequest requires a filter_class config');
|
||||
$this->filterClass = 'Account';
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ class FilterRequest extends ApiRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'filter' => ['min:0','max:255', $this->required, new IsValidFilterInstruction((string) $this->filterClass)],
|
||||
'filter' => ['min:0', 'max:255', $this->required, new IsValidFilterInstruction((string) $this->filterClass)],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -65,7 +65,8 @@ class FilterRequest extends ApiRequest
|
||||
}
|
||||
});
|
||||
}
|
||||
// private function convertToFilter(string $value): array {
|
||||
// return [];
|
||||
// }
|
||||
|
||||
// private function convertToFilter(string $value): array {
|
||||
// return [];
|
||||
// }
|
||||
}
|
||||
@@ -28,7 +28,6 @@ use FireflyIII\Api\V1\Requests\AggregateFormRequest;
|
||||
use FireflyIII\Api\V1\Requests\DateRangeRequest;
|
||||
use FireflyIII\Api\V1\Requests\DateRequest;
|
||||
use FireflyIII\Api\V1\Requests\Generic\ActiveObjectRequest;
|
||||
use FireflyIII\Api\V1\Requests\Generic\FilterRequest;
|
||||
use FireflyIII\Api\V1\Requests\PaginationRequest;
|
||||
use FireflyIII\Models\Account;
|
||||
|
||||
|
||||
@@ -50,21 +50,20 @@ class PaginationRequest extends ApiRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'sort' => ['nullable', new IsValidSortInstruction((string) $this->sortClass)],
|
||||
'filter' => ['nullable', new IsValidFilterInstruction((string)$this->sortClass)],
|
||||
'limit' => ['numeric', 'min:1', 'max:131337'],
|
||||
'page' => ['numeric', 'min:1', 'max:131337'],
|
||||
'sort' => ['nullable', new IsValidSortInstruction((string) $this->sortClass)],
|
||||
'filter' => ['nullable', new IsValidFilterInstruction((string) $this->sortClass)],
|
||||
'limit' => ['numeric', 'min:1', 'max:131337'],
|
||||
'page' => ['numeric', 'min:1', 'max:131337'],
|
||||
];
|
||||
}
|
||||
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
|
||||
$validator->after(function (Validator $validator): void {
|
||||
if (count($validator->failed()) > 0) {
|
||||
return;
|
||||
}
|
||||
$data = $validator->getData();
|
||||
$data = $validator->getData();
|
||||
$limit = $this->integerFromValue($data['limit'] ?? null);
|
||||
if (0 === $limit || null === $limit) {
|
||||
// get default for user:
|
||||
@@ -77,7 +76,7 @@ class PaginationRequest extends ApiRequest
|
||||
$page = clamp(value: $page, min: 1, max: 2 ** 16);
|
||||
$offset = ($page - 1) * $limit;
|
||||
$sort = null !== $this->sortClass ? $this->convertSortParameters('sort', $this->sortClass) : '';
|
||||
$filter = null !== $this->sortClass ? $this->convertFilterParameters('filter', $this->sortClass) : [];
|
||||
$filter = null !== $this->sortClass ? $this->convertFilterParameters('filter', $this->sortClass) : [];
|
||||
$this->attributes->set('limit', $limit);
|
||||
$this->attributes->set('sort', $sort);
|
||||
$this->attributes->set('filter', $filter);
|
||||
|
||||
@@ -175,9 +175,10 @@ class AttachmentHelper implements AttachmentHelperInterface
|
||||
$parts = explode('/', $attachment->fileName());
|
||||
$file = $parts[count($parts) - 1];
|
||||
Log::debug(sprintf('Write file to disk in file named "%s"', $file));
|
||||
$res = $this->uploadDisk->put($file, $content);
|
||||
if(false === $res) {
|
||||
$res = $this->uploadDisk->put($file, $content);
|
||||
if (false === $res) {
|
||||
Log::error('Could not write to disk.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -198,13 +198,7 @@ final class BudgetLimitController extends Controller
|
||||
if ($request->expectsJson()) {
|
||||
$array = $limit->toArray();
|
||||
// add some extra metadata:
|
||||
$spentArr = $this->opsRepository->sumExpenses(
|
||||
$limit->start_date,
|
||||
$limit->end_date,
|
||||
null,
|
||||
new Collection()->push($budget),
|
||||
$currency
|
||||
);
|
||||
$spentArr = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection()->push($budget), $currency);
|
||||
$array['spent'] = $spentArr[$currency->id]['sum'] ?? '0';
|
||||
$array['left_formatted'] = Amount::formatAnything($limit->transactionCurrency, bcadd($array['spent'], (string) $array['amount']));
|
||||
$array['amount_formatted'] = Amount::formatAnything($limit->transactionCurrency, $limit['amount']);
|
||||
|
||||
@@ -272,10 +272,7 @@ final class IndexController extends Controller
|
||||
|
||||
if (array_key_exists($currency->id, $spentArr) && array_key_exists('sum', $spentArr[$currency->id])) {
|
||||
$array['spent'][$currency->id]['spent'] = $spentArr[$currency->id]['sum'];
|
||||
$array['spent'][$currency->id]['spent_outside'] = Steam::negative(bcsub(
|
||||
$spentInLimits[$currency->id],
|
||||
$spentArr[$currency->id]['sum']
|
||||
));
|
||||
$array['spent'][$currency->id]['spent_outside'] = Steam::negative(bcsub($spentInLimits[$currency->id], $spentArr[$currency->id]['sum']));
|
||||
$array['spent'][$currency->id]['currency_id'] = $currency->id;
|
||||
$array['spent'][$currency->id]['currency_symbol'] = $currency->symbol;
|
||||
$array['spent'][$currency->id]['currency_decimal_places'] = $currency->decimal_places;
|
||||
|
||||
@@ -539,13 +539,7 @@ final class BudgetController extends Controller
|
||||
}
|
||||
|
||||
// get spent amount in this period for this currency.
|
||||
$sum = $this->opsRepository->sumExpenses(
|
||||
$currentStart,
|
||||
$currentEnd,
|
||||
$accounts,
|
||||
new Collection()->push($budget),
|
||||
$currency
|
||||
);
|
||||
$sum = $this->opsRepository->sumExpenses($currentStart, $currentEnd, $accounts, new Collection()->push($budget), $currency);
|
||||
$amount = Steam::positive($sum[$currency->id]['sum'] ?? '0');
|
||||
$chartData[0]['entries'][$title] = Steam::bcround($amount, $currency->decimal_places);
|
||||
|
||||
|
||||
@@ -139,8 +139,8 @@ final class BudgetController extends Controller
|
||||
*/
|
||||
public function avgExpenses(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end)
|
||||
{
|
||||
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, $budgets);
|
||||
$result = [];
|
||||
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, $budgets);
|
||||
$result = [];
|
||||
foreach ($spent as $currency) {
|
||||
foreach ($currency['budgets'] as $budget) {
|
||||
foreach ($budget['transaction_journals'] as $journal) {
|
||||
@@ -167,7 +167,7 @@ final class BudgetController extends Controller
|
||||
}
|
||||
// sort by amount_float
|
||||
// sort temp array by amount.
|
||||
$amounts = array_column($result, 'avg_float');
|
||||
$amounts = array_column($result, 'avg_float');
|
||||
array_multisort($amounts, SORT_ASC, $result);
|
||||
$incomeTopLength = count(array_keys($result));
|
||||
|
||||
|
||||
@@ -250,8 +250,8 @@ final class CategoryController extends Controller
|
||||
*/
|
||||
public function avgExpenses(Collection $accounts, Collection $categories, Carbon $start, Carbon $end)
|
||||
{
|
||||
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, $categories);
|
||||
$result = [];
|
||||
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, $categories);
|
||||
$result = [];
|
||||
foreach ($spent as $currency) {
|
||||
foreach ($currency['categories'] as $category) {
|
||||
foreach ($category['transaction_journals'] as $journal) {
|
||||
@@ -278,7 +278,7 @@ final class CategoryController extends Controller
|
||||
}
|
||||
// sort by amount_float
|
||||
// sort temp array by amount.
|
||||
$amounts = array_column($result, 'avg_float');
|
||||
$amounts = array_column($result, 'avg_float');
|
||||
array_multisort($amounts, SORT_ASC, $result);
|
||||
$incomeTopLength = count(array_keys($result));
|
||||
|
||||
|
||||
@@ -69,10 +69,10 @@ final class DoubleController extends Controller
|
||||
*/
|
||||
public function avgExpenses(Collection $accounts, Collection $doubles, Carbon $start, Carbon $end)
|
||||
{
|
||||
$expanded = $this->accountRepository->expandWithDoubles($doubles);
|
||||
$accounts = $accounts->merge($expanded);
|
||||
$spent = $this->opsRepository->listExpenses($start, $end, $accounts);
|
||||
$result = [];
|
||||
$expanded = $this->accountRepository->expandWithDoubles($doubles);
|
||||
$accounts = $accounts->merge($expanded);
|
||||
$spent = $this->opsRepository->listExpenses($start, $end, $accounts);
|
||||
$result = [];
|
||||
foreach ($spent as $currency) {
|
||||
foreach ($currency['transaction_journals'] as $journal) {
|
||||
$sourceId = $journal['source_account_id'];
|
||||
@@ -98,7 +98,7 @@ final class DoubleController extends Controller
|
||||
$incomeTopLength = count(array_keys($result));
|
||||
// sort by amount_float
|
||||
// sort temp array by amount.
|
||||
$amounts = array_column($result, 'avg_float');
|
||||
$amounts = array_column($result, 'avg_float');
|
||||
array_multisort($amounts, SORT_ASC, $result);
|
||||
|
||||
try {
|
||||
|
||||
@@ -132,13 +132,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
||||
// if has one, calculate expenses and use that as a base.
|
||||
$repository = app(OperationsRepositoryInterface::class);
|
||||
$repository->setUser($autoBudget->budget->user);
|
||||
$spent = $repository->sumExpenses(
|
||||
$previousStart,
|
||||
$previousEnd,
|
||||
null,
|
||||
new Collection()->push($autoBudget->budget),
|
||||
$autoBudget->transactionCurrency
|
||||
);
|
||||
$spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection()->push($autoBudget->budget), $autoBudget->transactionCurrency);
|
||||
$currencyId = $autoBudget->transaction_currency_id;
|
||||
$spentAmount = $spent[$currencyId]['sum'] ?? '0';
|
||||
Log::debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount));
|
||||
@@ -228,13 +222,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
||||
// if has one, calculate expenses and use that as a base.
|
||||
$repository = app(OperationsRepositoryInterface::class);
|
||||
$repository->setUser($autoBudget->budget->user);
|
||||
$spent = $repository->sumExpenses(
|
||||
$previousStart,
|
||||
$previousEnd,
|
||||
null,
|
||||
new Collection()->push($autoBudget->budget),
|
||||
$autoBudget->transactionCurrency
|
||||
);
|
||||
$spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection()->push($autoBudget->budget), $autoBudget->transactionCurrency);
|
||||
$currencyId = $autoBudget->transaction_currency_id;
|
||||
$spentAmount = $spent[$currencyId]['sum'] ?? '0';
|
||||
Log::debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount));
|
||||
|
||||
@@ -244,8 +244,8 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac
|
||||
// add sort parameters
|
||||
$allowed = config('firefly.allowed_db_sort_parameters.Account', []);
|
||||
$sorted = 0;
|
||||
$sort = $sort ?? [];
|
||||
$filter = $filter ?? [];
|
||||
$sort ??= [];
|
||||
$filter ??= [];
|
||||
if (0 !== count($sort)) {
|
||||
foreach ($sort as $param) {
|
||||
if (in_array($param[0], $allowed, true)) {
|
||||
@@ -259,7 +259,7 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac
|
||||
if (0 !== count($filter)) {
|
||||
foreach ($filter as $field => $search) {
|
||||
if (in_array($field, $allowed, true)) {
|
||||
if('' !== (string) $search) {
|
||||
if ('' !== (string) $search) {
|
||||
$query->whereLike($field, sprintf('%%%s%%', $search));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* IsValidFilterInstructions.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
@@ -25,6 +26,7 @@ namespace FireflyIII\Rules;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Override;
|
||||
|
||||
class IsValidFilterInstruction implements ValidationRule
|
||||
{
|
||||
@@ -32,13 +34,13 @@ class IsValidFilterInstruction implements ValidationRule
|
||||
private readonly string $class
|
||||
) {}
|
||||
|
||||
#[\Override]
|
||||
#[Override]
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
if(!is_array($value)) {
|
||||
if (!is_array($value)) {
|
||||
return;
|
||||
}
|
||||
if(0 === count($value)) {
|
||||
if (0 === count($value)) {
|
||||
return;
|
||||
}
|
||||
$shortClass = str_replace('FireflyIII\Models\\', '', $this->class);
|
||||
@@ -48,16 +50,18 @@ class IsValidFilterInstruction implements ValidationRule
|
||||
|
||||
return;
|
||||
}
|
||||
foreach($value as $key => $search) {
|
||||
if(!in_array($key, $validParameters, true)) {
|
||||
foreach ($value as $key => $search) {
|
||||
if (!in_array($key, $validParameters, true)) {
|
||||
$fail('validation.no_filter_instructions')->translate(['object' => $shortClass]);
|
||||
|
||||
return;
|
||||
}
|
||||
if(strlen($search) > 50) {
|
||||
if (strlen($search) > 50) {
|
||||
$fail('validation.no_filter_instructions')->translate(['object' => $shortClass]);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -222,14 +222,7 @@ trait AugumentData
|
||||
$currentEnd->addMonth();
|
||||
}
|
||||
// primary currency amount.
|
||||
$expenses = $opsRepository->sumExpenses(
|
||||
$currentStart,
|
||||
$currentEnd,
|
||||
null,
|
||||
$budgetCollection,
|
||||
$entry->transactionCurrency,
|
||||
$this->convertToPrimary
|
||||
);
|
||||
$expenses = $opsRepository->sumExpenses($currentStart, $currentEnd, null, $budgetCollection, $entry->transactionCurrency, $this->convertToPrimary);
|
||||
$spent = $expenses[$currency->id]['sum'] ?? '0';
|
||||
$entry->pc_spent = $spent;
|
||||
|
||||
|
||||
@@ -354,11 +354,7 @@ class RecurringEnrichment implements EnrichmentInterface
|
||||
|
||||
/** @var RecurrenceRepetition $repetition */
|
||||
foreach ($set as $repetition) {
|
||||
$recurrence = $this->collection->filter(
|
||||
static fn (Recurrence $item): bool => (int) $item->id === (int) $repetition->recurrence_id
|
||||
)
|
||||
->first()
|
||||
;
|
||||
$recurrence = $this->collection->filter(static fn (Recurrence $item): bool => (int) $item->id === (int) $repetition->recurrence_id)->first();
|
||||
$fromDate = clone ($recurrence->latest_date ?? $recurrence->first_date);
|
||||
$recurrenceId = (int) $repetition->recurrence_id;
|
||||
$repId = (int) $repetition->id;
|
||||
|
||||
@@ -151,6 +151,25 @@ trait ConvertsDataTypes
|
||||
return trim($string);
|
||||
}
|
||||
|
||||
public function convertFilterParameters(string $field, string $class): array
|
||||
{
|
||||
$parameter = request()->query->all($field);
|
||||
|
||||
if (0 === count($parameter)) {
|
||||
return [];
|
||||
}
|
||||
$shortClass = str_replace('FireflyIII\Models\\', '', $class);
|
||||
$validParameters = config(sprintf('firefly.allowed_filter_parameters.%s', $shortClass));
|
||||
$return = [];
|
||||
foreach ($parameter as $key => $search) {
|
||||
if (in_array($key, $validParameters, true) && strlen((string) $search) <= 50) {
|
||||
$return[$key] = trim((string) $search);
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function convertIban(string $field): string
|
||||
{
|
||||
return Steam::filterSpaces($this->convertString($field));
|
||||
@@ -186,24 +205,6 @@ trait ConvertsDataTypes
|
||||
return $sortParameters;
|
||||
}
|
||||
|
||||
public function convertFilterParameters(string $field, string $class): array
|
||||
{
|
||||
$parameter = request()->query->all($field);
|
||||
|
||||
if(0 === count($parameter)) {
|
||||
return [];
|
||||
}
|
||||
$shortClass = str_replace('FireflyIII\Models\\', '', $class);
|
||||
$validParameters = config(sprintf('firefly.allowed_filter_parameters.%s', $shortClass));
|
||||
$return = [];
|
||||
foreach($parameter as $key => $search) {
|
||||
if(in_array($key, $validParameters, true) && strlen((string)$search) <= 50) {
|
||||
$return[$key] = trim((string)$search);
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return string value.
|
||||
*/
|
||||
|
||||
+3
-3
@@ -9,9 +9,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
### Fixed
|
||||
|
||||
- #12928
|
||||
- #12922
|
||||
- #12929
|
||||
- [Issue 12928](https://github.com/firefly-iii/firefly-iii/issues/12928) (Webhook creation fails) reported by @Xav-v
|
||||
- [Issue 12922](https://github.com/firefly-iii/firefly-iii/issues/12922) (Undefined variable $incomeTopLength in category graphs) reported by @GunoH
|
||||
- [Issue 12929](https://github.com/firefly-iii/firefly-iii/issues/12929) (v6.7.6 Alpine.js requires 'unsafe-eval' in CSP – breaks behind strict proxies) reported by @37-b-j
|
||||
|
||||
## v6.7.6 - 2026-09-28
|
||||
|
||||
|
||||
Generated
+80
-79
@@ -1012,16 +1012,16 @@
|
||||
},
|
||||
{
|
||||
"name": "firebase/php-jwt",
|
||||
"version": "v7.2.0",
|
||||
"version": "v7.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/googleapis/php-jwt.git",
|
||||
"reference": "f502cdbf279cd7532060b041f7c22a05208c0b93"
|
||||
"reference": "0501f5729b9e2448516674aff07c7d1727869b8e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/googleapis/php-jwt/zipball/f502cdbf279cd7532060b041f7c22a05208c0b93",
|
||||
"reference": "f502cdbf279cd7532060b041f7c22a05208c0b93",
|
||||
"url": "https://api.github.com/repos/googleapis/php-jwt/zipball/0501f5729b9e2448516674aff07c7d1727869b8e",
|
||||
"reference": "0501f5729b9e2448516674aff07c7d1727869b8e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1080,9 +1080,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/googleapis/php-jwt/issues",
|
||||
"source": "https://github.com/googleapis/php-jwt/tree/v7.2.0"
|
||||
"source": "https://github.com/googleapis/php-jwt/tree/v7.2.1"
|
||||
},
|
||||
"time": "2026-09-21T19:29:58+00:00"
|
||||
"time": "2026-09-28T17:51:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "fruitcake/php-cors",
|
||||
@@ -1897,16 +1897,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v13.33.0",
|
||||
"version": "v13.34.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "91188a17ceaa3dbace6e8a5f7abd0d042e466359"
|
||||
"reference": "c829b4982d29344cbcf1d7ad78eb5d2bb8ae66c4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/91188a17ceaa3dbace6e8a5f7abd0d042e466359",
|
||||
"reference": "91188a17ceaa3dbace6e8a5f7abd0d042e466359",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/c829b4982d29344cbcf1d7ad78eb5d2bb8ae66c4",
|
||||
"reference": "c829b4982d29344cbcf1d7ad78eb5d2bb8ae66c4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2022,10 +2022,10 @@
|
||||
"league/flysystem-sftp-v3": "^3.25.1",
|
||||
"mockery/mockery": "^1.6.10",
|
||||
"opis/json-schema": "^2.4.1",
|
||||
"orchestra/testbench-core": "^11.0.0",
|
||||
"orchestra/testbench-core": "^11.5.0",
|
||||
"pda/pheanstalk": "^7.0.0 || ^8.0.0",
|
||||
"php-http/discovery": "^1.15",
|
||||
"phpstan/phpstan": "^2.2.14",
|
||||
"phpstan/phpstan": "2.2.15",
|
||||
"phpunit/phpunit": "^11.5.50 || ^12.5.8 || ^13.0.3",
|
||||
"predis/predis": "^2.3 || ^3.0",
|
||||
"rector/rector": "2.6.3",
|
||||
@@ -2124,7 +2124,7 @@
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2026-09-22T14:12:33+00:00"
|
||||
"time": "2026-09-29T12:46:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/passport",
|
||||
@@ -3535,16 +3535,16 @@
|
||||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
"version": "3.12.0",
|
||||
"version": "3.12.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Seldaek/monolog.git",
|
||||
"reference": "72c534fc0ab181ef52d92a68382318631e301608"
|
||||
"reference": "d7059e4c691d05ea1222b5124bed0f961be46e15"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/72c534fc0ab181ef52d92a68382318631e301608",
|
||||
"reference": "72c534fc0ab181ef52d92a68382318631e301608",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/d7059e4c691d05ea1222b5124bed0f961be46e15",
|
||||
"reference": "d7059e4c691d05ea1222b5124bed0f961be46e15",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3624,7 +3624,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Seldaek/monolog/issues",
|
||||
"source": "https://github.com/Seldaek/monolog/tree/3.12.0"
|
||||
"source": "https://github.com/Seldaek/monolog/tree/3.12.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3636,7 +3636,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-09-09T08:34:20+00:00"
|
||||
"time": "2026-09-29T19:21:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nesbot/carbon",
|
||||
@@ -6412,16 +6412,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/cache",
|
||||
"version": "v8.1.7",
|
||||
"version": "v8.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/cache.git",
|
||||
"reference": "47616ab077b5aff4d0f718b96680a48599fc1a3f"
|
||||
"reference": "8888daf06a2fc8440346215d3b52f3c611cc486d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/cache/zipball/47616ab077b5aff4d0f718b96680a48599fc1a3f",
|
||||
"reference": "47616ab077b5aff4d0f718b96680a48599fc1a3f",
|
||||
"url": "https://api.github.com/repos/symfony/cache/zipball/8888daf06a2fc8440346215d3b52f3c611cc486d",
|
||||
"reference": "8888daf06a2fc8440346215d3b52f3c611cc486d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -6487,7 +6487,7 @@
|
||||
"psr6"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/cache/tree/v8.1.7"
|
||||
"source": "https://github.com/symfony/cache/tree/v8.1.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -6507,7 +6507,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-09-08T13:39:13+00:00"
|
||||
"time": "2026-09-29T12:38:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/cache-contracts",
|
||||
@@ -6668,16 +6668,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v8.1.7",
|
||||
"version": "v8.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "29afb89f4e941f68a6e90f28e3f52ff1f6793a7d"
|
||||
"reference": "4b81146e3ee248ea969186f85499b54fbc78db08"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/29afb89f4e941f68a6e90f28e3f52ff1f6793a7d",
|
||||
"reference": "29afb89f4e941f68a6e90f28e3f52ff1f6793a7d",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/4b81146e3ee248ea969186f85499b54fbc78db08",
|
||||
"reference": "4b81146e3ee248ea969186f85499b54fbc78db08",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -6744,7 +6744,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v8.1.7"
|
||||
"source": "https://github.com/symfony/console/tree/v8.1.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -6764,7 +6764,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-09-13T10:55:57+00:00"
|
||||
"time": "2026-09-18T16:00:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
@@ -6908,16 +6908,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/error-handler",
|
||||
"version": "v8.1.5",
|
||||
"version": "v8.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/error-handler.git",
|
||||
"reference": "8b2a4289ffe5e2dc8fcf645b8e7870e1fa0325ce"
|
||||
"reference": "c146daceccd5d4f864c051b233c276641cec527b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/error-handler/zipball/8b2a4289ffe5e2dc8fcf645b8e7870e1fa0325ce",
|
||||
"reference": "8b2a4289ffe5e2dc8fcf645b8e7870e1fa0325ce",
|
||||
"url": "https://api.github.com/repos/symfony/error-handler/zipball/c146daceccd5d4f864c051b233c276641cec527b",
|
||||
"reference": "c146daceccd5d4f864c051b233c276641cec527b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -6965,7 +6965,7 @@
|
||||
"description": "Provides tools to manage errors and ease debugging PHP code",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/error-handler/tree/v8.1.5"
|
||||
"source": "https://github.com/symfony/error-handler/tree/v8.1.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -6985,7 +6985,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-08-21T17:47:34+00:00"
|
||||
"time": "2026-09-22T18:58:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
@@ -7222,16 +7222,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v8.1.7",
|
||||
"version": "v8.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "4fbe46a3eb64abf8a57f0364075b91f4a233e062"
|
||||
"reference": "bd4fed193a3132c91c5c93595cbfc78988915822"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/4fbe46a3eb64abf8a57f0364075b91f4a233e062",
|
||||
"reference": "4fbe46a3eb64abf8a57f0364075b91f4a233e062",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/bd4fed193a3132c91c5c93595cbfc78988915822",
|
||||
"reference": "bd4fed193a3132c91c5c93595cbfc78988915822",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7266,7 +7266,7 @@
|
||||
"description": "Finds files and directories via an intuitive fluent interface",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/finder/tree/v8.1.7"
|
||||
"source": "https://github.com/symfony/finder/tree/v8.1.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7286,20 +7286,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-09-10T19:14:39+00:00"
|
||||
"time": "2026-09-26T06:33:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-client",
|
||||
"version": "v8.1.7",
|
||||
"version": "v8.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-client.git",
|
||||
"reference": "099aae7d3619bffb5e68b33dbbadf6649c6d49c5"
|
||||
"reference": "e20aca4ceaa888197de5c792ffc551d99e43d98a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/099aae7d3619bffb5e68b33dbbadf6649c6d49c5",
|
||||
"reference": "099aae7d3619bffb5e68b33dbbadf6649c6d49c5",
|
||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/e20aca4ceaa888197de5c792ffc551d99e43d98a",
|
||||
"reference": "e20aca4ceaa888197de5c792ffc551d99e43d98a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7363,7 +7363,7 @@
|
||||
"http"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-client/tree/v8.1.7"
|
||||
"source": "https://github.com/symfony/http-client/tree/v8.1.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7383,7 +7383,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-09-08T13:39:13+00:00"
|
||||
"time": "2026-09-24T11:09:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-client-contracts",
|
||||
@@ -7469,16 +7469,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-foundation",
|
||||
"version": "v8.1.7",
|
||||
"version": "v8.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-foundation.git",
|
||||
"reference": "d8fdc670ed510a69e3a9eb4f5eac89f5ed627e17"
|
||||
"reference": "662f5257658d4f60deff1bc790e9f2c68cb391bf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/d8fdc670ed510a69e3a9eb4f5eac89f5ed627e17",
|
||||
"reference": "d8fdc670ed510a69e3a9eb4f5eac89f5ed627e17",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/662f5257658d4f60deff1bc790e9f2c68cb391bf",
|
||||
"reference": "662f5257658d4f60deff1bc790e9f2c68cb391bf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7526,7 +7526,7 @@
|
||||
"description": "Defines an object-oriented layer for the HTTP specification",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-foundation/tree/v8.1.7"
|
||||
"source": "https://github.com/symfony/http-foundation/tree/v8.1.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7546,20 +7546,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-09-14T17:47:24+00:00"
|
||||
"time": "2026-09-24T07:13:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v8.1.7",
|
||||
"version": "v8.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "ec7a3a5832c22cf880cf27d2fdc7ad2e94838e85"
|
||||
"reference": "51c014e881d8b4ce61e56c0d4105f65b50df786a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/ec7a3a5832c22cf880cf27d2fdc7ad2e94838e85",
|
||||
"reference": "ec7a3a5832c22cf880cf27d2fdc7ad2e94838e85",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/51c014e881d8b4ce61e56c0d4105f65b50df786a",
|
||||
"reference": "51c014e881d8b4ce61e56c0d4105f65b50df786a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7636,7 +7636,7 @@
|
||||
"description": "Provides a structured process for converting a Request into a Response",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v8.1.7"
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v8.1.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7656,7 +7656,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-09-15T07:12:52+00:00"
|
||||
"time": "2026-09-29T14:08:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/mailer",
|
||||
@@ -9030,16 +9030,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/routing",
|
||||
"version": "v8.1.6",
|
||||
"version": "v8.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/routing.git",
|
||||
"reference": "3c188091b6b4fa2e4bc83a135caede12deb8576c"
|
||||
"reference": "e46d741c56485441f37104a3e7eace4e17ed91f9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/routing/zipball/3c188091b6b4fa2e4bc83a135caede12deb8576c",
|
||||
"reference": "3c188091b6b4fa2e4bc83a135caede12deb8576c",
|
||||
"url": "https://api.github.com/repos/symfony/routing/zipball/e46d741c56485441f37104a3e7eace4e17ed91f9",
|
||||
"reference": "e46d741c56485441f37104a3e7eace4e17ed91f9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -9086,7 +9086,7 @@
|
||||
"url"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/routing/tree/v8.1.6"
|
||||
"source": "https://github.com/symfony/routing/tree/v8.1.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -9106,7 +9106,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-08-17T13:18:34+00:00"
|
||||
"time": "2026-09-28T19:13:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
@@ -9462,16 +9462,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/uid",
|
||||
"version": "v8.1.5",
|
||||
"version": "v8.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/uid.git",
|
||||
"reference": "a08aef47989093f32fe50fd11859be1b427df389"
|
||||
"reference": "f412a0b5edd75eb3104a93bd0d11fd2ea3da34e4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/uid/zipball/a08aef47989093f32fe50fd11859be1b427df389",
|
||||
"reference": "a08aef47989093f32fe50fd11859be1b427df389",
|
||||
"url": "https://api.github.com/repos/symfony/uid/zipball/f412a0b5edd75eb3104a93bd0d11fd2ea3da34e4",
|
||||
"reference": "f412a0b5edd75eb3104a93bd0d11fd2ea3da34e4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -9516,7 +9516,7 @@
|
||||
"uuid"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/uid/tree/v8.1.5"
|
||||
"source": "https://github.com/symfony/uid/tree/v8.1.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -9536,7 +9536,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-08-11T13:39:01+00:00"
|
||||
"time": "2026-09-23T17:45:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
@@ -12188,16 +12188,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "13.3.5",
|
||||
"version": "13.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "1b482b9a77774705a5c4a47ab7d60819d2589e90"
|
||||
"reference": "b7d0ee4ea89121cb963302b8708b1c09d9ebc7d6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1b482b9a77774705a5c4a47ab7d60819d2589e90",
|
||||
"reference": "1b482b9a77774705a5c4a47ab7d60819d2589e90",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b7d0ee4ea89121cb963302b8708b1c09d9ebc7d6",
|
||||
"reference": "b7d0ee4ea89121cb963302b8708b1c09d9ebc7d6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -12268,7 +12268,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/13.3.5"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/13.3.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -12276,7 +12276,7 @@
|
||||
"type": "other"
|
||||
}
|
||||
],
|
||||
"time": "2026-09-25T08:41:20+00:00"
|
||||
"time": "2026-09-29T12:12:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "rector/rector",
|
||||
@@ -13622,6 +13622,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"abandoned": true,
|
||||
"time": "2024-03-18T22:02:54+00:00"
|
||||
},
|
||||
{
|
||||
|
||||
+4
-4
@@ -78,8 +78,8 @@ return [
|
||||
'running_balance_column' => (bool)env_default_when_empty(env('USE_RUNNING_BALANCE'), true), // this is only the default value, is not used.
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => '6.7.6',
|
||||
'build_time' => 1790572120,
|
||||
'version' => 'develop/2026-09-29',
|
||||
'build_time' => 1790710740,
|
||||
'api_version' => '2.1.0', // field is no longer used.
|
||||
'db_version' => 28, // field is no longer used.
|
||||
|
||||
@@ -848,9 +848,9 @@ return [
|
||||
|
||||
// dynamic date ranges are as follows:
|
||||
'dynamic_date_ranges' => ['last7', 'last30', 'last90', 'last365', 'MTD', 'QTD', 'YTD'],
|
||||
'allowed_filter_parameters' => [
|
||||
'allowed_filter_parameters' => [
|
||||
'Account' => [
|
||||
'name'
|
||||
'name',
|
||||
],
|
||||
],
|
||||
'allowed_sort_parameters' => [
|
||||
|
||||
Generated
-48
@@ -474,9 +474,6 @@
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -498,9 +495,6 @@
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -522,9 +516,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -546,9 +537,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -570,9 +558,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -594,9 +579,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -786,9 +768,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -806,9 +785,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -826,9 +802,6 @@
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -846,9 +819,6 @@
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -866,9 +836,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -886,9 +853,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2566,9 +2530,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2590,9 +2551,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2614,9 +2572,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2638,9 +2593,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
||||
@@ -22,13 +22,13 @@ import "../../boot/bootstrap.js";
|
||||
import sidebar from "../shared/sidebar.js";
|
||||
import dates from "../shared/dates.js";
|
||||
import Alpine from "@alpinejs/csp";
|
||||
import {getVariable} from "../../store/get-variable.js";
|
||||
import { getVariable } from "../../store/get-variable.js";
|
||||
import Put from "../../api/model/account/put.js";
|
||||
import Get from "../../api/model/account/get.js";
|
||||
import format from '../../util/format.js';
|
||||
import format from "../../util/format.js";
|
||||
import formatMoney from "../../util/format-money.js";
|
||||
import i18next from "i18next";
|
||||
import {addDrag} from "../shared/drag-and-droppable-rows.js";
|
||||
import { addDrag } from "../shared/drag-and-droppable-rows.js";
|
||||
|
||||
window.enableDates = false;
|
||||
|
||||
@@ -40,7 +40,7 @@ let index = function () {
|
||||
accounts: [],
|
||||
sortDirection: "asc",
|
||||
page: 1,
|
||||
i18next:null,
|
||||
i18next: null,
|
||||
anonymous: false,
|
||||
totalPages: 1,
|
||||
loadingPage: true,
|
||||
@@ -53,22 +53,24 @@ let index = function () {
|
||||
debts: {},
|
||||
differences: {},
|
||||
filter: {
|
||||
name: '',
|
||||
name: "",
|
||||
},
|
||||
formatMoney: formatMoney,
|
||||
objectToQueryString(obj, prefix) {
|
||||
return Object.keys(obj).map(objKey => {
|
||||
if (Object.hasOwn(obj, objKey)) {
|
||||
const key = prefix ? `${prefix}[${objKey}]` : objKey;
|
||||
const value = obj[objKey];
|
||||
return Object.keys(obj)
|
||||
.map((objKey) => {
|
||||
if (Object.hasOwn(obj, objKey)) {
|
||||
const key = prefix ? `${prefix}[${objKey}]` : objKey;
|
||||
const value = obj[objKey];
|
||||
|
||||
return typeof value === "object" ?
|
||||
this.objectToQueryString(value, key) :
|
||||
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
||||
}
|
||||
return typeof value === "object"
|
||||
? this.objectToQueryString(value, key)
|
||||
: `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
||||
}
|
||||
|
||||
return null;
|
||||
}).join("&");
|
||||
return null;
|
||||
})
|
||||
.join("&");
|
||||
},
|
||||
|
||||
updateHistory() {
|
||||
@@ -78,27 +80,23 @@ let index = function () {
|
||||
page: this.page,
|
||||
column: this.sortColumn,
|
||||
direction: this.sortDirection,
|
||||
filter: this.filter
|
||||
filter: this.filter,
|
||||
};
|
||||
let string = this.objectToQueryString(obj);
|
||||
let newUrl =
|
||||
window.location.protocol +
|
||||
"//" +
|
||||
window.location.host +
|
||||
window.location.pathname +
|
||||
"?" + string;
|
||||
window.history.pushState({path: newUrl}, "", newUrl);
|
||||
window.store.set(this.storageKey, {column: this.sortColumn, direction: this.sortDirection});
|
||||
window.location.protocol + "//" + window.location.host + window.location.pathname + "?" + string;
|
||||
window.history.pushState({ path: newUrl }, "", newUrl);
|
||||
window.store.set(this.storageKey, { column: this.sortColumn, direction: this.sortDirection });
|
||||
}
|
||||
},
|
||||
updateFilterValue(field, newValue) {
|
||||
console.log('Update', field, newValue);
|
||||
console.log("Update", field, newValue);
|
||||
this.filter[field] = newValue;
|
||||
this.updateHistory();
|
||||
this.downloadAccounts();
|
||||
},
|
||||
init() {
|
||||
this.$watch('filter.name', (value) => this.updateFilterValue('name', value));
|
||||
this.$watch("filter.name", (value) => this.updateFilterValue("name", value));
|
||||
|
||||
this.i18next = i18next;
|
||||
this.anonymous = window.store.get("anonymous");
|
||||
@@ -156,17 +154,18 @@ let index = function () {
|
||||
el.addEventListener("click", () => {
|
||||
let column = el.dataset.column;
|
||||
// show search input again
|
||||
document.querySelector(`span.title[data-column="${column}"]`).classList.remove('d-none');
|
||||
document.querySelector(`span.search-spacer[data-column="${column}"]`).classList.remove('d-none');
|
||||
document.querySelector(`em.search-button[data-column="${column}"]`).classList.remove('d-none');
|
||||
document.querySelector(`div.search-filter[data-column="${column}"] input`).value = '';
|
||||
document.querySelector(`span.title[data-column="${column}"]`).classList.remove("d-none");
|
||||
document
|
||||
.querySelector(`span.search-spacer[data-column="${column}"]`)
|
||||
.classList.remove("d-none");
|
||||
document.querySelector(`em.search-button[data-column="${column}"]`).classList.remove("d-none");
|
||||
document.querySelector(`div.search-filter[data-column="${column}"] input`).value = "";
|
||||
|
||||
let input = document.querySelector(`div.search-filter[data-column="${column}"]`);
|
||||
input.classList.add('d-none');
|
||||
input.classList.add("d-none");
|
||||
|
||||
// TODO hardcoded!!
|
||||
this.filter.name = '';
|
||||
|
||||
this.filter.name = "";
|
||||
});
|
||||
});
|
||||
|
||||
@@ -175,20 +174,18 @@ let index = function () {
|
||||
el.addEventListener("click", () => {
|
||||
let column = el.dataset.column;
|
||||
// hide search buttons etc.
|
||||
el.classList.add('d-none');
|
||||
document.querySelector(`span.title[data-column="${column}"]`).classList.add('d-none');
|
||||
document.querySelector(`span.search-spacer[data-column="${column}"]`).classList.add('d-none');
|
||||
el.classList.add("d-none");
|
||||
document.querySelector(`span.title[data-column="${column}"]`).classList.add("d-none");
|
||||
document.querySelector(`span.search-spacer[data-column="${column}"]`).classList.add("d-none");
|
||||
|
||||
// show search input
|
||||
let input = document.querySelector(`div.search-filter[data-column="${column}"]`);
|
||||
input.classList.remove('d-none');
|
||||
input.querySelector('input').focus();
|
||||
input.classList.remove("d-none");
|
||||
input.querySelector("input").focus();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
getVariable("listPageSize").then((listPageSize) => {
|
||||
this.listPageSize = listPageSize;
|
||||
addDrag();
|
||||
@@ -198,7 +195,7 @@ let index = function () {
|
||||
let item = e.detail[i];
|
||||
if (item.order !== item.currentOrder) {
|
||||
// PUT new order to system.
|
||||
new Put().put({order: item.order}, {id: item.id});
|
||||
new Put().put({ order: item.order }, { id: item.id });
|
||||
// save new order as current order in the row.
|
||||
document
|
||||
.querySelector(`tr[data-id="${item.id}"]`)
|
||||
@@ -362,7 +359,11 @@ let index = function () {
|
||||
if (null === date) {
|
||||
return "";
|
||||
}
|
||||
return format(new Date(date), i18next.t("config.date_time_fns_short", {lng: window.store.get("locale")}), window.store.get("locale"));
|
||||
return format(
|
||||
new Date(date),
|
||||
i18next.t("config.date_time_fns_short", { lng: window.store.get("locale") }),
|
||||
window.store.get("locale"),
|
||||
);
|
||||
},
|
||||
handlePageClick(e) {
|
||||
let link = e.currentTarget;
|
||||
|
||||
@@ -36,8 +36,7 @@ let index = function () {
|
||||
anonymous: false,
|
||||
loadingPiggyBanks: true,
|
||||
init() {
|
||||
|
||||
console.log('I am translation');
|
||||
console.log("I am translation");
|
||||
console.log(i18next.t(format(new Date(), "d MMMM yyyy 'o' HH:mm")));
|
||||
|
||||
getVariable("anonymous").then((value) => {
|
||||
|
||||
@@ -342,7 +342,6 @@ let create = function () {
|
||||
},
|
||||
|
||||
save() {
|
||||
|
||||
this.notifications.error.show = false;
|
||||
this.notifications.success.show = false;
|
||||
this.notifications.wait.show = true;
|
||||
|
||||
@@ -37,11 +37,11 @@ export function changeDescription(item, ac) {
|
||||
}
|
||||
|
||||
export function changeDestinationAccount(item, ac) {
|
||||
console.log('changeDestinationAccount', item, ac);
|
||||
console.log("changeDestinationAccount", item, ac);
|
||||
if (typeof item === "object") {
|
||||
// changed account but did not select.
|
||||
const index = parseInt(ac._searchInput.dataset.index);
|
||||
console.log('changeDestinationAccount X', document.querySelector("#form"));
|
||||
console.log("changeDestinationAccount X", document.querySelector("#form"));
|
||||
document.querySelector("#form")._x_dataStack[0].$data.entries[index].destination_account = {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
@@ -69,7 +69,7 @@ export function changeDestinationAccount(item, ac) {
|
||||
|
||||
export function selectDestinationAccount(item, ac) {
|
||||
const index = parseInt(ac._searchInput.attributes["data-index"].value);
|
||||
console.log('selectDestinationAccount', index);
|
||||
console.log("selectDestinationAccount", index);
|
||||
document.querySelector("#form")._x_dataStack[0].$data.entries[index].destination_account = {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
export function keyUpFromCategory(e) {
|
||||
let target = null === e.currentTarget ? null : e.currentTarget.nextSibling;
|
||||
if ("Enter" === e.key && 'UL' === target.nodeName && !target.classList.contains("show")) {
|
||||
if ("Enter" === e.key && "UL" === target.nodeName && !target.classList.contains("show")) {
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,9 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
export function keyUpFromDescription(e) {
|
||||
let target = null === e.currentTarget ? null : e.currentTarget.nextSibling;
|
||||
if ("Enter" === e.key && 'UL' === target.nodeName && !target.classList.contains("show")) {
|
||||
if ("Enter" === e.key && "UL" === target.nodeName && !target.classList.contains("show")) {
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,7 @@
|
||||
|
||||
export function keyUpFromDestination(e) {
|
||||
let target = null === e.currentTarget ? null : e.currentTarget.nextSibling;
|
||||
if ("Enter" === e.key && 'UL' === target.nodeName && !target.classList.contains("show")) {
|
||||
if ("Enter" === e.key && "UL" === target.nodeName && !target.classList.contains("show")) {
|
||||
this.save();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
export function keyUpFromSource(e) {
|
||||
let target = null === e.currentTarget ? null : e.currentTarget.nextSibling;
|
||||
if ("Enter" === e.key && 'UL' === target.nodeName && !target.classList.contains("show")) {
|
||||
if ("Enter" === e.key && "UL" === target.nodeName && !target.classList.contains("show")) {
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
export default function (amount, currencyCode) {
|
||||
if ((typeof amount !== "number" && typeof amount !== "string") || isNaN(amount)) {
|
||||
if(null !== amount) {
|
||||
if (null !== amount) {
|
||||
console.warn("format-money: amount is not a number:", amount);
|
||||
}
|
||||
return "";
|
||||
|
||||
@@ -95,11 +95,11 @@ export default function (date, formatStr = "PP", locale) {
|
||||
if (typeof locale === "undefined") {
|
||||
locale = window.__localeId__.replace("_", "");
|
||||
}
|
||||
let res = '(DATE ERROR)';
|
||||
let res = "(DATE ERROR)";
|
||||
try {
|
||||
res = format(date, formatStr, {locale: locales[locale] ?? locales[locale.slice(0, 2)] ?? locales["enUS"],});
|
||||
} catch(error) {
|
||||
console.warn('Could not format date: ', error);
|
||||
res = format(date, formatStr, { locale: locales[locale] ?? locales[locale.slice(0, 2)] ?? locales["enUS"] });
|
||||
} catch (error) {
|
||||
console.warn("Could not format date: ", error);
|
||||
}
|
||||
// console.log("Locale is " + locale);
|
||||
return res;
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
|
||||
|
||||
export default function serialize (obj, prefix) {
|
||||
let str = [], p;
|
||||
export default function serialize(obj, prefix) {
|
||||
let str = [],
|
||||
p;
|
||||
for (p in obj) {
|
||||
if (Object.hasOwn(obj, p)) {
|
||||
let k = prefix ? prefix + "[" + p + "]" : p, v = obj[p];
|
||||
str.push((v !== null && typeof v === "object") ? serialize(v, k) : encodeURIComponent(k) + "=" + encodeURIComponent(v));
|
||||
let k = prefix ? prefix + "[" + p + "]" : p,
|
||||
v = obj[p];
|
||||
str.push(
|
||||
v !== null && typeof v === "object"
|
||||
? serialize(v, k)
|
||||
: encodeURIComponent(k) + "=" + encodeURIComponent(v),
|
||||
);
|
||||
}
|
||||
}
|
||||
return str.join("&");
|
||||
|
||||
@@ -31,7 +31,7 @@ return [
|
||||
'invalid_transaction_type_list' => 'Invalid transaction type list',
|
||||
'limit_exists' => 'There is already a budget limit (amount) for this budget and currency in the given period.',
|
||||
'invalid_sort_instruction' => 'The sort instruction is invalid for an object of type ":object".',
|
||||
'no_filter_instructions' => 'The filter instruction is invalid for an object of type ":object".',
|
||||
'no_filter_instructions' => 'The filter instruction is invalid for an object of type ":object".',
|
||||
'invalid_sort_instruction_index' => 'The sort instruction at index #:index is invalid for an object of type ":object".',
|
||||
'no_sort_instructions' => 'There are no sort instructions defined for an object of type ":object".',
|
||||
'webhook_budget_info' => 'Cannot deliver budget information for transaction related webhooks.',
|
||||
|
||||
Reference in new issue
Block a user