Compare commits

...

44 Commits

Author SHA1 Message Date
github-actions
8ed5092a76 Auto commit for release 'develop' on 2025-02-01 2025-02-01 06:23:31 +01:00
James Cole
d609821be6 Expand debug table. 2025-02-01 06:18:58 +01:00
github-actions
cd0201074c Auto commit for release 'v6.2.0' on 2025-01-31 2025-01-31 20:35:34 +01:00
James Cole
1d8feec7bc Update changelog. 2025-01-31 20:28:17 +01:00
James Cole
d941472c84 Merge branch 'main' into develop 2025-01-29 20:12:19 +01:00
James Cole
674a118fac Merge pull request #9709 from firefly-iii/dependabot/composer/composer-aeff1b7291
Bump twig/twig from 3.18.0 to 3.19.0 in the composer group across 1 directory
2025-01-29 20:11:54 +01:00
dependabot[bot]
1334d793f6 Bump twig/twig in the composer group across 1 directory
Bumps the composer group with 1 update in the / directory: [twig/twig](https://github.com/twigphp/Twig).


Updates `twig/twig` from 3.18.0 to 3.19.0
- [Changelog](https://github.com/twigphp/Twig/blob/3.x/CHANGELOG)
- [Commits](https://github.com/twigphp/Twig/compare/v3.18.0...v3.19.0)

---
updated-dependencies:
- dependency-name: twig/twig
  dependency-type: indirect
  dependency-group: composer
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-29 18:44:41 +00:00
James Cole
60354c0202 Fix https://github.com/firefly-iii/firefly-iii/issues/9704 2025-01-29 08:40:16 +01:00
James Cole
22081d3f0a Add skip help text 2025-01-29 07:52:16 +01:00
James Cole
4b3f8fc78d Remove logging [skip ci] 2025-01-29 07:48:43 +01:00
github-actions
4bd1aab86d Auto commit for release 'develop' on 2025-01-27 2025-01-27 04:08:46 +01:00
James Cole
60362cb60c Remove debug header, does not work. 2025-01-26 17:07:45 +01:00
github-actions
27f740bf98 Auto commit for release 'v6.2.0-beta.1' on 2025-01-26 2025-01-26 14:41:24 +01:00
github-actions
5e15747a5b Auto commit for release 'develop' on 2025-01-26 2025-01-26 14:33:19 +01:00
James Cole
8a6eaad2bb Fix vite 2025-01-26 14:29:11 +01:00
James Cole
5ab52d9f66 Fix vite 2025-01-26 14:26:17 +01:00
James Cole
21a327bf08 Merge branch 'main' into develop 2025-01-26 14:10:30 +01:00
James Cole
15dd175394 Add date [skip ci] 2025-01-26 14:10:09 +01:00
James Cole
3f35305beb Update changelog. 2025-01-26 14:09:49 +01:00
James Cole
768d8b1515 Fix currency exchange rates 2025-01-26 10:09:09 +01:00
James Cole
1c19428a12 Catch API endpoint errors. 2025-01-26 07:44:41 +01:00
James Cole
c204533195 Fix various API 500 errors. 2025-01-26 06:30:38 +01:00
James Cole
6d89485792 Fix endpoints, validate dates. 2025-01-25 09:17:21 +01:00
James Cole
949d818bad Cleanup code. 2025-01-25 04:51:09 +01:00
James Cole
a12e200a0a Put query count in debug header 2025-01-25 04:50:36 +01:00
James Cole
b4039b1f13 Report if the version is a dev version 2025-01-25 04:50:26 +01:00
James Cole
32921e15b1 Remove errors from handler 2025-01-25 04:50:15 +01:00
James Cole
4f7cc7d53b More strict date validation 2025-01-25 04:49:28 +01:00
James Cole
0986bfbc34 Rename warnings to notifications 2025-01-25 04:49:08 +01:00
James Cole
663202bfc6 Return correct headers 2025-01-25 04:48:51 +01:00
James Cole
6f63ddf5b0 [chore] Fix https://github.com/firefly-iii/firefly-iii/issues/9683 and rename default to native. 2025-01-22 05:24:12 +01:00
James Cole
a9805b144a Merge pull request #9684 from firefly-iii/dependabot/npm_and_yarn/npm_and_yarn-545022be4d 2025-01-22 04:37:48 +01:00
dependabot[bot]
e1b8b9b3ae Bump vite in the npm_and_yarn group across 1 directory
Bumps the npm_and_yarn group with 1 update in the / directory: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).


Updates `vite` from 6.0.7 to 6.0.9
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.0.9/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-21 21:02:17 +00:00
James Cole
d57327fd11 [chore] various code cleanup. 2025-01-21 06:34:39 +01:00
github-actions
15ac69bfad Auto commit for release 'develop' on 2025-01-20 2025-01-20 04:08:13 +01:00
github-actions
a5bd28f8d4 Auto commit for release 'develop' on 2025-01-19 2025-01-19 19:26:58 +01:00
github-actions
b2516ca1b4 Auto commit for release 'v6.2.0-alpha.2' on 2025-01-19 2025-01-19 19:12:01 +01:00
James Cole
053b46ae63 [chore] fix phpstan errors. 2025-01-19 19:07:19 +01:00
James Cole
6e836aceec Expand changelog. 2025-01-19 18:55:03 +01:00
James Cole
0e8bcd2e79 [chore] cod cleanup 2025-01-19 18:53:32 +01:00
James Cole
bd1f8b2497 [chore] Move to native from default. [skip ci] 2025-01-19 11:54:40 +01:00
James Cole
19dfcf7139 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop
# Conflicts:
#	app/Transformers/CurrencyTransformer.php
#	app/Transformers/V2/CurrencyTransformer.php
2025-01-19 11:48:18 +01:00
James Cole
ef7a3287bb Add info to changelog 2025-01-19 11:47:49 +01:00
James Cole
2900049498 Add default 2025-01-19 11:47:41 +01:00
172 changed files with 1490 additions and 1235 deletions

View File

@@ -406,16 +406,16 @@
}, },
{ {
"name": "friendsofphp/php-cs-fixer", "name": "friendsofphp/php-cs-fixer",
"version": "v3.68.1", "version": "v3.68.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "b9db2b2ea3cdba7201067acee46f984ef2397cff" "reference": "7bedb718b633355272428c60736dc97fb96daf27"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/b9db2b2ea3cdba7201067acee46f984ef2397cff", "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/7bedb718b633355272428c60736dc97fb96daf27",
"reference": "b9db2b2ea3cdba7201067acee46f984ef2397cff", "reference": "7bedb718b633355272428c60736dc97fb96daf27",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -497,7 +497,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.68.1" "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.68.5"
}, },
"funding": [ "funding": [
{ {
@@ -505,7 +505,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-01-17T09:20:36+00:00" "time": "2025-01-30T17:00:50+00:00"
}, },
{ {
"name": "psr/container", "name": "psr/container",

View File

@@ -16,6 +16,9 @@ parameters:
- '#Dynamic call to static method#' # all the Laravel ORM things depend on this. - '#Dynamic call to static method#' # all the Laravel ORM things depend on this.
- identifier: varTag.nativeType - identifier: varTag.nativeType
- identifier: varTag.type - identifier: varTag.type
-
identifier: larastan.noEnvCallsOutsideOfConfig
path: ../app/Console/Commands/System/CreatesDatabase.php
- identifier: missingType.iterableValue # not interesting enough to fix. - identifier: missingType.iterableValue # not interesting enough to fix.
- identifier: missingType.generics # not interesting enough to fix. - identifier: missingType.generics # not interesting enough to fix.
- "#Parameter \\#[1-2] \\$num[1-2] of function bc[a-z]+ expects numeric-string, [a-z\\-|&]+ given#" - "#Parameter \\#[1-2] \\$num[1-2] of function bc[a-z]+ expects numeric-string, [a-z\\-|&]+ given#"
@@ -28,49 +31,6 @@ parameters:
- '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::withTrashed#' - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::withTrashed#'
- '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::accountTypeIn#' - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::accountTypeIn#'
- '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\BelongsTo::withTrashed#' - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\BelongsTo::withTrashed#'
# - '#Control structures using switch should not be used.#' # switch is fine in some cases.
# - '#with no value type specified in iterable type array#' # remove this rule when all other issues are solved.
# - '#has no value type specified in iterable type array#' # remove this rule when all other issues are solved.
# - '#is not allowed to extend#'
# - '#does not specify its types#'
# - '#switch is forbidden to use#'
# - '#is neither abstract nor final#'
# - '#on left side of \?\?\= always exists and is not nullable#'
# - '#has a nullable return type declaration#' # perhaps throw errors instead?
# - '#with a nullable type declaration#' # decide what action should be if param is null.
# - '#with null as default value#'
# -
# message: '#Constructor in [a-zA-Z0-9\\_]+ has parameter \$[a-zA-Z0-9\\_]+ with default value#'
# paths:
# - ../app/Exceptions/IntervalException.php
# - ../app/Support/Navigation.php
# -
# message: '#but containers should not be injected#'
# paths:
# - ../app/Support/Authentication/RemoteUserGuard.php
# -
# message: '#Function compact\(\) should not be used#' # too useful in template rendering.
# paths:
# - ../app/Generator/Report/Account/MonthReportGenerator.php
# - ../app/Generator/Report/Audit/MonthReportGenerator.php
# - ../app/Generator/Report/Budget/MonthReportGenerator.php
# - ../app/Generator/Report/Category/MonthReportGenerator.php
# - ../app/Generator/Report/Standard/MonthReportGenerator.php
# - ../app/Generator/Report/Standard/MultiYearReportGenerator.php
# - ../app/Generator/Report/Standard/YearReportGenerator.php
# - ../app/Generator/Report/Tag/MonthReportGenerator.php
# - ../app/Http/Controllers/Account/*.php
# - ../app/Http/Controllers/Admin/*.php
# - ../app/Http/Controllers/*.php
# - ../app/Support/ExpandedForm.php
# - ../app/Support/Form/AccountForm.php
# - ../app/Support/Form/CurrencyForm.php
# - ../app/Support/Form/FormSupport.php
# -
# message: '#Either catch a more specific exception#'
# paths:
# - ../app/Support/Form/FormSupport.php
# The level 8 is the highest level. original was 5 # The level 8 is the highest level. original was 5
# 7 is more than enough, higher just leaves NULL things. # 7 is more than enough, higher just leaves NULL things.

View File

@@ -41,6 +41,7 @@ use Illuminate\Http\JsonResponse;
class AccountController extends Controller class AccountController extends Controller
{ {
use AccountFilter; use AccountFilter;
protected array $accepts = ['application/json'];
/** @var array<int, string> */ /** @var array<int, string> */
private array $balanceTypes; private array $balanceTypes;
@@ -84,12 +85,12 @@ class AccountController extends Controller
/** @var Account $account */ /** @var Account $account */
foreach ($result as $account) { foreach ($result as $account) {
$nameWithBalance = $account->name; $nameWithBalance = $account->name;
$currency = $this->repository->getAccountCurrency($account) ?? $this->defaultCurrency; $currency = $this->repository->getAccountCurrency($account) ?? $this->nativeCurrency;
$useCurrency = $currency; $useCurrency = $currency;
if (in_array($account->accountType->type, $this->balanceTypes, true)) { if (in_array($account->accountType->type, $this->balanceTypes, true)) {
$balance = Steam::finalAccountBalance($account, $date); $balance = Steam::finalAccountBalance($account, $date);
$key = $this->convertToNative && $currency->id !== $this->defaultCurrency->id ? 'native_balance' : 'balance'; $key = $this->convertToNative && $currency->id !== $this->nativeCurrency->id ? 'native_balance' : 'balance';
$useCurrency = $this->convertToNative && $currency->id !== $this->defaultCurrency->id ? $this->defaultCurrency : $currency; $useCurrency = $this->convertToNative && $currency->id !== $this->nativeCurrency->id ? $this->nativeCurrency : $currency;
$amount = $balance[$key] ?? '0'; $amount = $balance[$key] ?? '0';
$nameWithBalance = sprintf( $nameWithBalance = sprintf(
'%s (%s)', '%s (%s)',
@@ -123,6 +124,6 @@ class AccountController extends Controller
} }
); );
return response()->json($return); return response()->api($return);
} }
} }

View File

@@ -74,6 +74,6 @@ class BillController extends Controller
} }
); );
return response()->json($filtered->toArray()); return response()->api($filtered->toArray());
} }
} }

