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

This commit is contained in:
James Cole committed 2026-09-07 21:16:11 +02:00
commit 4ac6f2174a
24 files changed
+110 -67

No files matched your search

@@ -158,7 +158,10 @@ 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,7 +155,10 @@ 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
}
}
@@ -71,8 +71,8 @@ class StoreRequest extends FormRequest
public function rules(): array
{
return [
'start' => ['required', 'before:end', 'date', 'after:1970-01-02', 'before:2038-01-17'],
'end' => ['required', 'after:start', 'date', 'after:1970-01-02', 'before:2038-01-17'],
'start' => ['required', 'before:end', 'date', 'after:1970-01-02', 'before:2038-01-17'],
'end' => ['required', 'after:start', 'date', 'after:1970-01-02', 'before:2038-01-17'],
'amount' => ['required', new IsValidPositiveAmount()],
'currency_id' => ['numeric', 'exists:transaction_currencies,id'],
'currency_code' => ['min:3', 'max:51', 'exists:transaction_currencies,code'],
@@ -27,7 +27,6 @@ namespace FireflyIII\Helpers\Collector\Extensions;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\Builder;
/**
* Trait TimeCollection
@@ -67,7 +66,7 @@ trait TimeCollection
if ($end < $start) {
[$start, $end] = [$end, $start];
}
$end = clone $end; // this is so weird, but it works if $end and $start secretly point to the same object.
$end = clone $end; // this is so weird, but it works if $end and $start secretly point to the same object.
$end->endOfDay();
$start->startOfDay();
$this->withMetaDate($field);
@@ -108,12 +107,11 @@ trait TimeCollection
}
$startStr = $start->format('Y-m-d 00:00:00');
$endStr = $end->format('Y-m-d 23:59:59');
$this->query->where(function (EloquentBuilder $q) use ($startStr, $endStr) {
$this->query->where(function (EloquentBuilder $q) use ($startStr, $endStr): void {
$q->where('transaction_journals.date', '<', $startStr);
$q->orWhere('transaction_journals.date', '>', $endStr);
});
return $this;
}
@@ -123,7 +121,7 @@ trait TimeCollection
$filter = static function (array $object) use ($field, $day): bool {
foreach ($object['transactions'] as $transaction) {
if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon) {
return $transaction[$field]->day >= (int)$day;
return $transaction[$field]->day >= (int) $day;
}
}
@@ -140,7 +138,7 @@ trait TimeCollection
$filter = static function (array $object) use ($field, $day): bool {
foreach ($object['transactions'] as $transaction) {
if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon) {
return $transaction[$field]->day <= (int)$day;
return $transaction[$field]->day <= (int) $day;
}
}
@@ -157,7 +155,7 @@ trait TimeCollection
$filter = static function (array $object) use ($field, $day): bool {
foreach ($object['transactions'] as $transaction) {
if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon) {
return (int)$day === $transaction[$field]->day;
return (int) $day === $transaction[$field]->day;
}
}
@@ -174,7 +172,7 @@ trait TimeCollection
$filter = static function (array $object) use ($field, $day): bool {
foreach ($object['transactions'] as $transaction) {
if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon) {
return (int)$day !== $transaction[$field]->day;
return (int) $day !== $transaction[$field]->day;
}
}
@@ -191,7 +189,7 @@ trait TimeCollection
$filter = static function (array $object) use ($field, $month): bool {
foreach ($object['transactions'] as $transaction) {
if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon) {
return $transaction[$field]->month >= (int)$month;
return $transaction[$field]->month >= (int) $month;
}
}
@@ -208,7 +206,7 @@ trait TimeCollection
$filter = static function (array $object) use ($field, $month): bool {
foreach ($object['transactions'] as $transaction) {
if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon) {
return $transaction[$field]->month <= (int)$month;
return $transaction[$field]->month <= (int) $month;
}
}
@@ -225,7 +223,7 @@ trait TimeCollection
$filter = static function (array $object) use ($field, $month): bool {
foreach ($object['transactions'] as $transaction) {
if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon) {
return (int)$month === $transaction[$field]->month;
return (int) $month === $transaction[$field]->month;
}
}
@@ -242,7 +240,7 @@ trait TimeCollection
$filter = static function (array $object) use ($field, $month): bool {
foreach ($object['transactions'] as $transaction) {
if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon) {
return (int)$month !== $transaction[$field]->month;
return (int) $month !== $transaction[$field]->month;
}
}
@@ -259,7 +257,7 @@ trait TimeCollection
$filter = static function (array $object) use ($field, $year): bool {
foreach ($object['transactions'] as $transaction) {
if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon) {
return $transaction[$field]->year >= (int)$year;
return $transaction[$field]->year >= (int) $year;
}
}
@@ -276,7 +274,7 @@ trait TimeCollection
$filter = static function (array $object) use ($field, $year): bool {
foreach ($object['transactions'] as $transaction) {
if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon) {
return $transaction[$field]->year <= (int)$year;
return $transaction[$field]->year <= (int) $year;
}
}
@@ -293,7 +291,7 @@ trait TimeCollection
$filter = static function (array $object) use ($field, $year): bool {
foreach ($object['transactions'] as $transaction) {
if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon) {
return $year === (string)$transaction[$field]->year;
return $year === (string) $transaction[$field]->year;
}
}
@@ -310,7 +308,7 @@ trait TimeCollection
$filter = static function (array $object) use ($field, $year): bool {
foreach ($object['transactions'] as $transaction) {
if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon) {
return $year !== (string)$transaction[$field]->year;
return $year !== (string) $transaction[$field]->year;
}
}
@@ -521,7 +519,7 @@ trait TimeCollection
if ($end < $start) {
[$start, $end] = [$end, $start];
}
$end = clone $end; // this is so weird, but it works if $end and $start secretly point to the same object.
$end = clone $end; // this is so weird, but it works if $end and $start secretly point to the same object.
$end->endOfDay();
$start->startOfDay();
$this->withMetaDate($field);
@@ -198,7 +198,13 @@ 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']);
@@ -284,7 +284,10 @@ 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,7 +539,13 @@ 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);
-1
View File
@@ -34,7 +34,6 @@ use FireflyIII\Http\Middleware\Installer;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\TransactionRules\Engine\CustomExpressionLanguage;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
@@ -94,12 +94,12 @@ final class RecurrenceController extends Controller
$endsAt = (string) $request->input('ends');
$repetitionType = explode(',', (string) $request->input('type'))[0];
$repetitions = (int) $request->input('reps');
$repetitions = clamp($repetitions,1,100);
$repetitions = clamp($repetitions, 1, 100);
$weekend = (int) $request->input('weekend');
$repetitionMoment = '';
$skip = (int) $request->input('skip');
$skip = clamp($skip,0,31);
$weekend = clamp($weekend,1,4);
$skip = clamp($skip, 0, 31);
$weekend = clamp($weekend, 1, 4);
$start = session()->get('start');
+2 -3
View File
@@ -28,7 +28,6 @@ use FireflyIII\Http\Requests\NewUserFormRequest;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\Facades\AppConfiguration;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Http\Controllers\CreateStuff;
use Illuminate\Contracts\View\Factory;
@@ -77,9 +76,9 @@ final class NewUserController extends Controller
if ($count > 0) {
return redirect(route('index'));
}
$language = config('firefly.default_language') ?? config('firefly.default_preferences.language');
$language = config('firefly.default_language') ?? config('firefly.default_preferences.language');
return view('new-user.index', ['languages' => $languages,'language' => $language]);
return view('new-user.index', ['languages' => $languages, 'language' => $language]);
}
/**
+1 -1
View File
@@ -62,7 +62,7 @@ final class SearchController extends Controller
{
// search params:
$fullQuery = $request->input('search');
$fullQuery = substr($fullQuery, 0, 500);
$fullQuery = substr($fullQuery, 0, 500);
if (is_array($request->input('search'))) {
$fullQuery = '';
}
+14 -2
View File
@@ -132,7 +132,13 @@ 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));
@@ -222,7 +228,13 @@ 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));
+1 -1
View File
@@ -53,7 +53,7 @@ class RuleAction extends Model
try {
$journal['date'] = $journal['date']->toW3cString();
$result = $expr->evaluate($journal);
$result = $expr->evaluate($journal);
} catch (SyntaxError $e) {
Log::error(sprintf('Expression engine failed to evaluate expression "%s" with error "%s".', $this->action_value, $e->getMessage()));
$result = (string) $this->action_value;
@@ -382,7 +382,7 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte
*/
public function getXOccurrences(RecurrenceRepetition $repetition, Carbon $date, int $count): array
{
$count = clamp($count, 1,100);
$count = clamp($count, 1, 100);
$skipMod = $repetition->repetition_skip + 1;
$occurrences = [];
if ('daily' === $repetition->repetition_type) {
@@ -222,7 +222,14 @@ 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,7 +354,11 @@ 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;
@@ -73,8 +73,8 @@ class AvailableBudgetCalculator
Log::debug(sprintf('Limit period is from %s to %s', $start->format('Y-m-d'), $end->format('Y-m-d')));
// from the start until the end of the budget limit, need to loop!
$count = 0;
$max = 14_600; // 40 years in days.
$count = 0;
$max = 14_600; // 40 years in days.
$current = clone $start;
while ($current <= $end) {
if ($count > $max) {
@@ -82,7 +82,7 @@ class AvailableBudgetCalculator
return;
}
$count++;
++$count;
$this->refreshAvailableBudget($current);
$current = Navigation::addPeriod($current, $this->viewRange);
}
+12 -12
View File
@@ -64,19 +64,19 @@ class AccountSearch implements GenericSearchInterface
switch ($this->field) {
default:
case self::SEARCH_ALL:
$searchQuery->where(static function (Builder $q1) use ($like, $originalQuery): void {
$q1->where(static function (Builder $q2) use ($like): void {
$q2->whereLike('accounts.id', $like);
$q2->orWhereLike('accounts.name', $like);
$q2->orWhereLike('accounts.iban', $like);
$searchQuery->where(static function (Builder $q1) use ($like, $originalQuery): void {
$q1->where(static function (Builder $q2) use ($like): void {
$q2->whereLike('accounts.id', $like);
$q2->orWhereLike('accounts.name', $like);
$q2->orWhereLike('accounts.iban', $like);
});
// meta data:
$q1->orWhere(static function (Builder $q3) use ($originalQuery): void {
$json = json_encode($originalQuery, JSON_THROW_ON_ERROR);
$q3->where('account_meta.name', '=', 'account_number');
$q3->whereLike('account_meta.data', $json);
});
});
// meta data:
$q1->orWhere(static function (Builder $q3) use ($originalQuery): void {
$json = json_encode($originalQuery, JSON_THROW_ON_ERROR);
$q3->where('account_meta.name', '=', 'account_number');
$q3->whereLike('account_meta.data', $json);
});
});
break;
@@ -36,12 +36,14 @@ use function Safe\preg_split;
*/
class QueryParser implements QueryParserInterface
{
private int $position = 0;
private int $depth = 0;
private int $position = 0;
private int $depth = 0;
private const int MAX_DEPTH = 32;
// private string $query;
private array $chrArray = [];
private int $count = 0;
private array $chrArray = [];
private int $count = 0;
public function parse(string $query): NodeGroup
{
@@ -125,6 +127,7 @@ class QueryParser implements QueryParserInterface
++$this->depth;
$group = $this->buildNodeGroup(true, $prohibited);
--$this->depth;
return new NodeResult($group, false);
}
// In any other location, it's just a normal character
+1
View File
@@ -764,6 +764,7 @@ class Steam
public function getSafePreviousUrl(): string
{
return $this->getSafeUrl(session()->previousUrl() ?? route('index'), route('index'));
// Log::debug(sprintf('getSafePreviousUrl: "%s"', $res));
}
@@ -27,7 +27,6 @@ namespace FireflyIII\TransactionRules\Expressions;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\TransactionRules\Engine\CustomExpressionLanguage;
use Illuminate\Support\Facades\Log;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\ExpressionLanguage\SyntaxError;
class ActionExpression
Generated
+5 -5
View File
@@ -2513,16 +2513,16 @@
},
{
"name": "league/commonmark",
"version": "2.10.0",
"version": "2.10.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
"reference": "d2d1aa8b35e072966c89bc0c66cf926e56767dc4"
"reference": "9d489ab67a02960fd8ffe624d93f751daf95439e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d2d1aa8b35e072966c89bc0c66cf926e56767dc4",
"reference": "d2d1aa8b35e072966c89bc0c66cf926e56767dc4",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/9d489ab67a02960fd8ffe624d93f751daf95439e",
"reference": "9d489ab67a02960fd8ffe624d93f751daf95439e",
"shasum": ""
},
"require": {
@@ -2616,7 +2616,7 @@
"type": "tidelift"
}
],
"time": "2026-08-11T16:06:25+00:00"
"time": "2026-09-07T13:44:26+00:00"
},
{
"name": "league/config",
+1 -1
View File
@@ -79,7 +79,7 @@ return [
// see cer.php for exchange rates feature flag.
],
'version' => 'develop/2026-09-07',
'build_time' => 1788750859,
'build_time' => 1788808180,
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 28, // field is no longer used.
+3 -3
View File
@@ -701,9 +701,9 @@
}
},
"node_modules/alpinejs": {
"version": "3.17.1",
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.17.1.tgz",
"integrity": "sha512-HDUhA9juSA/pNYZ5FAAlW4qNsdeVORFSVONEIu0hVasqCOqLitf15R9tCr5GBCV0ZxiZW2Qx6BYdMSF7iGLT/Q==",
"version": "3.17.2",
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.17.2.tgz",
"integrity": "sha512-xbnG3LlnmFkiNO49C+qhqjEDqOdB0snKqvT48ZVp8TakpZOpsoQbc/jAxpptQRjZi7c9rLeCfvwQ/XOSirIZFQ==",
"license": "MIT",
"dependencies": {
"@vue/reactivity": "~3.5.40"