Compare commits

...

78 Commits

Author SHA1 Message Date
github-actions[bot]
7fd33035a6 Merge pull request #11741 from firefly-iii/release-1771230058
🤖 Automatically merge the PR into the develop branch.
2026-02-16 09:21:05 +01:00
JC5
5a87f6b2c0 🤖 Auto commit for release 'develop' on 2026-02-16 2026-02-16 09:20:58 +01:00
Sander Dorigo
cbebd7928f Fix #11738 2026-02-16 09:16:42 +01:00
github-actions[bot]
73089c2084 Merge pull request #11737 from firefly-iii/release-1771214139
🤖 Automatically merge the PR into the develop branch.
2026-02-16 04:55:46 +01:00
JC5
34260f2a4f 🤖 Auto commit for release 'develop' on 2026-02-16 2026-02-16 04:55:39 +01:00
James Cole
c3872aa738 Fix header in changelog. 2026-02-15 19:44:38 +01:00
github-actions[bot]
4bcd163b47 Merge pull request #11732 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-02-15 19:42:55 +01:00
github-actions[bot]
31d444292f Merge pull request #11731 from firefly-iii/release-1771180963
🤖 Automatically merge the PR into the develop branch.
2026-02-15 19:42:50 +01:00
JC5
a27e21f002 🤖 Auto commit for release 'v6.4.22' on 2026-02-15 2026-02-15 19:42:43 +01:00
github-actions[bot]
0f7118d5b3 Merge pull request #11730 from firefly-iii/release-1771176353
🤖 Automatically merge the PR into the develop branch.
2026-02-15 18:26:02 +01:00
JC5
4f9a1fde5d 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 18:25:54 +01:00
James Cole
bc596cb1c2 Update changelog and fix calls during startup. 2026-02-15 18:18:47 +01:00
github-actions[bot]
7110556ef0 Merge pull request #11729 from firefly-iii/release-1771170628
🤖 Automatically merge the PR into the develop branch.
2026-02-15 16:50:35 +01:00
JC5
3e1b703dfc 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 16:50:28 +01:00
James Cole
fce0750509 Remove unnecessary package. 2026-02-15 16:43:35 +01:00
github-actions[bot]
f5385d0229 Merge pull request #11727 from firefly-iii/release-1771154935
🤖 Automatically merge the PR into the develop branch.
2026-02-15 12:29:02 +01:00
JC5
8599c4a3c1 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 12:28:55 +01:00
James Cole
d210e7dcdd Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2026-02-15 12:24:46 +01:00
James Cole
8de9e0ba29 Remove checks for null 2026-02-15 12:24:33 +01:00
github-actions[bot]
7fa9e79f2a Merge pull request #11726 from firefly-iii/release-1771154592
🤖 Automatically merge the PR into the develop branch.
2026-02-15 12:23:20 +01:00
JC5
4a5281fd80 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 12:23:13 +01:00
James Cole
ad60974430 Update command so it is really the first attempt to connect. 2026-02-15 12:19:10 +01:00
James Cole
195794881b Catch nulls in dates. 2026-02-15 12:11:25 +01:00
James Cole
3e6a997dd5 Catch preferences with no date info. 2026-02-15 12:09:57 +01:00
James Cole
2eedfd9f26 Merge branch 'main' into develop 2026-02-15 12:06:20 +01:00
James Cole
9ec0515bb6 Fix string pointer 2026-02-15 12:06:05 +01:00
James Cole
6b197eecb9 Remove PR update rule from mergify configuration
Removed the rule for automatically updating PRs before merging.

Signed-off-by: James Cole <james@firefly-iii.org>
2026-02-15 11:58:07 +01:00
github-actions[bot]
591c970882 Merge pull request #11725 from firefly-iii/release-1771152972
🤖 Automatically merge the PR into the develop branch.
2026-02-15 11:56:22 +01:00
JC5
15d91dbe1b 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 11:56:12 +01:00
James Cole
6ff87bf447 Add command that first verifies the database connection. Saves some time booting up. 2026-02-15 11:51:04 +01:00
James Cole
147ce154d8 Remove unused file. 2026-02-15 11:25:26 +01:00
James Cole
2c8be33000 Clean up routes and API calls. 2026-02-15 11:25:12 +01:00
github-actions[bot]
b472890c84 Merge pull request #11724 from firefly-iii/release-1771138148
🤖 Automatically merge the PR into the develop branch.
2026-02-15 07:49:18 +01:00
JC5
2fabcf5193 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 07:49:08 +01:00
James Cole
5ed4e7aa79 Clean up kernel files and middleware. 2026-02-15 07:41:31 +01:00
James Cole
973caad7e4 Clean up providers 2026-02-15 07:27:28 +01:00
github-actions[bot]
d273503a15 Merge pull request #11723 from firefly-iii/release-1771134641
🤖 Automatically merge the PR into the develop branch.
2026-02-15 06:50:49 +01:00
JC5
cc149adb5d 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 06:50:41 +01:00
James Cole
ac8bcb786b Fix https://github.com/firefly-iii/firefly-iii/issues/11720 2026-02-15 06:18:52 +01:00
github-actions[bot]
642deefba5 Merge pull request #11718 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-02-14 20:40:32 +01:00
github-actions[bot]
5b13b64fd7 Merge pull request #11717 from firefly-iii/release-1771098019
🤖 Automatically merge the PR into the develop branch.
2026-02-14 20:40:27 +01:00
JC5
653a64d0a8 🤖 Auto commit for release 'v6.4.21' on 2026-02-14 2026-02-14 20:40:19 +01:00
github-actions[bot]
c9dcdc90ec Merge pull request #11716 from firefly-iii/release-1771097648
🤖 Automatically merge the PR into the develop branch.
2026-02-14 20:34:19 +01:00
JC5
1f04888331 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 20:34:08 +01:00
James Cole
28c21ecb7e Update changelog 2026-02-14 20:29:19 +01:00
github-actions[bot]
7382030e61 Merge pull request #11711 from firefly-iii/release-1771075473
🤖 Automatically merge the PR into the develop branch.
2026-02-14 14:24:39 +01:00
JC5
a573e19dbc 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 14:24:33 +01:00
James Cole
b152a2c8d9 Fix #11710 2026-02-14 14:20:24 +01:00
github-actions[bot]
7e14c36fc0 Merge pull request #11709 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-02-14 13:38:49 +01:00
github-actions[bot]
58bfc7f8ce Merge pull request #11708 from firefly-iii/release-1771072714
🤖 Automatically merge the PR into the develop branch.
2026-02-14 13:38:43 +01:00
JC5
6b8c005108 🤖 Auto commit for release 'v6.4.20' on 2026-02-14 2026-02-14 13:38:34 +01:00
James Cole
ceb660fd02 Fix missing setting. Restore 8.4 for the moment. Very professional. 2026-02-14 13:33:29 +01:00
James Cole
7fac1e8614 Remove statistics. 2026-02-14 13:25:39 +01:00
github-actions[bot]
809e34f5fe Merge pull request #11707 from firefly-iii/release-1771071314
🤖 Automatically merge the PR into the develop branch.
2026-02-14 13:15:20 +01:00
JC5
73ca57c8c4 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 13:15:14 +01:00
James Cole
63088ffeb1 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2026-02-14 13:10:54 +01:00
James Cole
4e88ebd52d Make Firefly III PHP 8.5 only. 2026-02-14 13:10:41 +01:00
github-actions[bot]
85fd38d146 Merge pull request #11706 from firefly-iii/release-1771070819
🤖 Automatically merge the PR into the develop branch.
2026-02-14 13:07:16 +01:00
JC5
e83d7051eb 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 13:06:59 +01:00
James Cole
567f2dae17 Remove problematic comment repetition by Mago. 2026-02-14 13:02:48 +01:00
github-actions[bot]
c231ae4016 Merge pull request #11705 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-02-14 12:55:26 +01:00
github-actions[bot]
fb13a4cdcb Merge pull request #11704 from firefly-iii/release-1771070112
🤖 Automatically merge the PR into the develop branch.
2026-02-14 12:55:21 +01:00
JC5
724042e1d8 🤖 Auto commit for release 'v6.4.19' on 2026-02-14 2026-02-14 12:55:12 +01:00
James Cole
84a30c3c8f Missing ltter. 2026-02-14 12:49:16 +01:00
github-actions[bot]
6c9dac831a Merge pull request #11703 from firefly-iii/release-1771069573
🤖 Automatically merge the PR into the develop branch.
2026-02-14 12:46:20 +01:00
JC5
e605ddb779 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 12:46:13 +01:00
James Cole
4b19ed8f07 Add changelog. 2026-02-14 12:41:28 +01:00
James Cole
57bd8e09d4 Fix #11702 2026-02-14 10:31:01 +01:00
github-actions[bot]
32f1a7c9c2 Merge pull request #11701 from firefly-iii/release-1771057524
🤖 Automatically merge the PR into the develop branch.
2026-02-14 09:25:32 +01:00
JC5
fc5b0db43f 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 09:25:24 +01:00
James Cole
c2721f3f48 Fix https://github.com/firefly-iii/firefly-iii/issues/11700 2026-02-14 08:18:53 +01:00
James Cole
96291c9bce Fix https://github.com/firefly-iii/firefly-iii/issues/11684 2026-02-14 08:17:19 +01:00
James Cole
ab9400aaee Merge branches 'develop' and 'develop' of github.com:firefly-iii/firefly-iii into develop 2026-02-13 08:05:02 +01:00
James Cole
31d1ee11cb Fix https://github.com/firefly-iii/firefly-iii/issues/11694 2026-02-13 08:04:39 +01:00
github-actions[bot]
694dc3816c Merge pull request #11699 from firefly-iii/release-1770965983
🤖 Automatically merge the PR into the develop branch.
2026-02-13 07:59:49 +01:00
JC5
c647fa7b94 🤖 Auto commit for release 'develop' on 2026-02-13 2026-02-13 07:59:43 +01:00
James Cole
022d59bfdd Fix https://github.com/firefly-iii/firefly-iii/issues/11688 2026-02-13 07:33:30 +01:00
James Cole
057b82b471 fix rename of rules when bill name changes. 2026-02-11 07:00:59 +01:00
57 changed files with 1137 additions and 771 deletions

View File