View File

@@ -73,6 +73,6 @@ class BudgetController extends Controller
} }
); );
return response()->json($filtered); return response()->api($filtered->toArray());
} }
} }

View File

@@ -73,6 +73,6 @@ class CategoryController extends Controller
} }
); );
return response()->json($filtered); return response()->api($filtered->toArray());
} }
} }

View File

@@ -77,7 +77,7 @@ class CurrencyController extends Controller
]; ];
} }
return response()->json($result); return response()->api($result);
} }
/** /**
@@ -103,6 +103,6 @@ class CurrencyController extends Controller
]; ];
} }
return response()->json($result); return response()->api($result);
} }
} }

View File

@@ -75,6 +75,6 @@ class ObjectGroupController extends Controller
]; ];
} }
return response()->json($return); return response()->api($return);
} }
} }

View File

@@ -87,7 +87,7 @@ class PiggyBankController extends Controller
]; ];
} }
return response()->json($response); return response()->api($response);
} }
/** /**
@@ -124,6 +124,6 @@ class PiggyBankController extends Controller
]; ];
} }
return response()->json($response); return response()->api($response);
} }
} }

View File

@@ -73,6 +73,6 @@ class RecurrenceController extends Controller
]; ];
} }
return response()->json($response); return response()->api($response);
} }
} }

View File

@@ -72,6 +72,6 @@ class RuleController extends Controller
]; ];
} }
return response()->json($response); return response()->api($response);
} }
} }

View File

@@ -72,6 +72,6 @@ class RuleGroupController extends Controller
]; ];
} }
return response()->json($response); return response()->api($response);
} }
} }

View File

@@ -75,6 +75,6 @@ class TagController extends Controller
]; ];
} }
return response()->json($array); return response()->api($array);
} }
} }

View File

@@ -84,7 +84,7 @@ class TransactionController extends Controller
]; ];
} }
return response()->json($array); return response()->api($array);
} }
/** /**
@@ -122,6 +122,6 @@ class TransactionController extends Controller
]; ];
} }
return response()->json($array); return response()->api($array);
} }
} }

View File

@@ -72,6 +72,6 @@ class TransactionTypeController extends Controller
]; ];
} }
return response()->json($array); return response()->api($array);
} }
} }

View File

@@ -97,8 +97,8 @@ class AccountController extends Controller
/** @var Account $account */ /** @var Account $account */
foreach ($accounts as $account) { foreach ($accounts as $account) {
$currency = $this->repository->getAccountCurrency($account) ?? $this->defaultCurrency; $currency = $this->repository->getAccountCurrency($account) ?? $this->nativeCurrency;
$field = $this->convertToNative && $currency->id !== $this->defaultCurrency->id ? 'native_balance' : 'balance'; $field = $this->convertToNative && $currency->id !== $this->nativeCurrency->id ? 'native_balance' : 'balance';
$currentSet = [ $currentSet = [
'label' => $account->name, 'label' => $account->name,
'currency_id' => (string) $currency->id, 'currency_id' => (string) $currency->id,

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Controllers;
use Carbon\Carbon; use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException; use Carbon\Exceptions\InvalidFormatException;
use FireflyIII\Exceptions\BadHttpHeaderException;
use FireflyIII\Models\Preference; use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Amount;
@@ -60,12 +61,14 @@ abstract class Controller extends BaseController
use ValidatesRequests; use ValidatesRequests;
protected const string CONTENT_TYPE = 'application/vnd.api+json'; protected const string CONTENT_TYPE = 'application/vnd.api+json';
protected const string JSON_CONTENT_TYPE = 'application/json';
/** @var array<int, string> */ /** @var array<int, string> */
protected array $allowedSort; protected array $allowedSort;
protected ParameterBag $parameters; protected ParameterBag $parameters;
protected bool $convertToNative = false; protected bool $convertToNative = false;
protected TransactionCurrency $defaultCurrency; protected array $accepts = ['application/json'];
protected TransactionCurrency $nativeCurrency;
/** /**
* Controller constructor. * Controller constructor.
@@ -80,11 +83,17 @@ abstract class Controller extends BaseController
if (auth()->check()) { if (auth()->check()) {
$language = Steam::getLanguage(); $language = Steam::getLanguage();
$this->convertToNative = Amount::convertToNative(); $this->convertToNative = Amount::convertToNative();
$this->defaultCurrency = Amount::getDefaultCurrency(); $this->nativeCurrency = Amount::getNativeCurrency();
app()->setLocale($language); 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); return $next($request);
} }
); );
@@ -148,7 +157,15 @@ abstract class Controller extends BaseController
$value = null; $value = null;
} }
if (null !== $value) { if (null !== $value) {
$bag->set($integer, (int) $value); $value = (int) $value;
if ($value < 1) {
$value = 1;
}
if ($value > 2 ** 16) {
$value = 2 ** 16;
}
$bag->set($integer, $value);
} }
if (null === $value if (null === $value
&& 'limit' === $integer // @phpstan-ignore-line && 'limit' === $integer // @phpstan-ignore-line

View File

@@ -70,7 +70,7 @@ class BillController extends Controller
$start = $request->getStart(); $start = $request->getStart();
$end = $request->getEnd(); $end = $request->getEnd();
$convertToNative = Amount::convertToNative(); $convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency(); $default = Amount::getNativeCurrency();
$response = []; $response = [];
// get all bills: // get all bills:
@@ -133,7 +133,7 @@ class BillController extends Controller
$start = $request->getStart(); $start = $request->getStart();
$end = $request->getEnd(); $end = $request->getEnd();
$convertToNative = Amount::convertToNative(); $convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency(); $default = Amount::getNativeCurrency();
$response = []; $response = [];
// collect all expenses in this period (regardless of type) by the given bills and accounts. // collect all expenses in this period (regardless of type) by the given bills and accounts.

View File

@@ -48,7 +48,7 @@ class PeriodController extends Controller
$end = $request->getEnd(); $end = $request->getEnd();
$response = []; $response = [];
$convertToNative = Amount::convertToNative(); $convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency(); $default = Amount::getNativeCurrency();
// collect all expenses in this period (regardless of type) // collect all expenses in this period (regardless of type)
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);

View File

@@ -69,7 +69,7 @@ class TagController extends Controller
$end = $request->getEnd(); $end = $request->getEnd();
$response = []; $response = [];
$convertToNative = Amount::convertToNative(); $convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency(); $default = Amount::getNativeCurrency();
// collect all expenses in this period (regardless of type) by the given bills and accounts. // collect all expenses in this period (regardless of type) by the given bills and accounts.
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);

View File

@@ -47,7 +47,7 @@ class PeriodController extends Controller
$end = $request->getEnd(); $end = $request->getEnd();
$response = []; $response = [];
$convertToNative = Amount::convertToNative(); $convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency(); $default = Amount::getNativeCurrency();
// collect all expenses in this period (regardless of type) // collect all expenses in this period (regardless of type)
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);

View File

@@ -69,7 +69,7 @@ class TagController extends Controller
$end = $request->getEnd(); $end = $request->getEnd();
$response = []; $response = [];
$convertToNative = Amount::convertToNative(); $convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency(); $default = Amount::getNativeCurrency();
// collect all expenses in this period (regardless of type) by the given bills and accounts. // collect all expenses in this period (regardless of type) by the given bills and accounts.
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);

View File

@@ -47,7 +47,7 @@ class PeriodController extends Controller
$end = $request->getEnd(); $end = $request->getEnd();
$response = []; $response = [];
$convertToNative = Amount::convertToNative(); $convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency(); $default = Amount::getNativeCurrency();
// collect all expenses in this period (regardless of type) // collect all expenses in this period (regardless of type)
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);

View File

@@ -67,7 +67,7 @@ class TagController extends Controller
$end = $request->getEnd(); $end = $request->getEnd();
$response = []; $response = [];
$convertToNative = Amount::convertToNative(); $convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency(); $default = Amount::getNativeCurrency();
// collect all expenses in this period (regardless of type) by the given bills and accounts. // collect all expenses in this period (regardless of type) by the given bills and accounts.

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Models\Budget; namespace FireflyIII\Api\V1\Controllers\Models\Budget;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
@@ -208,6 +209,8 @@ class ListController extends Controller
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector $collector
->setUser($admin) ->setUser($admin)
// withdrawals only
->setTypes([TransactionTypeEnum::WITHDRAWAL->value])
// filter on budget. // filter on budget.
->withoutBudget() ->withoutBudget()
// all info needed for the API: // all info needed for the API:

View File

@@ -2,7 +2,7 @@
/* /*
* DestroyController.php * DestroyController.php
* Copyright (c) 2024 james@firefly-iii.org. * Copyright (c) 2025 james@firefly-iii.org.
* *
* This file is part of Firefly III (https://github.com/firefly-iii). * This file is part of Firefly III (https://github.com/firefly-iii).
* *
@@ -22,10 +22,13 @@
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\Api\V2\Controllers\Model\ExchangeRate; namespace FireflyIII\Api\V1\Controllers\Models\CurrencyExchangeRate;
use FireflyIII\Api\V2\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V2\Request\Model\ExchangeRate\DestroyRequest; use FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate\DestroyRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Exceptions\ValidationException;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface; use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
@@ -36,6 +39,8 @@ class DestroyController extends Controller
{ {
use ValidatesUserGroupTrait; use ValidatesUserGroupTrait;
protected array $acceptedRoles = [UserRoleEnum::OWNER];
public const string RESOURCE_KEY = 'exchange-rates'; public const string RESOURCE_KEY = 'exchange-rates';
private ExchangeRateRepositoryInterface $repository; private ExchangeRateRepositoryInterface $repository;
@@ -56,6 +61,9 @@ class DestroyController extends Controller
public function destroy(DestroyRequest $request, TransactionCurrency $from, TransactionCurrency $to): JsonResponse public function destroy(DestroyRequest $request, TransactionCurrency $from, TransactionCurrency $to): JsonResponse
{ {
$date = $request->getDate(); $date = $request->getDate();
if (null === $date) {
throw new ValidationException('Date is required');
}
$rate = $this->repository->getSpecificRateOnDate($from, $to, $date); $rate = $this->repository->getSpecificRateOnDate($from, $to, $date);
if (null === $rate) { if (null === $rate) {
throw new NotFoundHttpException(); throw new NotFoundHttpException();
@@ -64,4 +72,11 @@ class DestroyController extends Controller
return response()->json([], 204); return response()->json([], 204);
} }
public function destroySingle(CurrencyExchangeRate $exchangeRate): JsonResponse
{
$this->repository->deleteRate($exchangeRate);
return response()->json([], 204);
}
} }

View File

@@ -1,8 +1,8 @@
<?php <?php
/* /*
* ShowController.php * IndexController.php
* Copyright (c) 2023 james@firefly-iii.org * Copyright (c) 2025 james@firefly-iii.org.
* *
* This file is part of Firefly III (https://github.com/firefly-iii). * This file is part of Firefly III (https://github.com/firefly-iii).
* *
@@ -17,12 +17,12 @@
* GNU Affero General Public License for more details. * GNU Affero General Public License for more details.
* *
* You should have received a copy of the GNU Affero General Public License * 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); declare(strict_types=1);
namespace FireflyIII\Api\V2\Controllers\Model\ExchangeRate; namespace FireflyIII\Api\V1\Controllers\Models\CurrencyExchangeRate;
use FireflyIII\Api\V2\Controllers\Controller; use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface; use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface;
@@ -38,7 +38,7 @@ class IndexController extends Controller
{ {
use ValidatesUserGroupTrait; use ValidatesUserGroupTrait;
public const string RESOURCE_KEY = 'exchange-rates'; public const string RESOURCE_KEY = 'currency_exchange_rates';
private ExchangeRateRepositoryInterface $repository; private ExchangeRateRepositoryInterface $repository;
@@ -62,9 +62,6 @@ class IndexController extends Controller
$count = $piggies->count(); $count = $piggies->count();
$piggies = $piggies->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); $piggies = $piggies->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
$paginator = new LengthAwarePaginator($piggies, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($piggies, $count, $pageSize, $this->parameters->get('page'));
var_dump('here we are');
$transformer = new ExchangeRateTransformer(); $transformer = new ExchangeRateTransformer();
$transformer->setParameters($this->parameters); // give params to transformer $transformer->setParameters($this->parameters); // give params to transformer

View File

@@ -2,7 +2,7 @@
/* /*
* ShowController.php * ShowController.php
* Copyright (c) 2023 james@firefly-iii.org * Copyright (c) 2025 james@firefly-iii.org.
* *
* This file is part of Firefly III (https://github.com/firefly-iii). * This file is part of Firefly III (https://github.com/firefly-iii).
* *
@@ -17,14 +17,15 @@
* GNU Affero General Public License for more details. * GNU Affero General Public License for more details.
* *
* You should have received a copy of the GNU Affero General Public License * 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); declare(strict_types=1);
namespace FireflyIII\Api\V2\Controllers\Model\ExchangeRate; namespace FireflyIII\Api\V1\Controllers\Models\CurrencyExchangeRate;
use FireflyIII\Api\V2\Controllers\Controller; use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface; use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
@@ -73,4 +74,15 @@ class ShowController extends Controller
->header('Content-Type', self::CONTENT_TYPE) ->header('Content-Type', self::CONTENT_TYPE)
; ;
} }
public function showSingle(CurrencyExchangeRate $exchangeRate): JsonResponse
{
$transformer = new ExchangeRateTransformer();
$transformer->setParameters($this->parameters);
return response()
->api($this->jsonApiObject(self::RESOURCE_KEY, $exchangeRate, $transformer))
->header('Content-Type', self::CONTENT_TYPE)
;
}
} }

View File

@@ -1,8 +1,8 @@
<?php <?php
/* /*
* DestroyController.php * StoreController.php
* Copyright (c) 2024 james@firefly-iii.org. * Copyright (c) 2025 james@firefly-iii.org.
* *
* This file is part of Firefly III (https://github.com/firefly-iii). * This file is part of Firefly III (https://github.com/firefly-iii).
* *
@@ -22,10 +22,10 @@
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\Api\V2\Controllers\Model\ExchangeRate; namespace FireflyIII\Api\V1\Controllers\Models\CurrencyExchangeRate;
use FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate\StoreRequest;
use FireflyIII\Api\V2\Controllers\Controller; use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Model\ExchangeRate\StoreRequest;
use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface; use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use FireflyIII\Transformers\V2\ExchangeRateTransformer; use FireflyIII\Transformers\V2\ExchangeRateTransformer;

View File

@@ -1,8 +1,8 @@
<?php <?php
/* /*
* DestroyController.php * UpdateController.php
* Copyright (c) 2024 james@firefly-iii.org. * Copyright (c) 2025 james@firefly-iii.org.
* *
* This file is part of Firefly III (https://github.com/firefly-iii). * This file is part of Firefly III (https://github.com/firefly-iii).
* *
@@ -22,10 +22,10 @@
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\Api\V2\Controllers\Model\ExchangeRate; namespace FireflyIII\Api\V1\Controllers\Models\CurrencyExchangeRate;
use FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate\UpdateRequest;
use FireflyIII\Api\V2\Controllers\Controller; use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Model\ExchangeRate\UpdateRequest;
use FireflyIII\Models\CurrencyExchangeRate; use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface; use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;

View File

@@ -72,13 +72,6 @@ class UpdateController extends Controller
{ {
app('log')->debug('Now in update routine for transaction group'); app('log')->debug('Now in update routine for transaction group');
$data = $request->getAll(); $data = $request->getAll();
// Fixes 8750.
$transactions = $data['transactions'] ?? [];
foreach ($transactions as $index => $info) {
unset($data['transactions'][$index]['type']);
}
$transactionGroup = $this->groupRepository->update($transactionGroup, $data); $transactionGroup = $this->groupRepository->update($transactionGroup, $data);
$manager = $this->getManager(); $manager = $this->getManager();

View File

@@ -107,7 +107,7 @@ class ShowController extends Controller
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$manager = $this->getManager(); $manager = $this->getManager();
$this->parameters->set('defaultCurrency', $this->defaultCurrency); $this->parameters->set('nativeCurrency', $this->nativeCurrency);
// update fields with user info. // update fields with user info.
$currency->refreshForUser($user); $currency->refreshForUser($user);
@@ -123,7 +123,7 @@ class ShowController extends Controller
/** /**
* This endpoint is documented at: * This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/getDefaultCurrency * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/getNativeCurrency
* *
* Show a currency. * Show a currency.
* *
@@ -134,7 +134,7 @@ class ShowController extends Controller
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$manager = $this->getManager(); $manager = $this->getManager();
$currency = $this->defaultCurrency; $currency = $this->nativeCurrency;
// update fields with user info. // update fields with user info.
$currency->refreshForUser($user); $currency->refreshForUser($user);

View File

@@ -100,7 +100,7 @@ class UpdateController extends Controller
/** /**
* This endpoint is documented at: * This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/defaultCurrency * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/nativeCurrency
* *
* Make the currency a default currency. * Make the currency a default currency.
* *

View File

@@ -124,7 +124,7 @@ class BasicController extends Controller
{ {
// some config settings // some config settings
$convertToNative = Amount::convertToNative(); $convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency(); $default = Amount::getNativeCurrency();
// prep some arrays: // prep some arrays:
$incomes = []; $incomes = [];
$expenses = []; $expenses = [];

View File

@@ -58,7 +58,7 @@ class AboutController extends Controller
'driver' => $currentDriver, 'driver' => $currentDriver,
]; ];
return response()->api(['data' => $data])->header('Content-Type', self::CONTENT_TYPE); return response()->api(['data' => $data])->header('Content-Type', self::JSON_CONTENT_TYPE);
} }
/** /**

View File

@@ -86,7 +86,7 @@ class ConfigurationController extends Controller
]; ];
} }
return response()->json($return); return response()->api($return);
} }
/** /**
@@ -142,7 +142,7 @@ class ConfigurationController extends Controller
]; ];
} }
return response()->json(['data' => $data])->header('Content-Type', self::CONTENT_TYPE); return response()->api(['data' => $data])->header('Content-Type', self::JSON_CONTENT_TYPE);
} }
/** /**
@@ -173,6 +173,6 @@ class ConfigurationController extends Controller
'editable' => true, 'editable' => true,
]; ];
return response()->json(['data' => $data])->header('Content-Type', self::CONTENT_TYPE); return response()->api(['data' => $data])->header('Content-Type', self::CONTENT_TYPE);
} }
} }

View File

@@ -52,8 +52,8 @@ class CronController extends Controller
if (true === config('cer.download_enabled')) { if (true === config('cer.download_enabled')) {
$return['exchange_rates'] = $this->exchangeRatesCronJob($config['force'], $config['date']); $return['exchange_rates'] = $this->exchangeRatesCronJob($config['force'], $config['date']);
} }
$return['bill_warnings'] = $this->billWarningCronJob($config['force'], $config['date']); $return['bill_notifications'] = $this->billWarningCronJob($config['force'], $config['date']);
return response()->json($return); return response()->api($return);
} }
} }

View File

@@ -32,7 +32,6 @@ use FireflyIII\Models\Preference;
use FireflyIII\Transformers\PreferenceTransformer; use FireflyIII\Transformers\PreferenceTransformer;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
@@ -86,7 +85,7 @@ class PreferencesController extends Controller
$manager = $this->getManager(); $manager = $this->getManager();
if ('currencyPreference' === $preference->name) { if ('currencyPreference' === $preference->name) {
throw new FireflyException('Please use api/v1/currencies/default instead.'); throw new FireflyException('Please use api/v1/currencies/native instead.');
} }
/** @var PreferenceTransformer $transformer */ /** @var PreferenceTransformer $transformer */
@@ -98,34 +97,6 @@ class PreferencesController extends Controller
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
} }
/**
* TODO This endpoint is not documented.
*
* Return a single preference by name.
*
* @param Collection<int, Preference> $collection
*/
public function showList(Collection $collection): JsonResponse
{
$manager = $this->getManager();
$count = $collection->count();
$pageSize = $this->parameters->get('limit');
$preferences = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($preferences, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.preferences.show-list').$this->buildParams());
/** @var PreferenceTransformer $transformer */
$transformer = app(PreferenceTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($preferences, $transformer, self::RESOURCE_KEY);
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
}
/** /**
* This endpoint is documented at: * This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/preferences/storePreference * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/preferences/storePreference
@@ -161,7 +132,7 @@ class PreferencesController extends Controller
public function update(PreferenceUpdateRequest $request, Preference $preference): JsonResponse public function update(PreferenceUpdateRequest $request, Preference $preference): JsonResponse
{ {
if ('currencyPreference' === $preference->name) { if ('currencyPreference' === $preference->name) {
throw new FireflyException('Please use api/v1/currencies/default instead.'); throw new FireflyException('Please use api/v1/currencies/native instead.');
} }
$manager = $this->getManager(); $manager = $this->getManager();

View File

@@ -58,6 +58,7 @@ class AutocompleteRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'date' => 'date|after:1900-01-01|before:2099-12-31',
]; ];
} }
} }

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Data; namespace FireflyIII\Api\V1\Requests\Data;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\ValidationException;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
@@ -49,7 +49,7 @@ class DateRequest extends FormRequest
$start->startOfDay(); $start->startOfDay();
$end->endOfDay(); $end->endOfDay();
if ($start->diffInYears($end, true) > 5) { if ($start->diffInYears($end, true) > 5) {
throw new FireflyException('Date range out of range.'); throw new ValidationException('Date range out of range.');
} }
return [ return [

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Requests\Models\Account;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Location; use FireflyIII\Models\Location;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\UniqueAccountNumber; use FireflyIII\Rules\UniqueAccountNumber;
use FireflyIII\Rules\UniqueIban; use FireflyIII\Rules\UniqueIban;
@@ -33,6 +34,8 @@ use FireflyIII\Support\Request\AppendsLocationData;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Validator;
/** /**
* Class UpdateRequest * Class UpdateRequest
@@ -112,4 +115,36 @@ class UpdateRequest extends FormRequest
return Location::requestRules($rules); return Location::requestRules($rules);
} }
/**
* Configure the validator instance with special rules for after the basic validation rules.
*/
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator): void {
// validate start before end only if both are there.
$data = $validator->getData();
/** @var Account $account */
$account = $this->route()->parameter('account');
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$currency = $repository->getAccountCurrency($account);
// how many piggies are attached?
$piggyBanks = $account->piggyBanks()->count();
if ($piggyBanks > 0 && array_key_exists('currency_code', $data) && $data['currency_code'] !== $currency->code) {
$validator->errors()->add('currency_code', (string) trans('validation.piggy_no_change_currency'));
}
if ($piggyBanks > 0 && array_key_exists('currency_id', $data) && (int) $data['currency_id'] !== $currency->id) {
$validator->errors()->add('currency_id', (string) trans('validation.piggy_no_change_currency'));
}
}
);
if ($validator->fails()) {
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
}
}
} }

