mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-01-20 04:44:10 +00:00
Compare commits
144 Commits
develop-20
...
develop-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55c762d55a | ||
|
|
734df18f4e | ||
|
|
30205d828a | ||
|
|
9c0eb8a028 | ||
|
|
103a6d1b15 | ||
|
|
99403ab9f6 | ||
|
|
b9e5851e38 | ||
|
|
eab18ed2f2 | ||
|
|
67d18e4b62 | ||
|
|
71774905b0 | ||
|
|
48f886d17c | ||
|
|
e28df272b5 | ||
|
|
95e98cfeca | ||
|
|
095d31fbe1 | ||
|
|
27044aeecf | ||
|
|
8bd7752e2b | ||
|
|
ee24d55554 | ||
|
|
740a14972a | ||
|
|
c6667c2293 | ||
|
|
8327a3d670 | ||
|
|
5534bedde0 | ||
|
|
83fe7f6f6d | ||
|
|
e41dbdc3a7 | ||
|
|
670f98eb66 | ||
|
|
7e02bee90b | ||
|
|
be6801654f | ||
|
|
93fe1f5558 | ||
|
|
d12fd40a57 | ||
|
|
4405b2ac8f | ||
|
|
bbbc8492db | ||
|
|
ba5b6ff694 | ||
|
|
edb8f811af | ||
|
|
492aec0652 | ||
|
|
c81bb44552 | ||
|
|
0431c95b49 | ||
|
|
6408db5414 | ||
|
|
5de88a70f8 | ||
|
|
968da83c67 | ||
|
|
e77b4ee4a6 | ||
|
|
a36c775bd2 | ||
|
|
7773862eb9 | ||
|
|
ae107b1776 | ||
|
|
d1b167447b | ||
|
|
a08ba43c23 | ||
|
|
64325fbce7 | ||
|
|
aeac59e851 | ||
|
|
151be7df8a | ||
|
|
040bd0f6e3 | ||
|
|
a859ca4e38 | ||
|
|
95c62d783a | ||
|
|
e7b67bc85e | ||
|
|
208f13ee75 | ||
|
|
437eecc1c9 | ||
|
|
1d34d81389 | ||
|
|
30844e99d4 | ||
|
|
d734449f63 | ||
|
|
69a9e3a198 | ||
|
|
3ad2f8c750 | ||
|
|
2166839768 | ||
|
|
8ecb9d7774 | ||
|
|
8814fb0806 | ||
|
|
7481c8d4c0 | ||
|
|
1e618fbf6d | ||
|
|
8b322dc903 | ||
|
|
812b0e6940 | ||
|
|
9070856b9c | ||
|
|
84082d38f2 | ||
|
|
822352827a | ||
|
|
7c1b550c2c | ||
|
|
94a25d3137 | ||
|
|
4c95ab49f4 | ||
|
|
be87a24b8f | ||
|
|
f6032d5502 | ||
|
|
7dcfb68fca | ||
|
|
419c796eac | ||
|
|
1d5733adba | ||
|
|
c589360ad9 | ||
|
|
d4687fb34f | ||
|
|
9c4159ca3d | ||
|
|
93507c8b96 | ||
|
|
23d49a4194 | ||
|
|
858c44fbce | ||
|
|
95a6543a94 | ||
|
|
855d40cf2f | ||
|
|
7d0fec6326 | ||
|
|
ebd7dca6a9 | ||
|
|
1d41fc9845 | ||
|
|
fe9ae9c810 | ||
|
|
84600c5208 | ||
|
|
f7e89cab0a | ||
|
|
8195630a6e | ||
|
|
d5bf80a0cb | ||
|
|
ef1c64096d | ||
|
|
ef35eaffb4 | ||
|
|
869ee7c735 | ||
|
|
6c534f01eb | ||
|
|
3a9d89b53d | ||
|
|
badff64cfd | ||
|
|
abacfa212e | ||
|
|
add2e859c4 | ||
|
|
92f6421fc4 | ||
|
|
f350c19ec1 | ||
|
|
8170804d74 | ||
|
|
4400f6217d | ||
|
|
e223cea74e | ||
|
|
2f62e11338 | ||
|
|
d96c7931d6 | ||
|
|
2ab105a902 | ||
|
|
7ced1f8cf3 | ||
|
|
03364d9530 | ||
|
|
1f75612741 | ||
|
|
a141cf6e67 | ||
|
|
c97fb07e8d | ||
|
|
9833dd49a9 | ||
|
|
b76f4fe7b9 | ||
|
|
6c114e2ffc | ||
|
|
bd396673ed | ||
|
|
ad72bc1722 | ||
|
|
466b42200d | ||
|
|
067112904e | ||
|
|
fc371e27b7 | ||
|
|
52b14b46a2 | ||
|
|
5260b770bb | ||
|
|
226c4c8f8e | ||
|
|
9ebafe64f1 | ||
|
|
ffa618101d | ||
|
|
280e531a76 | ||
|
|
f542a3fd88 | ||
|
|
581d67a92c | ||
|
|
a00e8b976c | ||
|
|
e5b3c3e6bd | ||
|
|
4d7f63273e | ||
|
|
04553f6fc5 | ||
|
|
54676715c0 | ||
|
|
23246e8f5a | ||
|
|
4ccd65b4d7 | ||
|
|
2209087b94 | ||
|
|
f655dcbcf8 | ||
|
|
13bb064734 | ||
|
|
5a3edbe68f | ||
|
|
76657b5519 | ||
|
|
775deb2142 | ||
|
|
8220d491f9 | ||
|
|
637d8e050a |
@@ -40,7 +40,8 @@ return $config->setRules(
|
||||
|
||||
[
|
||||
// rule sets
|
||||
'@PHP83Migration' => true,
|
||||
'@PHP8x3Migration' => true,
|
||||
'@PHP8x4Migration' => true,
|
||||
'@PhpCsFixer' => true,
|
||||
'@PhpCsFixer:risky' => true,
|
||||
'@PSR12' => true,
|
||||
|
||||
60
.ci/php-cs-fixer/composer.lock
generated
60
.ci/php-cs-fixer/composer.lock
generated
@@ -402,16 +402,16 @@
|
||||
},
|
||||
{
|
||||
"name": "friendsofphp/php-cs-fixer",
|
||||
"version": "v3.92.3",
|
||||
"version": "v3.92.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
|
||||
"reference": "2ba8f5a60f6f42fb65758cfb3768434fa2d1c7e8"
|
||||
"reference": "260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/2ba8f5a60f6f42fb65758cfb3768434fa2d1c7e8",
|
||||
"reference": "2ba8f5a60f6f42fb65758cfb3768434fa2d1c7e8",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58",
|
||||
"reference": "260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -443,17 +443,17 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"facile-it/paraunit": "^1.3.1 || ^2.7",
|
||||
"infection/infection": "^0.31.0",
|
||||
"justinrainbow/json-schema": "^6.5",
|
||||
"keradus/cli-executor": "^2.2",
|
||||
"infection/infection": "^0.31",
|
||||
"justinrainbow/json-schema": "^6.6",
|
||||
"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.25 || ^10.5.53 || ^11.5.34",
|
||||
"phpunit/phpunit": "^9.6.31 || ^10.5.60 || ^11.5.46",
|
||||
"symfony/polyfill-php85": "^1.33",
|
||||
"symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2 || ^8.0",
|
||||
"symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2 || ^8.0"
|
||||
"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"
|
||||
},
|
||||
"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.92.3"
|
||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.92.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -502,7 +502,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-12-18T10:45:02+00:00"
|
||||
"time": "2026-01-08T21:57:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
@@ -1252,16 +1252,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v8.0.1",
|
||||
"version": "v8.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "fcb73f69d655b48fcb894a262f074218df08bd58"
|
||||
"reference": "6145b304a5c1ea0bdbd0b04d297a5864f9a7d587"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/fcb73f69d655b48fcb894a262f074218df08bd58",
|
||||
"reference": "fcb73f69d655b48fcb894a262f074218df08bd58",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/6145b304a5c1ea0bdbd0b04d297a5864f9a7d587",
|
||||
"reference": "6145b304a5c1ea0bdbd0b04d297a5864f9a7d587",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1318,7 +1318,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v8.0.1"
|
||||
"source": "https://github.com/symfony/console/tree/v8.0.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1338,7 +1338,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-12-05T15:25:33+00:00"
|
||||
"time": "2025-12-23T14:52:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
@@ -1640,16 +1640,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v8.0.0",
|
||||
"version": "v8.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "7598dd5770580fa3517ec83e8da0c9b9e01f4291"
|
||||
"reference": "dd3a2953570a283a2ba4e17063bb98c734cf5b12"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/7598dd5770580fa3517ec83e8da0c9b9e01f4291",
|
||||
"reference": "7598dd5770580fa3517ec83e8da0c9b9e01f4291",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/dd3a2953570a283a2ba4e17063bb98c734cf5b12",
|
||||
"reference": "dd3a2953570a283a2ba4e17063bb98c734cf5b12",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1684,7 +1684,7 @@
|
||||
"description": "Finds files and directories via an intuitive fluent interface",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/finder/tree/v8.0.0"
|
||||
"source": "https://github.com/symfony/finder/tree/v8.0.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1704,7 +1704,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-11-05T14:36:47+00:00"
|
||||
"time": "2025-12-23T14:52:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
@@ -2358,16 +2358,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v8.0.0",
|
||||
"version": "v8.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "a0a750500c4ce900d69ba4e9faf16f82c10ee149"
|
||||
"reference": "0cbbd88ec836f8757641c651bb995335846abb78"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/a0a750500c4ce900d69ba4e9faf16f82c10ee149",
|
||||
"reference": "a0a750500c4ce900d69ba4e9faf16f82c10ee149",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/0cbbd88ec836f8757641c651bb995335846abb78",
|
||||
"reference": "0cbbd88ec836f8757641c651bb995335846abb78",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2399,7 +2399,7 @@
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v8.0.0"
|
||||
"source": "https://github.com/symfony/process/tree/v8.0.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2419,7 +2419,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-10-16T16:25:44+00:00"
|
||||
"time": "2025-12-19T10:01:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
|
||||
10
.env.example
10
.env.example
@@ -14,7 +14,15 @@ SITE_OWNER=mail@example.com
|
||||
# Change it to a string of exactly 32 chars or use something like `php artisan key:generate` to generate it.
|
||||
# If you use Docker or similar, you can set this variable from a file by using APP_KEY_FILE
|
||||
#
|
||||
# Avoid the "#" character in your APP_KEY, it may break things.
|
||||
# Try to avoid special characters like #, < and > in your app key. This string does not need full entropy
|
||||
# When in doubt, follow the link below and pick one.
|
||||
#
|
||||
# https://www.random.org/strings/?num=5&len=32&digits=on&upperalpha=on&loweralpha=on&unique=on&format=html&rnd=new
|
||||
#
|
||||
# If you are a fancy linux nerd like me, use this command:
|
||||
#
|
||||
# head /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 32 && echo
|
||||
#
|
||||
#
|
||||
APP_KEY=SomeRandomStringOf32CharsExactly
|
||||
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -42,7 +42,7 @@ jobs:
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ github.event.inputs.phpversion }}
|
||||
php-version: ${{ github.event.inputs.phpversion || '8.4' }}
|
||||
extensions: mbstring, intl, zip, bcmath
|
||||
- name: Switch and pull
|
||||
run: |
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
Over time, many people have contributed to Firefly III. Their efforts are not always visible, but always remembered and appreciated.
|
||||
Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution.
|
||||
|
||||
## 2026
|
||||
- embedded
|
||||
|
||||
## 2025
|
||||
- Diego Algorta
|
||||
- Jihad
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\AccountTypeEnum;
|
||||
@@ -37,6 +36,7 @@ use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
@@ -51,7 +51,7 @@ class AccountController extends Controller
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
/** @var array<int, string> */
|
||||
private array $balanceTypes;
|
||||
private array $balanceTypes;
|
||||
private AccountRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
@@ -60,16 +60,14 @@ class AccountController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
$this->balanceTypes = [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value];
|
||||
}
|
||||
|
||||
@@ -83,24 +81,18 @@ class AccountController extends Controller
|
||||
public function accounts(AutocompleteApiRequest $request): JsonResponse
|
||||
{
|
||||
Log::debug('Before All.');
|
||||
[
|
||||
'types' => $types,
|
||||
'query' => $query,
|
||||
'date' => $date,
|
||||
'limit' => $limit,
|
||||
]
|
||||
= $request->attributes->all();
|
||||
['types' => $types, 'query' => $query, 'date' => $date, 'limit' => $limit] = $request->attributes->all();
|
||||
|
||||
$date ??= today(config('app.timezone'));
|
||||
|
||||
// set date to end-of-day for account balance. so it is at $date 23:59:59
|
||||
$date->endOfDay();
|
||||
|
||||
$return = [];
|
||||
$timer = Timer::getInstance();
|
||||
$return = [];
|
||||
$timer = Timer::getInstance();
|
||||
$timer->start(sprintf('AC accounts "%s"', $query));
|
||||
$result = $this->repository->searchAccount((string)$query, $types, $limit);
|
||||
$allBalances = Steam::accountsBalancesOptimized($result, $date, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$result = $this->repository->searchAccount((string) $query, $types, $limit);
|
||||
$allBalances = Steam::accountsBalancesOptimized($result, $date, $this->primaryCurrency, $this->convertToPrimary);
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($result as $account) {
|
||||
@@ -118,17 +110,17 @@ class AccountController extends Controller
|
||||
}
|
||||
|
||||
$return[] = [
|
||||
'id' => (string)$account->id,
|
||||
'id' => (string) $account->id,
|
||||
'name' => $account->name,
|
||||
'name_with_balance' => $nameWithBalance,
|
||||
'active' => $account->active,
|
||||
'type' => $account->accountType->type,
|
||||
'currency_id' => (string)$useCurrency->id,
|
||||
'currency_id' => (string) $useCurrency->id,
|
||||
'currency_name' => $useCurrency->name,
|
||||
'currency_code' => $useCurrency->code,
|
||||
'currency_symbol' => $useCurrency->symbol,
|
||||
'currency_decimal_places' => $useCurrency->decimal_places,
|
||||
'account_currency_id' => (string)$currency->id,
|
||||
'account_currency_id' => (string) $currency->id,
|
||||
'account_currency_name' => $currency->name,
|
||||
'account_currency_code' => $currency->code,
|
||||
'account_currency_symbol' => $currency->symbol,
|
||||
@@ -137,16 +129,13 @@ class AccountController extends Controller
|
||||
}
|
||||
|
||||
// custom order.
|
||||
usort(
|
||||
$return,
|
||||
static function (array $left, array $right): int {
|
||||
$order = [AccountTypeEnum::ASSET->value, AccountTypeEnum::REVENUE->value, AccountTypeEnum::EXPENSE->value];
|
||||
$posA = (int)array_search($left['type'], $order, true);
|
||||
$posB = (int)array_search($right['type'], $order, true);
|
||||
usort($return, static function (array $left, array $right): int {
|
||||
$order = [AccountTypeEnum::ASSET->value, AccountTypeEnum::REVENUE->value, AccountTypeEnum::EXPENSE->value];
|
||||
$posA = (int) array_search($left['type'], $order, true);
|
||||
$posB = (int) array_search($right['type'], $order, true);
|
||||
|
||||
return $posA - $posB;
|
||||
}
|
||||
);
|
||||
return $posA - $posB;
|
||||
});
|
||||
$timer->stop(sprintf('AC accounts "%s"', $query));
|
||||
|
||||
return response()->api($return);
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class BillController
|
||||
@@ -46,16 +46,14 @@ class BillController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(BillRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(BillRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,13 +63,7 @@ class BillController extends Controller
|
||||
public function bills(AutocompleteApiRequest $request): JsonResponse
|
||||
{
|
||||
$result = $this->repository->searchBill($request->attributes->get('query'), $request->attributes->get('limit'));
|
||||
$filtered = $result->map(
|
||||
static fn (Bill $item): array => [
|
||||
'id' => (string) $item->id,
|
||||
'name' => $item->name,
|
||||
'active' => $item->active,
|
||||
]
|
||||
);
|
||||
$filtered = $result->map(static fn (Bill $item): array => ['id' => (string) $item->id, 'name' => $item->name, 'active' => $item->active]);
|
||||
|
||||
return response()->api($filtered->toArray());
|
||||
}
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class BudgetController
|
||||
@@ -46,16 +46,14 @@ class BudgetController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(BudgetRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(BudgetRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,13 +63,7 @@ class BudgetController extends Controller
|
||||
public function budgets(AutocompleteApiRequest $request): JsonResponse
|
||||
{
|
||||
$result = $this->repository->searchBudget($request->attributes->get('query'), $request->attributes->get('limit'));
|
||||
$filtered = $result->map(
|
||||
static fn (Budget $item): array => [
|
||||
'id' => (string) $item->id,
|
||||
'name' => $item->name,
|
||||
'active' => $item->active,
|
||||
]
|
||||
);
|
||||
$filtered = $result->map(static fn (Budget $item): array => ['id' => (string) $item->id, 'name' => $item->name, 'active' => $item->active]);
|
||||
|
||||
return response()->api($filtered->toArray());
|
||||
}
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class CategoryController
|
||||
@@ -46,16 +46,14 @@ class CategoryController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(CategoryRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(CategoryRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,12 +63,7 @@ class CategoryController extends Controller
|
||||
public function categories(AutocompleteApiRequest $request): JsonResponse
|
||||
{
|
||||
$result = $this->repository->searchCategory($request->attributes->get('query'), $request->attributes->get('limit'));
|
||||
$filtered = $result->map(
|
||||
static fn (Category $item): array => [
|
||||
'id' => (string) $item->id,
|
||||
'name' => $item->name,
|
||||
]
|
||||
);
|
||||
$filtered = $result->map(static fn (Category $item): array => ['id' => (string) $item->id, 'name' => $item->name]);
|
||||
|
||||
return response()->api($filtered->toArray());
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Deprecated;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
@@ -33,6 +32,7 @@ use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class CurrencyController
|
||||
@@ -48,16 +48,14 @@ class CurrencyController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(CurrencyRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(CurrencyRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\ObjectGroup;
|
||||
use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class ObjectGroupController
|
||||
@@ -46,16 +46,14 @@ class ObjectGroupController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(ObjectGroupRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(ObjectGroupRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,11 +67,7 @@ class ObjectGroupController extends Controller
|
||||
|
||||
/** @var ObjectGroup $objectGroup */
|
||||
foreach ($result as $objectGroup) {
|
||||
$return[] = [
|
||||
'id' => (string) $objectGroup->id,
|
||||
'name' => $objectGroup->title,
|
||||
'title' => $objectGroup->title,
|
||||
];
|
||||
$return[] = ['id' => (string) $objectGroup->id, 'name' => $objectGroup->title, 'title' => $objectGroup->title];
|
||||
}
|
||||
|
||||
return response()->api($return);
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
@@ -34,13 +33,14 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class PiggyBankController
|
||||
*/
|
||||
class PiggyBankController extends Controller
|
||||
{
|
||||
private AccountRepositoryInterface $accountRepository;
|
||||
private AccountRepositoryInterface $accountRepository;
|
||||
private PiggyBankRepositoryInterface $piggyRepository;
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_PIGGY_BANKS];
|
||||
|
||||
@@ -50,19 +50,17 @@ class PiggyBankController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->piggyRepository = app(PiggyBankRepositoryInterface::class);
|
||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||
$this->piggyRepository->setUser($this->user);
|
||||
$this->piggyRepository->setUserGroup($this->userGroup);
|
||||
$this->accountRepository->setUser($this->user);
|
||||
$this->accountRepository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->piggyRepository = app(PiggyBankRepositoryInterface::class);
|
||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||
$this->piggyRepository->setUser($this->user);
|
||||
$this->piggyRepository->setUserGroup($this->userGroup);
|
||||
$this->accountRepository->setUser($this->user);
|
||||
$this->accountRepository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function piggyBanks(AutocompleteApiRequest $request): JsonResponse
|
||||
@@ -84,6 +82,7 @@ class PiggyBankController extends Controller
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
'object_group_id' => null === $objectGroup ? null : (string) $objectGroup->id,
|
||||
'object_group_title' => $objectGroup?->title,
|
||||
'object_group_order' => $objectGroup?->order,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -108,7 +107,7 @@ class PiggyBankController extends Controller
|
||||
'%s (%s / %s)',
|
||||
$piggy->name,
|
||||
Amount::formatAnything($currency, $currentAmount, false),
|
||||
Amount::formatAnything($currency, $piggy->target_amount, false),
|
||||
Amount::formatAnything($currency, $piggy->target_amount, false)
|
||||
),
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_name' => $currency->name,
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\Recurrence;
|
||||
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class RecurrenceController
|
||||
@@ -46,16 +46,14 @@ class RecurrenceController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(RecurringRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(RecurringRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function recurring(AutocompleteApiRequest $request): JsonResponse
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class RuleController
|
||||
@@ -38,7 +38,7 @@ use Illuminate\Http\JsonResponse;
|
||||
class RuleController extends Controller
|
||||
{
|
||||
private RuleRepositoryInterface $repository;
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_RULES];
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_RULES];
|
||||
|
||||
/**
|
||||
* RuleController constructor.
|
||||
@@ -46,16 +46,14 @@ class RuleController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(RuleRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(RuleRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function rules(AutocompleteApiRequest $request): JsonResponse
|
||||
@@ -66,7 +64,7 @@ class RuleController extends Controller
|
||||
/** @var Rule $rule */
|
||||
foreach ($rules as $rule) {
|
||||
$response[] = [
|
||||
'id' => (string)$rule->id,
|
||||
'id' => (string) $rule->id,
|
||||
'name' => $rule->title,
|
||||
'description' => $rule->description,
|
||||
'active' => $rule->active,
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class RuleGroupController
|
||||
@@ -46,16 +46,14 @@ class RuleGroupController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(RuleGroupRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(RuleGroupRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function ruleGroups(AutocompleteApiRequest $request): JsonResponse
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class TagController
|
||||
@@ -46,16 +46,14 @@ class TagController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(TagRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(TagRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function tags(AutocompleteApiRequest $request): JsonResponse
|
||||
@@ -65,11 +63,7 @@ class TagController extends Controller
|
||||
|
||||
/** @var Tag $tag */
|
||||
foreach ($result as $tag) {
|
||||
$array[] = [
|
||||
'id' => (string) $tag->id,
|
||||
'name' => $tag->tag,
|
||||
'tag' => $tag->tag,
|
||||
];
|
||||
$array[] = ['id' => (string) $tag->id, 'name' => $tag->tag, 'tag' => $tag->tag];
|
||||
}
|
||||
|
||||
return response()->api($array);
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteTransactionApiRequest;
|
||||
@@ -34,6 +33,7 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@@ -43,7 +43,7 @@ class TransactionController extends Controller
|
||||
{
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
private TransactionGroupRepositoryInterface $groupRepository;
|
||||
private JournalRepositoryInterface $repository;
|
||||
private JournalRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
* TransactionController constructor.
|
||||
@@ -51,19 +51,17 @@ class TransactionController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(JournalRepositoryInterface::class);
|
||||
$this->groupRepository = app(TransactionGroupRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->groupRepository->setUser($this->user);
|
||||
$this->groupRepository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(JournalRepositoryInterface::class);
|
||||
$this->groupRepository = app(TransactionGroupRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->groupRepository->setUser($this->user);
|
||||
$this->groupRepository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function transactions(AutocompleteTransactionApiRequest $request): JsonResponse
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\TransactionType\TransactionTypeRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class TransactionTypeController
|
||||
@@ -46,14 +46,12 @@ class TransactionTypeController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(TransactionTypeRepositoryInterface::class);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(TransactionTypeRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function transactionTypes(AutocompleteApiRequest $request): JsonResponse
|
||||
@@ -64,11 +62,7 @@ class TransactionTypeController extends Controller
|
||||
/** @var TransactionType $type */
|
||||
foreach ($types as $type) {
|
||||
// different key for consistency.
|
||||
$array[] = [
|
||||
'id' => (string) $type->id,
|
||||
'name' => $type->type,
|
||||
'type' => $type->type,
|
||||
];
|
||||
$array[] = ['id' => (string) $type->id, 'name' => $type->type, 'type' => $type->type];
|
||||
}
|
||||
|
||||
return response()->api($array);
|
||||
|
||||
@@ -49,9 +49,9 @@ class AccountController extends Controller
|
||||
use CleansChartData;
|
||||
use CollectsAccountsFromFilter;
|
||||
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
private array $chartData = [];
|
||||
private array $chartData = [];
|
||||
private AccountRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
@@ -60,16 +60,14 @@ class AccountController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->repository->setUser($this->user);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,14 +113,14 @@ class AccountController extends Controller
|
||||
'label' => $account->name,
|
||||
|
||||
// the currency that belongs to the account.
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_name' => $currency->name,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
|
||||
// the primary currency
|
||||
'primary_currency_id' => (string)$this->primaryCurrency->id,
|
||||
'primary_currency_id' => (string) $this->primaryCurrency->id,
|
||||
|
||||
// the default currency of the user (could be the same!)
|
||||
'date' => $params['start']->toAtomString(),
|
||||
@@ -136,7 +134,7 @@ class AccountController extends Controller
|
||||
];
|
||||
if ($this->convertToPrimary) {
|
||||
$currentSet['pc_entries'] = [];
|
||||
$currentSet['primary_currency_id'] = (string)$this->primaryCurrency->id;
|
||||
$currentSet['primary_currency_id'] = (string) $this->primaryCurrency->id;
|
||||
$currentSet['primary_currency_code'] = $this->primaryCurrency->code;
|
||||
$currentSet['primary_currency_symbol'] = $this->primaryCurrency->symbol;
|
||||
$currentSet['primary_currency_decimal_places'] = $this->primaryCurrency->decimal_places;
|
||||
@@ -151,7 +149,6 @@ class AccountController extends Controller
|
||||
$previous = $balance;
|
||||
$currentSet['entries'][$label] = $balance;
|
||||
|
||||
|
||||
// do the same for the primary currency balance, if relevant:
|
||||
$pcBalance = null;
|
||||
if ($this->convertToPrimary) {
|
||||
@@ -160,6 +157,7 @@ class AccountController extends Controller
|
||||
$currentSet['pc_entries'][$label] = $pcBalance;
|
||||
}
|
||||
$currentStart = Navigation::addPeriod($currentStart, $period);
|
||||
|
||||
// $currentStart->addDay();
|
||||
}
|
||||
$this->chartData[] = $currentSet;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
|
||||
/*
|
||||
* BalanceController.php
|
||||
* Copyright (c) 2025 james@firefly-iii.org
|
||||
@@ -25,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Chart;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Chart\ChartRequest;
|
||||
use FireflyIII\Enums\TransactionTypeEnum;
|
||||
@@ -37,6 +35,7 @@ use FireflyIII\Support\Http\Api\AccountBalanceGrouped;
|
||||
use FireflyIII\Support\Http\Api\CleansChartData;
|
||||
use FireflyIII\Support\Http\Api\CollectsAccountsFromFilter;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class BalanceController
|
||||
@@ -45,10 +44,11 @@ class BalanceController extends Controller
|
||||
{
|
||||
use CleansChartData;
|
||||
use CollectsAccountsFromFilter;
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
private array $chartData = [];
|
||||
private GroupCollectorInterface $collector;
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
private array $chartData = [];
|
||||
private GroupCollectorInterface $collector;
|
||||
private AccountRepositoryInterface $repository;
|
||||
|
||||
// private TransactionCurrency $default;
|
||||
@@ -56,19 +56,17 @@ class BalanceController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->collector = app(GroupCollectorInterface::class);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->collector->setUserGroup($this->userGroup);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->collector->setUser($this->user);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->collector = app(GroupCollectorInterface::class);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->collector->setUserGroup($this->userGroup);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->collector->setUser($this->user);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,10 +87,16 @@ class BalanceController extends Controller
|
||||
|
||||
// get journals for entire period:
|
||||
|
||||
$this->collector->setRange($queryParameters['start'], $queryParameters['end'])
|
||||
$this->collector
|
||||
->setRange($queryParameters['start'], $queryParameters['end'])
|
||||
->withAccountInformation()
|
||||
->setXorAccounts($accounts)
|
||||
->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::RECONCILIATION->value, TransactionTypeEnum::TRANSFER->value])
|
||||
->setTypes([
|
||||
TransactionTypeEnum::WITHDRAWAL->value,
|
||||
TransactionTypeEnum::DEPOSIT->value,
|
||||
TransactionTypeEnum::RECONCILIATION->value,
|
||||
TransactionTypeEnum::TRANSFER->value,
|
||||
])
|
||||
;
|
||||
$journals = $this->collector->getExtractedJournals();
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Chart;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\DateRangeRequest;
|
||||
@@ -36,13 +35,14 @@ use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Http\Api\CleansChartData;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
|
||||
/**
|
||||
* Class BudgetController
|
||||
@@ -52,32 +52,30 @@ class BudgetController extends Controller
|
||||
use CleansChartData;
|
||||
use ValidatesUserGroupTrait;
|
||||
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
protected OperationsRepositoryInterface $opsRepository;
|
||||
private BudgetLimitRepositoryInterface $blRepository;
|
||||
private array $currencies = [];
|
||||
private BudgetRepositoryInterface $repository;
|
||||
private BudgetLimitRepositoryInterface $blRepository;
|
||||
private array $currencies = [];
|
||||
private BudgetRepositoryInterface $repository;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(BudgetRepositoryInterface::class);
|
||||
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
|
||||
$this->opsRepository = app(OperationsRepositoryInterface::class);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->opsRepository->setUserGroup($this->userGroup);
|
||||
$this->blRepository->setUserGroup($this->userGroup);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->opsRepository->setUser($this->user);
|
||||
$this->blRepository->setUser($this->user);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(BudgetRepositoryInterface::class);
|
||||
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
|
||||
$this->opsRepository = app(OperationsRepositoryInterface::class);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->opsRepository->setUserGroup($this->userGroup);
|
||||
$this->blRepository->setUserGroup($this->userGroup);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->opsRepository->setUser($this->user);
|
||||
$this->blRepository->setUser($this->user);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,18 +156,17 @@ class BudgetController extends Controller
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
|
||||
// is always an array
|
||||
$return = [];
|
||||
foreach ($rows as $row) {
|
||||
$current = [
|
||||
'label' => $budget->name,
|
||||
'currency_id' => (string)$row['currency_id'],
|
||||
'currency_id' => (string) $row['currency_id'],
|
||||
'currency_name' => $row['currency_name'],
|
||||
'currency_code' => $row['currency_code'],
|
||||
'currency_decimal_places' => $row['currency_decimal_places'],
|
||||
|
||||
'primary_currency_id' => (string)$this->primaryCurrency->id,
|
||||
'primary_currency_id' => (string) $this->primaryCurrency->id,
|
||||
'primary_currency_name' => $this->primaryCurrency->name,
|
||||
'primary_currency_code' => $this->primaryCurrency->code,
|
||||
'primary_currency_symbol' => $this->primaryCurrency->symbol,
|
||||
@@ -181,12 +178,7 @@ class BudgetController extends Controller
|
||||
'end_date' => $row['end'],
|
||||
'yAxisID' => 0,
|
||||
'type' => 'bar',
|
||||
'entries' => [
|
||||
'budgeted' => $row['budgeted'],
|
||||
'spent' => $row['spent'],
|
||||
'left' => $row['left'],
|
||||
'overspent' => $row['overspent'],
|
||||
],
|
||||
'entries' => ['budgeted' => $row['budgeted'], 'spent' => $row['spent'], 'left' => $row['left'], 'overspent' => $row['overspent']],
|
||||
'pc_entries' => [
|
||||
'budgeted' => $row['pc_budgeted'],
|
||||
'spent' => $row['pc_spent'],
|
||||
@@ -233,11 +225,11 @@ class BudgetController extends Controller
|
||||
foreach ($spent as $currencyId => $block) {
|
||||
$this->currencies[$currencyId] ??= Amount::getTransactionCurrencyById($currencyId);
|
||||
$return[$currencyId] ??= [
|
||||
'currency_id' => (string)$currencyId,
|
||||
'currency_id' => (string) $currencyId,
|
||||
'currency_code' => $block['currency_code'],
|
||||
'currency_name' => $block['currency_name'],
|
||||
'currency_symbol' => $block['currency_symbol'],
|
||||
'currency_decimal_places' => (int)$block['currency_decimal_places'],
|
||||
'currency_decimal_places' => (int) $block['currency_decimal_places'],
|
||||
'start' => $start->toAtomString(),
|
||||
'end' => $end->toAtomString(),
|
||||
'budgeted' => '0',
|
||||
@@ -251,7 +243,7 @@ class BudgetController extends Controller
|
||||
/** @var array $journal */
|
||||
foreach ($currentBudgetArray['transaction_journals'] as $journal) {
|
||||
/** @var numeric-string $amount */
|
||||
$amount = (string)$journal['amount'];
|
||||
$amount = (string) $journal['amount'];
|
||||
$return[$currencyId]['spent'] = bcadd($return[$currencyId]['spent'], $amount);
|
||||
}
|
||||
}
|
||||
@@ -270,14 +262,21 @@ class BudgetController extends Controller
|
||||
if ($this->convertToPrimary) {
|
||||
if ($current->transaction_currency_id === $this->primaryCurrency->id) {
|
||||
// simply add it.
|
||||
$amount = bcadd($amount, (string)$current->amount);
|
||||
$amount = bcadd($amount, (string) $current->amount);
|
||||
Log::debug(sprintf('Set amount in limit to %s', $amount));
|
||||
}
|
||||
if ($current->transaction_currency_id !== $this->primaryCurrency->id) {
|
||||
// convert and then add it.
|
||||
$converted = $converter->convert($current->transactionCurrency, $this->primaryCurrency, $current->start_date, $current->amount);
|
||||
$amount = bcadd($amount, $converted);
|
||||
Log::debug(sprintf('Budgeted in limit #%d: %s %s, converted to %s %s', $current->id, $current->transactionCurrency->code, $current->amount, $this->primaryCurrency->code, $converted));
|
||||
Log::debug(sprintf(
|
||||
'Budgeted in limit #%d: %s %s, converted to %s %s',
|
||||
$current->id,
|
||||
$current->transactionCurrency->code,
|
||||
$current->amount,
|
||||
$this->primaryCurrency->code,
|
||||
$converted
|
||||
));
|
||||
Log::debug(sprintf('Set amount in limit to %s', $amount));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Chart;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\DateRangeRequest;
|
||||
@@ -40,6 +39,7 @@ use FireflyIII\Support\Http\Api\CleansChartData;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
@@ -52,25 +52,23 @@ class CategoryController extends Controller
|
||||
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
private AccountRepositoryInterface $accountRepos;
|
||||
private AccountRepositoryInterface $accountRepos;
|
||||
private CurrencyRepositoryInterface $currencyRepos;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$this->accountRepos->setUserGroup($this->userGroup);
|
||||
$this->currencyRepos->setUserGroup($this->userGroup);
|
||||
$this->accountRepos->setUser($this->user);
|
||||
$this->currencyRepos->setUser($this->user);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$this->accountRepos->setUserGroup($this->userGroup);
|
||||
$this->currencyRepos->setUserGroup($this->userGroup);
|
||||
$this->accountRepos->setUser($this->user);
|
||||
$this->currencyRepos->setUser($this->user);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,7 +86,12 @@ class CategoryController extends Controller
|
||||
|
||||
/** @var Carbon $end */
|
||||
$end = $request->attributes->get('end');
|
||||
$accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value]);
|
||||
$accounts = $this->accountRepos->getAccountsByType([
|
||||
AccountTypeEnum::DEBT->value,
|
||||
AccountTypeEnum::LOAN->value,
|
||||
AccountTypeEnum::MORTGAGE->value,
|
||||
AccountTypeEnum::ASSET->value,
|
||||
]);
|
||||
$currencies = [];
|
||||
$return = [];
|
||||
$converter = new ExchangeRateConverter();
|
||||
@@ -104,7 +107,7 @@ class CategoryController extends Controller
|
||||
/** @var array $journal */
|
||||
foreach ($journals as $journal) {
|
||||
// find journal:
|
||||
$journalCurrencyId = (int)$journal['currency_id'];
|
||||
$journalCurrencyId = (int) $journal['currency_id'];
|
||||
$type = $journal['transaction_type_type'];
|
||||
$currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId);
|
||||
$currencies[$journalCurrencyId] = $currency;
|
||||
@@ -113,7 +116,7 @@ class CategoryController extends Controller
|
||||
$currencyCode = $currency->code;
|
||||
$currencySymbol = $currency->symbol;
|
||||
$currencyDecimalPlaces = $currency->decimal_places;
|
||||
$amount = Steam::positive((string)$journal['amount']);
|
||||
$amount = Steam::positive((string) $journal['amount']);
|
||||
$pcAmount = null;
|
||||
|
||||
// overrule if necessary:
|
||||
@@ -130,18 +133,17 @@ class CategoryController extends Controller
|
||||
Log::debug(sprintf('Converted %s %s to %s %s', $journal['currency_code'], $amount, $this->primaryCurrency->code, $pcAmount));
|
||||
}
|
||||
|
||||
|
||||
$categoryName = $journal['category_name'] ?? (string)trans('firefly.no_category');
|
||||
$categoryName = $journal['category_name'] ?? (string) trans('firefly.no_category');
|
||||
$key = sprintf('%s-%s', $categoryName, $currencyCode);
|
||||
// create arrays
|
||||
$return[$key] ??= [
|
||||
'label' => $categoryName,
|
||||
'currency_id' => (string)$currencyId,
|
||||
'currency_id' => (string) $currencyId,
|
||||
'currency_name' => $currencyName,
|
||||
'currency_code' => $currencyCode,
|
||||
'currency_symbol' => $currencySymbol,
|
||||
'currency_decimal_places' => $currencyDecimalPlaces,
|
||||
'primary_currency_id' => (string)$this->primaryCurrency->id,
|
||||
'primary_currency_id' => (string) $this->primaryCurrency->id,
|
||||
'primary_currency_name' => $this->primaryCurrency->name,
|
||||
'primary_currency_code' => $this->primaryCurrency->code,
|
||||
'primary_currency_symbol' => $this->primaryCurrency->symbol,
|
||||
@@ -151,14 +153,8 @@ class CategoryController extends Controller
|
||||
'end_date' => $end->toAtomString(),
|
||||
'yAxisID' => 0,
|
||||
'type' => 'bar',
|
||||
'entries' => [
|
||||
'spent' => '0',
|
||||
'earned' => '0',
|
||||
],
|
||||
'pc_entries' => [
|
||||
'spent' => '0',
|
||||
'earned' => '0',
|
||||
],
|
||||
'entries' => ['spent' => '0', 'earned' => '0'],
|
||||
'pc_entries' => ['spent' => '0', 'earned' => '0'],
|
||||
];
|
||||
|
||||
// add monies
|
||||
@@ -182,7 +178,10 @@ class CategoryController extends Controller
|
||||
$return = array_values($return);
|
||||
|
||||
// order by amount
|
||||
usort($return, static fn (array $a, array $b): int => ((float)$a['entries']['spent'] + (float)$a['entries']['earned']) < ((float)$b['entries']['spent'] + (float)$b['entries']['earned']) ? 1 : -1);
|
||||
usort($return, static fn (array $a, array $b): int => ((float) $a['entries']['spent'] + (float) $a['entries']['earned'])
|
||||
< ((float) $b['entries']['spent'] + (float) $b['entries']['earned'])
|
||||
? 1
|
||||
: -1);
|
||||
|
||||
return response()->json($this->clean($return));
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers;
|
||||
|
||||
use Deprecated;
|
||||
use Carbon\Carbon;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use Deprecated;
|
||||
use FireflyIII\Exceptions\BadHttpHeaderException;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
@@ -63,15 +63,16 @@ abstract class Controller extends BaseController
|
||||
use ValidatesRequests;
|
||||
use ValidatesUserGroupTrait;
|
||||
|
||||
protected const string CONTENT_TYPE = 'application/vnd.api+json';
|
||||
protected const string JSON_CONTENT_TYPE = 'application/json';
|
||||
protected array $accepts = ['application/json', 'application/vnd.api+json'];
|
||||
protected const string CONTENT_TYPE = 'application/vnd.api+json';
|
||||
protected const string JSON_CONTENT_TYPE = 'application/json';
|
||||
|
||||
protected bool $convertToPrimary = false;
|
||||
protected array $accepts = ['application/json', 'application/vnd.api+json'];
|
||||
|
||||
protected bool $convertToPrimary = false;
|
||||
protected TransactionCurrency $primaryCurrency;
|
||||
|
||||
/** @deprecated use Request classes */
|
||||
protected ParameterBag $parameters;
|
||||
protected ParameterBag $parameters;
|
||||
|
||||
/**
|
||||
* Controller constructor.
|
||||
@@ -79,26 +80,22 @@ abstract class Controller extends BaseController
|
||||
public function __construct()
|
||||
{
|
||||
// get global parameters
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->parameters = $this->getParameters();
|
||||
if (auth()->check()) {
|
||||
$language = Steam::getLanguage();
|
||||
$this->convertToPrimary = Amount::convertToPrimary();
|
||||
$this->primaryCurrency = Amount::getPrimaryCurrency();
|
||||
app()->setLocale($language);
|
||||
}
|
||||
|
||||
|
||||
// filter down what this endpoint accepts.
|
||||
if (!$request->accepts($this->accepts)) {
|
||||
throw new BadHttpHeaderException(sprintf('Sorry, Accept header "%s" is not something this endpoint can provide.', $request->header('Accept')));
|
||||
}
|
||||
|
||||
|
||||
return $next($request);
|
||||
$this->middleware(function ($request, $next) {
|
||||
$this->parameters = $this->getParameters();
|
||||
if (auth()->check()) {
|
||||
$language = Steam::getLanguage();
|
||||
$this->convertToPrimary = Amount::convertToPrimary();
|
||||
$this->primaryCurrency = Amount::getPrimaryCurrency();
|
||||
app()->setLocale($language);
|
||||
}
|
||||
);
|
||||
|
||||
// filter down what this endpoint accepts.
|
||||
if (!$request->accepts($this->accepts)) {
|
||||
throw new BadHttpHeaderException(sprintf('Sorry, Accept header "%s" is not something this endpoint can provide.', $request->header('Accept')));
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
#[Deprecated(message: <<<'TXT'
|
||||
@@ -108,7 +105,7 @@ abstract class Controller extends BaseController
|
||||
private function getParameters(): ParameterBag
|
||||
{
|
||||
$bag = new ParameterBag();
|
||||
$page = (int)request()->get('page');
|
||||
$page = (int) request()->get('page');
|
||||
$page = min(max(1, $page), 2 ** 16);
|
||||
$bag->set('page', $page);
|
||||
|
||||
@@ -127,10 +124,10 @@ abstract class Controller extends BaseController
|
||||
$obj = null;
|
||||
if (null !== $date) {
|
||||
try {
|
||||
$obj = Carbon::parse((string)$date, config('app.timezone'));
|
||||
$obj = Carbon::parse((string) $date, config('app.timezone'));
|
||||
} catch (InvalidFormatException $e) {
|
||||
// don't care
|
||||
Log::warning(sprintf('Ignored invalid date "%s" in API controller parameter check: %s', substr((string)$date, 0, 20), $e->getMessage()));
|
||||
Log::warning(sprintf('Ignored invalid date "%s" in API controller parameter check: %s', substr((string) $date, 0, 20), $e->getMessage()));
|
||||
}
|
||||
}
|
||||
if ($obj instanceof Carbon) {
|
||||
@@ -150,24 +147,27 @@ abstract class Controller extends BaseController
|
||||
$value = null;
|
||||
}
|
||||
if (null !== $value) {
|
||||
$value = (int)$value;
|
||||
$value = (int) $value;
|
||||
$value = min(max(1, $value), 2 ** 16);
|
||||
$bag->set($integer, $value);
|
||||
}
|
||||
if (null === $value
|
||||
if (
|
||||
null === $value
|
||||
&& 'limit' === $integer // @phpstan-ignore-line
|
||||
&& auth()->check()) {
|
||||
&& auth()->check()
|
||||
) {
|
||||
// set default for user:
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$pageSize = (int)Preferences::getForUser($user, 'listPageSize', 50)->data;
|
||||
$pageSize = (int) Preferences::getForUser($user, 'listPageSize', 50)->data;
|
||||
$bag->set($integer, $pageSize);
|
||||
}
|
||||
}
|
||||
|
||||
// sort fields:
|
||||
return $bag;
|
||||
|
||||
// return $this->getSortParameters($bag);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Data;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Data\DestroyRequest;
|
||||
use FireflyIII\Enums\AccountTypeEnum;
|
||||
@@ -49,6 +48,7 @@ use FireflyIII\Services\Internal\Destroy\AccountDestroyService;
|
||||
use FireflyIII\Services\Internal\Destroy\JournalDestroyService;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
@@ -63,13 +63,11 @@ class DestroyController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function destroy(DestroyRequest $request): JsonResponse
|
||||
@@ -77,10 +75,40 @@ class DestroyController extends Controller
|
||||
$objects = $request->getObjects();
|
||||
$this->unused = $request->boolean('unused');
|
||||
|
||||
$allExceptAssets = [AccountTypeEnum::BENEFICIARY->value, AccountTypeEnum::CASH->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::EXPENSE->value, AccountTypeEnum::IMPORT->value, AccountTypeEnum::INITIAL_BALANCE->value, AccountTypeEnum::LIABILITY_CREDIT->value, AccountTypeEnum::RECONCILIATION->value, AccountTypeEnum::REVENUE->value];
|
||||
$all = [AccountTypeEnum::ASSET->value, AccountTypeEnum::BENEFICIARY->value, AccountTypeEnum::CASH->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::EXPENSE->value, AccountTypeEnum::IMPORT->value, AccountTypeEnum::INITIAL_BALANCE->value, AccountTypeEnum::LIABILITY_CREDIT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::RECONCILIATION->value];
|
||||
$allExceptAssets = [
|
||||
AccountTypeEnum::BENEFICIARY->value,
|
||||
AccountTypeEnum::CASH->value,
|
||||
AccountTypeEnum::CREDITCARD->value,
|
||||
AccountTypeEnum::DEFAULT->value,
|
||||
AccountTypeEnum::EXPENSE->value,
|
||||
AccountTypeEnum::IMPORT->value,
|
||||
AccountTypeEnum::INITIAL_BALANCE->value,
|
||||
AccountTypeEnum::LIABILITY_CREDIT->value,
|
||||
AccountTypeEnum::RECONCILIATION->value,
|
||||
AccountTypeEnum::REVENUE->value,
|
||||
];
|
||||
$all = [
|
||||
AccountTypeEnum::ASSET->value,
|
||||
AccountTypeEnum::BENEFICIARY->value,
|
||||
AccountTypeEnum::CASH->value,
|
||||
AccountTypeEnum::CREDITCARD->value,
|
||||
AccountTypeEnum::DEBT->value,
|
||||
AccountTypeEnum::DEFAULT->value,
|
||||
AccountTypeEnum::EXPENSE->value,
|
||||
AccountTypeEnum::IMPORT->value,
|
||||
AccountTypeEnum::INITIAL_BALANCE->value,
|
||||
AccountTypeEnum::LIABILITY_CREDIT->value,
|
||||
AccountTypeEnum::LOAN->value,
|
||||
AccountTypeEnum::MORTGAGE->value,
|
||||
AccountTypeEnum::RECONCILIATION->value,
|
||||
];
|
||||
$liabilities = [AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::CREDITCARD->value];
|
||||
$transactions = [TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value, TransactionTypeEnum::RECONCILIATION->value];
|
||||
$transactions = [
|
||||
TransactionTypeEnum::WITHDRAWAL->value,
|
||||
TransactionTypeEnum::DEPOSIT->value,
|
||||
TransactionTypeEnum::TRANSFER->value,
|
||||
TransactionTypeEnum::RECONCILIATION->value,
|
||||
];
|
||||
|
||||
match ($objects) {
|
||||
'budgets' => $this->destroyBudgets(),
|
||||
@@ -101,7 +129,7 @@ class DestroyController extends Controller
|
||||
'withdrawals' => $this->destroyTransactions([TransactionTypeEnum::WITHDRAWAL->value]),
|
||||
'deposits' => $this->destroyTransactions([TransactionTypeEnum::DEPOSIT->value]),
|
||||
'transfers' => $this->destroyTransactions([TransactionTypeEnum::TRANSFER->value]),
|
||||
default => throw new FireflyException(sprintf('200033: This endpoint can\'t handle object "%s"', $objects)),
|
||||
default => throw new FireflyException(sprintf('200033: This endpoint can\'t handle object "%s"', $objects))
|
||||
};
|
||||
|
||||
Preferences::mark();
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Data;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\Account;
|
||||
@@ -40,6 +39,7 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class PurgeController
|
||||
@@ -51,13 +51,11 @@ class PurgeController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,6 +44,8 @@ use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use League\Fractal\Resource\Item;
|
||||
use Symfony\Component\HttpKernel\Exception\GoneHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
/**
|
||||
* Class StoreController
|
||||
@@ -82,7 +84,7 @@ class StoreController extends Controller
|
||||
*
|
||||
* Store a new transaction.
|
||||
*
|
||||
* @throws FireflyException|ValidationException
|
||||
* @throws FireflyException|GoneHttpException|ValidationException
|
||||
*/
|
||||
public function store(StoreRequest $request): JsonResponse
|
||||
{
|
||||
@@ -133,7 +135,7 @@ class StoreController extends Controller
|
||||
|
||||
$selectedGroup = $collector->getGroups()->first();
|
||||
if (null === $selectedGroup) {
|
||||
throw new FireflyException('200032: Cannot find transaction. Possibly, a rule deleted this transaction after its creation.');
|
||||
throw HttpException::fromStatusCode(410, '200032: Cannot find transaction. Possibly, a rule deleted this transaction after its creation.');
|
||||
}
|
||||
|
||||
// enrich
|
||||
|
||||
@@ -262,7 +262,7 @@ class ListController extends Controller
|
||||
// filter selection
|
||||
$collection = $unfiltered->filter(
|
||||
static function (Recurrence $recurrence) use ($currency): ?Recurrence { // @phpstan-ignore-line
|
||||
if (array_any($recurrence->recurrenceTransactions, fn ($transaction): bool => $transaction->transaction_currency_id === $currency->id || $transaction->foreign_currency_id === $currency->id)) {
|
||||
if (array_any($recurrence->recurrenceTransactions, static fn ($transaction): bool => $transaction->transaction_currency_id === $currency->id || $transaction->foreign_currency_id === $currency->id)) {
|
||||
return $recurrence;
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ class ListController extends Controller
|
||||
|
||||
$collection = $unfiltered->filter(
|
||||
static function (Rule $rule) use ($currency): ?Rule { // @phpstan-ignore-line
|
||||
if (array_any($rule->ruleTriggers, fn ($trigger): bool => 'currency_is' === $trigger->trigger_type && $currency->name === $trigger->trigger_value)) {
|
||||
if (array_any($rule->ruleTriggers, static fn ($trigger): bool => 'currency_is' === $trigger->trigger_type && $currency->name === $trigger->trigger_value)) {
|
||||
return $rule;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,11 +25,11 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Api\V1\Controllers\Search;
|
||||
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Search\TransactionSearchRequest;
|
||||
use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment;
|
||||
use FireflyIII\Support\Search\SearchInterface;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection;
|
||||
|
||||
@@ -42,12 +42,12 @@ class TransactionController extends Controller
|
||||
* This endpoint is documented at:
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/search/searchTransactions
|
||||
*/
|
||||
public function search(Request $request, SearchInterface $searcher): JsonResponse
|
||||
public function search(TransactionSearchRequest $request, SearchInterface $searcher): JsonResponse
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
$fullQuery = (string) $request->get('query');
|
||||
$page = 0 === (int) $request->get('page') ? 1 : (int) $request->get('page');
|
||||
$pageSize = $this->parameters->get('limit');
|
||||
$fullQuery = (string) $request->attributes->get('query');
|
||||
$page = $request->attributes->get('page');
|
||||
$pageSize = $request->attributes->get('limit');
|
||||
$searcher->parseQuery($fullQuery);
|
||||
$searcher->setPage($page);
|
||||
$searcher->setLimit($pageSize);
|
||||
|
||||
50
app/Api/V1/Requests/Search/SearchQueryRequest.php
Normal file
50
app/Api/V1/Requests/Search/SearchQueryRequest.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* SearchQueryRequest.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\Api\V1\Requests\Search;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\ApiRequest;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
|
||||
class SearchQueryRequest extends ApiRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'query' => sprintf('min:0|max:500|%s', $this->required),
|
||||
];
|
||||
}
|
||||
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator): void {
|
||||
if ($validator->failed()) {
|
||||
return;
|
||||
}
|
||||
$query = $this->convertString('query');
|
||||
$this->attributes->set('query', $query);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
42
app/Api/V1/Requests/Search/TransactionSearchRequest.php
Normal file
42
app/Api/V1/Requests/Search/TransactionSearchRequest.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* SearchRequest.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\Api\V1\Requests\Search;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\AggregateFormRequest;
|
||||
use FireflyIII\Api\V1\Requests\PaginationRequest;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Override;
|
||||
|
||||
class TransactionSearchRequest extends AggregateFormRequest
|
||||
{
|
||||
#[Override]
|
||||
protected function getRequests(): array
|
||||
{
|
||||
return [
|
||||
[PaginationRequest::class, 'sort_class' => TransactionJournal::class],
|
||||
SearchQueryRequest::class,
|
||||
// [ObjectTypeApiRequest::class, 'object_type' => Account::class],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ class ConvertsDatesToUTC extends Command
|
||||
}
|
||||
$this->friendlyInfo(sprintf('Converting field "%s" of model "%s" to UTC.', $field, $shortModel));
|
||||
$items->each(
|
||||
function ($item) use ($field, $timezoneField): void {
|
||||
static function ($item) use ($field, $timezoneField): void {
|
||||
$date = Carbon::parse($item->{$field}, $item->{$timezoneField}); // @phpstan-ignore-line
|
||||
$date->setTimezone('UTC');
|
||||
$item->{$field} = $date->format('Y-m-d H:i:s'); // @phpstan-ignore-line
|
||||
|
||||
@@ -103,7 +103,7 @@ class CorrectsPrimaryCurrencyAmounts extends Command
|
||||
|
||||
private function recalculateAccounts(UserGroup $userGroup): void
|
||||
{
|
||||
$set = $userGroup->accounts()->where(function (EloquentBuilder $q): void {
|
||||
$set = $userGroup->accounts()->where(static function (EloquentBuilder $q): void {
|
||||
$q->whereNotNull('virtual_balance');
|
||||
|
||||
// this needs a different piece of code for postgres.
|
||||
@@ -226,10 +226,10 @@ class CorrectsPrimaryCurrencyAmounts extends Command
|
||||
$set = DB::table('transactions')
|
||||
->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->where('transaction_journals.user_group_id', $userGroup->id)
|
||||
->where(function (DatabaseBuilder $q1) use ($currency): void {
|
||||
$q1->where(function (DatabaseBuilder $q2) use ($currency): void {
|
||||
->where(static function (DatabaseBuilder $q1) use ($currency): void {
|
||||
$q1->where(static function (DatabaseBuilder $q2) use ($currency): void {
|
||||
$q2->whereNot('transactions.transaction_currency_id', $currency->id)->whereNull('transactions.foreign_currency_id');
|
||||
})->orWhere(function (DatabaseBuilder $q3) use ($currency): void {
|
||||
})->orWhere(static function (DatabaseBuilder $q3) use ($currency): void {
|
||||
$q3->whereNot('transactions.transaction_currency_id', $currency->id)->whereNot('transactions.foreign_currency_id', $currency->id);
|
||||
});
|
||||
})
|
||||
|
||||
@@ -34,7 +34,7 @@ class RemovesBills extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Remove bills from transactions that shouldn\'t have one.';
|
||||
protected $description = 'Remove subscriptions from transactions that shouldn\'t have one.';
|
||||
protected $signature = 'correction:bills';
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace FireflyIII\Console\Commands\Correction;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Support\System\OAuthKeys;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class RestoresOAuthKeys extends Command
|
||||
{
|
||||
@@ -40,7 +41,9 @@ class RestoresOAuthKeys extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
Log::debug('Restore OAuth Keys command.');
|
||||
$this->restoreOAuthKeys();
|
||||
Log::debug('Done with OAuth Keys command.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
91
app/Console/Commands/Explain/ExplainAvailableBudget.php
Normal file
91
app/Console/Commands/Explain/ExplainAvailableBudget.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* ExplainAvailableBudget.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\Explain;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use FireflyIII\Console\Commands\VerifiesAccessToken;
|
||||
use FireflyIII\Support\Facades\Navigation;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class ExplainAvailableBudget extends Command
|
||||
{
|
||||
use VerifiesAccessToken;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'explain:available-budget
|
||||
{--date=now : A date formatted YYYY-MM-DD or the word "now"}
|
||||
{--user=1 : The user ID.}
|
||||
{--token= : The user\'s access token.}
|
||||
';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Explains why the available budget amount is what it is.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$date = $this->getDate((string) $this->option('date'));
|
||||
$range = Preferences::getForUser($this->getUser(), 'viewRange', '1M')->data ?? '1M';
|
||||
$title = Navigation::periodShow($date, $range);
|
||||
$this->line('This command explains why the "available" budget bar at the top of your /budget bar means.');
|
||||
$this->line(sprintf(
|
||||
'You submitted date %s and your settings show a %s period, so this explanation concerns the period "%s".',
|
||||
$date->format('Y-m-d'),
|
||||
$range,
|
||||
$title
|
||||
));
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function getDate(string $param): Carbon
|
||||
{
|
||||
if ('now' === $param) {
|
||||
return today();
|
||||
}
|
||||
|
||||
try {
|
||||
$date = Carbon::parse($param);
|
||||
} catch (InvalidFormatException) {
|
||||
$this->warn('Invalid date given. Fall back to today\'s date.');
|
||||
|
||||
return today();
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
}
|
||||
@@ -198,15 +198,29 @@ class ApplyRules extends Command
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$accountRepository->setUser($this->getUser());
|
||||
foreach ($accountList as $accountId) {
|
||||
$accountId = (int) $accountId;
|
||||
$account = $accountRepository->find($accountId);
|
||||
if (null !== $account && in_array($account->accountType->type, $this->acceptedAccounts, true)) {
|
||||
$finalList->push($account);
|
||||
$accountId = (int)$accountId;
|
||||
if (0 === $accountId) {
|
||||
$this->friendlyWarning('You provided an account with ID 0 (zero). It will be ignored.');
|
||||
|
||||
continue;
|
||||
}
|
||||
$account = $accountRepository->find($accountId);
|
||||
if (null === $account) {
|
||||
$this->friendlyWarning(sprintf('There is no account with ID #%d, it cannot be added.', $accountId));
|
||||
|
||||
continue;
|
||||
}
|
||||
$type = $account->accountType->type;
|
||||
if (!in_array($account->accountType->type, $this->acceptedAccounts, true)) {
|
||||
$this->friendlyWarning(sprintf('Account "%s" with ID #%d is of type "%s" and cannot be added.', $account->name, $accountId, $type));
|
||||
|
||||
continue;
|
||||
}
|
||||
$finalList->push($account);
|
||||
}
|
||||
|
||||
if (0 === $finalList->count()) {
|
||||
$this->friendlyError('Please make sure all accounts in --accounts are asset accounts or liabilities.');
|
||||
$this->friendlyError('There are no accounts in the selection. Please make sure all accounts in --accounts are asset accounts or liabilities.');
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -225,13 +239,27 @@ class ApplyRules extends Command
|
||||
$ruleGroupList = explode(',', $ruleGroupString);
|
||||
|
||||
foreach ($ruleGroupList as $ruleGroupId) {
|
||||
$ruleGroup = $this->ruleGroupRepository->find((int) $ruleGroupId);
|
||||
if (true === $ruleGroup->active) {
|
||||
$this->ruleGroupSelection[] = $ruleGroup->id;
|
||||
$ruleGroupId = (int)$ruleGroupId;
|
||||
|
||||
if (0 === $ruleGroupId) {
|
||||
$this->friendlyWarning('You added a rule group with ID 0 (zero). It will be skipped.');
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$ruleGroup = $this->ruleGroupRepository->find($ruleGroupId);
|
||||
|
||||
if (null === $ruleGroup) {
|
||||
$this->friendlyWarning(sprintf('There is no rule group with ID #%d, this ID will be ignored.', $ruleGroupId));
|
||||
|
||||
continue;
|
||||
}
|
||||
if (false === $ruleGroup->active) {
|
||||
$this->friendlyWarning(sprintf('Will ignore inactive rule group #%d ("%s")', $ruleGroup->id, $ruleGroup->title));
|
||||
$this->friendlyWarning(sprintf('Rule group with ID #%d is not active, so this ID will be ignored.', $ruleGroupId));
|
||||
|
||||
continue;
|
||||
}
|
||||
$this->ruleGroupSelection[] = $ruleGroupId;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -247,10 +275,24 @@ class ApplyRules extends Command
|
||||
$ruleList = explode(',', $ruleString);
|
||||
|
||||
foreach ($ruleList as $ruleId) {
|
||||
$rule = $this->ruleRepository->find((int) $ruleId);
|
||||
if ($rule instanceof Rule && true === $rule->active) {
|
||||
$this->ruleSelection[] = $rule->id;
|
||||
$ruleId = (int)$ruleId;
|
||||
if (0 === $ruleId) {
|
||||
$this->friendlyWarning('You added a rule with ID 0 (zero). It will be skipped.');
|
||||
|
||||
continue;
|
||||
}
|
||||
$rule = $this->ruleRepository->find($ruleId);
|
||||
if (null === $rule) {
|
||||
$this->friendlyWarning(sprintf('There is no rule with ID #%d, this ID will be ignored.', $ruleId));
|
||||
|
||||
continue;
|
||||
}
|
||||
if (false === $rule->active) {
|
||||
$this->friendlyWarning(sprintf('Rule with ID #%d is not active, so this ID will be ignored.', $ruleId));
|
||||
|
||||
continue;
|
||||
}
|
||||
$this->ruleSelection[] = $ruleId;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -304,10 +346,12 @@ class ApplyRules extends Command
|
||||
|
||||
private function getRulesToApply(): Collection
|
||||
{
|
||||
Log::debug('getRulesToApply()');
|
||||
$rulesToApply = new Collection();
|
||||
|
||||
/** @var RuleGroup $group */
|
||||
foreach ($this->groups as $group) {
|
||||
Log::debug(sprintf('Scanning rule group #%d', $group->id));
|
||||
$rules = $this->ruleGroupRepository->getActiveStoreRules($group);
|
||||
|
||||
/** @var Rule $rule */
|
||||
@@ -318,16 +362,20 @@ class ApplyRules extends Command
|
||||
Log::debug(sprintf('Will include rule #%d "%s"', $rule->id, $rule->title));
|
||||
$rulesToApply->push($rule);
|
||||
}
|
||||
if (!$test) {
|
||||
Log::debug(sprintf('Will not include rule #%d', $rule->id));
|
||||
}
|
||||
}
|
||||
}
|
||||
Log::debug(sprintf('Found %d rules to apply.', $rulesToApply->count()));
|
||||
|
||||
return $rulesToApply;
|
||||
}
|
||||
|
||||
private function includeRule(Rule $rule, RuleGroup $group): bool
|
||||
{
|
||||
return in_array($group->id, $this->ruleGroupSelection, true)
|
||||
|| in_array($rule->id, $this->ruleSelection, true)
|
||||
return in_array((int)$group->id, $this->ruleGroupSelection, true)
|
||||
|| in_array((int)$rule->id, $this->ruleSelection, true)
|
||||
|| $this->allRules;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Updated.php
|
||||
* Copyright (c) 2024 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\Events\Model\Account;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class Updated
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
public function __construct(public Account $account) {}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ use Illuminate\Support\Facades\Log;
|
||||
/**
|
||||
* Class ChangedAmount
|
||||
*/
|
||||
class ChangedAmount extends Event
|
||||
class PiggyBankAmountIsChanged extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* ChangedName.php
|
||||
* Copyright (c) 2025 james@firefly-iii.org
|
||||
* PiggyBankNameIsChanged.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -21,15 +21,16 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events\Model\PiggyBank;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ChangedName extends Event
|
||||
/**
|
||||
* Needs to be an event because system needs old value as well as the new value.
|
||||
*/
|
||||
class PiggyBankNameIsChanged extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
<?php
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* WarnUserAboutBill.php
|
||||
* Copyright (c) 2025 james@firefly-iii.org
|
||||
* SubscriptionNeedsExtensionOrRenewal.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -24,18 +22,15 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events\Model\Bill;
|
||||
namespace FireflyIII\Events\Model\Subscription;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\Models\Bill;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class WarnUserAboutBill.
|
||||
*/
|
||||
class WarnUserAboutBill extends Event
|
||||
class SubscriptionNeedsExtensionOrRenewal extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
public function __construct(public Bill $bill, public string $field, public int $diff) {}
|
||||
public function __construct(public Bill $subscription, public string $field, public int $diff) {}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* WarnUserAboutOverdueSubscriptions.php
|
||||
* Copyright (c) 2025 james@firefly-iii.org
|
||||
* SubscriptionIsOverdueForPayment.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -21,18 +21,15 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events\Model\Bill;
|
||||
namespace FireflyIII\Events\Model\Subscription;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class WarnUserAboutOverdueSubscriptions extends Event
|
||||
class SubscriptionsAreOverdueForPayment extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
public function __construct(public User $user, public array $overdue) {}
|
||||
|
||||
}
|
||||
@@ -24,15 +24,20 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Events\Model\TransactionGroup;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class TriggeredStoredTransactionGroup extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
public ?RuleGroup $ruleGroup = null;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct(public TransactionGroup $transactionGroup) {}
|
||||
public function __construct(public TransactionGroup $transactionGroup, ?RuleGroup $ruleGroup = null)
|
||||
{
|
||||
$this->ruleGroup = $ruleGroup;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace FireflyIII\Events;
|
||||
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* Class RequestedNewPassword.
|
||||
@@ -46,7 +47,7 @@ class RequestedNewPassword extends Event
|
||||
/**
|
||||
* Create a new event instance. This event is triggered when a users tries to reset his or her password.
|
||||
*/
|
||||
public function __construct(User $user, string $token, string $ipAddress)
|
||||
public function __construct(User $user, #[SensitiveParameter] string $token, string $ipAddress)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->token = $token;
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* EnabledMFA.php
|
||||
* Copyright (c) 2024 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\Events\Security;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class MFAUsedBackupCode extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
public User $user;
|
||||
|
||||
public function __construct(Authenticatable|User|null $user)
|
||||
{
|
||||
if ($user instanceof User) {
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* UnknownUserAttemptedLogin.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
* UnknownUserTriedLogin.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -17,16 +18,14 @@
|
||||
* 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/.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events\Security;
|
||||
namespace FireflyIII\Events\Security\System;
|
||||
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class UnknownUserAttemptedLogin
|
||||
class UnknownUserTriedLogin
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* EnabledMFA.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
* UserFailedLoginAttempt.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -17,19 +18,18 @@
|
||||
* 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/.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events\Security;
|
||||
namespace FireflyIII\Events\Security\User;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class EnabledMFA extends Event
|
||||
class UserFailedLoginAttempt extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
@@ -39,6 +39,10 @@ class EnabledMFA extends Event
|
||||
{
|
||||
if ($user instanceof User) {
|
||||
$this->user = $user;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('User must be an instance of User.');
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* EnabledMFA.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
* UserHasDisabledMFA.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -17,19 +18,18 @@
|
||||
* 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/.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events\Security;
|
||||
namespace FireflyIII\Events\Security\User;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class MFANewBackupCodes extends Event
|
||||
class UserHasDisabledMFA extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
@@ -39,6 +39,10 @@ class MFANewBackupCodes extends Event
|
||||
{
|
||||
if ($user instanceof User) {
|
||||
$this->user = $user;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('User must be an instance of User.');
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* EnabledMFA.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
* UserHasEnabledMFA.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -17,19 +18,18 @@
|
||||
* 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/.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events\Security;
|
||||
namespace FireflyIII\Events\Security\User;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class DisabledMFA extends Event
|
||||
class UserHasEnabledMFA extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
@@ -39,6 +39,10 @@ class DisabledMFA extends Event
|
||||
{
|
||||
if ($user instanceof User) {
|
||||
$this->user = $user;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('User must be an instance of User.');
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* EnabledMFA.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
* UserHasFewMFABackupCodesLeft.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -17,19 +18,18 @@
|
||||
* 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/.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events\Security;
|
||||
namespace FireflyIII\Events\Security\User;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class MFABackupFewLeft extends Event
|
||||
class UserHasFewMFABackupCodesLeft extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
@@ -39,6 +39,10 @@ class MFABackupFewLeft extends Event
|
||||
{
|
||||
if ($user instanceof User) {
|
||||
$this->user = $user;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('User must be an instance of User.');
|
||||
}
|
||||
}
|
||||
48
app/Events/Security/User/UserHasGeneratedNewBackupCodes.php
Normal file
48
app/Events/Security/User/UserHasGeneratedNewBackupCodes.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* UserHasGeneratedNewBackupCodes.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\Events\Security\User;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class UserHasGeneratedNewBackupCodes extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
public User $user;
|
||||
|
||||
public function __construct(Authenticatable|User|null $user)
|
||||
{
|
||||
if ($user instanceof User) {
|
||||
$this->user = $user;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('User must be an instance of User.');
|
||||
}
|
||||
}
|
||||
48
app/Events/Security/User/UserHasNoMFABackupCodesLeft.php
Normal file
48
app/Events/Security/User/UserHasNoMFABackupCodesLeft.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* UserHasNoMFABackupCodesLeft.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\Events\Security\User;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class UserHasNoMFABackupCodesLeft extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
public User $user;
|
||||
|
||||
public function __construct(Authenticatable|User|null $user)
|
||||
{
|
||||
if ($user instanceof User) {
|
||||
$this->user = $user;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('User must be an instance of User.');
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* EnabledMFA.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
* UserHasUsedBackupCode.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -17,19 +18,18 @@
|
||||
* 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/.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events\Security;
|
||||
namespace FireflyIII\Events\Security\User;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class MFABackupNoLeft extends Event
|
||||
class UserHasUsedBackupCode extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
@@ -39,6 +39,10 @@ class MFABackupNoLeft extends Event
|
||||
{
|
||||
if ($user instanceof User) {
|
||||
$this->user = $user;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('User must be an instance of User.');
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* EnabledMFA.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
* UserKeepsFailingMFA.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -17,19 +18,18 @@
|
||||
* 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/.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events\Security;
|
||||
namespace FireflyIII\Events\Security\User;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class MFAManyFailedAttempts extends Event
|
||||
class UserKeepsFailingMFA extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
@@ -39,6 +39,10 @@ class MFAManyFailedAttempts extends Event
|
||||
{
|
||||
if ($user instanceof User) {
|
||||
$this->user = $user;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('User must be an instance of User.');
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* UserAttemptedLogin.php
|
||||
* Copyright (c) 2024 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\Events\Security;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class UserAttemptedLogin extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
public User $user;
|
||||
|
||||
public function __construct(Authenticatable|User|null $user)
|
||||
{
|
||||
if ($user instanceof User) {
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@ use Override;
|
||||
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\GoneHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
@@ -71,6 +72,7 @@ class Handler extends ExceptionHandler
|
||||
AuthenticationException::class,
|
||||
LaravelValidationException::class,
|
||||
NotFoundHttpException::class,
|
||||
GoneHttpException::class,
|
||||
OAuthServerException::class,
|
||||
LaravelOAuthException::class,
|
||||
TokenMismatchException::class,
|
||||
|
||||
@@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Factory;
|
||||
|
||||
use FireflyIII\Events\Model\PiggyBank\ChangedAmount;
|
||||
use FireflyIII\Events\Model\PiggyBank\PiggyBankAmountIsChanged;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\ObjectGroup;
|
||||
@@ -305,7 +305,7 @@ class PiggyBankFactory
|
||||
if (0 !== bccomp($oldSavedAmount, $newSavedAmount)) {
|
||||
Log::debug('Amount changed, will create event for it.');
|
||||
// create event for difference.
|
||||
event(new ChangedAmount($piggyBank, bcsub($newSavedAmount, $oldSavedAmount), null, null));
|
||||
event(new PiggyBankAmountIsChanged($piggyBank, bcsub($newSavedAmount, $oldSavedAmount), null, null));
|
||||
}
|
||||
}
|
||||
if (0 === count($toBeLinked)) {
|
||||
|
||||
@@ -26,18 +26,16 @@ namespace FireflyIII\Handlers\Events;
|
||||
use Exception;
|
||||
use FireflyIII\Events\Admin\InvitationCreated;
|
||||
use FireflyIII\Events\NewVersionAvailable;
|
||||
use FireflyIII\Events\Security\UnknownUserAttemptedLogin;
|
||||
use FireflyIII\Events\Test\OwnerTestNotificationChannel;
|
||||
use FireflyIII\Notifications\Admin\UnknownUserLoginAttempt;
|
||||
use FireflyIII\Notifications\Admin\UserInvitation;
|
||||
use FireflyIII\Notifications\Admin\VersionCheckResult;
|
||||
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
|
||||
use FireflyIII\Notifications\Test\OwnerTestNotificationEmail;
|
||||
use FireflyIII\Notifications\Test\OwnerTestNotificationPushover;
|
||||
use FireflyIII\Notifications\Test\OwnerTestNotificationSlack;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
|
||||
/**
|
||||
* Class AdminEventHandler.
|
||||
@@ -70,28 +68,6 @@ class AdminEventHandler
|
||||
}
|
||||
}
|
||||
|
||||
public function sendLoginAttemptNotification(UnknownUserAttemptedLogin $event): void
|
||||
{
|
||||
try {
|
||||
$owner = new OwnerNotifiable();
|
||||
Notification::send($owner, new UnknownUserLoginAttempt($event->address));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send new version message to admin.
|
||||
*/
|
||||
|
||||
@@ -28,6 +28,7 @@ use FireflyIII\Enums\WebhookTrigger;
|
||||
use FireflyIII\Events\DestroyedTransactionGroup;
|
||||
use FireflyIII\Events\RequestedSendWebhookMessages;
|
||||
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\Support\Models\AccountBalanceCalculator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@@ -61,6 +62,9 @@ class DestroyedGroupEventHandler
|
||||
|
||||
private function updateRunningBalance(DestroyedTransactionGroup $event): void
|
||||
{
|
||||
if (false === FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data) {
|
||||
return;
|
||||
}
|
||||
Log::debug(__METHOD__);
|
||||
$group = $event->transactionGroup;
|
||||
foreach ($group->transactionJournals as $journal) {
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* MFAHandler.php
|
||||
* Copyright (c) 2024 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\Handlers\Events\Security;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\DisabledMFA;
|
||||
use FireflyIII\Events\Security\EnabledMFA;
|
||||
use FireflyIII\Events\Security\MFABackupFewLeft;
|
||||
use FireflyIII\Events\Security\MFABackupNoLeft;
|
||||
use FireflyIII\Events\Security\MFAManyFailedAttempts;
|
||||
use FireflyIII\Events\Security\MFANewBackupCodes;
|
||||
use FireflyIII\Events\Security\MFAUsedBackupCode;
|
||||
use FireflyIII\Notifications\Security\DisabledMFANotification;
|
||||
use FireflyIII\Notifications\Security\EnabledMFANotification;
|
||||
use FireflyIII\Notifications\Security\MFABackupFewLeftNotification;
|
||||
use FireflyIII\Notifications\Security\MFABackupNoLeftNotification;
|
||||
use FireflyIII\Notifications\Security\MFAManyFailedAttemptsNotification;
|
||||
use FireflyIII\Notifications\Security\MFAUsedBackupCodeNotification;
|
||||
use FireflyIII\Notifications\Security\NewBackupCodesNotification;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class MFAHandler
|
||||
{
|
||||
public function sendBackupFewLeftMail(MFABackupFewLeft $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
$count = $event->count;
|
||||
|
||||
try {
|
||||
Notification::send($user, new MFABackupFewLeftNotification($user, $count));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
|
||||
public function sendBackupNoLeftMail(MFABackupNoLeft $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
|
||||
try {
|
||||
Notification::send($user, new MFABackupNoLeftNotification($user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
|
||||
public function sendMFADisabledMail(DisabledMFA $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
|
||||
try {
|
||||
Notification::send($user, new DisabledMFANotification($user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
|
||||
public function sendMFAEnabledMail(EnabledMFA $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
|
||||
try {
|
||||
Notification::send($user, new EnabledMFANotification($user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
|
||||
public function sendMFAFailedAttemptsMail(MFAManyFailedAttempts $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
$count = $event->count;
|
||||
|
||||
try {
|
||||
Notification::send($user, new MFAManyFailedAttemptsNotification($user, $count));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
|
||||
public function sendNewMFABackupCodesMail(MFANewBackupCodes $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
|
||||
try {
|
||||
Notification::send($user, new NewBackupCodesNotification($user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
|
||||
public function sendUsedBackupCodeMail(MFAUsedBackupCode $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
|
||||
try {
|
||||
Notification::send($user, new MFAUsedBackupCodeNotification($user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ use FireflyIII\Events\Model\TransactionGroup\TriggeredStoredTransactionGroup;
|
||||
use FireflyIII\Events\RequestedSendWebhookMessages;
|
||||
use FireflyIII\Events\StoredTransactionGroup;
|
||||
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
|
||||
@@ -46,7 +47,7 @@ class StoredGroupEventHandler
|
||||
{
|
||||
public function runAllHandlers(StoredTransactionGroup $event): void
|
||||
{
|
||||
$this->processRules($event);
|
||||
$this->processRules($event, null);
|
||||
$this->recalculateCredit($event);
|
||||
$this->triggerWebhooks($event);
|
||||
$this->removePeriodStatistics($event);
|
||||
@@ -55,13 +56,13 @@ class StoredGroupEventHandler
|
||||
public function triggerRulesManually(TriggeredStoredTransactionGroup $event): void
|
||||
{
|
||||
$newEvent = new StoredTransactionGroup($event->transactionGroup, true, false);
|
||||
$this->processRules($newEvent);
|
||||
$this->processRules($newEvent, $event->ruleGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method grabs all the users rules and processes them.
|
||||
*/
|
||||
private function processRules(StoredTransactionGroup $storedGroupEvent): void
|
||||
private function processRules(StoredTransactionGroup $storedGroupEvent, ?RuleGroup $ruleGroup): void
|
||||
{
|
||||
if (false === $storedGroupEvent->applyRules) {
|
||||
Log::info(sprintf('Will not run rules on group #%d', $storedGroupEvent->transactionGroup->id));
|
||||
@@ -86,7 +87,14 @@ class StoredGroupEventHandler
|
||||
|
||||
// add the groups to the rule engine.
|
||||
// it should run the rules in the group and cancel the group if necessary.
|
||||
$groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal');
|
||||
if (null === $ruleGroup) {
|
||||
Log::debug('Fire processRules with ALL store-journal rule groups.');
|
||||
$groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal');
|
||||
}
|
||||
if (null !== $ruleGroup) {
|
||||
Log::debug(sprintf('Fire processRules with rule group #%d.', $ruleGroup->id));
|
||||
$groups = new Collection([$ruleGroup]);
|
||||
}
|
||||
|
||||
// create and fire rule engine.
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
|
||||
@@ -34,6 +34,7 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Support\CreditRecalculateService;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\Support\Models\AccountBalanceCalculator;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -70,8 +71,12 @@ class UpdatedGroupEventHandler
|
||||
foreach ($event->transactionGroup->transactionJournals as $journal) {
|
||||
$source = $journal->transactions()->where('amount', '<', '0')->first();
|
||||
$dest = $journal->transactions()->where('amount', '>', '0')->first();
|
||||
$repository->deleteStatisticsForModel($source->account, $journal->date);
|
||||
$repository->deleteStatisticsForModel($dest->account, $journal->date);
|
||||
if (null !== $source) {
|
||||
$repository->deleteStatisticsForModel($source->account, $journal->date);
|
||||
}
|
||||
if (null !== $dest) {
|
||||
$repository->deleteStatisticsForModel($dest->account, $journal->date);
|
||||
}
|
||||
|
||||
$categories = $journal->categories;
|
||||
$tags = $journal->tags;
|
||||
@@ -213,6 +218,9 @@ class UpdatedGroupEventHandler
|
||||
|
||||
private function updateRunningBalance(UpdatedTransactionGroup $event): void
|
||||
{
|
||||
if (false === FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data) {
|
||||
return;
|
||||
}
|
||||
Log::debug(__METHOD__);
|
||||
$group = $event->transactionGroup;
|
||||
foreach ($group->transactionJournals as $journal) {
|
||||
|
||||
@@ -32,7 +32,6 @@ use FireflyIII\Events\Admin\InvitationCreated;
|
||||
use FireflyIII\Events\DetectedNewIPAddress;
|
||||
use FireflyIII\Events\RegisteredUser;
|
||||
use FireflyIII\Events\RequestedNewPassword;
|
||||
use FireflyIII\Events\Security\UserAttemptedLogin;
|
||||
use FireflyIII\Events\Test\UserTestNotificationChannel;
|
||||
use FireflyIII\Events\UserChangedEmail;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
@@ -43,7 +42,6 @@ use FireflyIII\Models\GroupMembership;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\Models\UserRole;
|
||||
use FireflyIII\Notifications\Admin\UserRegistration as AdminRegistrationNotification;
|
||||
use FireflyIII\Notifications\Security\UserFailedLoginAttempt;
|
||||
use FireflyIII\Notifications\Test\UserTestNotificationEmail;
|
||||
use FireflyIII\Notifications\Test\UserTestNotificationPushover;
|
||||
use FireflyIII\Notifications\Test\UserTestNotificationSlack;
|
||||
@@ -51,13 +49,13 @@ use FireflyIII\Notifications\User\UserLogin;
|
||||
use FireflyIII\Notifications\User\UserNewPassword;
|
||||
use FireflyIII\Notifications\User\UserRegistration as UserRegistrationNotification;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Auth\Events\Login;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
|
||||
/**
|
||||
* Class UserEventHandler.
|
||||
@@ -298,27 +296,6 @@ class UserEventHandler
|
||||
}
|
||||
}
|
||||
|
||||
public function sendLoginAttemptNotification(UserAttemptedLogin $event): void
|
||||
{
|
||||
try {
|
||||
Notification::send($event->user, new UserFailedLoginAttempt($event->user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a new password to the user.
|
||||
*/
|
||||
|
||||
@@ -25,7 +25,9 @@ namespace FireflyIII\Handlers\Observer;
|
||||
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionJournalLink;
|
||||
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
@@ -48,13 +50,37 @@ class TransactionJournalObserver
|
||||
}
|
||||
});
|
||||
|
||||
// delete all links:
|
||||
TransactionJournalLink::where('source_id', $transactionJournal->id)->delete();
|
||||
TransactionJournalLink::where('destination_id', $transactionJournal->id)->delete();
|
||||
|
||||
// update events
|
||||
// TODO move to repository
|
||||
$transactionJournal->piggyBankEvents()->update(['transaction_journal_id' => null]);
|
||||
|
||||
// delete all from 'budget_transaction_journal'
|
||||
DB::table('budget_transaction_journal')->where('transaction_journal_id', $transactionJournal->id)->delete();
|
||||
|
||||
// delete all from 'category_transaction_journal'
|
||||
DB::table('category_transaction_journal')->where('transaction_journal_id', $transactionJournal->id)->delete();
|
||||
|
||||
// delete all from 'tag_transaction_journal'
|
||||
DB::table('tag_transaction_journal')->where('transaction_journal_id', $transactionJournal->id)->delete();
|
||||
|
||||
/** @var Attachment $attachment */
|
||||
foreach ($transactionJournal->attachments()->get() as $attachment) {
|
||||
$repository->destroy($attachment);
|
||||
}
|
||||
$transactionJournal->transactionJournalMeta()->delete();
|
||||
$transactionJournal->locations()->delete();
|
||||
$transactionJournal->notes()->delete();
|
||||
$transactionJournal->sourceJournalLinks()->delete();
|
||||
$transactionJournal->destJournalLinks()->delete();
|
||||
$transactionJournal->auditLogEntries()->delete();
|
||||
|
||||
// set all transactions AFTER this one to balance_dirty for recalc.
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -784,14 +784,23 @@ trait MetaCollection
|
||||
$filter = static function (array $object) use ($list): bool {
|
||||
Log::debug(sprintf('Now in setTags(%s) filter', implode(', ', $list)));
|
||||
foreach ($object['transactions'] as $transaction) {
|
||||
$total = count($transaction['tags']);
|
||||
$matched = 0;
|
||||
foreach ($transaction['tags'] as $tag) {
|
||||
Log::debug(sprintf('"%s" versus', strtolower((string) $tag['name'])), $list);
|
||||
if (in_array(strtolower((string) $tag['name']), $list, true)) {
|
||||
Log::debug(sprintf('Transaction has tag "%s" so return true.', $tag['name']));
|
||||
|
||||
return true;
|
||||
++$matched;
|
||||
if (1 === count($list)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count($list) > 1 && $total === $matched && $matched === count($list)) {
|
||||
Log::debug(sprintf('All %d searched tags are present.', $total));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Log::debug('Transaction has no tags from the list, so return false.');
|
||||
|
||||
|
||||
@@ -828,7 +828,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
*/
|
||||
foreach ($this->sorting as $field => $direction) {
|
||||
$func = 'ASC' === $direction ? 'sortBy' : 'sortByDesc';
|
||||
$collection = $collection->{$func}(function (array $product, int $key) use ($field) { // @phpstan-ignore-line
|
||||
$collection = $collection->{$func}(static function (array $product, int $key) use ($field) { // @phpstan-ignore-line
|
||||
// depends on $field:
|
||||
if ('description' === $field) {
|
||||
if (1 === count($product['transactions'])) {
|
||||
|
||||
@@ -66,7 +66,7 @@ class PopupReport implements PopupReportInterface
|
||||
if (null !== $currencyId) {
|
||||
/** @var CurrencyRepositoryInterface $repos */
|
||||
$repos = app(CurrencyRepositoryInterface::class);
|
||||
$currency = $repos->find((int) $currencyId);
|
||||
$currency = $repos->find((int)$currencyId);
|
||||
}
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
@@ -98,7 +98,7 @@ class PopupReport implements PopupReportInterface
|
||||
if (null !== $currencyId) {
|
||||
/** @var CurrencyRepositoryInterface $repos */
|
||||
$repos = app(CurrencyRepositoryInterface::class);
|
||||
$currency = $repos->find((int) $currencyId);
|
||||
$currency = $repos->find((int)$currencyId);
|
||||
}
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
@@ -135,7 +135,7 @@ class PopupReport implements PopupReportInterface
|
||||
if (null !== $currencyId) {
|
||||
/** @var CurrencyRepositoryInterface $repos */
|
||||
$repos = app(CurrencyRepositoryInterface::class);
|
||||
$currency = $repos->find((int) $currencyId);
|
||||
$currency = $repos->find((int)$currencyId);
|
||||
}
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
@@ -174,9 +174,10 @@ class PopupReport implements PopupReportInterface
|
||||
if (null !== $currencyId) {
|
||||
/** @var CurrencyRepositoryInterface $repos */
|
||||
$repos = app(CurrencyRepositoryInterface::class);
|
||||
$currency = $repos->find((int) $currencyId);
|
||||
$currency = $repos->find((int)$currencyId);
|
||||
}
|
||||
|
||||
|
||||
/** @var JournalRepositoryInterface $repository */
|
||||
$repository = app(JournalRepositoryInterface::class);
|
||||
$repository->setUser($account->user);
|
||||
@@ -187,11 +188,11 @@ class PopupReport implements PopupReportInterface
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
|
||||
// set report accounts + the request accounts:
|
||||
// $set = $attributes['accounts'] ?? new Collection;
|
||||
// $set->push($account);
|
||||
// the source account must be in the set.
|
||||
$set = $attributes['accounts'] ?? new Collection();
|
||||
|
||||
$collector->setDestinationAccounts(new Collection()->push($account))
|
||||
->setSourceAccounts($set)
|
||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||
->withAccountInformation()
|
||||
->withBudgetInformation()
|
||||
|
||||
@@ -25,12 +25,13 @@ namespace FireflyIII\Http\Controllers\Auth;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Events\ActuallyLoggedIn;
|
||||
use FireflyIII\Events\Security\UnknownUserAttemptedLogin;
|
||||
use FireflyIII\Events\Security\UserAttemptedLogin;
|
||||
use FireflyIII\Events\Security\System\UnknownUserTriedLogin;
|
||||
use FireflyIII\Events\Security\User\UserFailedLoginAttempt;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Providers\RouteServiceProvider;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
@@ -50,7 +51,6 @@ use Illuminate\Validation\ValidationException;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
use Symfony\Component\HttpFoundation\Response as ResponseAlias;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
|
||||
/**
|
||||
* Class LoginController
|
||||
@@ -138,10 +138,10 @@ class LoginController extends Controller
|
||||
$user = $this->repository->findByEmail($username);
|
||||
if (!$user instanceof User) {
|
||||
// send event to owner.
|
||||
event(new UnknownUserAttemptedLogin($username));
|
||||
event(new UnknownUserTriedLogin($username));
|
||||
}
|
||||
if ($user instanceof User) {
|
||||
event(new UserAttemptedLogin($user));
|
||||
event(new UserFailedLoginAttempt($user));
|
||||
}
|
||||
|
||||
// Copied directly from AuthenticatesUsers, but with logging added:
|
||||
|
||||
@@ -37,6 +37,7 @@ use Illuminate\View\View;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* Class ResetPasswordController
|
||||
@@ -97,7 +98,7 @@ class ResetPasswordController extends Controller
|
||||
// database. Otherwise, we will parse the error and return the response.
|
||||
$response = $this->broker()->reset(
|
||||
$this->credentials($request),
|
||||
function ($user, $password): void {
|
||||
function ($user, #[SensitiveParameter] $password): void {
|
||||
$this->resetPassword($user, $password);
|
||||
}
|
||||
);
|
||||
@@ -123,7 +124,7 @@ class ResetPasswordController extends Controller
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function showResetForm(Request $request, $token = null)
|
||||
public function showResetForm(Request $request, #[SensitiveParameter] $token = null)
|
||||
{
|
||||
if ('web' !== config('firefly.authentication_guard')) {
|
||||
$message = sprintf('Cannot reset password when authenticating over "%s".', config('firefly.authentication_guard'));
|
||||
|
||||
@@ -23,13 +23,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Auth;
|
||||
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Events\Security\MFABackupFewLeft;
|
||||
use FireflyIII\Events\Security\MFABackupNoLeft;
|
||||
use FireflyIII\Events\Security\MFAManyFailedAttempts;
|
||||
use FireflyIII\Events\Security\MFAUsedBackupCode;
|
||||
use FireflyIII\Events\Security\User\UserHasFewMFABackupCodesLeft;
|
||||
use FireflyIII\Events\Security\User\UserHasNoMFABackupCodesLeft;
|
||||
use FireflyIII\Events\Security\User\UserHasUsedBackupCode;
|
||||
use FireflyIII\Events\Security\User\UserKeepsFailingMFA;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Contracts\View\View;
|
||||
@@ -54,7 +54,7 @@ class TwoFactorController extends Controller
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$siteOwner = config('firefly.site_owner');
|
||||
$title = (string) trans('firefly.two_factor_forgot_title');
|
||||
$title = (string)trans('firefly.two_factor_forgot_title');
|
||||
|
||||
return view('auth.lost-two-factor', ['user' => $user, 'siteOwner' => $siteOwner, 'title' => $title]);
|
||||
}
|
||||
@@ -67,7 +67,7 @@ class TwoFactorController extends Controller
|
||||
{
|
||||
/** @var array $mfaHistory */
|
||||
$mfaHistory = Preferences::get('mfa_history', [])->data;
|
||||
$mfaCode = (string) $request->get('one_time_password');
|
||||
$mfaCode = (string)$request->get('one_time_password');
|
||||
|
||||
// is in history? then refuse to use it.
|
||||
if ($this->inMFAHistory($mfaCode, $mfaHistory)) {
|
||||
@@ -88,7 +88,7 @@ class TwoFactorController extends Controller
|
||||
if (3 === $counter || 10 === $counter) {
|
||||
// do not reset MFA failure counter, but DO send a warning to the user.
|
||||
Log::channel('audit')->info(sprintf('User "%s" has had %d failed MFA attempts.', $user->email, $counter));
|
||||
event(new MFAManyFailedAttempts($user, $counter));
|
||||
event(new UserKeepsFailingMFA($user, $counter));
|
||||
}
|
||||
unset($user);
|
||||
}
|
||||
@@ -116,7 +116,7 @@ class TwoFactorController extends Controller
|
||||
// send user notification.
|
||||
$user = auth()->user();
|
||||
Log::channel('audit')->info(sprintf('User "%s" has used a backup code.', $user->email));
|
||||
event(new MFAUsedBackupCode($user));
|
||||
event(new UserHasUsedBackupCode($user));
|
||||
|
||||
return redirect(route('home'));
|
||||
}
|
||||
@@ -168,7 +168,7 @@ class TwoFactorController extends Controller
|
||||
|
||||
private function addToMFAFailureCounter(): void
|
||||
{
|
||||
$preference = (int) Preferences::get('mfa_failure_count', 0)->data;
|
||||
$preference = (int)Preferences::get('mfa_failure_count', 0)->data;
|
||||
++$preference;
|
||||
Log::channel('audit')->info(sprintf('MFA failure count is set to %d.', $preference));
|
||||
Preferences::set('mfa_failure_count', $preference);
|
||||
@@ -176,7 +176,7 @@ class TwoFactorController extends Controller
|
||||
|
||||
private function getMFAFailureCounter(): int
|
||||
{
|
||||
$value = (int) Preferences::get('mfa_failure_count', 0)->data;
|
||||
$value = (int)Preferences::get('mfa_failure_count', 0)->data;
|
||||
Log::channel('audit')->info(sprintf('MFA failure count is %d.', $value));
|
||||
|
||||
return $value;
|
||||
@@ -230,13 +230,13 @@ class TwoFactorController extends Controller
|
||||
if (count($newList) <= 3 && count($newList) > 0) {
|
||||
$user = auth()->user();
|
||||
Log::channel('audit')->info(sprintf('User "%s" has used a backup code. They have %d backup codes left.', $user->email, count($newList)));
|
||||
event(new MFABackupFewLeft($user, count($newList)));
|
||||
event(new UserHasFewMFABackupCodesLeft($user, count($newList)));
|
||||
}
|
||||
// if the list is empty, send notification
|
||||
if (0 === count($newList)) {
|
||||
$user = auth()->user();
|
||||
Log::channel('audit')->info(sprintf('User "%s" has used their last backup code.', $user->email));
|
||||
event(new MFABackupNoLeft($user));
|
||||
event(new UserHasNoMFABackupCodesLeft($user));
|
||||
}
|
||||
|
||||
Preferences::set('mfa_recovery', $newList);
|
||||
|
||||
@@ -24,12 +24,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Bill;
|
||||
|
||||
use FireflyIII\Support\Facades\Navigation;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Repositories\ObjectGroup\OrganisesObjectGroups;
|
||||
use FireflyIII\Support\Facades\Navigation;
|
||||
use FireflyIII\Support\JsonApi\Enrichments\SubscriptionEnrichment;
|
||||
use FireflyIII\Transformers\BillTransformer;
|
||||
use FireflyIII\User;
|
||||
@@ -38,6 +37,7 @@ use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
|
||||
/**
|
||||
@@ -74,6 +74,7 @@ class IndexController extends Controller
|
||||
{
|
||||
$this->cleanupObjectGroups();
|
||||
$this->repository->correctOrder();
|
||||
$this->repository->correctTransfers();
|
||||
$start = session('start');
|
||||
$end = session('end');
|
||||
$collection = $this->repository->getBills();
|
||||
@@ -81,22 +82,18 @@ class IndexController extends Controller
|
||||
|
||||
|
||||
$parameters = new ParameterBag();
|
||||
// sub one day from temp start so the last paid date is one day before it should be.
|
||||
$tempStart = clone $start;
|
||||
// 2023-06-23 do not sub one day from temp start, fix is in BillTransformer::payDates instead
|
||||
// $tempStart->subDay();
|
||||
|
||||
// enrich
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
$enrichment = new SubscriptionEnrichment();
|
||||
$enrichment->setUser($admin);
|
||||
$enrichment->setStart($tempStart);
|
||||
$enrichment->setStart($start->clone());
|
||||
$enrichment->setEnd($end);
|
||||
$collection = $enrichment->enrich($collection);
|
||||
|
||||
|
||||
$parameters->set('start', $tempStart);
|
||||
$parameters->set('start', $start->clone());
|
||||
$parameters->set('end', $end);
|
||||
$parameters->set('convertToPrimary', $this->convertToPrimary);
|
||||
$parameters->set('primaryCurrency', $this->primaryCurrency);
|
||||
@@ -151,16 +148,28 @@ class IndexController extends Controller
|
||||
|
||||
private function getSums(array $bills): array
|
||||
{
|
||||
Log::debug(sprintf('now in getSums(count:%d)', count($bills)));
|
||||
$sums = [];
|
||||
$range = Navigation::getViewRange(true);
|
||||
|
||||
/** @var array $group */
|
||||
foreach ($bills as $groupOrder => $group) {
|
||||
Log::debug(sprintf('Summing up group "%s"', $group['object_group_title']));
|
||||
if (0 === count($group['bills'])) {
|
||||
Log::debug('Group has no subscriptions, continue');
|
||||
|
||||
continue;
|
||||
}
|
||||
Log::debug(sprintf('Group has %d subscription(s)', count($group['bills'])));
|
||||
|
||||
/** @var array $bill */
|
||||
foreach ($group['bills'] as $bill) {
|
||||
if (false === $bill['active']) {
|
||||
Log::debug(sprintf('Skip subscription #%d, inactive.', $bill['id']));
|
||||
|
||||
continue;
|
||||
}
|
||||
Log::debug(sprintf('Now at subscription #%d.', $bill['id']));
|
||||
|
||||
$currencyId = $bill['currency_id'];
|
||||
$sums[$groupOrder][$currencyId] ??= [
|
||||
@@ -174,26 +183,32 @@ class IndexController extends Controller
|
||||
'period' => $range,
|
||||
'per_period' => '0',
|
||||
];
|
||||
Log::debug(sprintf('Start with avg:%s, total_left_to_pay:%s, per_period:%s', $sums[$groupOrder][$currencyId]['avg'], $sums[$groupOrder][$currencyId]['total_left_to_pay'], $sums[$groupOrder][$currencyId]['per_period']));
|
||||
|
||||
// only fill in avg when bill is active.
|
||||
if (null !== $bill['next_expected_match']) {
|
||||
$avg = bcdiv(bcadd((string)$bill['amount_min'], (string)$bill['amount_max']), '2');
|
||||
$avg = bcmul($avg, (string)count($bill['pay_dates']));
|
||||
$sums[$groupOrder][$currencyId]['avg'] = bcadd($sums[$groupOrder][$currencyId]['avg'], $avg);
|
||||
}
|
||||
// only fill in total_left_to_pay when bill is not yet paid.
|
||||
if (count($bill['paid_dates']) < count($bill['pay_dates'])) {
|
||||
$count = count($bill['pay_dates']) - count($bill['paid_dates']);
|
||||
if ($count > 0) {
|
||||
$avg = bcdiv(bcadd((string)$bill['amount_min'], (string)$bill['amount_max']), '2');
|
||||
$avg = bcmul($avg, (string)$count);
|
||||
$sums[$groupOrder][$currencyId]['total_left_to_pay'] = bcadd($sums[$groupOrder][$currencyId]['total_left_to_pay'], $avg);
|
||||
Log::debug(sprintf('next expected match is "%s", avg is now %s', $bill['next_expected_match'], $sums[$groupOrder][$currencyId]['avg']));
|
||||
|
||||
// only fill in total_left_to_pay when bill is not yet paid.
|
||||
// #11474 and when it is expected in the current period
|
||||
if (count($bill['paid_dates']) < count($bill['pay_dates'])) {
|
||||
$count = count($bill['pay_dates']) - count($bill['paid_dates']);
|
||||
if ($count > 0) {
|
||||
$avg = bcdiv(bcadd((string)$bill['amount_min'], (string)$bill['amount_max']), '2');
|
||||
$avg = bcmul($avg, (string)$count);
|
||||
$sums[$groupOrder][$currencyId]['total_left_to_pay'] = bcadd($sums[$groupOrder][$currencyId]['total_left_to_pay'], $avg);
|
||||
Log::debug(sprintf('Bill has %d dates that need payment, total left to pay is now %s', $count, $sums[$groupOrder][$currencyId]['total_left_to_pay']), $bill['pay_dates']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$perPeriod = $this->amountPerPeriod($bill, $range);
|
||||
Log::debug(sprintf('Add amount %s to per_period', $perPeriod));
|
||||
// fill in per period regardless:
|
||||
$sums[$groupOrder][$currencyId]['per_period'] = bcadd($sums[$groupOrder][$currencyId]['per_period'], $this->amountPerPeriod($bill, $range));
|
||||
$sums[$groupOrder][$currencyId]['per_period'] = bcadd($sums[$groupOrder][$currencyId]['per_period'], $perPeriod);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ use FireflyIII\Support\Facades\Navigation;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Support\JsonApi\Enrichments\SubscriptionEnrichment;
|
||||
@@ -122,6 +121,7 @@ class ShowController extends Controller
|
||||
*/
|
||||
public function show(Request $request, Bill $bill): Factory|\Illuminate\Contracts\View\View
|
||||
{
|
||||
$this->repository->correctTransfers();
|
||||
// add info about rules:
|
||||
$rules = $this->repository->getRulesForBill($bill);
|
||||
$subTitle = $bill->name;
|
||||
@@ -184,7 +184,7 @@ class ShowController extends Controller
|
||||
/** @var AttachmentTransformer $transformer */
|
||||
$transformer = app(AttachmentTransformer::class);
|
||||
$attachments = $collection->each(
|
||||
static fn (Attachment $attachment) => $transformer->transform($attachment)
|
||||
$transformer->transform(...)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,9 @@ class DebugController extends Controller
|
||||
|
||||
// also do some recalculations.
|
||||
Artisan::call('correction:recalculates-liabilities');
|
||||
AccountBalanceCalculator::recalculateAll(false);
|
||||
if (true === FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data) {
|
||||
AccountBalanceCalculator::recalculateAll(false);
|
||||
}
|
||||
|
||||
try {
|
||||
Artisan::call('twig:clean');
|
||||
|
||||
@@ -41,7 +41,7 @@ class IndexController extends Controller
|
||||
|
||||
// translations:
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
static function ($request, $next) {
|
||||
app('view')->share('mainTitleIcon', 'fa-exchange');
|
||||
app('view')->share('title', (string) trans('firefly.header_exchange_rates'));
|
||||
|
||||
|
||||
@@ -212,6 +212,13 @@ class ReconcileController extends Controller
|
||||
|
||||
$startBalance = Steam::accountsBalancesOptimized(new Collection()->push($account), $startDate)[$account->id];
|
||||
$endBalance = Steam::accountsBalancesOptimized(new Collection()->push($account), $end)[$account->id];
|
||||
// round balances.
|
||||
foreach ($startBalance as $key => $value) {
|
||||
$startBalance[$key] = Steam::bcround($value, $currency->decimal_places);
|
||||
}
|
||||
foreach ($endBalance as $key => $value) {
|
||||
$endBalance[$key] = Steam::bcround($value, $currency->decimal_places);
|
||||
}
|
||||
|
||||
|
||||
// get the transactions
|
||||
|
||||
@@ -198,12 +198,14 @@ class PreferencesController extends Controller
|
||||
*/
|
||||
public function postIndex(PreferencesRequest $request): Redirector|RedirectResponse
|
||||
{
|
||||
Log::debug('postIndex for preferences.');
|
||||
// front page accounts
|
||||
$frontpageAccounts = [];
|
||||
if (is_array($request->get('frontpageAccounts')) && count($request->get('frontpageAccounts')) > 0) {
|
||||
foreach ($request->get('frontpageAccounts') as $id) {
|
||||
$frontpageAccounts[] = (int)$id;
|
||||
}
|
||||
Log::debug('Update frontpageAccounts', $frontpageAccounts);
|
||||
Preferences::set('frontpageAccounts', $frontpageAccounts);
|
||||
}
|
||||
|
||||
@@ -212,14 +214,17 @@ class PreferencesController extends Controller
|
||||
foreach (config('notifications.notifications.user') as $key => $info) {
|
||||
$key = sprintf('notification_%s', $key);
|
||||
if (array_key_exists($key, $all)) {
|
||||
Log::debug(sprintf('update notification to true: %s', $key));
|
||||
Preferences::set($key, true);
|
||||
}
|
||||
if (!array_key_exists($key, $all)) {
|
||||
Log::debug(sprintf('update notification to false: %s', $key));
|
||||
Preferences::set($key, false);
|
||||
}
|
||||
}
|
||||
|
||||
// view range:
|
||||
Log::debug(sprintf('Let viewRange to "%s"', $request->get('viewRange')));
|
||||
Preferences::set('viewRange', $request->get('viewRange'));
|
||||
// forget session values:
|
||||
session()->forget('start');
|
||||
@@ -319,6 +324,7 @@ class PreferencesController extends Controller
|
||||
// save and continue
|
||||
session()->flash('success', (string)trans('firefly.saved_preferences'));
|
||||
Preferences::mark();
|
||||
Log::debug('Done saving settings.');
|
||||
|
||||
return redirect(route('preferences.index'));
|
||||
}
|
||||
|
||||
@@ -24,16 +24,16 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Profile;
|
||||
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Events\Security\DisabledMFA;
|
||||
use FireflyIII\Events\Security\EnabledMFA;
|
||||
use FireflyIII\Events\Security\MFANewBackupCodes;
|
||||
use FireflyIII\Events\Security\User\UserHasDisabledMFA;
|
||||
use FireflyIII\Events\Security\User\UserHasEnabledMFA;
|
||||
use FireflyIII\Events\Security\User\UserHasGeneratedNewBackupCodes;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Middleware\IsDemoUser;
|
||||
use FireflyIII\Http\Requests\ExistingTokenFormRequest;
|
||||
use FireflyIII\Http\Requests\TokenFormRequest;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
@@ -71,7 +71,7 @@ class MfaController extends Controller
|
||||
|
||||
$this->middleware(
|
||||
static function ($request, $next) {
|
||||
app('view')->share('title', (string) trans('firefly.profile'));
|
||||
app('view')->share('title', (string)trans('firefly.profile'));
|
||||
app('view')->share('mainTitleIcon', 'fa-user');
|
||||
|
||||
return $next($request);
|
||||
@@ -131,7 +131,7 @@ class MfaController extends Controller
|
||||
// send user notification.
|
||||
$user = auth()->user();
|
||||
Log::channel('audit')->info(sprintf('User "%s" has generated new backup codes.', $user->email));
|
||||
event(new MFANewBackupCodes($user));
|
||||
event(new UserHasGeneratedNewBackupCodes($user));
|
||||
|
||||
return view('profile.mfa.backup-codes-post')->with(['codes' => $codes]);
|
||||
|
||||
@@ -150,7 +150,7 @@ class MfaController extends Controller
|
||||
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
$subTitle = (string) trans('firefly.mfa_index_title');
|
||||
$subTitle = (string)trans('firefly.mfa_index_title');
|
||||
$subTitleIcon = 'fa-calculator';
|
||||
|
||||
return view('profile.mfa.disable-mfa')->with(['subTitle' => $subTitle, 'subTitleIcon' => $subTitleIcon, 'enabledMFA' => $enabledMFA]);
|
||||
@@ -178,8 +178,8 @@ class MfaController extends Controller
|
||||
$repository->setMFACode($user, null);
|
||||
Preferences::mark();
|
||||
|
||||
session()->flash('success', (string) trans('firefly.pref_two_factor_auth_disabled'));
|
||||
session()->flash('info', (string) trans('firefly.pref_two_factor_auth_remove_it'));
|
||||
session()->flash('success', (string)trans('firefly.pref_two_factor_auth_disabled'));
|
||||
session()->flash('info', (string)trans('firefly.pref_two_factor_auth_remove_it'));
|
||||
|
||||
// also logout current 2FA tokens.
|
||||
$cookieName = config('google2fa.cookie_name', 'google2fa_token');
|
||||
@@ -187,7 +187,7 @@ class MfaController extends Controller
|
||||
|
||||
// send user notification.
|
||||
Log::channel('audit')->info(sprintf('User "%s" has disabled MFA', $user->email));
|
||||
event(new DisabledMFA($user));
|
||||
event(new UserHasDisabledMFA($user));
|
||||
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
@@ -210,7 +210,7 @@ class MfaController extends Controller
|
||||
// If FF3 already has a secret, just set the two-factor auth enabled to 1,
|
||||
// and let the user continue with the existing secret.
|
||||
if ($enabledMFA) {
|
||||
session()->flash('info', (string) trans('firefly.2fa_already_enabled'));
|
||||
session()->flash('info', (string)trans('firefly.2fa_already_enabled'));
|
||||
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
@@ -257,13 +257,13 @@ class MfaController extends Controller
|
||||
if (is_array($secret)) {
|
||||
$secret = null;
|
||||
}
|
||||
$secret = (string) $secret;
|
||||
$secret = (string)$secret;
|
||||
|
||||
$repository->setMFACode($user, $secret);
|
||||
|
||||
Preferences::delete('temp-mfa-secret');
|
||||
|
||||
session()->flash('success', (string) trans('firefly.saved_preferences'));
|
||||
session()->flash('success', (string)trans('firefly.saved_preferences'));
|
||||
Preferences::mark();
|
||||
|
||||
// also save the code so replay attack is prevented.
|
||||
@@ -280,7 +280,7 @@ class MfaController extends Controller
|
||||
|
||||
// send user notification.
|
||||
Log::channel('audit')->info(sprintf('User "%s" has enabled MFA', $user->email));
|
||||
event(new EnabledMFA($user));
|
||||
event(new UserHasEnabledMFA($user));
|
||||
|
||||
return redirect(route('profile.mfa.backup-codes'));
|
||||
}
|
||||
@@ -335,7 +335,7 @@ class MfaController extends Controller
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
|
||||
$subTitle = (string) trans('firefly.mfa_index_title');
|
||||
$subTitle = (string)trans('firefly.mfa_index_title');
|
||||
$subTitleIcon = 'fa-calculator';
|
||||
$enabledMFA = null !== auth()->user()->mfa_secret;
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ use Illuminate\View\View;
|
||||
use Laravel\Passport\ClientRepository;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* Class ProfileController.
|
||||
@@ -91,7 +92,7 @@ class ProfileController extends Controller
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function confirmEmailChange(UserRepositoryInterface $repository, string $token): Redirector|RedirectResponse
|
||||
public function confirmEmailChange(UserRepositoryInterface $repository, #[SensitiveParameter] string $token): Redirector|RedirectResponse
|
||||
{
|
||||
if (!$this->internalAuth) {
|
||||
throw new FireflyException(trans('firefly.external_user_mgt_disabled'));
|
||||
@@ -388,7 +389,7 @@ class ProfileController extends Controller
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function undoEmailChange(UserRepositoryInterface $repository, string $token, string $hash): Redirector|RedirectResponse
|
||||
public function undoEmailChange(UserRepositoryInterface $repository, #[SensitiveParameter] string $token, string $hash): Redirector|RedirectResponse
|
||||
{
|
||||
if (!$this->internalAuth) {
|
||||
throw new FireflyException(trans('firefly.external_user_mgt_disabled'));
|
||||
|
||||
@@ -35,6 +35,7 @@ use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\View\View;
|
||||
|
||||
@@ -70,13 +71,20 @@ class ExecutionController extends Controller
|
||||
*/
|
||||
public function execute(SelectTransactionsRequest $request, RuleGroup $ruleGroup): RedirectResponse
|
||||
{
|
||||
Log::debug(sprintf('You have selected rule group #%d', $ruleGroup->id));
|
||||
// Get parameters specified by the user
|
||||
$accounts = $request->get('accounts');
|
||||
$set = $this->repository->getAccountsById($accounts);
|
||||
$set = new Collection();
|
||||
if (is_array($accounts)) {
|
||||
$set = $this->repository->getAccountsById($accounts);
|
||||
}
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setAccounts($set);
|
||||
if (count($set) > 0) {
|
||||
$collector->setAccounts($set);
|
||||
}
|
||||
|
||||
// add date operators.
|
||||
if (null !== $request->get('start')) {
|
||||
$startDate = new Carbon($request->get('start'));
|
||||
@@ -96,7 +104,7 @@ class ExecutionController extends Controller
|
||||
/** @var TransactionGroup $group */
|
||||
foreach ($groups as $group) {
|
||||
Log::debug(sprintf('Processing group #%d.', $group->id));
|
||||
event(new TriggeredStoredTransactionGroup($group));
|
||||
event(new TriggeredStoredTransactionGroup($group, $ruleGroup));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ class AcceptHeaders
|
||||
// some routes are exempt from this.
|
||||
$exempt = [
|
||||
'api.v1.data.bulk.transactions',
|
||||
'api.v1.attachments.upload',
|
||||
];
|
||||
|
||||
if (('POST' === $method || 'PUT' === $method) && !$request->hasHeader('Content-Type') && !in_array($request->route()->getName(), $exempt, true)) {
|
||||
|
||||
@@ -157,7 +157,7 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
private function filterRecurrences(Collection $recurrences): Collection
|
||||
{
|
||||
return $recurrences->filter(
|
||||
fn (Recurrence $recurrence): bool => $this->validRecurrence($recurrence)
|
||||
$this->validRecurrence(...)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Jobs;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Events\Model\Bill\WarnUserAboutBill;
|
||||
use FireflyIII\Events\Model\Bill\WarnUserAboutOverdueSubscriptions;
|
||||
use FireflyIII\Events\Model\Subscription\SubscriptionNeedsExtensionOrRenewal;
|
||||
use FireflyIII\Events\Model\Subscription\SubscriptionsAreOverdueForPayment;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Support\Facades\Navigation;
|
||||
use FireflyIII\Support\JsonApi\Enrichments\SubscriptionEnrichment;
|
||||
@@ -143,7 +143,7 @@ class WarnAboutBills implements ShouldQueue
|
||||
{
|
||||
$diff = $this->getDiff($bill, $field);
|
||||
Log::debug('Will now send warning!');
|
||||
event(new WarnUserAboutBill($bill, $field, $diff));
|
||||
event(new SubscriptionNeedsExtensionOrRenewal($bill, $field, $diff));
|
||||
}
|
||||
|
||||
public function setDate(Carbon $date): void
|
||||
@@ -191,14 +191,14 @@ class WarnAboutBills implements ShouldQueue
|
||||
$diff = $earliest->diffInDays($this->date);
|
||||
Log::debug(sprintf('Difference in days is %s', $diff));
|
||||
|
||||
return $diff >= 2;
|
||||
return $diff >= 6; // FIXME hard coded value.
|
||||
}
|
||||
|
||||
private function sendOverdueAlerts(User $user, array $overdue): void
|
||||
{
|
||||
if (count($overdue) > 0) {
|
||||
Log::debug(sprintf('Will now send warning about overdue bill for user #%d.', $user->id));
|
||||
event(new WarnUserAboutOverdueSubscriptions($user, $overdue));
|
||||
Log::debug(sprintf('Will now send warning about overdue subscription(s) for user #%d.', $user->id));
|
||||
event(new SubscriptionsAreOverdueForPayment($user, $overdue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* PiggyBankEventHandler.php
|
||||
* Copyright (c) 2023 james@firefly-iii.org
|
||||
* CreatesPiggyBankEventForChangedAmount.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -20,43 +21,17 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace FireflyIII\Listeners\Model\PiggyBank;
|
||||
|
||||
namespace FireflyIII\Handlers\Events\Model;
|
||||
|
||||
use FireflyIII\Events\Model\PiggyBank\ChangedAmount;
|
||||
use FireflyIII\Events\Model\PiggyBank\ChangedName;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Events\Model\PiggyBank\PiggyBankAmountIsChanged;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\RuleAction;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class PiggyBankEventHandler
|
||||
*/
|
||||
class PiggyBankEventHandler
|
||||
class CreatesPiggyBankEventForChangedAmount implements ShouldQueue
|
||||
{
|
||||
public function changedPiggyBankName(ChangedName $event): void
|
||||
{
|
||||
// loop all accounts, collect all user's rules.
|
||||
/** @var Account $account */
|
||||
foreach ($event->piggyBank->accounts as $account) {
|
||||
/** @var Rule $rule */
|
||||
foreach ($account->user->rules as $rule) {
|
||||
/** @var RuleAction $ruleAction */
|
||||
foreach ($rule->ruleActions()->where('action_type', 'update_piggy')->get() as $ruleAction) {
|
||||
if ($event->oldName === $ruleAction->action_value) {
|
||||
$ruleAction->action_value = $event->newName;
|
||||
$ruleAction->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function changePiggyAmount(ChangedAmount $event): void
|
||||
public function handle(PiggyBankAmountIsChanged $event): void
|
||||
{
|
||||
// find journal if group is present.
|
||||
$journal = $event->transactionJournal;
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* UpdatesRulesForChangedPiggyBankName.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\PiggyBank;
|
||||
|
||||
use FireflyIII\Events\Model\PiggyBank\PiggyBankNameIsChanged;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\RuleAction;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class UpdatesRulesForChangedPiggyBankName implements ShouldQueue
|
||||
{
|
||||
public function handle(PiggyBankNameIsChanged $event): void
|
||||
{
|
||||
// loop all accounts, collect all user's rules.
|
||||
/** @var Account $account */
|
||||
foreach ($event->piggyBank->accounts as $account) {
|
||||
/** @var Rule $rule */
|
||||
foreach ($account->user->rules as $rule) {
|
||||
/** @var RuleAction $ruleAction */
|
||||
foreach ($rule->ruleActions()->where('action_type', 'update_piggy')->get() as $ruleAction) {
|
||||
if ($event->oldName === $ruleAction->action_value) {
|
||||
$ruleAction->action_value = $event->newName;
|
||||
$ruleAction->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* RuleHandler.php
|
||||
* Copyright (c) 2023 james@firefly-iii.org
|
||||
* NotifiesUserAboutFailedRuleAction.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -20,24 +21,20 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Handlers\Events\Model;
|
||||
namespace FireflyIII\Listeners\Model\Rule;
|
||||
|
||||
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
|
||||
use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject;
|
||||
use FireflyIII\Notifications\User\RuleActionFailed;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
/**
|
||||
* Class RuleHandler
|
||||
*/
|
||||
class RuleHandler
|
||||
class NotifiesUserAboutFailedRuleAction implements ShouldQueue
|
||||
{
|
||||
public function ruleActionFailedOnArray(RuleActionFailedOnArray $event): void
|
||||
public function handle(RuleActionFailedOnArray|RuleActionFailedOnObject $event): void
|
||||
{
|
||||
$ruleAction = $event->ruleAction;
|
||||
$rule = $ruleAction->rule;
|
||||
@@ -52,9 +49,12 @@ class RuleHandler
|
||||
$error = $event->error;
|
||||
$user = $ruleAction->rule->user;
|
||||
|
||||
$mainMessage = trans('rules.main_message', ['rule' => $rule->title, 'action' => $ruleAction->action_type, 'group' => $journal['transaction_group_id'], 'error' => $error]);
|
||||
$groupTitle = $journal['description'] ?? '';
|
||||
$groupLink = route('transactions.show', [$journal['transaction_group_id']]);
|
||||
$groupId = is_array($journal) ? $journal['transaction_group_id'] : $journal->transaction_group_id;
|
||||
$groupTitle = is_array($journal) ? ($journal['description'] ?? '') : ($journal->description ?? '');
|
||||
|
||||
|
||||
$mainMessage = trans('rules.main_message', ['rule' => $rule->title, 'action' => $ruleAction->action_type, 'group' => $groupId, 'error' => $error]);
|
||||
$groupLink = route('transactions.show', [$groupId]);
|
||||
$ruleTitle = $rule->title;
|
||||
$ruleLink = route('rules.edit', [$rule->id]);
|
||||
$params = [$mainMessage, $groupTitle, $groupLink, $ruleTitle, $ruleLink];
|
||||
@@ -65,33 +65,4 @@ class RuleHandler
|
||||
Log::error(sprintf('[a] Error sending notification that the rule action failed: %s', $e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
public function ruleActionFailedOnObject(RuleActionFailedOnObject $event): void
|
||||
{
|
||||
$ruleAction = $event->ruleAction;
|
||||
$rule = $ruleAction->rule;
|
||||
|
||||
/** @var bool $preference */
|
||||
$preference = Preferences::getForUser($rule->user, 'notification_rule_action_failures', true)->data;
|
||||
if (false === $preference) {
|
||||
return;
|
||||
}
|
||||
Log::debug('Now in ruleActionFailedOnObject');
|
||||
$journal = $event->journal;
|
||||
$error = $event->error;
|
||||
$user = $ruleAction->rule->user;
|
||||
|
||||
$mainMessage = trans('rules.main_message', ['rule' => $rule->title, 'action' => $ruleAction->action_type, 'group' => $journal->transaction_group_id, 'error' => $error]);
|
||||
$groupTitle = $journal->description ?? '';
|
||||
$groupLink = route('transactions.show', [$journal->transaction_group_id]);
|
||||
$ruleTitle = $rule->title;
|
||||
$ruleLink = route('rules.edit', [$rule->id]);
|
||||
$params = [$mainMessage, $groupTitle, $groupLink, $ruleTitle, $ruleLink];
|
||||
|
||||
try {
|
||||
Notification::send($user, new RuleActionFailed($params));
|
||||
} catch (ClientException $e) {
|
||||
Log::error(sprintf('[b] Error sending notification that the rule action failed: %s', $e->getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* NotifiesAboutExtensionOrRenewal.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\Subscription;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Model\Subscription\SubscriptionNeedsExtensionOrRenewal;
|
||||
use FireflyIII\Notifications\User\BillReminder;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesAboutExtensionOrRenewal implements ShouldQueue
|
||||
{
|
||||
public function handle(SubscriptionNeedsExtensionOrRenewal $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
$subscription = $event->subscription;
|
||||
|
||||
/** @var bool $preference */
|
||||
$preference = Preferences::getForUser($subscription->user, 'notification_bill_reminder', true)->data;
|
||||
|
||||
if (true === $preference) {
|
||||
Log::debug('Subscription reminder is true!');
|
||||
|
||||
try {
|
||||
Notification::send($subscription->user, new BillReminder($subscription, $event->field, $event->diff));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
Log::debug('User has disabled subscription reminders.');
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* BillEventHandler.php
|
||||
* Copyright (c) 2022 james@firefly-iii.org
|
||||
* NotifiesAboutOverdueSubscription.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -20,35 +21,27 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
namespace FireflyIII\Listeners\Model\Subscription;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Model\Bill\WarnUserAboutBill;
|
||||
use FireflyIII\Events\Model\Bill\WarnUserAboutOverdueSubscriptions;
|
||||
use FireflyIII\Events\Model\Subscription\SubscriptionsAreOverdueForPayment;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Notifications\User\BillReminder;
|
||||
use FireflyIII\Notifications\User\SubscriptionsOverdueReminder;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
use function Safe\json_encode;
|
||||
|
||||
/**
|
||||
* Class BillEventHandler
|
||||
*/
|
||||
class BillEventHandler
|
||||
class NotifiesAboutOverdueSubscriptions implements ShouldQueue
|
||||
{
|
||||
public function warnAboutOverdueSubscriptions(WarnUserAboutOverdueSubscriptions $event): void
|
||||
public function handle(SubscriptionsAreOverdueForPayment $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
// make sure user does not get the warning twice.
|
||||
$overdue = $event->overdue;
|
||||
$user = $event->user;
|
||||
$toBeWarned = [];
|
||||
Log::debug(sprintf('%d bills to warn about.', count($overdue)));
|
||||
Log::debug(sprintf('%d subscriptions to warn about.', count($overdue)));
|
||||
foreach ($overdue as $item) {
|
||||
/** @var Bill $bill */
|
||||
$bill = $item['bill'];
|
||||
@@ -62,12 +55,12 @@ class BillEventHandler
|
||||
$toBeWarned[] = $item;
|
||||
}
|
||||
unset($bill);
|
||||
Log::debug(sprintf('Now %d bills to warn about.', count($toBeWarned)));
|
||||
Log::debug(sprintf('Now %d subscription(s) to warn about.', count($toBeWarned)));
|
||||
|
||||
/** @var bool $sendNotification */
|
||||
$sendNotification = Preferences::getForUser($user, 'notification_bill_reminder', true)->data;
|
||||
if (false === $sendNotification) {
|
||||
Log::debug('User has disabled bill reminders.');
|
||||
Log::debug('User has disabled subscription reminders.');
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -105,39 +98,4 @@ class BillEventHandler
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function warnAboutBill(WarnUserAboutBill $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$bill = $event->bill;
|
||||
|
||||
/** @var bool $preference */
|
||||
$preference = Preferences::getForUser($bill->user, 'notification_bill_reminder', true)->data;
|
||||
|
||||
if (true === $preference) {
|
||||
Log::debug('Bill reminder is true!');
|
||||
|
||||
try {
|
||||
Notification::send($bill->user, new BillReminder($bill, $event->field, $event->diff));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
Log::debug('User has disabled bill reminders.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* NotifiesOwnerAboutUnknownUser.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\Security\System;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\System\UnknownUserTriedLogin;
|
||||
use FireflyIII\Notifications\Admin\UnknownUserLoginAttempt;
|
||||
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesOwnerAboutUnknownUser
|
||||
{
|
||||
public function handle(UnknownUserTriedLogin $event): void
|
||||
{
|
||||
try {
|
||||
$owner = new OwnerNotifiable();
|
||||
Notification::send($owner, new UnknownUserLoginAttempt($event->address));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
59
app/Listeners/Security/User/NotifiesUserAboutDisabledMFA.php
Normal file
59
app/Listeners/Security/User/NotifiesUserAboutDisabledMFA.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* NotifiesUserAboutDisabledMFA.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\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserHasDisabledMFA;
|
||||
use FireflyIII\Notifications\Security\DisabledMFANotification;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutDisabledMFA implements ShouldQueue
|
||||
{
|
||||
public function handle(UserHasDisabledMFA $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
|
||||
try {
|
||||
Notification::send($user, new DisabledMFANotification($user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
58
app/Listeners/Security/User/NotifiesUserAboutEnabledMFA.php
Normal file
58
app/Listeners/Security/User/NotifiesUserAboutEnabledMFA.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* NotifiesUserAboutEnabledMFA.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\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserHasEnabledMFA;
|
||||
use FireflyIII\Notifications\Security\EnabledMFANotification;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutEnabledMFA
|
||||
{
|
||||
public function handle(UserHasEnabledMFA $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
|
||||
try {
|
||||
Notification::send($user, new EnabledMFANotification($user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
53
app/Listeners/Security/User/NotifiesUserAboutFailedLogin.php
Normal file
53
app/Listeners/Security/User/NotifiesUserAboutFailedLogin.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* NotifiesUserAboutFailedLogin.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\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserFailedLoginAttempt;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutFailedLogin
|
||||
{
|
||||
public function handle(UserFailedLoginAttempt $event): void
|
||||
{
|
||||
try {
|
||||
Notification::send($event->user, new \FireflyIII\Notifications\Security\UserFailedLoginAttempt($event->user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* NotifiesUserAboutFewCodesLeft.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\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserHasFewMFABackupCodesLeft;
|
||||
use FireflyIII\Notifications\Security\MFABackupFewLeftNotification;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutFewCodesLeft
|
||||
{
|
||||
public function handle(UserHasFewMFABackupCodesLeft $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
$count = $event->count;
|
||||
|
||||
try {
|
||||
Notification::send($user, new MFABackupFewLeftNotification($user, $count));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* NotifiesUserAboutNewBackupCodes.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\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserHasGeneratedNewBackupCodes;
|
||||
use FireflyIII\Notifications\Security\NewBackupCodesNotification;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutNewBackupCodes
|
||||
{
|
||||
public function handle(UserHasGeneratedNewBackupCodes $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
|
||||
try {
|
||||
Notification::send($user, new NewBackupCodesNotification($user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
58
app/Listeners/Security/User/NotifiesUserAboutNoCodesLeft.php
Normal file
58
app/Listeners/Security/User/NotifiesUserAboutNoCodesLeft.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* NotifiesUserAboutNoCodesLeft.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\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserHasNoMFABackupCodesLeft;
|
||||
use FireflyIII\Notifications\Security\MFABackupNoLeftNotification;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutNoCodesLeft
|
||||
{
|
||||
public function handle(UserHasNoMFABackupCodesLeft $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
|
||||
try {
|
||||
Notification::send($user, new MFABackupNoLeftNotification($user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* NotifiesUserAboutRepeatedMFAFailures.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\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserKeepsFailingMFA;
|
||||
use FireflyIII\Notifications\Security\MFAManyFailedAttemptsNotification;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutRepeatedMFAFailures
|
||||
{
|
||||
public function handle(UserKeepsFailingMFA $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
$count = $event->count;
|
||||
|
||||
try {
|
||||
Notification::send($user, new MFAManyFailedAttemptsNotification($user, $count));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* NotifiesUserAboutUsedBackupCode.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\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserHasUsedBackupCode;
|
||||
use FireflyIII\Notifications\Security\MFAUsedBackupCodeNotification;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutUsedBackupCode
|
||||
{
|
||||
public function handle(UserHasUsedBackupCode $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
|
||||
try {
|
||||
Notification::send($user, new MFAUsedBackupCodeNotification($user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,6 @@ class AccountMeta extends Model
|
||||
|
||||
protected function data(): Attribute
|
||||
{
|
||||
return Attribute::make(get: fn (mixed $value): string => (string)json_decode((string)$value, true), set: fn (mixed $value): array => ['data' => json_encode($value)]);
|
||||
return Attribute::make(get: static fn (mixed $value): string => (string)json_decode((string)$value, true), set: static fn (mixed $value): array => ['data' => json_encode($value)]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,16 +103,16 @@ class AvailableBudget extends Model
|
||||
protected function endDate(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn (string $value): Carbon => Carbon::parse($value),
|
||||
set: fn (Carbon $value): string => $value->format('Y-m-d'),
|
||||
get: static fn (string $value): Carbon => Carbon::parse($value),
|
||||
set: static fn (Carbon $value): string => $value->format('Y-m-d'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function startDate(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn (string $value): Carbon => Carbon::parse($value),
|
||||
set: fn (Carbon $value): string => $value->format('Y-m-d'),
|
||||
get: static fn (string $value): Carbon => Carbon::parse($value),
|
||||
set: static fn (Carbon $value): string => $value->format('Y-m-d'),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,6 @@ class Configuration extends Model
|
||||
*/
|
||||
protected function data(): Attribute
|
||||
{
|
||||
return Attribute::make(get: fn ($value): mixed => json_decode((string)$value), set: fn ($value): array => ['data' => json_encode($value)]);
|
||||
return Attribute::make(get: static fn ($value): mixed => json_decode((string)$value), set: static fn ($value): array => ['data' => json_encode($value)]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ class Rule extends Model
|
||||
|
||||
protected function description(): Attribute
|
||||
{
|
||||
return Attribute::make(set: fn ($value): array => ['description' => e($value)]);
|
||||
return Attribute::make(set: static fn ($value): array => ['description' => e($value)]);
|
||||
}
|
||||
|
||||
protected function order(): Attribute
|
||||
|
||||
@@ -57,7 +57,7 @@ class TransactionJournalMeta extends Model
|
||||
|
||||
protected function data(): Attribute
|
||||
{
|
||||
return Attribute::make(get: fn ($value): mixed => json_decode((string)$value, false), set: function ($value): array {
|
||||
return Attribute::make(get: static fn ($value): mixed => json_decode((string)$value, false), set: static function ($value): array {
|
||||
$data = json_encode($value);
|
||||
|
||||
return ['data' => $data, 'hash' => hash('sha256', $data)];
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user