mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-08-26 13:17:30 -05:00
🤖 Auto commit for release 'develop' on 2026-08-09
This commit is contained in:
Generated
+6
-6
@@ -1331,16 +1331,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v8.1.2",
|
||||
"version": "v8.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "535e18a1b8925f6c01a55b171d157ab66c2ace15"
|
||||
"reference": "68efa2ebfd9a362951eb5a8b09fd177c66ddec24"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/535e18a1b8925f6c01a55b171d157ab66c2ace15",
|
||||
"reference": "535e18a1b8925f6c01a55b171d157ab66c2ace15",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/68efa2ebfd9a362951eb5a8b09fd177c66ddec24",
|
||||
"reference": "68efa2ebfd9a362951eb5a8b09fd177c66ddec24",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1407,7 +1407,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v8.1.2"
|
||||
"source": "https://github.com/symfony/console/tree/v8.1.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1427,7 +1427,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-07-27T13:58:19+00:00"
|
||||
"time": "2026-07-31T12:43:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
|
||||
@@ -158,10 +158,7 @@ final class TagController extends Controller
|
||||
'currency_id' => (string) $foreignCurrencyId,
|
||||
'currency_code' => $journal['foreign_currency_code'],
|
||||
];
|
||||
$response[$foreignKey]['difference'] = bcadd(
|
||||
(string) $response[$foreignKey]['difference'],
|
||||
Steam::positive($journal['foreign_amount'])
|
||||
);
|
||||
$response[$foreignKey]['difference'] = bcadd((string) $response[$foreignKey]['difference'], Steam::positive($journal['foreign_amount']));
|
||||
$response[$foreignKey]['difference_float'] = (float) $response[$foreignKey]['difference'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,10 +155,7 @@ final class TagController extends Controller
|
||||
'currency_id' => (string) $foreignCurrencyId,
|
||||
'currency_code' => $journal['foreign_currency_code'],
|
||||
];
|
||||
$response[$foreignKey]['difference'] = bcadd(
|
||||
(string) $response[$foreignKey]['difference'],
|
||||
Steam::positive($journal['foreign_amount'])
|
||||
);
|
||||
$response[$foreignKey]['difference'] = bcadd((string) $response[$foreignKey]['difference'], Steam::positive($journal['foreign_amount']));
|
||||
$response[$foreignKey]['difference_float'] = (float) $response[$foreignKey]['difference']; // intentional float
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ class GracefulNotFoundHandler extends ExceptionHandler
|
||||
if (!auth()->check()) {
|
||||
return parent::render($request, $e);
|
||||
}
|
||||
|
||||
switch ($name) {
|
||||
default:
|
||||
Log::warning(sprintf('GracefulNotFoundHandler cannot handle route with name "%s"', $name));
|
||||
@@ -261,6 +262,7 @@ class GracefulNotFoundHandler extends ExceptionHandler
|
||||
|
||||
if (TransactionTypeEnum::RECONCILIATION->value === $type) {
|
||||
session()->flash('info', trans('errors.note_not_found_reconciliation', ['description' => $group->title ?? $journal->description]));
|
||||
|
||||
return redirect(route('accounts.index', ['asset']));
|
||||
}
|
||||
session()->flash('info', trans('errors.note_not_found_group', ['description' => $group->title ?? $journal->description]));
|
||||
|
||||
@@ -44,7 +44,7 @@ final class CreateController extends Controller
|
||||
{
|
||||
use ModelInformation;
|
||||
|
||||
private AttachmentHelperInterface $attachments;
|
||||
private AttachmentHelperInterface $attachments;
|
||||
private AccountRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
@@ -57,7 +57,7 @@ final class CreateController extends Controller
|
||||
// translations:
|
||||
$this->middleware(function ($request, $next) {
|
||||
app('view')->share('mainTitleIcon', 'bi-credit-card');
|
||||
app('view')->share('title', (string)trans('firefly.accounts'));
|
||||
app('view')->share('title', (string) trans('firefly.accounts'));
|
||||
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->attachments = app(AttachmentHelperInterface::class);
|
||||
@@ -71,12 +71,12 @@ final class CreateController extends Controller
|
||||
*
|
||||
* @return Factory|View
|
||||
*/
|
||||
public function create(Request $request, string $objectType): Factory | \Illuminate\Contracts\View\View
|
||||
public function create(Request $request, string $objectType): Factory|\Illuminate\Contracts\View\View
|
||||
{
|
||||
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType));
|
||||
$subTitle = (string)trans(sprintf('firefly.make_new_%s_account', $objectType));
|
||||
$subTitle = (string) trans(sprintf('firefly.make_new_%s_account', $objectType));
|
||||
$roles = $this->getRoles();
|
||||
$from = $request->input('_from', '');
|
||||
$from = $request->input('_from', '');
|
||||
$liabilityTypes = $this->getLiabilityTypes();
|
||||
$hasOldInput = null !== $request->old('_token');
|
||||
$locations = [
|
||||
@@ -90,7 +90,7 @@ final class CreateController extends Controller
|
||||
$liabilityDirections = ['debit' => trans('firefly.liability_direction_debit'), 'credit' => trans('firefly.liability_direction_credit')];
|
||||
|
||||
// interest calculation periods:
|
||||
$interestPeriods = [];
|
||||
$interestPeriods = [];
|
||||
foreach (config('firefly.interest_periods') as $period) {
|
||||
$interestPeriods[$period] = trans(sprintf('firefly.interest_calc_%s', $period));
|
||||
}
|
||||
@@ -98,10 +98,10 @@ final class CreateController extends Controller
|
||||
// prefill some data
|
||||
$request->session()->flash('preFilled', [
|
||||
'currency_id' => $this->primaryCurrency->id,
|
||||
'include_net_worth' => !$hasOldInput || (bool)$request->old('include_net_worth'),
|
||||
'include_net_worth' => !$hasOldInput || (bool) $request->old('include_net_worth'),
|
||||
]);
|
||||
// issue #8321
|
||||
$showNetWorth = true;
|
||||
$showNetWorth = true;
|
||||
if ('liabilities' !== $objectType && 'asset' !== $objectType) {
|
||||
$showNetWorth = false;
|
||||
}
|
||||
@@ -129,9 +129,9 @@ final class CreateController extends Controller
|
||||
|
||||
public function store(AccountFormRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getAccountData();
|
||||
$account = $this->repository->store($data);
|
||||
$request->session()->flash('success', (string)trans('firefly.stored_new_account', ['name' => $account->name]));
|
||||
$data = $request->getAccountData();
|
||||
$account = $this->repository->store($data);
|
||||
$request->session()->flash('success', (string) trans('firefly.stored_new_account', ['name' => $account->name]));
|
||||
Preferences::mark();
|
||||
|
||||
Log::channel('audit')->info('Stored new account.', $data);
|
||||
@@ -148,13 +148,13 @@ final class CreateController extends Controller
|
||||
|
||||
// store attachment(s):
|
||||
/** @var null|array $files */
|
||||
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
|
||||
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
|
||||
if (null !== $files && !auth()->user()->hasRole('demo')) {
|
||||
$this->attachments->saveAttachmentsForModel($account, $files);
|
||||
}
|
||||
if (null !== $files && auth()->user()->hasRole('demo')) {
|
||||
Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
|
||||
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
|
||||
session()->flash('info', (string) trans('firefly.no_att_demo_user'));
|
||||
}
|
||||
|
||||
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
|
||||
@@ -162,9 +162,9 @@ final class CreateController extends Controller
|
||||
}
|
||||
|
||||
// redirect to previous URL.
|
||||
$from = $request->input('_from', '');
|
||||
$redirect = redirect(route('index') . $from);
|
||||
if (1 === (int)$request->input('create_another')) {
|
||||
$from = $request->input('_from', '');
|
||||
$redirect = redirect(route('index').$from);
|
||||
if (1 === (int) $request->input('create_another')) {
|
||||
// set value so create routine will not overwrite URL:
|
||||
$request->session()->put('accounts.create.fromStore', true);
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ final class EditController extends Controller
|
||||
if (!$this->isEditableAccount($account)) {
|
||||
return $this->redirectAccountToAccount($account);
|
||||
}
|
||||
$from = $request->input('_from', '');
|
||||
$from = $request->input('_from', '');
|
||||
$objectType = config('firefly.shortNamesByFullName')[$account->accountType->type];
|
||||
$subTitle = (string) trans(sprintf('firefly.edit_%s_account', $objectType), ['name' => $account->name]);
|
||||
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType));
|
||||
@@ -165,7 +165,7 @@ final class EditController extends Controller
|
||||
$request->session()->flash('preFilled', $preFilled);
|
||||
|
||||
return view('accounts.edit', [
|
||||
'from'=>$from,
|
||||
'from' => $from,
|
||||
'account' => $account,
|
||||
'currency' => $currency,
|
||||
'canEditCurrency' => $canEditCurrency,
|
||||
@@ -214,8 +214,9 @@ final class EditController extends Controller
|
||||
}
|
||||
|
||||
// redirect
|
||||
$from = $request->input('_from', '');
|
||||
$redirect = redirect(route('index') . $from);;
|
||||
$from = $request->input('_from', '');
|
||||
$redirect = redirect(route('index').$from);
|
||||
|
||||
if (1 === (int) $request->input('return_to_edit')) {
|
||||
// set value so edit routine will not overwrite URL:
|
||||
$request->session()->put('accounts.edit.fromUpdate', true);
|
||||
|
||||
@@ -198,13 +198,7 @@ final class BudgetLimitController extends Controller
|
||||
if ($request->expectsJson()) {
|
||||
$array = $limit->toArray();
|
||||
// add some extra metadata:
|
||||
$spentArr = $this->opsRepository->sumExpenses(
|
||||
$limit->start_date,
|
||||
$limit->end_date,
|
||||
null,
|
||||
new Collection()->push($budget),
|
||||
$currency
|
||||
);
|
||||
$spentArr = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection()->push($budget), $currency);
|
||||
$array['spent'] = $spentArr[$currency->id]['sum'] ?? '0';
|
||||
$array['left_formatted'] = Amount::formatAnything($limit->transactionCurrency, bcadd($array['spent'], (string) $array['amount']));
|
||||
$array['amount_formatted'] = Amount::formatAnything($limit->transactionCurrency, $limit['amount']);
|
||||
|
||||
@@ -284,10 +284,7 @@ final class IndexController extends Controller
|
||||
|
||||
if (array_key_exists($currency->id, $spentArr) && array_key_exists('sum', $spentArr[$currency->id])) {
|
||||
$array['spent'][$currency->id]['spent'] = $spentArr[$currency->id]['sum'];
|
||||
$array['spent'][$currency->id]['spent_outside'] = Steam::negative(bcsub(
|
||||
$spentInLimits[$currency->id],
|
||||
$spentArr[$currency->id]['sum']
|
||||
));
|
||||
$array['spent'][$currency->id]['spent_outside'] = Steam::negative(bcsub($spentInLimits[$currency->id], $spentArr[$currency->id]['sum']));
|
||||
$array['spent'][$currency->id]['currency_id'] = $currency->id;
|
||||
$array['spent'][$currency->id]['currency_symbol'] = $currency->symbol;
|
||||
$array['spent'][$currency->id]['currency_decimal_places'] = $currency->decimal_places;
|
||||
|
||||
@@ -539,13 +539,7 @@ final class BudgetController extends Controller
|
||||
}
|
||||
|
||||
// get spent amount in this period for this currency.
|
||||
$sum = $this->opsRepository->sumExpenses(
|
||||
$currentStart,
|
||||
$currentEnd,
|
||||
$accounts,
|
||||
new Collection()->push($budget),
|
||||
$currency
|
||||
);
|
||||
$sum = $this->opsRepository->sumExpenses($currentStart, $currentEnd, $accounts, new Collection()->push($budget), $currency);
|
||||
$amount = Steam::positive($sum[$currency->id]['sum'] ?? '0');
|
||||
$chartData[0]['entries'][$title] = Steam::bcround($amount, $currency->decimal_places);
|
||||
|
||||
|
||||
@@ -41,9 +41,10 @@ use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\Facades\View;
|
||||
|
||||
use function Safe\ini_get;
|
||||
use function Safe\realpath;
|
||||
use function Safe\parse_url;
|
||||
use function Safe\realpath;
|
||||
|
||||
/**
|
||||
* Class Controller.
|
||||
@@ -60,13 +61,13 @@ abstract class Controller extends BaseController
|
||||
use ValidatesRequests;
|
||||
|
||||
// fails on PHP < 8.4
|
||||
public protected(set) string $name;
|
||||
protected bool $convertToPrimary = false;
|
||||
protected string $dateTimeFormat;
|
||||
public protected(set) string $name;
|
||||
protected bool $convertToPrimary = false;
|
||||
protected string $dateTimeFormat;
|
||||
protected ?TransactionCurrency $primaryCurrency;
|
||||
protected string $monthAndDayFormat;
|
||||
protected string $monthFormat;
|
||||
protected string $redirectUrl = '/';
|
||||
protected string $monthAndDayFormat;
|
||||
protected string $monthFormat;
|
||||
protected string $redirectUrl = '/';
|
||||
|
||||
/**
|
||||
* Controller constructor.
|
||||
@@ -82,7 +83,7 @@ abstract class Controller extends BaseController
|
||||
|
||||
return;
|
||||
}
|
||||
$isDemoSite = (bool)$isDemoSiteConfig->data;
|
||||
$isDemoSite = (bool) $isDemoSiteConfig->data;
|
||||
View::share('IS_DEMO_SITE', $isDemoSite);
|
||||
View::share('DEMO_USERNAME', config('firefly.demo_username'));
|
||||
View::share('DEMO_PASSWORD', config('firefly.demo_password'));
|
||||
@@ -90,12 +91,12 @@ abstract class Controller extends BaseController
|
||||
View::share('FF_BUILD_TIME', config('firefly.build_time'));
|
||||
View::share('FF3_FROM', $this->getFromUrl());
|
||||
// this breaks when running < PHP 8.5 and is totally intentional.
|
||||
$input = ' James is cool';
|
||||
$output = $input
|
||||
|> trim(...)
|
||||
|> (fn(string $string) => str_replace(' ', '-', $string))
|
||||
|> (fn(string $string) => str_replace(['.', '/', '…'], '', $string))
|
||||
|> strtolower(...);
|
||||
$input = ' James is cool';
|
||||
$output = $input
|
||||
|> trim(...)
|
||||
|> (fn (string $string) => str_replace(' ', '-', $string))
|
||||
|> (fn (string $string) => str_replace(['.', '/', '…'], '', $string))
|
||||
|> strtolower(...);
|
||||
|
||||
// is webhooks enabled?
|
||||
View::share(
|
||||
@@ -106,8 +107,8 @@ abstract class Controller extends BaseController
|
||||
View::share('featuringCer', true === AppConfiguration::get('enable_exchange_rates', config('cer.enabled'))->data);
|
||||
|
||||
// share custom auth guard info.
|
||||
$authGuard = config('firefly.authentication_guard');
|
||||
$logoutUrl = config('firefly.custom_logout_url');
|
||||
$authGuard = config('firefly.authentication_guard');
|
||||
$logoutUrl = config('firefly.custom_logout_url');
|
||||
|
||||
// overrule v2 layout back to v1.
|
||||
|
||||
@@ -127,17 +128,17 @@ abstract class Controller extends BaseController
|
||||
View::share('uploadSize', $uploadSize);
|
||||
|
||||
// share is alpha, is beta
|
||||
$isAlpha = false;
|
||||
$isBeta = false;
|
||||
$isDevelop = false;
|
||||
if (str_contains((string)config('firefly.version'), 'alpha')) {
|
||||
$isAlpha = false;
|
||||
$isBeta = false;
|
||||
$isDevelop = false;
|
||||
if (str_contains((string) config('firefly.version'), 'alpha')) {
|
||||
$isAlpha = true;
|
||||
}
|
||||
if (str_contains((string)config('firefly.version'), 'develop') || str_contains((string)config('firefly.version'), 'branch')) {
|
||||
if (str_contains((string) config('firefly.version'), 'develop') || str_contains((string) config('firefly.version'), 'branch')) {
|
||||
$isDevelop = true;
|
||||
}
|
||||
|
||||
if (str_contains((string)config('firefly.version'), 'beta')) {
|
||||
if (str_contains((string) config('firefly.version'), 'beta')) {
|
||||
$isBeta = true;
|
||||
}
|
||||
|
||||
@@ -146,11 +147,11 @@ abstract class Controller extends BaseController
|
||||
View::share('FF_IS_DEVELOP', $isDevelop);
|
||||
|
||||
$this->middleware(function ($request, $next): mixed {
|
||||
$locale = Steam::getLocale();
|
||||
$locale = Steam::getLocale();
|
||||
// translations for specific strings:
|
||||
$this->monthFormat = (string)trans('config.month_js', [], $locale);
|
||||
$this->monthAndDayFormat = (string)trans('config.month_and_day_js', [], $locale);
|
||||
$this->dateTimeFormat = (string)trans('config.date_time_js', [], $locale);
|
||||
$this->monthFormat = (string) trans('config.month_js', [], $locale);
|
||||
$this->monthAndDayFormat = (string) trans('config.month_and_day_js', [], $locale);
|
||||
$this->dateTimeFormat = (string) trans('config.date_time_js', [], $locale);
|
||||
$darkMode = 'browser';
|
||||
$this->primaryCurrency = null;
|
||||
// get shown-intro-preference:
|
||||
@@ -191,12 +192,13 @@ abstract class Controller extends BaseController
|
||||
if (array_key_exists('path', $current)) {
|
||||
$from = $current['path'];
|
||||
}
|
||||
if (array_key_exists('query', $current) && strlen($current['query']) > 0) {
|
||||
$from .= '?' . $current['query'];
|
||||
if (array_key_exists('query', $current) && '' !== $current['query']) {
|
||||
$from .= '?'.$current['query'];
|
||||
}
|
||||
if (array_key_exists('fragment', $current) && strlen($current['fragment']) > 0) {
|
||||
$from .= '#' . $current['fragment'];
|
||||
if (array_key_exists('fragment', $current) && '' !== $current['fragment']) {
|
||||
$from .= '#'.$current['fragment'];
|
||||
}
|
||||
|
||||
return $from;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ class AccountFormRequest extends FormRequest
|
||||
'what' => 'in:'.$types,
|
||||
'interest_period' => 'in:daily,monthly,yearly',
|
||||
'notes' => ['min:1', 'max:32768', 'nullable'],
|
||||
'_from' => ['string','min:0','max:255', new IsValidOriginUrl()],
|
||||
'_from' => ['string', 'min:0', 'max:255', new IsValidOriginUrl()],
|
||||
];
|
||||
$rules = Location::requestRules($rules);
|
||||
|
||||
|
||||
@@ -122,13 +122,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
||||
// if has one, calculate expenses and use that as a base.
|
||||
$repository = app(OperationsRepositoryInterface::class);
|
||||
$repository->setUser($autoBudget->budget->user);
|
||||
$spent = $repository->sumExpenses(
|
||||
$previousStart,
|
||||
$previousEnd,
|
||||
null,
|
||||
new Collection()->push($autoBudget->budget),
|
||||
$autoBudget->transactionCurrency
|
||||
);
|
||||
$spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection()->push($autoBudget->budget), $autoBudget->transactionCurrency);
|
||||
$currencyId = $autoBudget->transaction_currency_id;
|
||||
$spentAmount = $spent[$currencyId]['sum'] ?? '0';
|
||||
Log::debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount));
|
||||
@@ -218,13 +212,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
||||
// if has one, calculate expenses and use that as a base.
|
||||
$repository = app(OperationsRepositoryInterface::class);
|
||||
$repository->setUser($autoBudget->budget->user);
|
||||
$spent = $repository->sumExpenses(
|
||||
$previousStart,
|
||||
$previousEnd,
|
||||
null,
|
||||
new Collection()->push($autoBudget->budget),
|
||||
$autoBudget->transactionCurrency
|
||||
);
|
||||
$spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection()->push($autoBudget->budget), $autoBudget->transactionCurrency);
|
||||
$currencyId = $autoBudget->transaction_currency_id;
|
||||
$spentAmount = $spent[$currencyId]['sum'] ?? '0';
|
||||
Log::debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount));
|
||||
|
||||
@@ -63,7 +63,7 @@ class Account extends Model
|
||||
/**
|
||||
* Route binder. Converts the key in the URL to the specified object (or throw 404).
|
||||
*
|
||||
* @throws NotFoundHttpException|AuthenticationException
|
||||
* @throws AuthenticationException|NotFoundHttpException
|
||||
*/
|
||||
public static function routeBinder(self|string $value): self
|
||||
{
|
||||
@@ -81,6 +81,7 @@ class Account extends Model
|
||||
if (null !== $account) {
|
||||
return $account;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ class Attachment extends Model
|
||||
if (null !== $attachment) {
|
||||
return $attachment;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ class AvailableBudget extends Model
|
||||
if (null !== $availableBudget) {
|
||||
return $availableBudget;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@ class Bill extends Model
|
||||
if (null !== $bill) {
|
||||
return $bill;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ class Budget extends Model
|
||||
if (null !== $budget) {
|
||||
return $budget;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ class BudgetLimit extends Model
|
||||
if (null !== $budgetLimit) {
|
||||
return $budgetLimit;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ class Category extends Model
|
||||
if (null !== $category) {
|
||||
return $category;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ class InvitedUser extends Model
|
||||
if (null !== $attempt) {
|
||||
return $attempt;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ class LinkType extends Model
|
||||
if (null !== $linkType) {
|
||||
return $linkType;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ class ObjectGroup extends Model
|
||||
if (null !== $objectGroup) {
|
||||
return $objectGroup;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ class PiggyBank extends Model
|
||||
if (null !== $piggyBank) {
|
||||
return $piggyBank;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ class Preference extends Model
|
||||
|
||||
return $preference;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +93,7 @@ class Recurrence extends Model
|
||||
if (null !== $recurrence) {
|
||||
return $recurrence;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ class Rule extends Model
|
||||
if (null !== $rule) {
|
||||
return $rule;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ class RuleGroup extends Model
|
||||
if (null !== $ruleGroup) {
|
||||
return $ruleGroup;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ class Tag extends Model
|
||||
if (null !== $tag) {
|
||||
return $tag;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ class TransactionCurrency extends Model
|
||||
|
||||
return $currency;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ class TransactionJournal extends Model
|
||||
if (null !== $journal) {
|
||||
return $journal;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ class TransactionJournalLink extends Model
|
||||
if (null !== $link) {
|
||||
return $link;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ class WebhookAttempt extends Model
|
||||
if (null !== $attempt && $attempt->webhookMessage->webhook->user_id === $user->id) {
|
||||
return $attempt;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ class WebhookMessage extends Model
|
||||
if (null !== $message && $message->webhook->user_id === $user->id) {
|
||||
return $message;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* IsValidOriginUrl.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
@@ -24,27 +27,29 @@ namespace FireflyIII\Rules\System;
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Safe\Exceptions\UrlException;
|
||||
use Override;
|
||||
|
||||
use function Safe\parse_url;
|
||||
|
||||
class IsValidOriginUrl implements ValidationRule
|
||||
{
|
||||
|
||||
#[\Override]
|
||||
#[Override]
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
if (!auth()->check()) {
|
||||
$fail('validation.no_auth_present')->translate();
|
||||
$fail('validation.no_auth_present')->translate();
|
||||
|
||||
return;
|
||||
}
|
||||
$value = (string)$value;
|
||||
if(str_contains($value, '%2F')) {
|
||||
$value = (string) $value;
|
||||
if (str_contains($value, '%2F')) {
|
||||
$value = urldecode($value);
|
||||
}
|
||||
if('' === $value) {
|
||||
if ('' === $value) {
|
||||
// string can be empty.
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$parts = parse_url($value);
|
||||
} catch (UrlException) {
|
||||
@@ -52,12 +57,14 @@ class IsValidOriginUrl implements ValidationRule
|
||||
|
||||
return;
|
||||
}
|
||||
if(!array_key_exists('path', $parts) || array_key_exists('scheme', $parts) || array_key_exists('host', $parts)) {
|
||||
if (!array_key_exists('path', $parts) || array_key_exists('scheme', $parts) || array_key_exists('host', $parts)) {
|
||||
$fail('validation.bad_url_parts')->translate();
|
||||
|
||||
return;
|
||||
}
|
||||
if(!str_starts_with($parts['path'], '/')) {
|
||||
if (!str_starts_with($parts['path'], '/')) {
|
||||
$fail('validation.bad_url_parts')->translate();
|
||||
|
||||
// return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,14 +222,7 @@ trait AugumentData
|
||||
$currentEnd->addMonth();
|
||||
}
|
||||
// primary currency amount.
|
||||
$expenses = $opsRepository->sumExpenses(
|
||||
$currentStart,
|
||||
$currentEnd,
|
||||
null,
|
||||
$budgetCollection,
|
||||
$entry->transactionCurrency,
|
||||
$this->convertToPrimary
|
||||
);
|
||||
$expenses = $opsRepository->sumExpenses($currentStart, $currentEnd, null, $budgetCollection, $entry->transactionCurrency, $this->convertToPrimary);
|
||||
$spent = $expenses[$currency->id]['sum'] ?? '0';
|
||||
$entry->pc_spent = $spent;
|
||||
|
||||
|
||||
@@ -354,11 +354,7 @@ class RecurringEnrichment implements EnrichmentInterface
|
||||
|
||||
/** @var RecurrenceRepetition $repetition */
|
||||
foreach ($set as $repetition) {
|
||||
$recurrence = $this->collection->filter(
|
||||
static fn (Recurrence $item): bool => (int) $item->id === (int) $repetition->recurrence_id
|
||||
)
|
||||
->first()
|
||||
;
|
||||
$recurrence = $this->collection->filter(static fn (Recurrence $item): bool => (int) $item->id === (int) $repetition->recurrence_id)->first();
|
||||
$fromDate = clone ($recurrence->latest_date ?? $recurrence->first_date);
|
||||
$recurrenceId = (int) $repetition->recurrence_id;
|
||||
$repId = (int) $repetition->id;
|
||||
|
||||
@@ -102,6 +102,7 @@ class User extends Authenticatable implements OAuthenticatable
|
||||
if (null !== $user) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
|
||||
Generated
+5
-5
@@ -2508,16 +2508,16 @@
|
||||
},
|
||||
{
|
||||
"name": "league/commonmark",
|
||||
"version": "2.9.0",
|
||||
"version": "2.9.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/commonmark.git",
|
||||
"reference": "5703d83ba3da3b2e356a5fedc848ed6d8ffb6529"
|
||||
"reference": "73cb188c785abfa7a7bec73487148202968274c6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/5703d83ba3da3b2e356a5fedc848ed6d8ffb6529",
|
||||
"reference": "5703d83ba3da3b2e356a5fedc848ed6d8ffb6529",
|
||||
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/73cb188c785abfa7a7bec73487148202968274c6",
|
||||
"reference": "73cb188c785abfa7a7bec73487148202968274c6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2611,7 +2611,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-08-03T13:42:31+00:00"
|
||||
"time": "2026-08-09T14:10:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/config",
|
||||
|
||||
+2
-2
@@ -78,8 +78,8 @@ return [
|
||||
'running_balance_column' => (bool)env_default_when_empty(env('USE_RUNNING_BALANCE'), true), // this is only the default value, is not used.
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => 'develop/2026-08-07',
|
||||
'build_time' => 1786105226,
|
||||
'version' => 'develop/2026-08-09',
|
||||
'build_time' => 1786295232,
|
||||
'api_version' => '2.1.0', // field is no longer used.
|
||||
'db_version' => 28, // field is no longer used.
|
||||
|
||||
|
||||
Generated
+23
-23
@@ -2701,9 +2701,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "26.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz",
|
||||
"integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==",
|
||||
"version": "26.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz",
|
||||
"integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3499,9 +3499,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.11.12",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.12.tgz",
|
||||
"integrity": "sha512-r7WnVImvVCeFpf2DOXfy41aPWzeNg3H/A2X4dKmy1QL0MSyyk/e7z8ihJ3N6Nn2PsdhkVlqnEfnUE4a05P2aTA==",
|
||||
"version": "2.11.13",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.13.tgz",
|
||||
"integrity": "sha512-k9HNuUVMlqVjQ9UHzfPjIqiDbWw7WqT1AoT7GL8VwvF3r0ZfArtgiSPAlmupyNquNgOJHTuH4CKYf8ttMTWBTQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
@@ -3779,9 +3779,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.28.7",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz",
|
||||
"integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==",
|
||||
"version": "4.28.8",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz",
|
||||
"integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -3799,11 +3799,11 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.10.44",
|
||||
"caniuse-lite": "^1.0.30001806",
|
||||
"electron-to-chromium": "^1.5.393",
|
||||
"node-releases": "^2.0.51",
|
||||
"update-browserslist-db": "^1.2.3"
|
||||
"baseline-browser-mapping": "^2.11.12",
|
||||
"caniuse-lite": "^1.0.30001809",
|
||||
"electron-to-chromium": "^1.5.402",
|
||||
"node-releases": "^2.0.53",
|
||||
"update-browserslist-db": "^1.3.0"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
@@ -5186,9 +5186,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.402",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.402.tgz",
|
||||
"integrity": "sha512-/oOpMaPT6Yg+6/1XQhyIPlzgj7Ye9zf+nNM2Uh6OcE2G2oNptWazFa+qB2Pdqqbsc9KnIDzgAntoYN0dbwOXwA==",
|
||||
"version": "1.5.403",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.403.tgz",
|
||||
"integrity": "sha512-MQsYmdaLzvaCX5j+ZZBr5Fm6uCCnPQcRtlvmvRlWqrXy+BH2O4ffXIAScF+JQznQWB9brWp4lSD9Z4yNmaf2BA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@@ -7954,9 +7954,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.17",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz",
|
||||
"integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==",
|
||||
"version": "3.3.18",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
|
||||
"integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -11888,9 +11888,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.21.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.2.tgz",
|
||||
"integrity": "sha512-54dMVAo4WIe6SKy3vBgN+9bJZqqQ8IMRevAkOLQALhi49qkkQDQfWdAZ8KQlXiEabw88ARXXdUrlvtbKQX+aKw==",
|
||||
"version": "8.21.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz",
|
||||
"integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
||||
@@ -23,37 +23,37 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'404_header' => 'Firefly III cannot find this page.',
|
||||
'404_page_does_not_exist' => 'The page you have requested does not exist. Please check that you have not entered the wrong URL. Did you make a typo perhaps?',
|
||||
'404_send_error' => 'If you were redirected to this page automatically, please accept my apologies. There is a mention of this error in your log files and I would be grateful if you sent me the error to me.',
|
||||
'404_github_link' => 'If you are sure this page should exist, please open a ticket on <strong><a href="https://github.com/firefly-iii/firefly-iii/issues">GitHub</a></strong>.',
|
||||
'whoops' => 'Whoops',
|
||||
'note_not_found_account' => 'Account ":name" has been deleted and can no longer be viewed. Please enjoy this overview of all other accounts of the same type.',
|
||||
'note_not_found_group' => 'Transaction ":description" has been deleted and can no longer be viewed. Please enjoy this overview of all other transactions of the same type.',
|
||||
'404_header' => 'Firefly III cannot find this page.',
|
||||
'404_page_does_not_exist' => 'The page you have requested does not exist. Please check that you have not entered the wrong URL. Did you make a typo perhaps?',
|
||||
'404_send_error' => 'If you were redirected to this page automatically, please accept my apologies. There is a mention of this error in your log files and I would be grateful if you sent me the error to me.',
|
||||
'404_github_link' => 'If you are sure this page should exist, please open a ticket on <strong><a href="https://github.com/firefly-iii/firefly-iii/issues">GitHub</a></strong>.',
|
||||
'whoops' => 'Whoops',
|
||||
'note_not_found_account' => 'Account ":name" has been deleted and can no longer be viewed. Please enjoy this overview of all other accounts of the same type.',
|
||||
'note_not_found_group' => 'Transaction ":description" has been deleted and can no longer be viewed. Please enjoy this overview of all other transactions of the same type.',
|
||||
'note_not_found_reconciliation' => 'Reconciliation ":description" has been deleted and can no longer be viewed. Here is an overview of the account it belonged to.',
|
||||
|
||||
'fatal_error' => 'There was a fatal error. Please check the log files in "storage/logs" or use "docker logs -f [container]" to see what\'s going on.',
|
||||
'maintenance_mode' => 'Firefly III is in maintenance mode.',
|
||||
'be_right_back' => 'Be right back!',
|
||||
'check_back' => 'Firefly III is down for some necessary maintenance. Please check back in a second. If you happen to see this message on the demo site, just wait a few minutes. The database is reset every few hours.',
|
||||
'error_occurred' => 'Whoops! An error occurred.',
|
||||
'db_error_occurred' => 'Whoops! A database error occurred.',
|
||||
'error_not_recoverable' => 'Unfortunately, this error was not recoverable :(. Firefly III broke. The error is:',
|
||||
'error' => 'Error',
|
||||
'error_location' => 'This error occurred in file <span style="font-family: monospace;">:file</span> on line :line with code :code.',
|
||||
'stacktrace' => 'Stack trace',
|
||||
'more_info' => 'More information',
|
||||
'fatal_error' => 'There was a fatal error. Please check the log files in "storage/logs" or use "docker logs -f [container]" to see what\'s going on.',
|
||||
'maintenance_mode' => 'Firefly III is in maintenance mode.',
|
||||
'be_right_back' => 'Be right back!',
|
||||
'check_back' => 'Firefly III is down for some necessary maintenance. Please check back in a second. If you happen to see this message on the demo site, just wait a few minutes. The database is reset every few hours.',
|
||||
'error_occurred' => 'Whoops! An error occurred.',
|
||||
'db_error_occurred' => 'Whoops! A database error occurred.',
|
||||
'error_not_recoverable' => 'Unfortunately, this error was not recoverable :(. Firefly III broke. The error is:',
|
||||
'error' => 'Error',
|
||||
'error_location' => 'This error occurred in file <span style="font-family: monospace;">:file</span> on line :line with code :code.',
|
||||
'stacktrace' => 'Stack trace',
|
||||
'more_info' => 'More information',
|
||||
|
||||
'collect_info' => 'Please collect more information in the <code>storage/logs</code> directory where you will find log files. If you\'re running Docker, use <code>docker logs -f [container]</code>.',
|
||||
'collect_info_more' => 'You can read more about collecting error information in <a href="https://docs.firefly-iii.org/how-to/general/debug/">the FAQ</a>.',
|
||||
'github_help' => 'Get help on GitHub',
|
||||
'github_instructions' => 'You\'re more than welcome to open a new issue <strong><a href="https://github.com/firefly-iii/firefly-iii/issues">on GitHub</a></strong>.',
|
||||
'use_search' => 'Use the search!',
|
||||
'include_info' => 'Include the information <a href=":link">from this debug page</a>.',
|
||||
'tell_more' => 'Tell us more than "it says Whoops!"',
|
||||
'include_logs' => 'Include error logs (see above).',
|
||||
'what_did_you_do' => 'Tell us what you were doing.',
|
||||
'offline_header' => 'You are probably offline',
|
||||
'offline_unreachable' => 'Firefly III is unreachable. Your device is currently offline or the server is not working.',
|
||||
'offline_github' => 'If you are sure both your device and the server are online, please open a ticket on <strong><a href="https://github.com/firefly-iii/firefly-iii/issues">GitHub</a></strong>.',
|
||||
'collect_info' => 'Please collect more information in the <code>storage/logs</code> directory where you will find log files. If you\'re running Docker, use <code>docker logs -f [container]</code>.',
|
||||
'collect_info_more' => 'You can read more about collecting error information in <a href="https://docs.firefly-iii.org/how-to/general/debug/">the FAQ</a>.',
|
||||
'github_help' => 'Get help on GitHub',
|
||||
'github_instructions' => 'You\'re more than welcome to open a new issue <strong><a href="https://github.com/firefly-iii/firefly-iii/issues">on GitHub</a></strong>.',
|
||||
'use_search' => 'Use the search!',
|
||||
'include_info' => 'Include the information <a href=":link">from this debug page</a>.',
|
||||
'tell_more' => 'Tell us more than "it says Whoops!"',
|
||||
'include_logs' => 'Include error logs (see above).',
|
||||
'what_did_you_do' => 'Tell us what you were doing.',
|
||||
'offline_header' => 'You are probably offline',
|
||||
'offline_unreachable' => 'Firefly III is unreachable. Your device is currently offline or the server is not working.',
|
||||
'offline_github' => 'If you are sure both your device and the server are online, please open a ticket on <strong><a href="https://github.com/firefly-iii/firefly-iii/issues">GitHub</a></strong>.',
|
||||
];
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'no_auth_present' => 'Looks like you\'re not authenticated',
|
||||
'bad_url_parts' => 'The redirect URL is invalid.',
|
||||
'no_auth_present' => 'Looks like you\'re not authenticated',
|
||||
'bad_url_parts' => 'The redirect URL is invalid.',
|
||||
'verifying_password' => 'Verifying password...',
|
||||
'invalid_account_list' => 'Invalid account type list entry ":value"',
|
||||
'invalid_transaction_type_list' => 'Invalid transaction type list',
|
||||
|
||||
Reference in New Issue
Block a user