View File

@@ -66,8 +66,8 @@ class Request extends FormRequest
'currency_id' => 'numeric|exists:transaction_currencies,id', 'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code', 'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'amount' => ['nullable', new IsValidPositiveAmount()], 'amount' => ['nullable', new IsValidPositiveAmount()],
'start' => 'date', 'start' => 'date|after:1900-01-01|before:2099-12-31',
'end' => 'date', 'end' => 'date|after:1900-01-01|before:2099-12-31',
]; ];
} }

View File

@@ -78,9 +78,9 @@ class StoreRequest extends FormRequest
'amount_max' => ['required', new IsValidPositiveAmount()], 'amount_max' => ['required', new IsValidPositiveAmount()],
'currency_id' => 'numeric|exists:transaction_currencies,id', 'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code', 'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'date' => 'date|required', 'date' => 'date|required|after:1900-01-01|before:2099-12-31',
'end_date' => 'nullable|date|after:date', 'end_date' => 'nullable|date|after:date|after:1900-01-01|before:2099-12-31',
'extension_date' => 'nullable|date|after:date', 'extension_date' => 'nullable|date|after:date|after:1900-01-01|before:2099-12-31',
'repeat_freq' => 'in:weekly,monthly,quarterly,half-year,yearly|required', 'repeat_freq' => 'in:weekly,monthly,quarterly,half-year,yearly|required',
'skip' => 'min:0|max:31|numeric', 'skip' => 'min:0|max:31|numeric',
'active' => [new IsBoolean()], 'active' => [new IsBoolean()],
@@ -96,15 +96,39 @@ class StoreRequest extends FormRequest
$validator->after( $validator->after(
static function (Validator $validator): void { static function (Validator $validator): void {
$data = $validator->getData(); $data = $validator->getData();
$min = (string) ($data['amount_min'] ?? '0'); $min = $data['amount_min'] ?? '0';
$max = (string) ($data['amount_max'] ?? '0'); $max = $data['amount_max'] ?? '0';
if (1 === bccomp($min, $max)) { if (is_array($min) || is_array($max)) {
$validator->errors()->add('amount_min', (string) trans('validation.generic_invalid'));
$validator->errors()->add('amount_max', (string) trans('validation.generic_invalid'));
$min = '0';
$max = '0';
}
$result = false;
try {
$result = bccomp($min, $max);
} catch (\ValueError $e) {
Log::error($e->getMessage());
$validator->errors()->add('amount_min', (string) trans('validation.generic_invalid'));
$validator->errors()->add('amount_max', (string) trans('validation.generic_invalid'));
}
if (1 === $result) {
$validator->errors()->add('amount_min', (string) trans('validation.amount_min_over_max')); $validator->errors()->add('amount_min', (string) trans('validation.amount_min_over_max'));
} }
} }
); );
if ($validator->fails()) { $failed = false;
try {
$failed = $validator->fails();
} catch (\TypeError $e) {
Log::error($e->getMessage());
$failed = false;
}
if ($failed) {
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
} }
} }