@@ -402,16 +402,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.93.1",
"version": "v3.94.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "b3546ab487c0762c39f308dc1ec0ea2c461fc21a"
"reference": "883b20fb38c7866de9844ab6d0a205c423bde2d4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/b3546ab487c0762c39f308dc1ec0ea2c461fc21a",
"reference": "b3546ab487c0762c39f308dc1ec0ea2c461fc21a",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/883b20fb38c7866de9844ab6d0a205c423bde2d4",
"reference": "883b20fb38c7866de9844ab6d0a205c423bde2d4",
"shasum": ""
},
"require": {
@@ -428,7 +428,7 @@
"react/event-loop": "^1.5",
"react/socket": "^1.16",
"react/stream": "^1.4",
"sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0",
"sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0 || ^8.0",
"symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0",
"symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0",
"symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0",
@@ -442,18 +442,18 @@
"symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0"
},
"require-dev": {
"facile-it/paraunit": "^1.3.1 || ^2.7",
"infection/infection": "^0.32",
"justinrainbow/json-schema": "^6.6",
"facile-it/paraunit": "^1.3.1 || ^2.7.1",
"infection/infection": "^0.32.3",
"justinrainbow/json-schema": "^6.6.4",
"keradus/cli-executor": "^2.3",
"mikey179/vfsstream": "^1.6.12",
"php-coveralls/php-coveralls": "^2.9",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6",
"phpunit/phpunit": "^9.6.31 || ^10.5.60 || ^11.5.48",
"php-coveralls/php-coveralls": "^2.9.1",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.7",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.7",
"phpunit/phpunit": "^9.6.34 || ^10.5.63 || ^11.5.51",
"symfony/polyfill-php85": "^1.33",
"symfony/var-dumper": "^5.4.48 || ^6.4.26 || ^7.4.0 || ^8.0",
"symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0"
"symfony/var-dumper": "^5.4.48 || ^6.4.32 || ^7.4.4 || ^8.0.4",
"symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0.1"
},
"suggest": {
"ext-dom": "For handling output formats in XML",
@@ -494,7 +494,7 @@
],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.93.1"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.94.0"
},
"funding": [
{
@@ -502,7 +502,7 @@
"type": "github"
}
],
"time": "2026-01-28T23:50:50+00:00"
"time": "2026-02-11T16:44:33+00:00"
},
{
"name": "psr/container",
@@ -1185,29 +1185,29 @@
},
{
"name": "sebastian/diff",
"version": "7.0.0",
"version": "8.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "7ab1ea946c012266ca32390913653d844ecd085f"
"reference": "a2b6d09d7729ee87d605a439469f9dcc39be5ea3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f",
"reference": "7ab1ea946c012266ca32390913653d844ecd085f",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/a2b6d09d7729ee87d605a439469f9dcc39be5ea3",
"reference": "a2b6d09d7729ee87d605a439469f9dcc39be5ea3",
"shasum": ""
},
"require": {
"php": ">=8.3"
"php": ">=8.4"
},
"require-dev": {
"phpunit/phpunit": "^12.0",
"phpunit/phpunit": "^13.0",
"symfony/process": "^7.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "7.0-dev"
"dev-main": "8.0-dev"
}
},
"autoload": {
@@ -1240,15 +1240,27 @@
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
"security": "https://github.com/sebastianbergmann/diff/security/policy",
"source": "https://github.com/sebastianbergmann/diff/tree/7.0.0"
"source": "https://github.com/sebastianbergmann/diff/tree/8.0.0"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
},
{
"url": "https://liberapay.com/sebastianbergmann",
"type": "liberapay"
},
{
"url": "https://thanks.dev/u/gh/sebastianbergmann",
"type": "thanks_dev"
},
{
"url": "https://tidelift.com/funding/github/packagist/sebastian/diff",
"type": "tidelift"
}
],
"time": "2025-02-07T04:55:46+00:00"
"time": "2026-02-06T04:42:27+00:00"
},
{
"name": "symfony/console",

7
.github/mergify.yml vendored
View File

@@ -1,11 +1,4 @@
pull_request_rules:
- name: Make sure PR are up to date before merging
description: This automatically updates PRs when they are out-of-date with the
base branch to avoid semantic conflicts (next step is using a merge
queue).
conditions: []
actions:
update:
- name: Close all on main
conditions:
- base=main

View File

@@ -63,6 +63,9 @@ class UpdateRequest extends FormRequest
{
/** @var TransactionCurrency $currency */
$currency = $this->route()->parameter('currency_code');
if (is_string($currency)) {
$currency = TransactionCurrency::whereCode($currency)->first();
}
return [
'name' => sprintf('min:1|max:255|unique:transaction_currencies,name,%d', $currency->id),

View File

@@ -56,8 +56,8 @@ class CorrectsGroupAccounts extends Command
}
}
$flags = new TransactionGroupEventFlags();
$flags->applyRules = true;
$flags->fireWebhooks = true;
$flags->applyRules = false;
$flags->fireWebhooks = false;
$flags->recalculateCredit = true;
$objects = new TransactionGroupEventObjects();
foreach ($groups as $groupId) {

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\System;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Console\Commands\Tools\VerifiesDatabaseConnectionTrait;
use Illuminate\Console\Command;
use PDO;
use PDOException;
@@ -32,6 +33,7 @@ use PDOException;
class CreatesDatabase extends Command
{
use ShowsFriendlyMessages;
use VerifiesDatabaseConnectionTrait;
protected $description = 'Tries to create the database if it doesn\'t exist yet.';
@@ -39,21 +41,27 @@ class CreatesDatabase extends Command
public function handle(): int
{
if ('mysql' !== env('DB_CONNECTION')) { // @phpstan-ignore larastan.noEnvCallsOutsideOfConfig */
$this->friendlyInfo(sprintf('CreateDB does not apply to "%s", skipped.', env('DB_CONNECTION')));
$connected = $this->verifyDatabaseConnection();
if (!$connected) {
$this->friendlyError('Failed to connect to the database. Is it up?');
return Command::FAILURE;
}
if ('mysql' !== config('database.default')) { // @phpstan-ignore larastan.noEnvCallsOutsideOfConfig */
$this->friendlyInfo(sprintf('CreateDB does not apply to "%s", skipped.', config('database.default')));
return 0;
}
// try to set up a raw connection:
$exists = false;
$dsn = sprintf('mysql:host=%s;port=%d;charset=utf8mb4', env('DB_HOST'), env('DB_PORT'));
$exists = false;
$dsn = sprintf('mysql:host=%s;port=%d;charset=utf8mb4', env('DB_HOST'), env('DB_PORT'));
if ('' !== (string) env('DB_SOCKET')) {
$dsn = sprintf('mysql:unix_socket=%s;charset=utf8mb4', env('DB_SOCKET'));
}
$this->friendlyLine(sprintf('DSN is %s', $dsn));
$options = [
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
@@ -71,7 +79,7 @@ class CreatesDatabase extends Command
// only continue when no error.
// with PDO, try to list DB's (
/** @var array $stmt */
$stmt = $pdo->query('SHOW DATABASES;');
$stmt = $pdo->query('SHOW DATABASES;');
// slightly more complex but less error-prone.
foreach ($stmt as $row) {
$name = $row['Database'] ?? false;

View File

@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/*
* VerifiesDatabaseConnection.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Tools;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command;
class VerifiesDatabaseConnection extends Command
{
use ShowsFriendlyMessages;
use VerifiesDatabaseConnectionTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:verify-database-connection';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This command tries to connect to the database.';
/**
* Execute the console command.
*/
public function handle(): int
{
$connected = $this->verifyDatabaseConnection();
if ($connected) {
$this->friendlyPositive('Connected to the database.');
return Command::SUCCESS;
}
$this->friendlyError('Failed to connect to the database. Is it up?');
return Command::FAILURE;
}
}

View File

@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
/*
* VerifiesDatabaseConnectionTrait.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Tools;
use Exception;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
trait VerifiesDatabaseConnectionTrait
{
protected function verifyDatabaseConnection(): bool
{
$loops = 30;
$loop = 0;
$queries = ['pgsql' => 'SELECT * FROM pg_catalog.pg_tables;', 'sqlite' => 'SELECT name FROM sqlite_schema;', 'mysql' => 'SHOW TABLES;'];
$default = config('database.default');
if (!array_key_exists($default, $queries)) {
$this->friendlyWarning(sprintf('Cannot validate database connection for "%s"', $default));
return true;
}
$query = $queries[$default];
$connected = false;
Log::debug(sprintf('Connecting to database "%s"...', config('database.default')));
while (!$connected && $loop < $loops) {
try {
DB::select($query);
$connected = true;
} catch (QueryException $e) {
Log::error(sprintf('Loop #%d: connection failed: %s', $loop, $e->getMessage()));
$this->friendlyWarning(sprintf('Database connection attempt #%d failed. Sleep for 10 seconds...', $loop + 1));
sleep(10);
} catch (Exception $e) {
Log::error(sprintf('Loop #%d: not connected yet because of a %s: %s', $loop, get_class($e), $e->getMessage()));
$this->friendlyWarning(sprintf('Database connection attempt #%d failed. Sleep for 10 seconds...', $loop + 1));
sleep(10);
}
++$loop;
}
return $connected;
}
}

View File

@@ -94,6 +94,7 @@ class UpgradesDatabase extends Command
private function callInitialCommands(): void
{
$this->call('firefly-iii:verify-database-connection');
$this->call('migrate', ['--seed' => true, '--force' => true, '--no-interaction' => true]);
$this->call('upgrade:600-pgsql-sequences');
$this->call('upgrade:480-decrypt-all');

View File

@@ -1,69 +0,0 @@
<?php
/**
* Kernel.php
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Facades\Log;
use Override;
/**
* File to make sure commands work.
*/
class Kernel extends ConsoleKernel
{
/**
* Register the commands for the application.
*/
#[Override]
protected function commands(): void
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
/**
* Define the application's command schedule.
*/
#[Override]
protected function schedule(Schedule $schedule): void
{
$schedule->call(static function (): void {
Log::error('Firefly III no longer users the Laravel scheduler to do cron jobs! Please read the instructions at https://docs.firefly-iii.org/');
echo "\n";
echo '------------';
echo "\n";
echo wordwrap('Firefly III no longer users the Laravel scheduler to do cron jobs! Please read the instructions here:');
echo "\n";
echo 'https://docs.firefly-iii.org/';
echo "\n\n";
echo 'Disable this cron job!';
echo "\n";
echo '------------';
echo "\n";
})->daily();
}
}

View File

@@ -1,8 +1,10 @@
<?php
declare(strict_types=1);
/*
* api-noauth.php
* Copyright (c) 2021 james@firefly-iii.org
* UpdatedExistingBill.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -20,20 +22,21 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events\Model\Bill;
use Illuminate\Support\Facades\Route;
// Cron job API routes:
use FireflyIII\Http\Middleware\AcceptHeaders;
use FireflyIII\Events\Event;
use FireflyIII\Models\Bill;
use Illuminate\Queue\SerializesModels;
Route::group(
[
'namespace' => 'FireflyIII\Api\V1\Controllers\System',
'prefix' => '',
'as' => 'api.v1.cron.',
'middleware' => [AcceptHeaders::class],
],
static function (): void {
Route::get('{cliToken}', ['uses' => 'CronController@cron', 'as' => 'index']);
}
);
class UpdatedExistingBill extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(
public Bill $bill,
public array $oldData
) {}
}

View File

@@ -36,13 +36,13 @@ class ConvertsAmountToPrimaryAmount
$primaryAmountField = $params->primaryAmountField;
if (!Amount::convertToPrimary($params->user)) {
Log::debug(sprintf(
'User does not want to do conversion, no need to convert "%s" and store it in field "%s" for %s #%d.',
$params->amountField,
$params->primaryAmountField,
get_class($params->model),
$params->model->id
));
// Log::debug(sprintf(
// 'User does not want to do conversion, no need to convert "%s" and store it in field "%s" for %s #%d.',
// $params->amountField,
// $params->primaryAmountField,
// get_class($params->model),
// $params->model->id
// ));
$params->model->{$primaryAmountField} = null;
$params->model->saveQuietly();

View File

@@ -115,6 +115,7 @@ class ConfigurationController extends Controller
FireflyConfig::set('enable_external_map', $data['enable_external_map']);
FireflyConfig::set('enable_external_rates', $data['enable_external_rates']);
FireflyConfig::set('allow_webhooks', $data['allow_webhooks']);
FireflyConfig::set('enable_batch_processing', $data['enable_batch_processing']);
FireflyConfig::set('valid_url_protocols', $data['valid_url_protocols']);
FireflyConfig::set('is_demo_site', $data['is_demo_site']);

View File

@@ -190,6 +190,14 @@ class LoginController extends Controller
*/
public function showLoginForm(Request $request): Factory|Redirector|RedirectResponse|View
{
if ('remote_user_guard' === config('auth.defaults.guard')) {
$message = sprintf(
'Firefly III is configured to use the "remote user guard", but was unable to link you to a user. Are you sure the "%s" header is in place?',
config('auth.guard_header')
);
return view('errors.error', ['message' => $message]);
}
Log::channel('audit')->info('Show login form (1.1).');
$count = DB::table('users')->count();

View File

@@ -80,6 +80,14 @@ abstract class Controller extends BaseController
View::share('FF_VERSION', config('firefly.version'));
View::share('FF_BUILD_TIME', config('firefly.build_time'));
// this breaks when running < php 8.5 and is totally intentional.
// $input = ' James is cool';
// $output = $input
// |> trim(...)
// |> (fn (string $string) => str_replace(' ', '-', $string))
// |> (fn (string $string) => str_replace(['.', '/', '…'], '', $string))
// |> strtolower(...);
// is webhooks enabled?
View::share(
'featuringWebhooks',

View File

@@ -103,10 +103,11 @@ class IndexController extends Controller
$firstJournal = $this->repository->firstNull();
$startPeriod = $firstJournal instanceof TransactionJournal ? $firstJournal->date : new Carbon();
$endPeriod = clone $end;
// limit to 3 years for the time being.
if (now()->diffInYears($startPeriod, true) > 3) {
$startPeriod = now()->subYears(3);
$endPeriod->endOfDay();
// limit to 6 years for the time being.
$max = 6;
if (now()->diffInYears($startPeriod, true) > $max) {
$startPeriod = now()->subYears($max);
}
$periods = $this->getTransactionPeriodOverview($objectType, $startPeriod, $endPeriod);

View File

@@ -1,65 +0,0 @@
<?php
/**
* Kernel.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http;
use FireflyIII\Http\Middleware\Authenticate;
use FireflyIII\Http\Middleware\Binder;
use FireflyIII\Http\Middleware\InstallationId;
use FireflyIII\Http\Middleware\RedirectIfAuthenticated;
use FireflyIII\Http\Middleware\StartFireflySession;
use FireflyIII\Http\Middleware\TrimStrings;
use FireflyIII\Http\Middleware\TrustProxies;
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
use Illuminate\Auth\Middleware\Authorize;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode;
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\View\Middleware\ShareErrorsFromSession;
/**
* Class Kernel
*/
class Kernel extends HttpKernel
{
protected $middleware = [
// SecureHeaders::class,
CheckForMaintenanceMode::class,
ValidatePostSize::class,
TrimStrings::class,
ConvertEmptyStringsToNull::class,
TrustProxies::class,
InstallationId::class,
];
protected $middlewareAliases = [
'auth' => Authenticate::class,
'auth.basic' => AuthenticateWithBasicAuth::class,
'bindings' => Binder::class,
'can' => Authorize::class,
'guest' => RedirectIfAuthenticated::class,
'throttle' => ThrottleRequests::class,
];
protected $middlewarePriority = [StartFireflySession::class, ShareErrorsFromSession::class, Authenticate::class, Binder::class, Authorize::class];
}

View File

@@ -82,22 +82,25 @@ class Authenticate
protected function authenticate($request, array $guards)
{
if (0 === count($guards)) {
// go for default guard:
// @noinspection PhpUndefinedMethodInspection
if ($this->auth->check()) {
// do an extra check on user object.
/** @noinspection PhpUndefinedMethodInspection */
/** @var User $user */
$user = $this->auth->authenticate();
Log::debug('in Authenticate::authenticate() with zero guards.');
// There are no guards defined, go for the default guard:
if (auth()->check()) {
Log::debug('User is authenticated.');
$user = auth()->user();
$this->validateBlockedUser($user, $guards);
}
return;
}
// @noinspection PhpUndefinedMethodInspection
return $this->auth->authenticate();
$this->auth->authenticate();
if (!$this->auth->check()) {
throw new AuthenticationException('The user is not logged in but must be.', $guards);
}
}
exit('five');
foreach ($guards as $guard) {
exit('six');
if ('api' !== $guard) {
$this->auth->guard($guard)->authenticate();
}
@@ -111,6 +114,7 @@ class Authenticate
}
}
exit('seven');
// this is a massive hack, but if the handler has the oauth exception
// at this point we can report its error instead of a generic one.
$message = 'Unauthenticated.';
@@ -143,5 +147,6 @@ class Authenticate
// @phpstan-ignore-line (thinks function is undefined)
throw new AuthenticationException('Blocked account.', $guards);
}
Log::debug(sprintf('User #%d is not blocked.', $user->id));
}
}

View File

@@ -32,7 +32,7 @@ use Override;
/**
* Class StartFireflySession.
*/
class StartFireflySession extends StartSession
class StartFireflyIIISession extends StartSession
{
/**
* Store the current URL for the request if necessary.

View File

@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/*
* UpdatesRulesForChangedBill.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Listeners\Model\Bill;
use FireflyIII\Events\Model\Bill\UpdatedExistingBill;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\RuleTrigger;
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log;
class UpdatesRulesForChangedBill implements ShouldQueue
{
public function handle(UpdatedExistingBill $event): void
{
// update rule actions.
if ($event->bill->name !== $event->oldData['name']) {
$this->updateBillTriggersAndActions($event->bill, $event->oldData);
}
}
private function updateBillTriggersAndActions(Bill $bill, array $oldData): void
{
Log::debug(sprintf('Now in updateBillTriggersAndActions(#%d)', $bill->id));
$repository = app(RuleRepositoryInterface::class);
$repository->setUser($bill->user);
$rules = $repository->getAll();
/** @var Rule $rule */
foreach ($rules as $rule) {
$this->updateRule($bill, $rule, $oldData);
}
}
private function updateRule(Bill $bill, Rule $rule, array $oldData): void
{
$triggers = ['bill_is', 'bill_ends', 'bill_starts', 'bill_contains'];
/** @var RuleTrigger $trigger */
foreach ($rule->ruleTriggers as $trigger) {
if (in_array($trigger->trigger_type, $triggers, true) && $trigger->trigger_value === $oldData['name']) {
Log::debug(sprintf('Updated trigger #%d in rule #%d to new subscription name', $trigger->id, $rule->id));
$trigger->trigger_value = $bill->name;
$trigger->save();
}
}
/** @var RuleAction $action */
foreach ($rule->ruleActions as $action) {
if ('link_to_bill' === $action->action_type && $action->action_value === $oldData['name']) {
Log::debug(sprintf('Updated action #%d in rule #%d to new subscription name', $action->id, $rule->id));
$action->action_value = $bill->name;
$action->save();
}
}
}
}

View File

@@ -104,7 +104,9 @@ trait SupportsGroupProcessingTrait
$repository->deleteStatisticsForType(Category::class, $objects->categories, $dates);
$repository->deleteStatisticsForType(Tag::class, $objects->tags, $dates);
// remove if no stuff present:
// remove generic statistics:
$repository->deleteStatisticsForPrefix('all_', $dates);
// remove for no tag, no cat, etc.
if (0 === $objects->budgets->count()) {
Log::debug('No budgets, delete "no_category" stats.');

View File

@@ -149,12 +149,10 @@ class RecalculatesPrimaryCurrencyAmounts
->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.user_group_id', $userGroup->id)
->where(static function (Builder $q): void {
$q
->whereNotNull('native_amount')
->orWhereNotNull('native_foreign_amount')
->orWhere('native_amount', '!=', '')
->orWhere('native_foreign_amount', '!=', '')
;
$q->whereNotNull('native_amount')->orWhereNotNull('native_foreign_amount');
if ('pgsql' !== config('database.default')) {
$q->orWhere('native_amount', '!=', '')->orWhere('native_foreign_amount', '!=', '');
}
})
->update(['native_amount' => null, 'native_foreign_amount' => null])
;

View File

@@ -34,10 +34,6 @@ use Laravel\Passport\Passport;
*/
class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
// 'FireflyIII\Model' => 'FireflyIII\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*

View File

@@ -23,7 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Providers;
use FireflyIII\Http\Middleware\StartFireflySession;
use FireflyIII\Http\Middleware\StartFireflyIIISession;
use Illuminate\Session\SessionManager;
use Illuminate\Support\ServiceProvider;
use Override;
@@ -43,7 +43,7 @@ class FireflySessionProvider extends ServiceProvider
$this->registerSessionDriver();
$this->app->singleton(StartFireflySession::class);
$this->app->singleton(StartFireflyIIISession::class);
}
/**

View File

@@ -42,20 +42,18 @@ class RouteServiceProvider extends ServiceProvider
#[Override]
public function boot(): void
{
$this->routes(function (): void {
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'))
;
Route::prefix('api/v1/cron')
->middleware('api_basic')
->namespace($this->namespace)
->group(base_path('routes/api-noauth.php'))
;
Route::middleware('web')->namespace($this->namespace)->group(base_path('routes/web.php'));
});
// $this->routes(function (): void {
// Route::prefix('api')
// ->middleware('api')
// ->namespace($this->namespace)
// ->group(base_path('routes/api.php'))
// ;
// Route::prefix('api/v1/cron')
// ->middleware('api_basic')
// ->namespace($this->namespace)
// ->group(base_path('routes/api-noauth.php'))
// ;
// Route::middleware('web')->namespace($this->namespace)->group(base_path('routes/web.php'));
// });
}
}

View File

@@ -23,7 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Providers;
use FireflyIII\Http\Middleware\StartFireflySession;
use FireflyIII\Http\Middleware\StartFireflyIIISession;
use Illuminate\Session\SessionServiceProvider as BaseSessionServiceProvider;
use Override;
@@ -42,6 +42,6 @@ class SessionServiceProvider extends BaseSessionServiceProvider
$this->registerSessionDriver();
$this->app->singleton(StartFireflySession::class);
$this->app->singleton(StartFireflyIIISession::class);
}
}

View File

@@ -96,7 +96,7 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
foreach ($set as $bill) {
if ($bill->order !== $current) {
$bill->order = $current;
$bill->save();
$bill->saveQuietly();
}
++$current;
}
@@ -613,7 +613,7 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
public function setOrder(Bill $bill, int $order): void
{
$bill->order = $order;
$bill->save();
$bill->saveQuietly();
}
/**

View File

@@ -49,6 +49,9 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U
#[Override]
public function allInRangeForPrefix(string $prefix, Carbon $start, Carbon $end): Collection
{
Log::debug(sprintf('Collect all statistics where type starts with "%s"', $prefix));
Log::debug(sprintf('Between %s and %s', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
return $this->userGroup
->periodStatistics()
->where('type', 'LIKE', sprintf('%s%%', $prefix))
@@ -156,6 +159,7 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U
int $count,
string $amount
): PeriodStatistic {
Log::debug(sprintf('Store as type "%s"', sprintf('%s_%s', $prefix, $type)));
$stat = new PeriodStatistic();
$stat->transaction_currency_id = $currencyId;
$stat->user_group_id = $this->getUserGroup()->id;

View File

@@ -102,7 +102,7 @@ class RuleRepository implements RuleRepositoryInterface, UserGroupInterface
{
return $this->user
->rules()
->with(['ruleGroup'])
->with(['ruleGroup', 'ruleTriggers', 'ruleActions'])
->get()
;
}

View File

@@ -37,6 +37,7 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Rules\UniqueIban;
use FireflyIII\Support\NullArrayObject;
use Illuminate\Database\UniqueConstraintViolationException;
use Illuminate\Support\Facades\Log;
use Safe\Exceptions\JsonException;
@@ -227,7 +228,12 @@ trait JournalServiceTrait
$set = array_unique($set);
Log::debug('End of loop.');
Log::debug(sprintf('Total nr. of tags: %d', count($tags)), $tags);
$journal->tags()->sync($set);
try {
$journal->tags()->sync($set);
} catch (UniqueConstraintViolationException $e) {
Log::error(sprintf('Firefly III could not sync tags: %s', $e->getMessage()));
}
}
/**

View File

@@ -24,12 +24,12 @@ declare(strict_types=1);
namespace FireflyIII\Services\Internal\Update;
use FireflyIII\Events\Model\Bill\UpdatedExistingBill;
use FireflyIII\Factory\TransactionCurrencyFactory;
use FireflyIII\Models\Bill;
use FireflyIII\Models\ObjectGroup;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleTrigger;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups;
use FireflyIII\Services\Internal\Support\BillServiceTrait;
use FireflyIII\Support\Facades\Amount;
@@ -50,6 +50,7 @@ class BillUpdateService
public function update(Bill $bill, array $data): Bill
{
$this->user = $bill->user;
$oldData = $bill->toArray();
if (array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) {
$factory = app(TransactionCurrencyFactory::class);
@@ -68,13 +69,6 @@ class BillUpdateService
$bill = $this->updateBillProperties($bill, $data);
$bill->save();
$bill->refresh();
// old values
$oldData = [
'name' => $bill->name,
'amount_min' => $bill->amount_min,
'amount_max' => $bill->amount_max,
'transaction_currency_name' => $bill->transactionCurrency->name,
];
// update note:
if (array_key_exists('notes', $data)) {
$this->updateNote($bill, (string) $data['notes']);
@@ -90,12 +84,6 @@ class BillUpdateService
}
}
// update rule actions.
if (array_key_exists('name', $data)) {
$this->updateBillActions($bill, $oldData['name'], $data['name']);
$this->updateBillTriggers($bill, $oldData, $data);
}
// update using name:
if (array_key_exists('object_group_title', $data)) {
$objectGroupTitle = $data['object_group_title'] ?? '';
@@ -127,6 +115,7 @@ class BillUpdateService
$bill->objectGroups()->sync([]);
$bill->save();
}
event(new UpdatedExistingBill($bill, $oldData));
return $bill;
}
@@ -181,39 +170,6 @@ class BillUpdateService
return $bill;
}
private function updateBillTriggers(Bill $bill, array $oldData, array $newData): void
{
Log::debug(sprintf('Now in updateBillTriggers(%d, "%s")', $bill->id, $bill->name));
/** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class);
$repository->setUser($bill->user);
$rules = $repository->getRulesForBill($bill);
if (0 === $rules->count()) {
Log::debug('Found no rules.');
return;
}
Log::debug(sprintf('Found %d rules', $rules->count()));
$fields = [
'name' => 'description_contains',
'amount_min' => 'amount_more',
'amount_max' => 'amount_less',
'transaction_currency_name' => 'currency_is',
];
foreach ($fields as $field => $ruleTriggerKey) {
if (!array_key_exists($field, $newData)) {
continue;
}
if ($oldData[$field] === $newData[$field]) {
Log::debug(sprintf('Field %s is unchanged ("%s"), continue.', $field, $oldData[$field]));
continue;
}
$this->updateRules($rules, $ruleTriggerKey, $oldData[$field], $newData[$field]);
}
}
private function updateOrder(Bill $bill, int $oldOrder, int $newOrder): void
{
if ($newOrder > $oldOrder) {

View File

@@ -31,6 +31,7 @@ use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Log;
/**
@@ -40,6 +41,7 @@ class RemoteUserGuard implements Guard
{
protected Application $application;
protected ?User $user = null;
private $tried = false;
/**
* Create a new authentication guard.
@@ -49,13 +51,19 @@ class RemoteUserGuard implements Guard
Application $app
) {
$app->get('request');
// Log::debug(sprintf('Created RemoteUserGuard for %s "%s"', $request?->getMethod(), $request?->getRequestUri()));
Log::debug(sprintf('Created RemoteUserGuard for %s "%s"', $app->get('request')?->getMethod(), $app->get('request')?->getRequestUri()));
$this->application = $app;
}
public function authenticate(): void
{
// Log::debug(sprintf('Now at %s', __METHOD__));
$this->tried = true;
Log::debug(sprintf('Now at %s', __METHOD__));
if (App::runningInConsole()) {
Log::debug('Running in console, will not authenticate.');
return;
}
if ($this->user instanceof User) {
Log::debug(sprintf('%s is found: #%d, "%s".', $this->user::class, $this->user->id, $this->user->email));
@@ -70,10 +78,14 @@ class RemoteUserGuard implements Guard
$userID = request()->server($header) ?? apache_request_headers()[$header] ?? null;
}
// test value for development
// $userID = 'james@firefly';
if (null === $userID || '' === $userID) {
Log::error(sprintf('No user in header "%s".', $header));
throw new FireflyException('The guard header was unexpectedly empty. See the logs.');
// throw new FireflyException('The guard header was unexpectedly empty. See the logs.');
return;
}
Log::debug(sprintf('User ID found in header is "%s"', $userID));
@@ -145,8 +157,18 @@ class RemoteUserGuard implements Guard
public function user(): ?User
{
if (App::runningInConsole()) {
Log::debug('Running in console, will not authenticate.');
return null;
}
if (false === $this->tried) {
Log::debug('Have not tried authentication, do it now.');
$this->authenticate();
}
// Log::debug(sprintf('Now at %s', __METHOD__));
$user = $this->user;
if (!$user instanceof User) {
Log::debug('User is NULL');

View File

@@ -35,20 +35,8 @@ class Calculator
{
public const int DEFAULT_INTERVAL = 1;
private static ?SplObjectStorage $intervalMap = null; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private static array $intervals = [];
private static ?SplObjectStorage $intervalMap = null;
private static array $intervals = [];
public function isAvailablePeriodicity(Periodicity $periodicity): bool
{

View File

@@ -114,6 +114,46 @@ class ExportDataGenerator
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
public function __construct()
{
$this->accounts = new Collection();

View File

@@ -37,7 +37,6 @@ use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Navigation;
use FireflyIII\Support\Facades\Steam;
@@ -80,44 +79,8 @@ trait PeriodOverview
protected TagRepositoryInterface $tagRepository;
protected JournalRepositoryInterface $journalRepos;
protected PeriodStatisticRepositoryInterface $periodStatisticRepo;
private Collection $statistics; // temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
private array $transactions; // temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
// temp data holder
private Collection $statistics;
private array $transactions;
/**
* This method returns "period entries", so nov-2015, dec-2015, etc. (this depends on the users session range)
@@ -178,6 +141,34 @@ trait PeriodOverview
return $entries;
}
protected function getGenericPeriod(string $type, string $period, Carbon $start, Carbon $end): array
{
$return = [
'title' => Navigation::periodShow($start, $period),
'route' => route('transactions.index', [$type, $start->format('Y-m-d'), $end->format('Y-m-d')]),
'total_transactions' => 0,
];
$setTypes = [
'withdrawal' => 'spent',
'expenses' => 'spent',
'deposit' => 'earned',
'revenue' => 'earned',
'transfer' => 'transferred',
'transfers' => 'transferred',
];
if (!array_key_exists($type, $setTypes)) {
throw new FireflyException(sprintf('[c] Cannot deal with type "%s"', $type));
}
$setType = $setTypes[$type];
$this->transactions = [];
$set = $this->getSingleGenericPeriodByType($start, $end, $type);
$return['total_transactions'] += $set['count'];
$return[$setType] = $set;
return $return;
}
/**
* Same as above, but for lists that involve transactions without a budget.
*
@@ -263,60 +254,18 @@ trait PeriodOverview
*/
protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array
{
$range = Navigation::getViewRange(true);
$types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType));
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for cache
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('transactions-period-entries');
$cache->addProperty($transactionType);
if ($cache->has()) {
return $cache->get();
}
$this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class);
$range = Navigation::getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
/** @var array $dates */
$dates = Navigation::blockPeriods($start, $end, $range);
$entries = [];
$spent = [];
$earned = [];
$transferred = [];
// collect all journals in this period (regardless of type)
$collector = app(GroupCollectorInterface::class);
$collector->setTypes($types)->setRange($start, $end);
$genericSet = $collector->getExtractedJournals();
$loops = 0;
$dates = Navigation::blockPeriods($start, $end, $range);
$entries = [];
$this->statistics = $this->periodStatisticRepo->allInRangeForPrefix('all_', $start, $end);
Log::debug(sprintf('Collected %d statistics', $this->statistics->count()));
foreach ($dates as $currentDate) {
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
if ($loops < 10) {
// set to correct array
if ('expenses' === $transactionType || 'withdrawal' === $transactionType) {
$spent = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']);
}
if ('revenue' === $transactionType || 'deposit' === $transactionType) {
$earned = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']);
}
if ('transfer' === $transactionType || 'transfers' === $transactionType) {
$transferred = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']);
}
}
$entries[] = [
'title' => $title,
'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),
'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred),
];
++$loops;
$entries[] = $this->getGenericPeriod($transactionType, $currentDate['period'], $currentDate['start'], $currentDate['end']);
}
return $entries;
@@ -363,10 +312,11 @@ trait PeriodOverview
return new Collection();
}
Log::debug(sprintf('Now in filterStatistics("%s")', $type));
return $this->statistics->filter(
static fn (PeriodStatistic $statistic): bool => $statistic->start->eq($start) && $statistic->end->eq($end) && $statistic->type === $type
);
return $this->statistics->filter(static function (PeriodStatistic $statistic) use ($start, $end, $type): bool {
return $statistic->start->isSameSecond($start) && $statistic->end->isSameSecond($end) && $statistic->type === $type;
});
}
private function filterTransactionsByType(TransactionTypeEnum $type, Carbon $start, Carbon $end): array
@@ -428,21 +378,63 @@ trait PeriodOverview
return [$start, $end];
}
private function getSingleGenericPeriodByType(Carbon $start, Carbon $end, string $type): array
{
$filterType = sprintf('all_%s', $type);
$statistics = $this->filterStatistics($start, $end, $filterType);
$types = config(sprintf('firefly.transactionTypesByType.%s', $type));
// nothing found, regenerate them.
if (0 === $statistics->count()) {
if (0 === count($this->transactions)) {
// get collection!
// collect all journals in this period (regardless of type)
$collector = app(GroupCollectorInterface::class);
$collector->setTypes($types)->setRange($start, $end);
$this->transactions = $collector->getExtractedJournals();
Log::debug(sprintf('Going to group %d found journal(s)', count($types)));
}
$grouped = $this->groupByCurrency($this->filterJournalsByDate($this->transactions, $start, $end));
$this->saveGroupedForPrefix('all', $start, $end, $type, $grouped);
return $grouped;
}
$grouped = ['count' => 0];
/** @var PeriodStatistic $statistic */
foreach ($statistics as $statistic) {
$id = (int) $statistic->transaction_currency_id;
$currency = Amount::getTransactionCurrencyById($id);
$grouped[$id] = [
'amount' => (string) $statistic->amount,
'count' => (int) $statistic->count,
'currency_id' => $currency->id,
'currency_name' => $currency->name,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
];
$grouped['count'] += (int) $statistic->count;
}
return $grouped;
}
private function getSingleModelPeriodByType(Model $model, Carbon $start, Carbon $end, string $type): array
{
Log::debug(sprintf(
'Now in getSingleModelPeriodByType(%s #%d, %s %s, %s)',
$model::class,
$model->id,
$start->format('Y-m-d'),
$end->format('Y-m-d'),
$start->format('Y-m-d H:i:s.u'),
$end->format('Y-m-d H:i:s.u'),
$type
));
$statistics = $this->filterStatistics($start, $end, $type);
// nothing found, regenerate them.
if (0 === $statistics->count()) {
Log::debug(sprintf('Found nothing in this period for type "%s"', $type));
Log::debug(sprintf('Found nothing between %s and %s for type "%s"', $start->format('Y-m-d H:i:s.u'), $end->format('Y-m-d H:i:s.u'), $type));
if (0 === count($this->transactions)) {
switch ($model::class) {
default:
@@ -467,7 +459,7 @@ trait PeriodOverview
switch ($type) {
default:
throw new FireflyException(sprintf('Cannot deal with category period type %s', $type));
throw new FireflyException(sprintf('Cannot deal with type %s', $type));
case 'spent':
$result = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $start, $end);
@@ -529,7 +521,7 @@ trait PeriodOverview
switch ($model) {
default:
throw new FireflyException(sprintf('Cannot deal with model of type "%s"', $model));
throw new FireflyException(sprintf('[b] Cannot deal with model of type "%s"', $model));
case 'budget':
// get all expenses without a budget.

View File

@@ -53,6 +53,26 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private readonly bool $convertToPrimary; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
@@ -66,6 +86,26 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $currencies = [];
private array $currencyIds = [];
private array $ids = [];

View File

@@ -53,6 +53,26 @@ class BudgetLimitEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $currencies = [];
private array $currencyIds = [];
private Carbon $end;

View File

@@ -55,6 +55,26 @@ class PiggyBankEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $accounts = []; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
@@ -68,6 +88,26 @@ class PiggyBankEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $amounts = [];
private Collection $collection;
private array $currencies = [];

View File

@@ -50,6 +50,26 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $accountIds = []; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
@@ -63,6 +83,26 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private Collection $collection;
private array $currencies = [];
private array $groupIds = [];

View File

@@ -59,6 +59,26 @@ class SubscriptionEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private readonly bool $convertToPrimary;
private ?Carbon $end = null;
private array $mappedObjects = [];

View File

@@ -79,6 +79,46 @@ class TransactionGroupEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
public function __construct()
{
$this->dateFields = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date'];

View File

@@ -55,6 +55,26 @@ class WebhookEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $ids = []; // @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
@@ -68,6 +88,26 @@ class WebhookEnrichment implements EnrichmentInterface
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
// @phpstan-ignore-line
private array $responses = [];
private array $triggers = [];
private array $webhookDeliveries = [];

View File

@@ -51,8 +51,8 @@ class ObjectGroupTransformer extends AbstractTransformer
return [
'id' => (string) $objectGroup->id,
'created_at' => $objectGroup->created_at?->toAtomString(),
'updated_at' => $objectGroup->updated_at?->toAtomString(),
'created_at' => $objectGroup->created_at->toAtomString(),
'updated_at' => $objectGroup->updated_at->toAtomString(),
'title' => $objectGroup->title,
'order' => $objectGroup->order,
'links' => [['rel' => 'self', 'uri' => '/object_groups/'.$objectGroup->id]],

View File

@@ -66,8 +66,8 @@ class PiggyBankEventTransformer extends AbstractTransformer
return [
'id' => (string) $event->id,
'created_at' => $event->created_at?->toAtomString(),
'updated_at' => $event->updated_at?->toAtomString(),
'created_at' => $event->created_at->toAtomString(),
'updated_at' => $event->updated_at->toAtomString(),
'amount' => $amount,
'pc_amount' => $primaryAmount,

View File

@@ -32,11 +32,9 @@ use FireflyIII\Http\Middleware\IsAdmin;
use FireflyIII\Http\Middleware\Range;
use FireflyIII\Http\Middleware\RedirectIfAuthenticated;
use FireflyIII\Http\Middleware\SecureHeaders;
use FireflyIII\Http\Middleware\StartFireflySession;
use FireflyIII\Http\Middleware\TrustProxies;
use FireflyIII\Http\Middleware\StartFireflyIIISession;
use FireflyIII\Http\Middleware\VerifyCsrfToken;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
@@ -47,7 +45,6 @@ use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance;
use Illuminate\Foundation\Http\Middleware\TrimStrings;
use Illuminate\Http\Middleware\HandleCors;
use Illuminate\Http\Middleware\ValidatePostSize;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use Laravel\Passport\Http\Middleware\CreateFreshApiToken;
use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;
@@ -92,115 +89,99 @@ if (!function_exists('stringIsEqual')) {
$app = Application::configure(basePath: dirname(__DIR__))
->withRouting(
web : __DIR__ . '/../routes/web.php',
api : __DIR__ . '/../routes/api.php',
commands: __DIR__ . '/../routes/console.php',
health : '/up',
)
->withMiddleware(function (Middleware $middleware): void {
// overrule the standard middleware
$middleware->use(
[
InvokeDeferredCallbacks::class,
\Illuminate\Http\Middleware\TrustProxies::class, // use the DEFAULT middleware for this.
HandleCors::class,
PreventRequestsDuringMaintenance::class,
ValidatePostSize::class,
TrimStrings::class,
ConvertEmptyStringsToNull::class,
SecureHeaders::class,
TrustProxies::class,
SecureHeaders::class, // is a Firefly III specific middleware class.
]
);
// overrule the web group
// append and extend the default "web" middleware
// to include our own custom "StartFireflyIIISession" class.
// this class in turns contains a better "previous URL" feature.
// See https://laravel.com/docs/12.x/middleware for the default list.
$middleware->group('web',
[
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartFireflySession::class,
StartFireflyIIISession::class, // this is different.
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
Binder::class, // this is also different.
CreateFreshApiToken::class,
]
);
// new group?
$middleware->appendToGroup('binders-only',
[
Installer::class,
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
Binder::class,
]);
//
$middleware->appendToGroup('user-not-logged-in', [
Installer::class,
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartFireflySession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
Binder::class,
RedirectIfAuthenticated::class,
]);
// the default API group only contains "substitute bindings" middleware
// so here we replace the entire API group and add more sensible stuff.
$middleware->group('api',
[
AcceptHeaders::class,
// EnsureFrontendRequestsAreStateful::class,
'auth:api',
Binder::class,
]
);
$middleware->appendToGroup('api_basic', [AcceptHeaders::class, Binder::class]);
// more
$middleware->appendToGroup('user-logged-in-no-2fa', [
Installer::class,
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartFireflySession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
Binder::class,
Authenticate::class,
]);
// simple auth
// "simple auth" means the user must be logged in and present,
// but does not have to be 2FA authenticated. This is so all users
// can always log out, for example.
$middleware->appendToGroup('user-simple-auth', [
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartFireflySession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
Binder::class,
Authenticate::class,
]);
// user full auth
// This middleware is added for all routes where the user MUST have full authentication.
// this includes 2FA etc.
// incidentally, this group also includes the range middleware and the message thing.
$middleware->appendToGroup('user-full-auth', [
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartFireflySession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
Authenticate::class,
MFAMiddleware::class,
Range::class,
Binder::class,
InterestingMessage::class,
CreateFreshApiToken::class,
]);
// admin
// This middleware is added to ensure that the user is not only logged in and
// authenticated (with MFA and everything), but also admin.
$middleware->appendToGroup('admin', [
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartFireflySession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
Authenticate::class,
// AuthenticateTwoFactor::class,
MFAMiddleware::class,
IsAdmin::class,
Range::class,
Binder::class,
CreateFreshApiToken::class,
InterestingMessage::class,
]);
// api
$middleware->appendToGroup('api', [AcceptHeaders::class, EnsureFrontendRequestsAreStateful::class, 'auth:api,sanctum', Binder::class]);
// api basic,
$middleware->appendToGroup('api_basic', [AcceptHeaders::class, Binder::class]);
// if the user is not logged in, this group applies.
// on top of everything else of course.
$middleware->appendToGroup('user-not-logged-in', [
Installer::class,
RedirectIfAuthenticated::class,
]);
// the "binders only" group does not need or ask for authentication
// it just makes sure strings from routes are bound to objects if possible.
$middleware->group('binders-only',
[
Installer::class,
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
Binder::class,
]);
// $middleware->priority([StartFireflyIIISession::class, ShareErrorsFromSession::class, Authenticate::class, Binder::class, Authorize::class]);
})
->withEvents(discover: [
__DIR__ . '/../app/Listeners',
@@ -225,16 +206,6 @@ $app = Application::configure(basePath: dirname(__DIR__))
|
*/
$app->singleton(
Kernel::class,
FireflyIII\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
FireflyIII\Console\Kernel::class
);
$app->singleton(
ExceptionHandler::class,
Handler::class

71
bootstrap/providers.php Normal file
View File

@@ -0,0 +1,71 @@
<?php
/*
* providers.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use FireflyIII\Providers\AccountServiceProvider;
use FireflyIII\Providers\AdminServiceProvider;
use FireflyIII\Providers\AppServiceProvider;
use FireflyIII\Providers\AttachmentServiceProvider;
use FireflyIII\Providers\BillServiceProvider;
use FireflyIII\Providers\BudgetServiceProvider;
use FireflyIII\Providers\CategoryServiceProvider;
use FireflyIII\Providers\CurrencyServiceProvider;
use FireflyIII\Providers\FireflyServiceProvider;
use FireflyIII\Providers\JournalServiceProvider;
use FireflyIII\Providers\PiggyBankServiceProvider;
use FireflyIII\Providers\RecurringServiceProvider;
use FireflyIII\Providers\RouteServiceProvider;
use FireflyIII\Providers\RuleGroupServiceProvider;
use FireflyIII\Providers\RuleServiceProvider;
use FireflyIII\Providers\SearchServiceProvider;
use FireflyIII\Providers\TagServiceProvider;
use TwigBridge\ServiceProvider;
return [
// Package Service Providers...
// Application Service Providers...
AppServiceProvider::class,
FireflyIII\Providers\AuthServiceProvider::class,
// FireflyIII\Providers\BroadcastServiceProvider::class,
// EventServiceProvider::class,
RouteServiceProvider::class,
// own stuff:
PragmaRX\Google2FALaravel\ServiceProvider::class,
ServiceProvider::class,
// More service providers.
AccountServiceProvider::class,
AttachmentServiceProvider::class,
BillServiceProvider::class,
BudgetServiceProvider::class,
CategoryServiceProvider::class,
CurrencyServiceProvider::class,
FireflyServiceProvider::class,
JournalServiceProvider::class,
PiggyBankServiceProvider::class,
RuleServiceProvider::class,
RuleGroupServiceProvider::class,
SearchServiceProvider::class,
TagServiceProvider::class,
AdminServiceProvider::class,
RecurringServiceProvider::class,
];

View File

@@ -3,6 +3,70 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## v6.4.22
This release and several previous ones fix authentication problems mainly. Cleaning up the libraries that make up Firefly III's excellent security (with me standing on the shoulders of giants) means that many edge cases that worked in the past no longer worked. Notable issues are listed below.
As far as I know it all works as it should, but feel free to open new issues when necessary. My apologies for the mess.
### Fixed
- Removed Laravel Sanctum as it was unused.
- Migrated away from Laravel 10's kernel and bootstrap structure.
- Fixed remote user guard for console and unauthenticated routes (like `/up` and `health`)
- [Issue 11710](https://github.com/firefly-iii/firefly-iii/issues/11710) (Firefly throws error on startup and when trying to use importer) reported by @avee87
- [Issue 11712](https://github.com/firefly-iii/firefly-iii/issues/11712) (`RemoteUserGuard` regression in v6.4.19 blocks `/health` endpoint and internal Artisan commands) reported by @Dual-0
- [Issue 11720](https://github.com/firefly-iii/firefly-iii/issues/11720) (`RemoteUserGuard` regression from v6.4.19 (including v6.4.21) blocks `/health` endpoint) reported by @rjhenry
## v6.4.21
### Added
- The ability to undo the recording of a database migration, which may help with database issues. [See the docs](https://docs.firefly-iii.org/references/faq/firefly-iii/using/#i-get-errors-about-missing-tables-how-do-i-fix-this)
- Added debug logs to file permission checks.
### Fixed
- View range issue for subscription overview
- Amount log entries were recorded for the transaction group, not the journal
- Subscriptions were not being renamed in rules when their names were changed
- [Issue 11688](https://github.com/firefly-iii/firefly-iii/issues/11688) (Token endpoints returning 401 unauthorized) reported by @molnarti
- [Issue 11694](https://github.com/firefly-iii/firefly-iii/issues/11694) (Foreign currency amount is always positive in transfers) reported by @SledgehammerPL
- [Issue 11684](https://github.com/firefly-iii/firefly-iii/issues/11684) (Transaction summary duplicated after more than 10 rows) reported by @jkmf
- [Issue 11700](https://github.com/firefly-iii/firefly-iii/issues/11700) (Duplicate entry for key 'tag_transaction_journal_tag_id_transaction_journal_id_unique') reported by @beatbesmer
- [Issue 11702](https://github.com/firefly-iii/firefly-iii/issues/11702) (Can't enable displaying primary currency when using Postgres) reported by @absdjfh
- [Issue 11710](https://github.com/firefly-iii/firefly-iii/issues/11710) (Firefly throws error on startup and when trying to use importer) reported by @avee87
## v6.4.20
### Added
- The ability to undo the recording of a database migration, which may help with database issues. [See the docs](https://docs.firefly-iii.org/references/faq/firefly-iii/using/#i-get-errors-about-missing-tables-how-do-i-fix-this)
- Added debug logs to file permission checks.
### Fixed
- View range issue for subscription overview
- Amount log entries were recorded for the transaction group, not the journal
- Subscriptions were not being renamed in rules when their names were changed
- [Issue 11688](https://github.com/firefly-iii/firefly-iii/issues/11688) (Token endpoints returning 401 unauthorized) reported by @molnarti
- [Issue 11694](https://github.com/firefly-iii/firefly-iii/issues/11694) (Foreign currency amount is always positive in transfers) reported by @SledgehammerPL
- [Issue 11684](https://github.com/firefly-iii/firefly-iii/issues/11684) (Transaction summary duplicated after more than 10 rows) reported by @jkmf
- [Issue 11700](https://github.com/firefly-iii/firefly-iii/issues/11700) (Duplicate entry for key 'tag_transaction_journal_tag_id_transaction_journal_id_unique') reported by @beatbesmer
- [Issue 11702](https://github.com/firefly-iii/firefly-iii/issues/11702) (Can't enable displaying primary currency when using Postgres) reported by @absdjfh
## v6.4.19
### Added
- The ability to undo the recording of a database migration, which may help with database issues. [See the docs](https://docs.firefly-iii.org/references/faq/firefly-iii/using/#i-get-errors-about-missing-tables-how-do-i-fix-this)
- Added debug logs to file permission checks
### Fixed
- View range issue for subscription overview
- Amount log entries were recorded for the transaction group, not the journal
- Subscriptions were not being renamed in rules when their names were changed
- [Issue 11688](https://github.com/firefly-iii/firefly-iii/issues/11688) (Token endpoints returning 401 unauthorized) reported by @molnarti
- [Issue 11694](https://github.com/firefly-iii/firefly-iii/issues/11694) (Foreign currency amount is always positive in transfers) reported by @SledgehammerPL
- [Issue 11684](https://github.com/firefly-iii/firefly-iii/issues/11684) (Transaction summary duplicated after more than 10 rows) reported by @jkmf
- [Issue 11700](https://github.com/firefly-iii/firefly-iii/issues/11700) (Duplicate entry for key 'tag_transaction_journal_tag_id_transaction_journal_id_unique') reported by @beatbesmer
- [Issue 11702](https://github.com/firefly-iii/firefly-iii/issues/11702) (Can't enable displaying primary currency when using Postgres) reported by @absdjfh
## v6.4.18
### Fixed

View File

@@ -34,9 +34,6 @@
"transfers",
"management"
],
"platform": {
"php": "8.4"
},
"license": "AGPL-3.0-or-later",
"homepage": "https://github.com/firefly-iii/firefly-iii",
"type": "project",
@@ -93,7 +90,6 @@
"laravel-notification-channels/pushover": "^4.0",
"laravel/framework": "^12",
"laravel/passport": "^12.0",
"laravel/sanctum": "^4.1",
"laravel/slack-notification-channel": "^3.3",
"laravel/ui": "^4.2",
"league/commonmark": "^2",

175
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "c4e7dd2df7bae96ea6e4df82530411b9",
"content-hash": "6c4181d945517372c00358f3828806bc",
"packages": [
{
"name": "bacon/bacon-qr-code",
@@ -2233,69 +2233,6 @@
},
"time": "2026-02-06T12:17:10+00:00"
},
{
"name": "laravel/sanctum",
"version": "v4.3.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/sanctum.git",
"reference": "e3b85d6e36ad00e5db2d1dcc27c81ffdf15cbf76"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/sanctum/zipball/e3b85d6e36ad00e5db2d1dcc27c81ffdf15cbf76",
"reference": "e3b85d6e36ad00e5db2d1dcc27c81ffdf15cbf76",
"shasum": ""
},
"require": {
"ext-json": "*",
"illuminate/console": "^11.0|^12.0|^13.0",
"illuminate/contracts": "^11.0|^12.0|^13.0",
"illuminate/database": "^11.0|^12.0|^13.0",
"illuminate/support": "^11.0|^12.0|^13.0",
"php": "^8.2",
"symfony/console": "^7.0|^8.0"
},
"require-dev": {
"mockery/mockery": "^1.6",
"orchestra/testbench": "^9.15|^10.8|^11.0",
"phpstan/phpstan": "^1.10"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Laravel\\Sanctum\\SanctumServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Laravel\\Sanctum\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.",
"keywords": [
"auth",
"laravel",
"sanctum"
],
"support": {
"issues": "https://github.com/laravel/sanctum/issues",
"source": "https://github.com/laravel/sanctum"
},
"time": "2026-02-07T17:19:31+00:00"
},
{
"name": "laravel/serializable-closure",
"version": "v2.0.9",
@@ -3894,16 +3831,16 @@
},
{
"name": "nette/utils",
"version": "v4.1.2",
"version": "v4.1.3",
"source": {
"type": "git",
"url": "https://github.com/nette/utils.git",
"reference": "f76b5dc3d6c6d3043c8d937df2698515b99cbaf5"
"reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nette/utils/zipball/f76b5dc3d6c6d3043c8d937df2698515b99cbaf5",
"reference": "f76b5dc3d6c6d3043c8d937df2698515b99cbaf5",
"url": "https://api.github.com/repos/nette/utils/zipball/bb3ea637e3d131d72acc033cfc2746ee893349fe",
"reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe",
"shasum": ""
},
"require": {
@@ -3915,8 +3852,10 @@
},
"require-dev": {
"jetbrains/phpstorm-attributes": "^1.2",
"nette/phpstan-rules": "^1.0",
"nette/tester": "^2.5",
"phpstan/phpstan": "^2.0@stable",
"phpstan/extension-installer": "^1.4@stable",
"phpstan/phpstan": "^2.1@stable",
"tracy/tracy": "^2.9"
},
"suggest": {
@@ -3977,9 +3916,9 @@
],
"support": {
"issues": "https://github.com/nette/utils/issues",
"source": "https://github.com/nette/utils/tree/v4.1.2"
"source": "https://github.com/nette/utils/tree/v4.1.3"
},
"time": "2026-02-03T17:21:09+00:00"
"time": "2026-02-13T03:05:33+00:00"
},
{
"name": "nunomaduro/collision",
@@ -5120,16 +5059,16 @@
},
{
"name": "predis/predis",
"version": "v3.3.0",
"version": "v3.4.0",
"source": {
"type": "git",
"url": "https://github.com/predis/predis.git",
"reference": "153097374b39a2f737fe700ebcd725642526cdec"
"reference": "1183f5732e6b10efd33f64984a96726eaecb59aa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/predis/predis/zipball/153097374b39a2f737fe700ebcd725642526cdec",
"reference": "153097374b39a2f737fe700ebcd725642526cdec",
"url": "https://api.github.com/repos/predis/predis/zipball/1183f5732e6b10efd33f64984a96726eaecb59aa",
"reference": "1183f5732e6b10efd33f64984a96726eaecb59aa",
"shasum": ""
},
"require": {
@@ -5171,7 +5110,7 @@
],
"support": {
"issues": "https://github.com/predis/predis/issues",
"source": "https://github.com/predis/predis/tree/v3.3.0"
"source": "https://github.com/predis/predis/tree/v3.4.0"
},
"funding": [
{
@@ -5179,7 +5118,7 @@
"type": "github"
}
],
"time": "2025-11-24T17:48:50+00:00"
"time": "2026-02-11T17:30:28+00:00"
},
{
"name": "psr/cache",
@@ -9646,16 +9585,16 @@
},
{
"name": "thecodingmachine/safe",
"version": "v3.3.0",
"version": "v3.4.0",
"source": {
"type": "git",
"url": "https://github.com/thecodingmachine/safe.git",
"reference": "2cdd579eeaa2e78e51c7509b50cc9fb89a956236"
"reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thecodingmachine/safe/zipball/2cdd579eeaa2e78e51c7509b50cc9fb89a956236",
"reference": "2cdd579eeaa2e78e51c7509b50cc9fb89a956236",
"url": "https://api.github.com/repos/thecodingmachine/safe/zipball/705683a25bacf0d4860c7dea4d7947bfd09eea19",
"reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19",
"shasum": ""
},
"require": {
@@ -9765,7 +9704,7 @@
"description": "PHP core functions that throw exceptions instead of returning FALSE on error",
"support": {
"issues": "https://github.com/thecodingmachine/safe/issues",
"source": "https://github.com/thecodingmachine/safe/tree/v3.3.0"
"source": "https://github.com/thecodingmachine/safe/tree/v3.4.0"
},
"funding": [
{
@@ -9776,12 +9715,16 @@
"url": "https://github.com/shish",
"type": "github"
},
{
"url": "https://github.com/silasjoisten",
"type": "github"
},
{
"url": "https://github.com/staabm",
"type": "github"
}
],
"time": "2025-05-14T06:15:44+00:00"
"time": "2026-02-04T18:08:13+00:00"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
@@ -10530,16 +10473,16 @@
},
{
"name": "fruitcake/laravel-debugbar",
"version": "v4.0.7",
"version": "v4.0.8",
"source": {
"type": "git",
"url": "https://github.com/fruitcake/laravel-debugbar.git",
"reference": "a9cc62c81cd0bda4ca7410229487638d7df786be"
"reference": "ad7a5b11c11bf7773c9acd04d0fe0d79a229b1c7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/a9cc62c81cd0bda4ca7410229487638d7df786be",
"reference": "a9cc62c81cd0bda4ca7410229487638d7df786be",
"url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/ad7a5b11c11bf7773c9acd04d0fe0d79a229b1c7",
"reference": "ad7a5b11c11bf7773c9acd04d0fe0d79a229b1c7",
"shasum": ""
},
"require": {
@@ -10616,7 +10559,7 @@
],
"support": {
"issues": "https://github.com/fruitcake/laravel-debugbar/issues",
"source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.0.7"
"source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.0.8"
},
"funding": [
{
@@ -10628,7 +10571,7 @@
"type": "github"
}
],
"time": "2026-02-06T20:53:50+00:00"
"time": "2026-02-14T13:26:03+00:00"
},
{
"name": "hamcrest/hamcrest-php",
@@ -11198,16 +11141,16 @@
},
{
"name": "php-debugbar/php-debugbar",
"version": "v3.3.1",
"version": "v3.4.0",
"source": {
"type": "git",
"url": "https://github.com/php-debugbar/php-debugbar.git",
"reference": "afdaa2e56aca9d56b5bb2bad041bd2f6002017cf"
"reference": "e50d470344b62a033a76d3d10a803b04c8e3be69"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/afdaa2e56aca9d56b5bb2bad041bd2f6002017cf",
"reference": "afdaa2e56aca9d56b5bb2bad041bd2f6002017cf",
"url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/e50d470344b62a033a76d3d10a803b04c8e3be69",
"reference": "e50d470344b62a033a76d3d10a803b04c8e3be69",
"shasum": ""
},
"require": {
@@ -11284,7 +11227,7 @@
],
"support": {
"issues": "https://github.com/php-debugbar/php-debugbar/issues",
"source": "https://github.com/php-debugbar/php-debugbar/tree/v3.3.1"
"source": "https://github.com/php-debugbar/php-debugbar/tree/v3.4.0"
},
"funding": [
{
@@ -11296,7 +11239,7 @@
"type": "github"
}
],
"time": "2026-02-06T21:09:38+00:00"
"time": "2026-02-14T14:10:26+00:00"
},
{
"name": "php-debugbar/symfony-bridge",
@@ -11414,11 +11357,11 @@
},
{
"name": "phpstan/phpstan",
"version": "2.1.38",
"version": "2.1.39",
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/dfaf1f530e1663aa167bc3e52197adb221582629",
"reference": "dfaf1f530e1663aa167bc3e52197adb221582629",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224",
"reference": "c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224",
"shasum": ""
},
"require": {
@@ -11463,25 +11406,25 @@
"type": "github"
}
],
"time": "2026-01-30T17:12:46+00:00"
"time": "2026-02-11T14:48:56+00:00"
},
{
"name": "phpstan/phpstan-deprecation-rules",
"version": "2.0.3",
"version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-deprecation-rules.git",
"reference": "468e02c9176891cc901143da118f09dc9505fc2f"
"reference": "6b5571001a7f04fa0422254c30a0017ec2f2cacc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/468e02c9176891cc901143da118f09dc9505fc2f",
"reference": "468e02c9176891cc901143da118f09dc9505fc2f",
"url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/6b5571001a7f04fa0422254c30a0017ec2f2cacc",
"reference": "6b5571001a7f04fa0422254c30a0017ec2f2cacc",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0",
"phpstan/phpstan": "^2.1.15"
"phpstan/phpstan": "^2.1.39"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.2",
@@ -11506,29 +11449,32 @@
"MIT"
],
"description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.",
"keywords": [
"static analysis"
],
"support": {
"issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues",
"source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.3"
"source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.4"
},
"time": "2025-05-14T10:56:57+00:00"
"time": "2026-02-09T13:21:14+00:00"
},
{
"name": "phpstan/phpstan-strict-rules",
"version": "2.0.8",
"version": "2.0.10",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "1ed9e626a37f7067b594422411539aa807190573"
"reference": "1aba28b697c1e3b6bbec8a1725f8b11b6d3e5a5f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/1ed9e626a37f7067b594422411539aa807190573",
"reference": "1ed9e626a37f7067b594422411539aa807190573",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/1aba28b697c1e3b6bbec8a1725f8b11b6d3e5a5f",
"reference": "1aba28b697c1e3b6bbec8a1725f8b11b6d3e5a5f",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0",
"phpstan/phpstan": "^2.1.29"
"phpstan/phpstan": "^2.1.39"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.2",
@@ -11554,11 +11500,14 @@
"MIT"
],
"description": "Extra strict and opinionated rules for PHPStan",
"keywords": [
"static analysis"
],
"support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.8"
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.10"
},
"time": "2026-01-27T08:10:25+00:00"
"time": "2026-02-11T14:17:32+00:00"
},
{
"name": "phpunit/php-code-coverage",

View File

@@ -22,59 +22,18 @@
declare(strict_types=1);
use FireflyIII\Providers\AccountServiceProvider;
use FireflyIII\Providers\AdminServiceProvider;
use FireflyIII\Providers\AppServiceProvider;
use FireflyIII\Providers\AttachmentServiceProvider;
use FireflyIII\Providers\BillServiceProvider;
use FireflyIII\Providers\BudgetServiceProvider;
use FireflyIII\Providers\CategoryServiceProvider;
use FireflyIII\Providers\CurrencyServiceProvider;
use FireflyIII\Providers\EventServiceProvider;
use FireflyIII\Providers\FireflyServiceProvider;
use FireflyIII\Providers\JournalServiceProvider;
use FireflyIII\Providers\PiggyBankServiceProvider;
use FireflyIII\Providers\RecurringServiceProvider;
use FireflyIII\Providers\RouteServiceProvider;
use FireflyIII\Providers\RuleGroupServiceProvider;
use FireflyIII\Providers\RuleServiceProvider;
use FireflyIII\Providers\SearchServiceProvider;
use FireflyIII\Providers\SessionServiceProvider;
use FireflyIII\Providers\TagServiceProvider;
use FireflyIII\Support\Facades\AccountForm;
use FireflyIII\Support\Facades\CurrencyForm;
use FireflyIII\Support\Facades\ExpandedForm;
use FireflyIII\Support\Facades\PiggyBankForm;
use FireflyIII\Support\Facades\RuleForm;
use Illuminate\Auth\AuthServiceProvider;
use Illuminate\Auth\Passwords\PasswordResetServiceProvider;
use Illuminate\Broadcasting\BroadcastServiceProvider;
use Illuminate\Bus\BusServiceProvider;
use Illuminate\Cache\CacheServiceProvider;
use Illuminate\Cookie\CookieServiceProvider;
use Illuminate\Database\DatabaseServiceProvider;
use Illuminate\Encryption\EncryptionServiceProvider;
use Illuminate\Filesystem\FilesystemServiceProvider;
use Illuminate\Foundation\Providers\ConsoleSupportServiceProvider;
use Illuminate\Foundation\Providers\FoundationServiceProvider;
use Illuminate\Hashing\HashServiceProvider;
use Illuminate\Mail\MailServiceProvider;
use Illuminate\Notifications\NotificationServiceProvider;
use Illuminate\Pagination\PaginationServiceProvider;
use Illuminate\Pipeline\PipelineServiceProvider;
use Illuminate\Queue\QueueServiceProvider;
use Illuminate\Redis\RedisServiceProvider;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\URL;
use Illuminate\Translation\TranslationServiceProvider;
use Illuminate\Validation\ValidationServiceProvider;
use Illuminate\View\ViewServiceProvider;
use Spatie\Html\Facades\Html;
use TwigBridge\ServiceProvider;
return [
'name' => envNonEmpty('APP_NAME', 'Firefly III'),
@@ -86,61 +45,6 @@ return [
'fallback_locale' => 'en_US',
'key' => env('APP_KEY'),
'cipher' => 'AES-256-CBC',
'providers' => [
// Laravel Framework Service Providers...
AuthServiceProvider::class,
BroadcastServiceProvider::class,
BusServiceProvider::class,
CacheServiceProvider::class,
ConsoleSupportServiceProvider::class,
CookieServiceProvider::class,
DatabaseServiceProvider::class,
EncryptionServiceProvider::class,
FilesystemServiceProvider::class,
FoundationServiceProvider::class,
HashServiceProvider::class,
MailServiceProvider::class,
NotificationServiceProvider::class,
PaginationServiceProvider::class,
PipelineServiceProvider::class,
QueueServiceProvider::class,
RedisServiceProvider::class,
PasswordResetServiceProvider::class,
SessionServiceProvider::class,
TranslationServiceProvider::class,
ValidationServiceProvider::class,
ViewServiceProvider::class,
// Package Service Providers...
// Application Service Providers...
AppServiceProvider::class,
FireflyIII\Providers\AuthServiceProvider::class,
// FireflyIII\Providers\BroadcastServiceProvider::class,
// EventServiceProvider::class,
RouteServiceProvider::class,
// own stuff:
PragmaRX\Google2FALaravel\ServiceProvider::class,
ServiceProvider::class,
// More service providers.
AccountServiceProvider::class,
AttachmentServiceProvider::class,
BillServiceProvider::class,
BudgetServiceProvider::class,
CategoryServiceProvider::class,
CurrencyServiceProvider::class,
FireflyServiceProvider::class,
JournalServiceProvider::class,
PiggyBankServiceProvider::class,
RuleServiceProvider::class,
RuleGroupServiceProvider::class,
SearchServiceProvider::class,
TagServiceProvider::class,
AdminServiceProvider::class,
RecurringServiceProvider::class,
],
'aliases' => [
'Auth' => Auth::class,
'Route' => Route::class,

View File

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

92
package-lock.json generated
View File

@@ -3814,9 +3814,9 @@
}
},
"node_modules/ajv-formats/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4240,6 +4240,22 @@
"dev": true,
"license": "MIT"
},
"node_modules/body-parser/node_modules/qs": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/bonjour-service": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz",
@@ -4578,9 +4594,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001769",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz",
"integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==",
"version": "1.0.30001770",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz",
"integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==",
"dev": true,
"funding": [
{
@@ -6191,6 +6207,22 @@
"dev": true,
"license": "MIT"
},
"node_modules/express/node_modules/qs": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -7118,9 +7150,9 @@
}
},
"node_modules/i18next": {
"version": "25.8.4",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.4.tgz",
"integrity": "sha512-a9A0MnUjKvzjEN/26ZY1okpra9kA8MEwzYEz1BNm+IyxUKPRH6ihf0p7vj8YvULwZHKHl3zkJ6KOt4hewxBecQ==",
"version": "25.8.9",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.9.tgz",
"integrity": "sha512-PJ/dVpSXVUs1ZLOLG61V97JTHBqz0FCle41BpgF1z1VCl37kPJfX/ZzMDNDYEgSZxha5SY7ZfWITcNJCrgm7ug==",
"funding": [
{
"type": "individual",
@@ -9717,9 +9749,9 @@
"license": "MIT"
},
"node_modules/qs": {
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz",
"integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -11033,9 +11065,9 @@
}
},
"node_modules/terser-webpack-plugin/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11801,9 +11833,9 @@
"license": "BSD-2-Clause"
},
"node_modules/webpack": {
"version": "5.105.1",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.1.tgz",
"integrity": "sha512-Gdj3X74CLJJ8zy4URmK42W7wTZUJrqL+z8nyGEr4dTN0kb3nVs+ZvjbTOqRYPD7qX4tUmwyHL9Q9K6T1seW6Yw==",
"version": "5.105.2",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.2.tgz",
"integrity": "sha512-dRXm0a2qcHPUBEzVk8uph0xWSjV/xZxenQQbLwnwP7caQCYpqG1qddwlyEkIDkYn0K8tvmcrZ+bOrzoQ3HxCDw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11922,9 +11954,9 @@
}
},
"node_modules/webpack-dev-middleware/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -12039,9 +12071,9 @@
}
},
"node_modules/webpack-dev-server/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -12159,9 +12191,9 @@
}
},
"node_modules/webpack/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -12216,9 +12248,9 @@
}
},
"node_modules/webpack/node_modules/webpack-sources": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz",
"integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==",
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz",
"integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==",
"dev": true,
"license": "MIT",
"engines": {

View File

@@ -22,8 +22,8 @@
"not_enough_currencies": "Not enough currencies",
"not_enough_currencies_enabled": "If you have just one currency enabled, there is no need to add exchange rates.",
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> has been stored.",
"webhook_stored_link": "<a href=\"webhooks\/show\/{ID}\">Webhook #{ID} (\"{title}\")<\/a> has been stored.",
"webhook_updated_link": "<a href=\"webhooks\/show\/{ID}\">Webhook #{ID}<\/a> (\"{title}\") has been updated.",
"webhook_stored_link": "<a href=\"webhooks\/show\/{ID}\">Webhook #{ID} (\"{title}\")<\/a> er blevet gemt.",
"webhook_updated_link": "<a href=\"webhooks\/show\/{ID}\">Webhook #{ID}<\/a> (\"{title}\") er blevet opdateret.",
"transaction_updated_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> (\"{title}\") has been updated.",
"transaction_new_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID}<\/a> has been stored.",
"transaction_journal_information": "Transaction information",
@@ -31,7 +31,7 @@
"apply_rules_checkbox": "Apply rules",
"fire_webhooks_checkbox": "Fire webhooks",
"no_budget_pointer": "Det ser ud til, at du ikke har oprettet budgetter endnu. Du burde oprette nogle p\u00e5 <a href=\"\/budgets\">budgetsiden<\/a>. Budgetter kan hj\u00e6lpe dig med at holde styr p\u00e5 udgifter.",
"no_bill_pointer": "You seem to have no subscription yet. You should create some on the <a href=\"subscriptions\">subscription<\/a>-page. Subscriptions can help you keep track of expenses.",
"no_bill_pointer": "Det ser ikke ud til, at du har nogle budgetter endnu. Du burde oprette nogle p\u00e5 <a href=\"\/budgets\">budgetsiden<\/a>. Budgetter kan hj\u00e6lpe dig med at holde styr p\u00e5 udgifter.",
"source_account": "Kildekonto",
"hidden_fields_preferences": "You can enable more transaction options in your <a href=\"preferences\">preferences<\/a>.",
"destination_account": "Destinationskonto",
@@ -107,42 +107,42 @@
"multi_account_warning_withdrawal": "Husk, at kildekontoen for efterf\u00f8lgende opdelinger vil blive overstyret af hvad der er defineret i den f\u00f8rste opdeling af tilbagetr\u00e6kningen.",
"multi_account_warning_deposit": "Husk, at destinationskontoen for efterf\u00f8lgende opdelinger vil blive tilsidesat af hvad der er defineret i den f\u00f8rste opsplitning af depositummet.",
"multi_account_warning_transfer": "Husk p\u00e5, at kilden + destination konto for efterf\u00f8lgende opdelinger vil blive overstyret af hvad der er defineret i den f\u00f8rste opdeling af overf\u00f8rslen.",
"webhook_trigger_ANY": "After any event",
"webhook_trigger_ANY": "Efter enhver begivenhed",
"webhook_trigger_STORE_TRANSACTION": "Efter oprettelse af transaktion",
"webhook_trigger_UPDATE_TRANSACTION": "Efter opdatering af transaktion",
"webhook_trigger_DESTROY_TRANSACTION": "Efter sletning af transaktion",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_trigger_STORE_BUDGET": "Efter budgetoprettelse",
"webhook_trigger_UPDATE_BUDGET": "Efter budgetopdatering",
"webhook_trigger_DESTROY_BUDGET": "Efter sletning af budget",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "Efter budgetteret bel\u00f8b \u00e6ndring",
"webhook_response_TRANSACTIONS": "Transaktionsdetaljer",
"webhook_response_RELEVANT": "Relevant details",
"webhook_response_RELEVANT": "Relevante oplysninger",
"webhook_response_ACCOUNTS": "Kontodetaljer",
"webhook_response_NONE": "No details",
"webhook_response_NONE": "Ingen detaljer",
"webhook_delivery_JSON": "JSON",
"actions": "Handlinger",
"meta_data": "Meta data",
"webhook_messages": "Webhook-besked",
"inactive": "Inactive",
"no_webhook_messages": "Der er ingen webhook-beskeder",
"inspect": "Inspect",
"inspect": "Unders\u00f8g",
"create_new_webhook": "Opret ny webhook",
"webhooks": "Webhooks",
"webhook_trigger_form_help": "Indicate on what event the webhook will trigger",
"webhook_response_form_help": "Indicate what the webhook must submit to the URL.",
"webhook_trigger_form_help": "Indik\u00e9r p\u00e5 hvilken begivenhed webhook vil udl\u00f8se",
"webhook_response_form_help": "Angiv, hvad webhook skal indsende til URL.",
"webhook_delivery_form_help": "Hvilket format webhook skal levere data i.",
"webhook_active_form_help": "Webhooken skal v\u00e6re aktiv, ellers vil den ikke blive kaldt.",
"edit_webhook_js": "Rediger webhook \"{title}\"",
"webhook_was_triggered": "The webhook was triggered on the indicated transaction. Please wait for results to appear.",
"webhook_was_triggered": "Webhooken blev udl\u00f8st p\u00e5 den angivne transaktion. Vent venligst p\u00e5 at resultaterne vises.",
"view_message": "Vis besked",
"view_attempts": "Vis mislykkede fors\u00f8g",
"message_content_title": "Webhook-beskedindhold",
"message_content_help": "This is the content of the message that was sent (or tried) using this webhook.",
"message_content_help": "Dette er indholdet af den besked, der blev sendt (eller fors\u00f8gt) ved hj\u00e6lp af denne webhook.",
"attempt_content_title": "Webhook-fors\u00f8g",
"attempt_content_help": "These are all the unsuccessful attempts of this webhook message to submit to the configured URL. After some time, Firefly III will stop trying.",
"attempt_content_help": "Disse er alle de mislykkede fors\u00f8g p\u00e5 at sende denne webhook-meddelelse til den konfigurerede URL. Efter et stykke tid vil Firefly III stoppe med at fors\u00f8ge.",
"no_attempts": "Der er ingen mislykkede fors\u00f8g. Det er en god ting!",
"webhook_attempt_at": "Attempt at {moment}",
"logs": "Logs",
"webhook_attempt_at": "Fors\u00f8g p\u00e5 {moment}",
"logs": "Logfiler",
"response": "Svar",
"visit_webhook_url": "Bes\u00f8g webhook-URL",
"reset_webhook_secret": "Nulstil webhook-hemmelighed",

View File

@@ -107,18 +107,18 @@
"multi_account_warning_withdrawal": "Tenga en cuenta que la cuenta de origen de las divisiones posteriores ser\u00e1 anulada por lo que se defina en la primera divisi\u00f3n del gasto.",
"multi_account_warning_deposit": "Tenga en cuenta que la cuenta de destino de las divisiones posteriores ser\u00e1 anulada por lo que se defina en la primera divisi\u00f3n del retiro.",
"multi_account_warning_transfer": "Tenga en cuenta que la cuenta de origen + destino de divisiones posteriores ser\u00e1 anulada por lo que se defina en la primera divisi\u00f3n de la transferencia.",
"webhook_trigger_ANY": "After any event",
"webhook_trigger_ANY": "Despu\u00e9s de cualquier evento",
"webhook_trigger_STORE_TRANSACTION": "Despu\u00e9s de crear la transacci\u00f3n",
"webhook_trigger_UPDATE_TRANSACTION": "Despu\u00e9s de actualizar la transacci\u00f3n",
"webhook_trigger_DESTROY_TRANSACTION": "Despu\u00e9s de eliminar la transacci\u00f3n",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_STORE_BUDGET": "Despu\u00e9s de crear un presupuesto",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_response_TRANSACTIONS": "Detalles de la transacci\u00f3n",
"webhook_response_RELEVANT": "Relevant details",
"webhook_response_ACCOUNTS": "Detalles de la cuenta",
"webhook_response_NONE": "No details",
"webhook_response_NONE": "Sin detalles",
"webhook_delivery_JSON": "JSON",
"actions": "Acciones",
"meta_data": "Meta Datos",

View File

@@ -62,7 +62,7 @@
"destination_account_reconciliation": "\uc870\uc815 \uac70\ub798\uc758 \ubaa9\uc801\uc9c0 \uacc4\uc815\uc740 \ud3b8\uc9d1\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.",
"source_account_reconciliation": "\uc870\uc815 \uac70\ub798\uc758 \ucd9c\ubc1c\uc9c0 \uacc4\uc815\uc740 \ud3b8\uc9d1\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.",
"budget": "\uc608\uc0b0",
"bill": "Subscription",
"bill": "\uad6c\ub3c5",
"you_create_withdrawal": "\ucd9c\uae08\uc744 \uc0dd\uc131\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4.",
"you_create_transfer": "\uc804\uc1a1\uc744 \uc0dd\uc131\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4.",
"you_create_deposit": "\uc785\uae08\uc744 \uc0dd\uc131\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4.",

View File

@@ -184,11 +184,14 @@
{{ formatAmountBySymbol(transaction.amount, transaction.currency_symbol, transaction.currency_decimal_places, false) }}
{% endif %}
{% if transaction.source_account_id != account.id %}
{{ formatAmountBySymbol(transaction.amount*-1, transaction.currency_symbol, transaction.currency_decimal_places, false) }}
{{ formatAmountBySymbol(transaction.amount*-1, transaction.currency_symbol, transaction.currency_decimal_places, false) }}
{% endif %}
{# foreign amount of transfer #}
{% if null != transaction.foreign_amount %}
{% if null != transaction.foreign_amount and transaction.source_account_id == account.id %}
({{ formatAmountBySymbol(transaction.foreign_amount, transaction.foreign_currency_symbol, transaction.foreign_currency_decimal_places, false) }})
{% endif %}
{% if null != transaction.foreign_amount and transaction.source_account_id != account.id %}
({{ formatAmountBySymbol(transaction.foreign_amount*-1, transaction.foreign_currency_symbol, transaction.foreign_currency_decimal_places, false) }})
{% endif %}

View File

@@ -21,6 +21,9 @@
*/
declare(strict_types=1);
use FireflyIII\Http\Middleware\AcceptHeaders;
use FireflyIII\Http\Middleware\Binder;
use Illuminate\Support\Facades\Route;
use function Safe\define;
@@ -38,6 +41,19 @@ if (!defined('DATEFORMAT')) {
define('DATEFORMAT', '(19|20)[0-9]{2}-?[0-9]{2}-?[0-9]{2}');
}
// API route for cron
Route::group(
[
'namespace' => 'FireflyIII\Api\V1\Controllers\System',
'prefix' => 'v1',
'as' => 'api.v1.cron.',
'middleware' => [Binder::class, AcceptHeaders::class],
],
static function (): void {
Route::get('cron/{cliToken}', ['uses' => 'CronController@cron', 'as' => 'index'])->withoutMiddleware(['api']);
}
);
// Autocomplete controllers
Route::group(
[
@@ -716,7 +732,6 @@ Route::group(
// Users API routes:
Route::group(
[
'middleware' => ['auth:api,sanctum', 'bindings'],
'namespace' => 'FireflyIII\Api\V1\Controllers\System',
'prefix' => 'v1/users',
'as' => 'api.v1.users.',
@@ -733,7 +748,6 @@ Route::group(
// Batch API routes:
Route::group(
[
'middleware' => ['auth:api,sanctum', 'bindings'],
'namespace' => 'FireflyIII\Api\V1\Controllers\System',
'prefix' => 'v1/batch',
'as' => 'api.v1.batch.',

View File

@@ -73,24 +73,24 @@ Route::group(
}
);
Route::group(
['middleware' => 'binders-only', 'namespace' => 'FireflyIII\Http\Controllers\System', 'as' => 'cron.', 'prefix' => 'cron'],
static function (): void {
Route::get('run/{cliToken}', ['uses' => 'CronController@cron', 'as' => 'cron']);
}
);
// Route::group(
// ['middleware' => 'binders-only', 'namespace' => 'FireflyIII\Http\Controllers\System', 'as' => 'cron.', 'prefix' => 'cron'],
// static function (): void {
// Route::get('run/{cliToken}', ['uses' => 'CronController@cron', 'as' => 'cron']);
// }
// );
Route::group(
['middleware' => 'binders-only', 'namespace' => 'FireflyIII\Http\Controllers\System'],
['middleware' => ['binders-only'], 'namespace' => 'FireflyIII\Http\Controllers\System'],
static function (): void {
// Route::get('offline', static fn () => view('errors.offline'));
Route::get('health', ['uses' => 'HealthcheckController@check', 'as' => 'healthcheck']);
Route::get('health', ['uses' => 'HealthcheckController@check', 'as' => 'healthcheck'])->withoutMiddleware(['web']);
}
);
// These routes only work when the user is NOT logged in.
Route::group(
['middleware' => 'user-not-logged-in', 'namespace' => 'FireflyIII\Http\Controllers'],
['middleware' => ['user-not-logged-in'], 'namespace' => 'FireflyIII\Http\Controllers'],
static function (): void {
// Authentication Routes...
Route::get('login', ['uses' => 'Auth\LoginController@showLoginForm', 'as' => 'login']);
@@ -128,7 +128,7 @@ Route::group(
// For the two factor routes, the user must be logged in, but NOT 2FA. Account confirmation does not matter here.
Route::group(
['middleware' => 'user-logged-in-no-2fa', 'prefix' => 'two-factor', 'as' => 'two-factor.', 'namespace' => 'FireflyIII\Http\Controllers\Auth'],
['middleware' => 'user-simple-auth', 'prefix' => 'two-factor', 'as' => 'two-factor.', 'namespace' => 'FireflyIII\Http\Controllers\Auth'],
static function (): void {
Route::post('submit', ['uses' => 'TwoFactorController@submitMFA', 'as' => 'submit']);
Route::get('lost', ['uses' => 'TwoFactorController@lostTwoFactor', 'as' => 'lost']); // can be removed when v2 is live.