View File

@@ -81,9 +81,9 @@ class UpdateRequest extends FormRequest
'amount_max' => ['nullable', new IsValidPositiveAmount()], 'amount_max' => ['nullable', new IsValidPositiveAmount()],
'currency_id' => 'numeric|exists:transaction_currencies,id', 'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code', 'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'date' => 'date', 'date' => 'date|after:1900-01-01|before:2099-12-31',
'end_date' => 'date|after:date', 'end_date' => 'date|after:date|after:1900-01-01|before:2099-12-31',
'extension_date' => 'date|after:date', 'extension_date' => 'date|after:date|after:1900-01-01|before:2099-12-31',
'repeat_freq' => 'in:weekly,monthly,quarterly,half-year,yearly', 'repeat_freq' => 'in:weekly,monthly,quarterly,half-year,yearly',
'skip' => 'min:0|max:31|numeric', 'skip' => 'min:0|max:31|numeric',
'active' => [new IsBoolean()], 'active' => [new IsBoolean()],

View File

@@ -67,8 +67,8 @@ class UpdateRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'start' => 'date', 'start' => 'date|after:1900-01-01|before:2099-12-31',
'end' => 'date', 'end' => 'date|after:1900-01-01|before:2099-12-31',
'amount' => ['nullable', new IsValidPositiveAmount()], 'amount' => ['nullable', new IsValidPositiveAmount()],
'currency_id' => 'numeric|exists:transaction_currencies,id', 'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code', 'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',

View File

@@ -2,7 +2,7 @@
/* /*
* DestroyRequest.php * DestroyRequest.php
* Copyright (c) 2024 james@firefly-iii.org. * Copyright (c) 2025 james@firefly-iii.org.
* *
* This file is part of Firefly III (https://github.com/firefly-iii). * This file is part of Firefly III (https://github.com/firefly-iii).
* *
@@ -22,7 +22,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\Api\V2\Request\Model\ExchangeRate; namespace FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;
@@ -34,7 +34,7 @@ class DestroyRequest extends FormRequest
use ChecksLogin; use ChecksLogin;
use ConvertsDataTypes; use ConvertsDataTypes;
public function getDate(): Carbon public function getDate(): ?Carbon
{ {
return $this->getCarbonDate('date'); return $this->getCarbonDate('date');
} }

View File

@@ -1,8 +1,8 @@
<?php <?php
/* /*
* DestroyRequest.php * StoreRequest.php
* Copyright (c) 2024 james@firefly-iii.org. * Copyright (c) 2025 james@firefly-iii.org.
* *
* This file is part of Firefly III (https://github.com/firefly-iii). * This file is part of Firefly III (https://github.com/firefly-iii).
* *
@@ -22,7 +22,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\Api\V2\Request\Model\ExchangeRate; namespace FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;

View File

@@ -1,8 +1,8 @@
<?php <?php
/* /*
* DestroyRequest.php * UpdateRequest.php
* Copyright (c) 2024 james@firefly-iii.org. * Copyright (c) 2025 james@firefly-iii.org.
* *
* This file is part of Firefly III (https://github.com/firefly-iii). * This file is part of Firefly III (https://github.com/firefly-iii).
* *
@@ -22,7 +22,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\Api\V2\Request\Model\ExchangeRate; namespace FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;

View File

@@ -154,7 +154,7 @@ class UpdateRequest extends FormRequest
return [ return [
'title' => sprintf('min:1|max:255|uniqueObjectForUser:recurrences,title,%d', $recurrence->id), 'title' => sprintf('min:1|max:255|uniqueObjectForUser:recurrences,title,%d', $recurrence->id),
'description' => 'min:1|max:32768', 'description' => 'min:1|max:32768',
'first_date' => 'date', 'first_date' => 'date|after:1900-01-01|before:2099-12-31',
'apply_rules' => [new IsBoolean()], 'apply_rules' => [new IsBoolean()],
'active' => [new IsBoolean()], 'active' => [new IsBoolean()],
'repeat_until' => 'nullable|date', 'repeat_until' => 'nullable|date',

View File

@@ -71,8 +71,8 @@ class TestRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'start' => 'date', 'start' => 'date|after:1900-01-01|before:2099-12-31',
'end' => 'date|after_or_equal:start', 'end' => 'date|after_or_equal:start|after:1900-01-01|before:2099-12-31',
'accounts' => '', 'accounts' => '',
'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts', 'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts',
]; ];

View File

@@ -65,8 +65,8 @@ class TriggerRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'start' => 'date', 'start' => 'date|after:1900-01-01|before:2099-12-31',
'end' => 'date|after_or_equal:start', 'end' => 'date|after_or_equal:start|after:1900-01-01|before:2099-12-31',
'accounts' => '', 'accounts' => '',
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts', 'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
]; ];

View File

@@ -65,8 +65,8 @@ class TestRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'start' => 'date', 'start' => 'date|after:1900-01-01|before:2099-12-31',
'end' => 'date|after_or_equal:start', 'end' => 'date|after_or_equal:start|after:1900-01-01|before:2099-12-31',
'accounts' => '', 'accounts' => '',
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts', 'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
]; ];

View File

@@ -69,8 +69,8 @@ class TriggerRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'start' => 'date', 'start' => 'date|after:1900-01-01|before:2099-12-31',
'end' => 'date|after_or_equal:start', 'end' => 'date|after_or_equal:start|after:1900-01-01|before:2099-12-31',
]; ];
} }
} }

View File

@@ -62,7 +62,7 @@ class StoreRequest extends FormRequest
$rules = [ $rules = [
'tag' => 'required|min:1|uniqueObjectForUser:tags,tag|max:1024', 'tag' => 'required|min:1|uniqueObjectForUser:tags,tag|max:1024',
'description' => 'min:1|nullable|max:32768', 'description' => 'min:1|nullable|max:32768',
'date' => 'date|nullable', 'date' => 'date|nullable|after:1900-01-01|before:2099-12-31',
]; ];
return Location::requestRules($rules); return Location::requestRules($rules);

View File

@@ -63,11 +63,10 @@ class UpdateRequest extends FormRequest
{ {
/** @var Tag $tag */ /** @var Tag $tag */
$tag = $this->route()->parameter('tagOrId'); $tag = $this->route()->parameter('tagOrId');
// TODO check if uniqueObjectForUser is obsolete
$rules = [ $rules = [
'tag' => 'min:1|max:1024|uniqueObjectForUser:tags,tag,'.$tag->id, 'tag' => 'min:1|max:1024|uniqueObjectForUser:tags,tag,'.$tag->id,
'description' => 'min:1|nullable|max:32768', 'description' => 'min:1|nullable|max:32768',
'date' => 'date|nullable', 'date' => 'date|nullable|after:1900-01-01|before:2099-12-31',
]; ];
return Location::requestRules($rules); return Location::requestRules($rules);

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Transaction; namespace FireflyIII\Api\V1\Requests\Models\Transaction;
use FireflyIII\Models\Location;
use FireflyIII\Rules\BelongsUser; use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsDateOrTime; use FireflyIII\Rules\IsDateOrTime;
@@ -89,6 +90,11 @@ class StoreRequest extends FormRequest
'currency_id' => $this->integerFromValue((string) $object['currency_id']), 'currency_id' => $this->integerFromValue((string) $object['currency_id']),
'currency_code' => $this->clearString((string) $object['currency_code']), 'currency_code' => $this->clearString((string) $object['currency_code']),
// location
'latitude' => $this->floatFromValue((string) $object['latitude']),
'longitude' => $this->floatFromValue((string) $object['longitude']),
'zoom_level' => $this->integerFromValue((string) $object['zoom_level']),
// foreign currency info: // foreign currency info:
'foreign_currency_id' => $this->integerFromValue((string) $object['foreign_currency_id']), 'foreign_currency_id' => $this->integerFromValue((string) $object['foreign_currency_id']),
'foreign_currency_code' => $this->clearString((string) $object['foreign_currency_code']), 'foreign_currency_code' => $this->clearString((string) $object['foreign_currency_code']),
@@ -171,6 +177,7 @@ class StoreRequest extends FormRequest
{ {
app('log')->debug('Collect rules of TransactionStoreRequest'); app('log')->debug('Collect rules of TransactionStoreRequest');
$validProtocols = config('firefly.valid_url_protocols'); $validProtocols = config('firefly.valid_url_protocols');
$locationRules = Location::requestRules([]);
return [ return [
// basic fields for group: // basic fields for group:
@@ -178,6 +185,11 @@ class StoreRequest extends FormRequest
'error_if_duplicate_hash' => [new IsBoolean()], 'error_if_duplicate_hash' => [new IsBoolean()],
'apply_rules' => [new IsBoolean()], 'apply_rules' => [new IsBoolean()],
// location rules
'transactions.*.latitude' => $locationRules['latitude'],
'transactions.*.longitude' => $locationRules['longitude'],
'transactions.*.zoom_level' => $locationRules['zoom_level'],
// transaction rules (in array for splits): // transaction rules (in array for splits):
'transactions.*.type' => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation', 'transactions.*.type' => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation',
'transactions.*.date' => ['required', new IsDateOrTime()], 'transactions.*.date' => ['required', new IsDateOrTime()],

View File

@@ -57,6 +57,10 @@ class CronRequest extends FormRequest
if ($this->has('date')) { if ($this->has('date')) {
$data['date'] = $this->getCarbonDate('date'); $data['date'] = $this->getCarbonDate('date');
} }
// catch NULL.
if (null === $data['date']) {
$data['date'] = today(config('app.timezone'));
}
return $data; return $data;
} }
@@ -68,7 +72,7 @@ class CronRequest extends FormRequest
{ {
return [ return [
'force' => 'in:true,false', 'force' => 'in:true,false',
'date' => 'date', 'date' => 'nullable|date|after:1900-01-01|before:2099-12-31',
]; ];
} }
} }

View File

@@ -55,7 +55,7 @@ class AccountController extends Controller
$userGroup = $this->validateUserGroup($request); $userGroup = $this->validateUserGroup($request);
$this->repository = app(AccountRepositoryInterface::class); $this->repository = app(AccountRepositoryInterface::class);
$this->repository->setUserGroup($userGroup); $this->repository->setUserGroup($userGroup);
$this->default = app('amount')->getDefaultCurrency(); $this->default = app('amount')->getNativeCurrency();
$this->converter = app(ExchangeRateConverter::class); $this->converter = app(ExchangeRateConverter::class);
return $next($request); return $next($request);

View File

@@ -57,7 +57,7 @@ class AccountController extends Controller
$this->repository = app(AccountRepositoryInterface::class); $this->repository = app(AccountRepositoryInterface::class);
$this->repository->setUserGroup($this->validateUserGroup($request)); $this->repository->setUserGroup($this->validateUserGroup($request));
$this->chartData = new ChartData(); $this->chartData = new ChartData();
$this->default = app('amount')->getDefaultCurrency(); $this->default = app('amount')->getNativeCurrency();
return $next($request); return $next($request);
} }

View File

@@ -62,7 +62,7 @@ class BalanceController extends Controller
$this->repository->setUserGroup($userGroup); $this->repository->setUserGroup($userGroup);
$this->collector->setUserGroup($userGroup); $this->collector->setUserGroup($userGroup);
$this->chartData = new ChartData(); $this->chartData = new ChartData();
// $this->default = app('amount')->getDefaultCurrency(); // $this->default = app('amount')->getNativeCurrency();
return $next($request); return $next($request);
} }
@@ -87,7 +87,7 @@ class BalanceController extends Controller
// prepare for currency conversion and data collection: // prepare for currency conversion and data collection:
/** @var TransactionCurrency $default */ /** @var TransactionCurrency $default */
$default = app('amount')->getDefaultCurrency(); $default = app('amount')->getNativeCurrency();
// get journals for entire period: // get journals for entire period:

View File

@@ -63,7 +63,7 @@ class BudgetController extends Controller
$this->repository = app(BudgetRepositoryInterface::class); $this->repository = app(BudgetRepositoryInterface::class);
$this->blRepository = app(BudgetLimitRepositoryInterface::class); $this->blRepository = app(BudgetLimitRepositoryInterface::class);
$this->opsRepository = app(OperationsRepositoryInterface::class); $this->opsRepository = app(OperationsRepositoryInterface::class);
$this->currency = app('amount')->getDefaultCurrency(); $this->currency = app('amount')->getNativeCurrency();
$userGroup = $this->validateUserGroup($request); $userGroup = $this->validateUserGroup($request);
$this->repository->setUserGroup($userGroup); $this->repository->setUserGroup($userGroup);
$this->opsRepository->setUserGroup($userGroup); $this->opsRepository->setUserGroup($userGroup);

View File

@@ -82,7 +82,7 @@ class CategoryController extends Controller
/** @var Carbon $end */ /** @var Carbon $end */
$end = $this->parameters->get('end'); $end = $this->parameters->get('end');
$accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]); $accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]);
$default = app('amount')->getDefaultCurrency(); $default = app('amount')->getNativeCurrency();
$converter = new ExchangeRateConverter(); $converter = new ExchangeRateConverter();
$currencies = []; $currencies = [];
$return = []; $return = [];

View File

@@ -118,7 +118,7 @@ class BasicController extends Controller
private function getBalanceInformation(Carbon $start, Carbon $end): array private function getBalanceInformation(Carbon $start, Carbon $end): array
{ {
$object = new SummaryBalanceGrouped(); $object = new SummaryBalanceGrouped();
$default = app('amount')->getDefaultCurrency(); $default = app('amount')->getNativeCurrency();
$object->setDefault($default); $object->setDefault($default);
@@ -233,7 +233,7 @@ class BasicController extends Controller
$available = $this->abRepository->getAvailableBudgetWithCurrency($start, $end); $available = $this->abRepository->getAvailableBudgetWithCurrency($start, $end);
$budgets = $this->budgetRepository->getActiveBudgets(); $budgets = $this->budgetRepository->getActiveBudgets();
$spent = $this->opsRepository->listExpenses($start, $end, null, $budgets); $spent = $this->opsRepository->listExpenses($start, $end, null, $budgets);
$default = app('amount')->getDefaultCurrency(); $default = app('amount')->getNativeCurrency();
$currencies = []; $currencies = [];
$converter = new ExchangeRateConverter(); $converter = new ExchangeRateConverter();

View File

@@ -109,8 +109,8 @@ class InfiniteListRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'start' => 'date', 'start' => 'date|after:1900-01-01|before:2099-12-31',
'end' => 'date|after:start', 'end' => 'date|after:start|after:1900-01-01|before:2099-12-31',
'start_row' => 'integer|min:0|max:4294967296', 'start_row' => 'integer|min:0|max:4294967296',
'end_row' => 'integer|min:0|max:4294967296|gt:start_row', 'end_row' => 'integer|min:0|max:4294967296|gt:start_row',
]; ];

View File

@@ -84,8 +84,8 @@ class ListRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'start' => 'date', 'start' => 'date|after:1900-01-01|before:2099-12-31',
'end' => 'date|after:start', 'end' => 'date|after:start|after:1900-01-01|before:2099-12-31',
]; ];
} }
} }

View File

@@ -63,7 +63,7 @@ class CorrectsCurrencies extends Command
$repos = app(CurrencyRepositoryInterface::class); $repos = app(CurrencyRepositoryInterface::class);
// first check if the user has any default currency (not necessarily the case, so can be forced). // first check if the user has any default currency (not necessarily the case, so can be forced).
$defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($userGroup); $defaultCurrency = app('amount')->getNativeCurrencyByUserGroup($userGroup);
Log::debug(sprintf('Now correcting currencies for user group #%d', $userGroup->id)); Log::debug(sprintf('Now correcting currencies for user group #%d', $userGroup->id));
$found = [$defaultCurrency->id]; $found = [$defaultCurrency->id];

View File

@@ -88,7 +88,7 @@ class CorrectsNativeAmounts extends Command
// do a check with the group's currency so we can skip some stuff. // do a check with the group's currency so we can skip some stuff.
Preferences::mark(); Preferences::mark();
$currency = app('amount')->getDefaultCurrencyByUserGroup($userGroup); $currency = app('amount')->getNativeCurrencyByUserGroup($userGroup);
$this->recalculatePiggyBanks($userGroup, $currency); $this->recalculatePiggyBanks($userGroup, $currency);
$this->recalculateBudgets($userGroup, $currency); $this->recalculateBudgets($userGroup, $currency);

View File

@@ -132,6 +132,6 @@ class CorrectsOpeningBalanceCurrencies extends Command
$repos = app(AccountRepositoryInterface::class); $repos = app(AccountRepositoryInterface::class);
$repos->setUser($account->user); $repos->setUser($account->user);
return $repos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->userGroup); return $repos->getAccountCurrency($account) ?? app('amount')->getNativeCurrencyByUserGroup($account->userGroup);
} }
} }

View File

@@ -38,7 +38,7 @@ class CreatesDatabase extends Command
public function handle(): int public function handle(): int
{ {
if ('mysql' !== env('DB_CONNECTION')) { if ('mysql' !== env('DB_CONNECTION')) { // @phpstan-ignore larastan.noEnvCallsOutsideOfConfig */
$this->friendlyInfo(sprintf('CreateDB does not apply to "%s", skipped.', env('DB_CONNECTION'))); $this->friendlyInfo(sprintf('CreateDB does not apply to "%s", skipped.', env('DB_CONNECTION')));
return 0; return 0;
@@ -60,7 +60,7 @@ class CreatesDatabase extends Command
// when it fails, display error // when it fails, display error
try { try {
$pdo = new \PDO($dsn, env('DB_USERNAME'), env('DB_PASSWORD'), $options); $pdo = new \PDO($dsn, (string) env('DB_USERNAME'), (string) env('DB_PASSWORD'), $options);
} catch (\PDOException $e) { } catch (\PDOException $e) {
$this->friendlyError(sprintf('Error when connecting to DB: %s', $e->getMessage())); $this->friendlyError(sprintf('Error when connecting to DB: %s', $e->getMessage()));

View File

@@ -107,7 +107,7 @@ class UpgradesAccountCurrencies extends Command
$accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value]); $accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value]);
// get user's currency preference: // get user's currency preference:
$defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); $defaultCurrency = app('amount')->getNativeCurrencyByUserGroup($user->userGroup);
/** @var Account $account */ /** @var Account $account */
foreach ($accounts as $account) { foreach ($accounts as $account) {

View File

@@ -66,7 +66,7 @@ class UpgradesBudgetLimits extends Command
/** @var null|User $user */ /** @var null|User $user */
$user = $budget->user; $user = $budget->user;
if (null !== $user) { if (null !== $user) {
$currency = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); $currency = app('amount')->getNativeCurrencyByUserGroup($user->userGroup);
$budgetLimit->transaction_currency_id = $currency->id; $budgetLimit->transaction_currency_id = $currency->id;
$budgetLimit->save(); $budgetLimit->save();
$this->friendlyInfo( $this->friendlyInfo(

View File

@@ -90,7 +90,7 @@ class UpgradesMultiPiggyBanks extends Command
$this->repository->setUser($piggyBank->account->user); $this->repository->setUser($piggyBank->account->user);
$this->accountRepository->setUser($piggyBank->account->user); $this->accountRepository->setUser($piggyBank->account->user);
$repetition = $this->repository->getRepetition($piggyBank, true); $repetition = $this->repository->getRepetition($piggyBank, true);
$currency = $this->accountRepository->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrencyByUserGroup($piggyBank->account->user->userGroup); $currency = $this->accountRepository->getAccountCurrency($piggyBank->account) ?? app('amount')->getNativeCurrencyByUserGroup($piggyBank->account->user->userGroup);
// update piggy bank to have a currency. // update piggy bank to have a currency.
$piggyBank->transaction_currency_id = $currency->id; $piggyBank->transaction_currency_id = $currency->id;

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Exceptions; namespace FireflyIII\Exceptions;
use Brick\Math\Exception\NumberFormatException;
use FireflyIII\Jobs\MailError; use FireflyIII\Jobs\MailError;
use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\AuthenticationException; use Illuminate\Auth\AuthenticationException;
@@ -124,7 +125,7 @@ class Handler extends ExceptionHandler
if ($e instanceof BadRequestHttpException) { if ($e instanceof BadRequestHttpException) {
app('log')->debug('Return JSON BadRequestHttpException.'); app('log')->debug('Return JSON BadRequestHttpException.');
return response()->json(['message' => $e->getMessage(), 'exception' => 'BadRequestHttpException'], 400); return response()->json(['message' => $e->getMessage(), 'exception' => 'HttpException'], 400);
} }
if ($e instanceof BadHttpHeaderException) { if ($e instanceof BadHttpHeaderException) {
@@ -133,6 +134,14 @@ class Handler extends ExceptionHandler
return response()->json(['message' => $e->getMessage(), 'exception' => 'BadHttpHeaderException'], $e->statusCode); return response()->json(['message' => $e->getMessage(), 'exception' => 'BadHttpHeaderException'], $e->statusCode);
} }
if (($e instanceof ValidationException || $e instanceof NumberFormatException) && $expectsJson) {
$errorCode = 422;
return response()->json(
['message' => sprintf('Validation exception: %s', $e->getMessage()), 'errors' => ['field' => 'Field is invalid']],
$errorCode
);
}
if ($expectsJson) { if ($expectsJson) {
$errorCode = 500; $errorCode = 500;
@@ -156,7 +165,7 @@ class Handler extends ExceptionHandler
app('log')->debug(sprintf('Return JSON %s.', get_class($e))); app('log')->debug(sprintf('Return JSON %s.', get_class($e)));
return response()->json( return response()->json(
['message' => sprintf('Internal Firefly III Exception: %s', $e->getMessage()), 'exception' => get_class($e)], ['message' => sprintf('Internal Firefly III Exception: %s', $e->getMessage()), 'exception' => 'UndisclosedException'],
$errorCode $errorCode
); );
} }

View File

@@ -49,7 +49,7 @@ class BillFactory
app('log')->debug(sprintf('Now in %s', __METHOD__), $data); app('log')->debug(sprintf('Now in %s', __METHOD__), $data);
$factory = app(TransactionCurrencyFactory::class); $factory = app(TransactionCurrencyFactory::class);
$currency = $factory->find((int) ($data['currency_id'] ?? null), (string) ($data['currency_code'] ?? null)) ?? $currency = $factory->find((int) ($data['currency_id'] ?? null), (string) ($data['currency_code'] ?? null)) ??
app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); app('amount')->getNativeCurrencyByUserGroup($this->user->userGroup);
try { try {
$skip = array_key_exists('skip', $data) ? $data['skip'] : 0; $skip = array_key_exists('skip', $data) ? $data['skip'] : 0;

View File

@@ -121,7 +121,7 @@ class PiggyBankFactory
private function getCurrency(array $data): TransactionCurrency private function getCurrency(array $data): TransactionCurrency
{ {
// currency: // currency:
$defaultCurrency = app('amount')->getDefaultCurrency(); $defaultCurrency = app('amount')->getNativeCurrency();
$currency = null; $currency = null;
if (array_key_exists('transaction_currency_code', $data)) { if (array_key_exists('transaction_currency_code', $data)) {
$currency = $this->currencyRepository->findByCode((string) ($data['transaction_currency_code'] ?? '')); $currency = $this->currencyRepository->findByCode((string) ($data['transaction_currency_code'] ?? ''));

View File

@@ -466,7 +466,7 @@ class TransactionJournalFactory
$preference = $this->accountRepository->getAccountCurrency($account); $preference = $this->accountRepository->getAccountCurrency($account);
if (null === $preference && null === $currency) { if (null === $preference && null === $currency) {
// return user's default: // return user's default:
return app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); return app('amount')->getNativeCurrencyByUserGroup($this->user->userGroup);
} }
$result = $preference ?? $currency; $result = $preference ?? $currency;
app('log')->debug(sprintf('Currency is now #%d (%s) because of account #%d (%s)', $result->id, $result->code, $account->id, $account->name)); app('log')->debug(sprintf('Currency is now #%d (%s) because of account #%d (%s)', $result->id, $result->code, $account->id, $account->name));
@@ -576,7 +576,7 @@ class TransactionJournalFactory
private function storeLocation(TransactionJournal $journal, NullArrayObject $data): void private function storeLocation(TransactionJournal $journal, NullArrayObject $data): void
{ {
if (true === $data['store_location']) { if (null !== $data['longitude'] && null !== $data['latitude'] && null !== $data['zoom_level']) {
$location = new Location(); $location = new Location();
$location->longitude = $data['longitude']; $location->longitude = $data['longitude'];
$location->latitude = $data['latitude']; $location->latitude = $data['latitude'];

View File

@@ -135,7 +135,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
$journals = array_reverse($journals, true); $journals = array_reverse($journals, true);
$dayBeforeBalance = Steam::finalAccountBalance($account, $date); $dayBeforeBalance = Steam::finalAccountBalance($account, $date);
$startBalance = $dayBeforeBalance['balance']; $startBalance = $dayBeforeBalance['balance'];
$defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); $defaultCurrency = app('amount')->getNativeCurrencyByUserGroup($account->user->userGroup);
$currency = $accountRepository->getAccountCurrency($account) ?? $defaultCurrency; $currency = $accountRepository->getAccountCurrency($account) ?? $defaultCurrency;
foreach ($journals as $index => $journal) { foreach ($journals as $index => $journal) {

View File

@@ -47,7 +47,7 @@ class AccountObserver
if (!Amount::convertToNative($account->user)) { if (!Amount::convertToNative($account->user)) {
return; return;
} }
$userCurrency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); $userCurrency = app('amount')->getNativeCurrencyByUserGroup($account->user->userGroup);
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
$currency = $repository->getAccountCurrency($account); $currency = $repository->getAccountCurrency($account);
if (null !== $currency && $currency->id !== $userCurrency->id && '' !== (string) $account->virtual_balance && 0 !== bccomp($account->virtual_balance, '0')) { if (null !== $currency && $currency->id !== $userCurrency->id && '' !== (string) $account->virtual_balance && 0 !== bccomp($account->virtual_balance, '0')) {

View File

@@ -48,7 +48,7 @@ class AutoBudgetObserver
if (!Amount::convertToNative($autoBudget->budget->user)) { if (!Amount::convertToNative($autoBudget->budget->user)) {
return; return;
} }
$userCurrency = app('amount')->getDefaultCurrencyByUserGroup($autoBudget->budget->user->userGroup); $userCurrency = app('amount')->getNativeCurrencyByUserGroup($autoBudget->budget->user->userGroup);
$autoBudget->native_amount = null; $autoBudget->native_amount = null;
if ($autoBudget->transactionCurrency->id !== $userCurrency->id) { if ($autoBudget->transactionCurrency->id !== $userCurrency->id) {
$converter = new ExchangeRateConverter(); $converter = new ExchangeRateConverter();

View File

@@ -50,7 +50,7 @@ class AvailableBudgetObserver
return; return;
} }
$userCurrency = app('amount')->getDefaultCurrencyByUserGroup($availableBudget->user->userGroup); $userCurrency = app('amount')->getNativeCurrencyByUserGroup($availableBudget->user->userGroup);
$availableBudget->native_amount = null; $availableBudget->native_amount = null;
if ($availableBudget->transactionCurrency->id !== $userCurrency->id) { if ($availableBudget->transactionCurrency->id !== $userCurrency->id) {
$converter = new ExchangeRateConverter(); $converter = new ExchangeRateConverter();

View File

@@ -59,7 +59,7 @@ class BillObserver
if (!Amount::convertToNative($bill->user)) { if (!Amount::convertToNative($bill->user)) {
return; return;
} }
$userCurrency = app('amount')->getDefaultCurrencyByUserGroup($bill->user->userGroup); $userCurrency = app('amount')->getNativeCurrencyByUserGroup($bill->user->userGroup);
$bill->native_amount_min = null; $bill->native_amount_min = null;
$bill->native_amount_max = null; $bill->native_amount_max = null;
if ($bill->transactionCurrency->id !== $userCurrency->id) { if ($bill->transactionCurrency->id !== $userCurrency->id) {

View File

@@ -50,7 +50,7 @@ class BudgetLimitObserver
return; return;
} }
$userCurrency = app('amount')->getDefaultCurrencyByUserGroup($budgetLimit->budget->user->userGroup); $userCurrency = app('amount')->getNativeCurrencyByUserGroup($budgetLimit->budget->user->userGroup);
$budgetLimit->native_amount = null; $budgetLimit->native_amount = null;
if ($budgetLimit->transactionCurrency->id !== $userCurrency->id) { if ($budgetLimit->transactionCurrency->id !== $userCurrency->id) {
$converter = new ExchangeRateConverter(); $converter = new ExchangeRateConverter();

View File

@@ -48,7 +48,7 @@ class PiggyBankEventObserver
if (!Amount::convertToNative($event->piggyBank->accounts()->first()->user)) { if (!Amount::convertToNative($event->piggyBank->accounts()->first()->user)) {
return; return;
} }
$userCurrency = app('amount')->getDefaultCurrencyByUserGroup($event->piggyBank->accounts()->first()->user->userGroup); $userCurrency = app('amount')->getNativeCurrencyByUserGroup($event->piggyBank->accounts()->first()->user->userGroup);
$event->native_amount = null; $event->native_amount = null;
if ($event->piggyBank->transactionCurrency->id !== $userCurrency->id) { if ($event->piggyBank->transactionCurrency->id !== $userCurrency->id) {
$converter = new ExchangeRateConverter(); $converter = new ExchangeRateConverter();

View File

@@ -70,7 +70,7 @@ class PiggyBankObserver
return; return;
} }
$userCurrency = app('amount')->getDefaultCurrencyByUserGroup($group); $userCurrency = app('amount')->getNativeCurrencyByUserGroup($group);
$piggyBank->native_target_amount = null; $piggyBank->native_target_amount = null;
if ($piggyBank->transactionCurrency->id !== $userCurrency->id) { if ($piggyBank->transactionCurrency->id !== $userCurrency->id) {
$converter = new ExchangeRateConverter(); $converter = new ExchangeRateConverter();

View File

@@ -71,7 +71,7 @@ class TransactionObserver
if (!Amount::convertToNative($transaction->transactionJournal->user)) { if (!Amount::convertToNative($transaction->transactionJournal->user)) {
return; return;
} }
$userCurrency = app('amount')->getDefaultCurrencyByUserGroup($transaction->transactionJournal->user->userGroup); $userCurrency = app('amount')->getNativeCurrencyByUserGroup($transaction->transactionJournal->user->userGroup);
$transaction->native_amount = null; $transaction->native_amount = null;
$transaction->native_foreign_amount = null; $transaction->native_foreign_amount = null;
// first normal amount // first normal amount

View File

@@ -77,7 +77,7 @@ class NetWorth implements NetWorthInterface
return $cache->get(); return $cache->get();
} }
Log::debug(sprintf('Now in byAccounts("%s", "%s")', $ids, $date->format('Y-m-d H:i:s'))); Log::debug(sprintf('Now in byAccounts("%s", "%s")', $ids, $date->format('Y-m-d H:i:s')));
$default = Amount::getDefaultCurrency(); $default = Amount::getNativeCurrency();
$netWorth = []; $netWorth = [];
$balances = Steam::finalAccountsBalance($accounts, $date); $balances = Steam::finalAccountsBalance($accounts, $date);

View File

@@ -106,6 +106,7 @@ class IndexController extends Controller
$account->interestPeriod = (string) trans(sprintf('firefly.interest_calc_%s', $this->repository->getMetaValue($account, 'interest_period'))); $account->interestPeriod = (string) trans(sprintf('firefly.interest_calc_%s', $this->repository->getMetaValue($account, 'interest_period')));
$account->accountTypeString = (string) trans(sprintf('firefly.account_type_%s', $account->accountType->type)); $account->accountTypeString = (string) trans(sprintf('firefly.account_type_%s', $account->accountType->type));
$account->current_debt = '0'; $account->current_debt = '0';
$account->currency = $currency ?? $this->defaultCurrency;
$account->iban = implode(' ', str_split((string) $account->iban, 4)); $account->iban = implode(' ', str_split((string) $account->iban, 4));
} }
); );

View File

@@ -95,17 +95,22 @@ abstract class Controller extends BaseController
// share is alpha, is beta // share is alpha, is beta
$isAlpha = false; $isAlpha = false;
$isBeta = false;
$isDevelop = false;
if (str_contains(config('firefly.version'), 'alpha')) { if (str_contains(config('firefly.version'), 'alpha')) {
$isAlpha = true; $isAlpha = true;
} }
if (str_contains(config('firefly.version'), 'develop') || str_contains(config('firefly.version'), 'branch')) {
$isDevelop = true;
}
$isBeta = false;
if (str_contains(config('firefly.version'), 'beta')) { if (str_contains(config('firefly.version'), 'beta')) {
$isBeta = true; $isBeta = true;
} }
View::share('FF_IS_ALPHA', $isAlpha); View::share('FF_IS_ALPHA', $isAlpha);
View::share('FF_IS_BETA', $isBeta); View::share('FF_IS_BETA', $isBeta);
View::share('FF_IS_DEVELOP', $isDevelop);
$this->middleware( $this->middleware(
function ($request, $next): mixed { function ($request, $next): mixed {
@@ -118,7 +123,7 @@ abstract class Controller extends BaseController
$this->defaultCurrency =null; $this->defaultCurrency =null;
// get shown-intro-preference: // get shown-intro-preference:
if (auth()->check()) { if (auth()->check()) {
$this->defaultCurrency = Amount::getDefaultCurrency(); $this->defaultCurrency = Amount::getNativeCurrency();
$language = Steam::getLanguage(); $language = Steam::getLanguage();
$locale = Steam::getLocale(); $locale = Steam::getLocale();
$darkMode = app('preferences')->get('darkMode', 'browser')->data; $darkMode = app('preferences')->get('darkMode', 'browser')->data;

View File

@@ -31,6 +31,9 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Middleware\IsDemoUser; use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Controllers\GetConfigurationData; use FireflyIII\Support\Http\Controllers\GetConfigurationData;
use FireflyIII\Support\Models\AccountBalanceCalculator; use FireflyIII\Support\Models\AccountBalanceCalculator;
use FireflyIII\User; use FireflyIII\User;
@@ -61,7 +64,7 @@ class DebugController extends Controller
$this->middleware(IsDemoUser::class)->except(['displayError']); $this->middleware(IsDemoUser::class)->except(['displayError']);
} }
public function routes(): never public function routes(Request $request): never
{ {
if (!auth()->user()->hasRole('owner')) { if (!auth()->user()->hasRole('owner')) {
throw new NotFoundHttpException(); throw new NotFoundHttpException();
@@ -69,6 +72,46 @@ class DebugController extends Controller
/** @var iterable $routes */ /** @var iterable $routes */
$routes = Route::getRoutes(); $routes = Route::getRoutes();
if ('true' === $request->get('api')) {
$collection = [];
$i = 0;
echo 'PATHS="';
/** @var \Illuminate\Routing\Route $route */
foreach ($routes as $route) {
++$i;
// skip API and other routes.
if (!str_starts_with($route->uri(), 'api/v1')
) {
continue;
}
// skip non GET routes
if (!in_array('GET', $route->methods(), true)) {
continue;
}
// no name route:
if (null === $route->getName()) {
var_dump($route);
exit;
}
echo substr($route->uri(), 3);
if (0 === $i % 5) {
echo '"<br>PATHS="${PATHS},';
}
if (0 !== $i % 5) {
echo ',';
}
}
exit;
}
$return = []; $return = [];
/** @var \Illuminate\Routing\Route $route */ /** @var \Illuminate\Routing\Route $route */
@@ -153,7 +196,7 @@ class DebugController extends Controller
*/ */
public function flush(Request $request) public function flush(Request $request)
{ {
app('preferences')->mark(); Preferences::mark();
$request->session()->forget(['start', 'end', '_previous', 'viewRange', 'range', 'is_custom_range', 'temp-mfa-secret', 'temp-mfa-codes']); $request->session()->forget(['start', 'end', '_previous', 'viewRange', 'range', 'is_custom_range', 'temp-mfa-secret', 'temp-mfa-codes']);
Artisan::call('cache:clear'); Artisan::call('cache:clear');
@@ -223,8 +266,8 @@ class DebugController extends Controller
private function getSystemInformation(): array private function getSystemInformation(): array
{ {
$maxFileSize = app('steam')->phpBytes((string) ini_get('upload_max_filesize')); $maxFileSize = Steam::phpBytes((string) ini_get('upload_max_filesize'));
$maxPostSize = app('steam')->phpBytes((string) ini_get('post_max_size')); $maxPostSize = Steam::phpBytes((string) ini_get('post_max_size'));
$drivers = \DB::availableDrivers(); $drivers = \DB::availableDrivers();
$currentDriver = \DB::getDriverName(); $currentDriver = \DB::getDriverName();
@@ -314,7 +357,7 @@ class DebugController extends Controller
]; ];
} }
private function getuserInfo(): array private function getUserInfo(): array
{ {
$userFlags = $this->getUserFlags(); $userFlags = $this->getUserFlags();
@@ -324,7 +367,7 @@ class DebugController extends Controller
// set languages, see what happens: // set languages, see what happens:
$original = setlocale(LC_ALL, '0'); $original = setlocale(LC_ALL, '0');
$localeAttempts = []; $localeAttempts = [];
$parts = app('steam')->getLocaleArray(app('steam')->getLocale()); $parts = Steam::getLocaleArray(Steam::getLocale());
foreach ($parts as $code) { foreach ($parts as $code) {
$code = trim($code); $code = trim($code);
app('log')->debug(sprintf('Trying to set %s', $code)); app('log')->debug(sprintf('Trying to set %s', $code));
@@ -338,10 +381,12 @@ class DebugController extends Controller
'user_count' => User::count(), 'user_count' => User::count(),
'user_flags' => $userFlags, 'user_flags' => $userFlags,
'user_agent' => $userAgent, 'user_agent' => $userAgent,
'native' => Amount::getNativeCurrency(),
'convert_to_native' => Amount::convertToNative(),
'locale_attempts' => $localeAttempts, 'locale_attempts' => $localeAttempts,
'locale' => app('steam')->getLocale(), 'locale' => Steam::getLocale(),
'language' => app('steam')->getLanguage(), 'language' => Steam::getLanguage(),
'view_range' => app('preferences')->get('viewRange', '1M')->data, 'view_range' => Preferences::get('viewRange', '1M')->data,
]; ];
} }

View File

@@ -27,6 +27,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
/** /**
@@ -43,10 +44,14 @@ class FrontpageController extends Controller
{ {
$set = $repository->getPiggyBanks(); $set = $repository->getPiggyBanks();
$info = []; $info = [];
$native = Amount::getNativeCurrency();
$convertToNative = Amount::convertToNative();
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
foreach ($set as $piggyBank) { foreach ($set as $piggyBank) {
$amount = $repository->getCurrentAmount($piggyBank); $amount = $repository->getCurrentAmount($piggyBank);
$nativeAmount = $repository->getCurrentNativeAmount($piggyBank);
if (1 === bccomp($amount, '0')) { if (1 === bccomp($amount, '0')) {
// percentage! // percentage!
$pct = 0; $pct = 0;
@@ -58,8 +63,16 @@ class FrontpageController extends Controller
'id' => $piggyBank->id, 'id' => $piggyBank->id,
'name' => $piggyBank->name, 'name' => $piggyBank->name,
'amount' => $amount, 'amount' => $amount,
'native_amount' => $nativeAmount,
'target' => $piggyBank->target_amount, 'target' => $piggyBank->target_amount,
'native_target' => $piggyBank->native_target_amount,
'percentage' => $pct, 'percentage' => $pct,
// currency:
'currency_symbol' => $piggyBank->transactionCurrency->symbol,
'currency_decimal_places' => $piggyBank->transactionCurrency->decimal_places,
'native_currency_symbol' => $native->symbol,
'native_currency_decimal_places' => $native->decimal_places,
]; ];
$info[] = $entry; $info[] = $entry;
@@ -74,11 +87,10 @@ class FrontpageController extends Controller
} }
); );
$html = ''; $html = '';
if (0 !== count($info)) { if (0 !== count($info)) {
try { try {
$html = view('json.piggy-banks', compact('info'))->render(); $html = view('json.piggy-banks', compact('info', 'convertToNative', 'native'))->render();
} catch (\Throwable $e) { } catch (\Throwable $e) {
app('log')->error(sprintf('Cannot render json.piggy-banks: %s', $e->getMessage())); app('log')->error(sprintf('Cannot render json.piggy-banks: %s', $e->getMessage()));
app('log')->error($e->getTraceAsString()); app('log')->error($e->getTraceAsString());

View File

@@ -31,6 +31,7 @@ use FireflyIII\Http\Requests\PreferencesRequest;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Preference; use FireflyIII\Models\Preference;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
@@ -93,35 +94,35 @@ class PreferencesController extends Controller
/** @var array<int, int> $accountIds */ /** @var array<int, int> $accountIds */
$accountIds = $accounts->pluck('id')->toArray(); $accountIds = $accounts->pluck('id')->toArray();
$viewRange = app('navigation')->getViewRange(false); $viewRange = app('navigation')->getViewRange(false);
$frontpageAccountsPref = app('preferences')->get('frontpageAccounts', $accountIds); $frontpageAccountsPref = Preferences::get('frontpageAccounts', $accountIds);
$frontpageAccounts = $frontpageAccountsPref->data; $frontpageAccounts = $frontpageAccountsPref->data;
if (!is_array($frontpageAccounts)) { if (!is_array($frontpageAccounts)) {
$frontpageAccounts = $accountIds; $frontpageAccounts = $accountIds;
} }
$language = app('steam')->getLanguage(); $language = app('steam')->getLanguage();
$languages = config('firefly.languages'); $languages = config('firefly.languages');
$locale = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data; $locale = Preferences::get('locale', config('firefly.default_locale', 'equal'))->data;
$listPageSize = app('preferences')->get('listPageSize', 50)->data; $listPageSize = Preferences::get('listPageSize', 50)->data;
$darkMode = app('preferences')->get('darkMode', 'browser')->data; $darkMode = Preferences::get('darkMode', 'browser')->data;
$customFiscalYear = app('preferences')->get('customFiscalYear', 0)->data; $customFiscalYear = Preferences::get('customFiscalYear', 0)->data;
$fiscalYearStartStr = app('preferences')->get('fiscalYearStart', '01-01')->data; $fiscalYearStartStr = Preferences::get('fiscalYearStart', '01-01')->data;
$convertToNative = $this->convertToNative; $convertToNative = $this->convertToNative;
if (is_array($fiscalYearStartStr)) { if (is_array($fiscalYearStartStr)) {
$fiscalYearStartStr = '01-01'; $fiscalYearStartStr = '01-01';
} }
$fiscalYearStart = sprintf('%s-%s', date('Y'), (string) $fiscalYearStartStr); $fiscalYearStart = sprintf('%s-%s', date('Y'), (string) $fiscalYearStartStr);
$tjOptionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data; $tjOptionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
$availableDarkModes = config('firefly.available_dark_modes'); $availableDarkModes = config('firefly.available_dark_modes');
// notifications settings // notifications settings
$slackUrl = app('preferences')->getEncrypted('slack_webhook_url', '')->data; $slackUrl = Preferences::getEncrypted('slack_webhook_url', '')->data;
$pushoverAppToken = (string) app('preferences')->getEncrypted('pushover_app_token', '')->data; $pushoverAppToken = (string) Preferences::getEncrypted('pushover_app_token', '')->data;
$pushoverUserToken = (string) app('preferences')->getEncrypted('pushover_user_token', '')->data; $pushoverUserToken = (string) Preferences::getEncrypted('pushover_user_token', '')->data;
$ntfyServer = app('preferences')->getEncrypted('ntfy_server', 'https://ntfy.sh')->data; $ntfyServer = Preferences::getEncrypted('ntfy_server', 'https://ntfy.sh')->data;
$ntfyTopic = (string) app('preferences')->getEncrypted('ntfy_topic', '')->data; $ntfyTopic = (string) Preferences::getEncrypted('ntfy_topic', '')->data;
$ntfyAuth = app('preferences')->get('ntfy_auth', false)->data; $ntfyAuth = Preferences::get('ntfy_auth', false)->data;
$ntfyUser = app('preferences')->getEncrypted('ntfy_user', '')->data; $ntfyUser = Preferences::getEncrypted('ntfy_user', '')->data;
$ntfyPass = (string) app('preferences')->getEncrypted('ntfy_pass', '')->data; $ntfyPass = (string) Preferences::getEncrypted('ntfy_pass', '')->data;
$channels = config('notifications.channels'); $channels = config('notifications.channels');
$forcedAvailability = []; $forcedAvailability = [];
@@ -131,7 +132,7 @@ class PreferencesController extends Controller
if (true === $info['enabled']) { if (true === $info['enabled']) {
$notifications[$key] $notifications[$key]
= [ = [
'enabled' => true === app('preferences')->get(sprintf('notification_%s', $key), true)->data, 'enabled' => true === Preferences::get(sprintf('notification_%s', $key), true)->data,
'configurable' => $info['configurable'], 'configurable' => $info['configurable'],
]; ];
} }
@@ -221,7 +222,7 @@ class PreferencesController extends Controller
foreach ($request->get('frontpageAccounts') as $id) { foreach ($request->get('frontpageAccounts') as $id) {
$frontpageAccounts[] = (int) $id; $frontpageAccounts[] = (int) $id;
} }
app('preferences')->set('frontpageAccounts', $frontpageAccounts); Preferences::set('frontpageAccounts', $frontpageAccounts);
} }
// extract notifications: // extract notifications:
@@ -229,15 +230,15 @@ class PreferencesController extends Controller
foreach (config('notifications.notifications.user') as $key => $info) { foreach (config('notifications.notifications.user') as $key => $info) {
$key = sprintf('notification_%s', $key); $key = sprintf('notification_%s', $key);
if (array_key_exists($key, $all)) { if (array_key_exists($key, $all)) {
app('preferences')->set($key, true); Preferences::set($key, true);
} }
if (!array_key_exists($key, $all)) { if (!array_key_exists($key, $all)) {
app('preferences')->set($key, false); Preferences::set($key, false);
} }
} }
// view range: // view range:
app('preferences')->set('viewRange', $request->get('viewRange')); Preferences::set('viewRange', $request->get('viewRange'));
// forget session values: // forget session values:
session()->forget('start'); session()->forget('start');
session()->forget('end'); session()->forget('end');
@@ -249,13 +250,13 @@ class PreferencesController extends Controller
$variables = ['slack_webhook_url', 'pushover_app_token', 'pushover_user_token', 'ntfy_server', 'ntfy_topic', 'ntfy_user', 'ntfy_pass']; $variables = ['slack_webhook_url', 'pushover_app_token', 'pushover_user_token', 'ntfy_server', 'ntfy_topic', 'ntfy_user', 'ntfy_pass'];
foreach ($variables as $variable) { foreach ($variables as $variable) {
if ('' === $all[$variable]) { if ('' === $all[$variable]) {
app('preferences')->delete($variable); Preferences::delete($variable);
} }
if ('' !== $all[$variable]) { if ('' !== $all[$variable]) {
app('preferences')->setEncrypted($variable, $all[$variable]); Preferences::setEncrypted($variable, $all[$variable]);
} }
} }
app('preferences')->set('ntfy_auth', $all['ntfy_auth'] ?? false); Preferences::set('ntfy_auth', $all['ntfy_auth'] ?? false);
} }
// convert native // convert native
@@ -265,30 +266,30 @@ class PreferencesController extends Controller
Log::debug('User sets convertToNative to true.'); Log::debug('User sets convertToNative to true.');
event(new UserGroupChangedDefaultCurrency(auth()->user()->userGroup)); event(new UserGroupChangedDefaultCurrency(auth()->user()->userGroup));
} }
app('preferences')->set('convert_to_native', $convertToNative); Preferences::set('convert_to_native', $convertToNative);
// custom fiscal year // custom fiscal year
$customFiscalYear = 1 === (int) $request->get('customFiscalYear'); $customFiscalYear = 1 === (int) $request->get('customFiscalYear');
$string = strtotime((string) $request->get('fiscalYearStart')); $string = strtotime((string) $request->get('fiscalYearStart'));
if (false !== $string) { if (false !== $string) {
$fiscalYearStart = date('m-d', $string); $fiscalYearStart = date('m-d', $string);
app('preferences')->set('customFiscalYear', $customFiscalYear); Preferences::set('customFiscalYear', $customFiscalYear);
app('preferences')->set('fiscalYearStart', $fiscalYearStart); Preferences::set('fiscalYearStart', $fiscalYearStart);
} }
// save page size: // save page size:
app('preferences')->set('listPageSize', 50); Preferences::set('listPageSize', 50);
$listPageSize = (int) $request->get('listPageSize'); $listPageSize = (int) $request->get('listPageSize');
if ($listPageSize > 0 && $listPageSize < 1337) { if ($listPageSize > 0 && $listPageSize < 1337) {
app('preferences')->set('listPageSize', $listPageSize); Preferences::set('listPageSize', $listPageSize);
} }
// language: // language:
/** @var Preference $currentLang */ /** @var Preference $currentLang */
$currentLang = app('preferences')->get('language', 'en_US'); $currentLang = Preferences::get('language', 'en_US');
$lang = $request->get('language'); $lang = $request->get('language');
if (array_key_exists($lang, config('firefly.languages'))) { if (array_key_exists($lang, config('firefly.languages'))) {
app('preferences')->set('language', $lang); Preferences::set('language', $lang);
} }
if ($currentLang->data !== $lang) { if ($currentLang->data !== $lang) {
// this string is untranslated on purpose. // this string is untranslated on purpose.
@@ -299,7 +300,7 @@ class PreferencesController extends Controller
if (!auth()->user()->hasRole('demo')) { if (!auth()->user()->hasRole('demo')) {
$locale = (string) $request->get('locale'); $locale = (string) $request->get('locale');
$locale = '' === $locale ? null : $locale; $locale = '' === $locale ? null : $locale;
app('preferences')->set('locale', $locale); Preferences::set('locale', $locale);
} }
// optional fields for transactions: // optional fields for transactions:
@@ -318,16 +319,16 @@ class PreferencesController extends Controller
'location' => array_key_exists('location', $setOptions), 'location' => array_key_exists('location', $setOptions),
'links' => array_key_exists('links', $setOptions), 'links' => array_key_exists('links', $setOptions),
]; ];
app('preferences')->set('transaction_journal_optional_fields', $optionalTj); Preferences::set('transaction_journal_optional_fields', $optionalTj);
// dark mode // dark mode
$darkMode = $request->get('darkMode') ?? 'browser'; $darkMode = $request->get('darkMode') ?? 'browser';
if (in_array($darkMode, config('firefly.available_dark_modes'), true)) { if (in_array($darkMode, config('firefly.available_dark_modes'), true)) {
app('preferences')->set('darkMode', $darkMode); Preferences::set('darkMode', $darkMode);
} }
session()->flash('success', (string) trans('firefly.saved_preferences')); session()->flash('success', (string) trans('firefly.saved_preferences'));
app('preferences')->mark(); Preferences::mark();
return redirect(route('preferences.index')); return redirect(route('preferences.index'));
} }

View File

@@ -42,8 +42,6 @@ class EditController extends Controller
$mainTitleIcon = 'fa-book'; $mainTitleIcon = 'fa-book';
app('log')->debug(sprintf('Now at %s', __METHOD__)); app('log')->debug(sprintf('Now at %s', __METHOD__));
return view('administrations.edit') // @phpstan-ignore-line return view('administrations.edit')->with(compact('title', 'subTitle', 'mainTitleIcon'));
->with(compact('title', 'subTitle', 'mainTitleIcon'))
;
} }
} }

View File

@@ -159,6 +159,7 @@ class InterestingMessage
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
/** @var null|UserGroup $userGroup */
$userGroup = UserGroup::find($userGroupId); $userGroup = UserGroup::find($userGroupId);
$valid = false; $valid = false;
$memberships = $user->groupMemberships()->get(); $memberships = $user->groupMemberships()->get();

View File

@@ -118,7 +118,7 @@ class Range
// save some formats: // save some formats:
$monthAndDayFormat = (string) trans('config.month_and_day_js', [], $locale); $monthAndDayFormat = (string) trans('config.month_and_day_js', [], $locale);
$dateTimeFormat = (string) trans('config.date_time_js', [], $locale); $dateTimeFormat = (string) trans('config.date_time_js', [], $locale);
$defaultCurrency = Amount::getDefaultCurrency(); $defaultCurrency = Amount::getNativeCurrency();
// also format for moment JS: // also format for moment JS:
$madMomentJS = (string) trans('config.month_and_day_moment_js', [], $locale); $madMomentJS = (string) trans('config.month_and_day_moment_js', [], $locale);

View File

@@ -126,7 +126,7 @@ class PiggyBankStoreRequest extends FormRequest
$currencyId = (int) ($data['transaction_currency_id'] ?? 0); $currencyId = (int) ($data['transaction_currency_id'] ?? 0);
$currency = TransactionCurrency::find($currencyId); $currency = TransactionCurrency::find($currencyId);
if (null === $currency) { if (null === $currency) {
return Amount::getDefaultCurrency(); return Amount::getNativeCurrency();
} }
return $currency; return $currency;

View File

@@ -129,7 +129,7 @@ class PiggyBankUpdateRequest extends FormRequest
$currencyId = (int) ($data['transaction_currency_id'] ?? 0); $currencyId = (int) ($data['transaction_currency_id'] ?? 0);
$currency = TransactionCurrency::find($currencyId); $currency = TransactionCurrency::find($currencyId);
if (null === $currency) { if (null === $currency) {
return Amount::getDefaultCurrency(); return Amount::getNativeCurrency();
} }
return $currency; return $currency;

View File

@@ -419,7 +419,9 @@ class CreateRecurringTransactions implements ShouldQueue
/** @var RecurrenceTransaction $transaction */ /** @var RecurrenceTransaction $transaction */
foreach ($transactions as $index => $transaction) { foreach ($transactions as $index => $transaction) {
$single = [ $single = [
'type' => null === $transaction->transactionType->type ? strtolower($recurrence->transactionType->type) : strtolower($transaction->transactionType->type), 'type' => null === $transaction?->transactionType?->type ?
strtolower($recurrence->transactionType->type) :
strtolower($transaction->transactionType->type),
'date' => $date, 'date' => $date,
'user' => $recurrence->user_id, 'user' => $recurrence->user_id,
'currency_id' => $transaction->transaction_currency_id, 'currency_id' => $transaction->transaction_currency_id,

View File

@@ -73,7 +73,7 @@ class TransactionCurrency extends Model
public function refreshForUser(User $user): void public function refreshForUser(User $user): void
{ {
$current = $user->userGroup->currencies()->where('transaction_currencies.id', $this->id)->first(); $current = $user->userGroup->currencies()->where('transaction_currencies.id', $this->id)->first();
$native = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); $native = app('amount')->getNativeCurrencyByUserGroup($user->userGroup);
$this->userGroupNative = $native->id === $this->id; $this->userGroupNative = $native->id === $this->id;
$this->userGroupEnabled = null !== $current; $this->userGroupEnabled = null !== $current;
} }

View File

@@ -355,7 +355,7 @@ class AccountRepository implements AccountRepositoryInterface
if (AccountTypeEnum::ASSET->value !== $account->accountType->type) { if (AccountTypeEnum::ASSET->value !== $account->accountType->type) {
throw new FireflyException(sprintf('%s is not an asset account.', $account->name)); throw new FireflyException(sprintf('%s is not an asset account.', $account->name));
} }
$currency = $this->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency(); $currency = $this->getAccountCurrency($account) ?? app('amount')->getNativeCurrency();
$name = trans('firefly.reconciliation_account_name', ['name' => $account->name, 'currency' => $currency->code]); $name = trans('firefly.reconciliation_account_name', ['name' => $account->name, 'currency' => $currency->code]);
/** @var AccountType $type */ /** @var AccountType $type */

View File

@@ -53,7 +53,7 @@ class AccountTasker implements AccountTaskerInterface
/** @var AccountRepositoryInterface $repository */ /** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
$defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); $defaultCurrency = app('amount')->getNativeCurrencyByUserGroup($this->user->userGroup);
$return = [ $return = [
'accounts' => [], 'accounts' => [],
@@ -143,7 +143,7 @@ class AccountTasker implements AccountTaskerInterface
*/ */
private function groupExpenseByDestination(array $array): array private function groupExpenseByDestination(array $array): array
{ {
$defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); $defaultCurrency = app('amount')->getNativeCurrencyByUserGroup($this->user->userGroup);
/** @var CurrencyRepositoryInterface $currencyRepos */ /** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class); $currencyRepos = app(CurrencyRepositoryInterface::class);
@@ -231,7 +231,7 @@ class AccountTasker implements AccountTaskerInterface
*/ */
private function groupIncomeBySource(array $array): array private function groupIncomeBySource(array $array): array
{ {
$defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); $defaultCurrency = app('amount')->getNativeCurrencyByUserGroup($this->user->userGroup);
/** @var CurrencyRepositoryInterface $currencyRepos */ /** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class); $currencyRepos = app(CurrencyRepositoryInterface::class);

Some files were not shown because too many files have changed in this diff Show More