mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-01-14 18:04:08 +00:00
Compare commits
64 Commits
develop-20
...
develop-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be87a24b8f | ||
|
|
f6032d5502 | ||
|
|
7dcfb68fca | ||
|
|
419c796eac | ||
|
|
1d5733adba | ||
|
|
c589360ad9 | ||
|
|
d4687fb34f | ||
|
|
9c4159ca3d | ||
|
|
93507c8b96 | ||
|
|
23d49a4194 | ||
|
|
858c44fbce | ||
|
|
95a6543a94 | ||
|
|
855d40cf2f | ||
|
|
7d0fec6326 | ||
|
|
ebd7dca6a9 | ||
|
|
1d41fc9845 | ||
|
|
fe9ae9c810 | ||
|
|
84600c5208 | ||
|
|
f7e89cab0a | ||
|
|
8195630a6e | ||
|
|
d5bf80a0cb | ||
|
|
ef1c64096d | ||
|
|
ef35eaffb4 | ||
|
|
869ee7c735 | ||
|
|
6c534f01eb | ||
|
|
3a9d89b53d | ||
|
|
badff64cfd | ||
|
|
abacfa212e | ||
|
|
add2e859c4 | ||
|
|
92f6421fc4 | ||
|
|
f350c19ec1 | ||
|
|
8170804d74 | ||
|
|
4400f6217d | ||
|
|
e223cea74e | ||
|
|
2f62e11338 | ||
|
|
d96c7931d6 | ||
|
|
2ab105a902 | ||
|
|
7ced1f8cf3 | ||
|
|
03364d9530 | ||
|
|
1f75612741 | ||
|
|
a141cf6e67 | ||
|
|
c97fb07e8d | ||
|
|
9833dd49a9 | ||
|
|
b76f4fe7b9 | ||
|
|
6c114e2ffc | ||
|
|
bd396673ed | ||
|
|
ad72bc1722 | ||
|
|
466b42200d | ||
|
|
067112904e | ||
|
|
fc371e27b7 | ||
|
|
52b14b46a2 | ||
|
|
5260b770bb | ||
|
|
226c4c8f8e | ||
|
|
9ebafe64f1 | ||
|
|
ffa618101d | ||
|
|
280e531a76 | ||
|
|
f542a3fd88 | ||
|
|
581d67a92c | ||
|
|
a00e8b976c | ||
|
|
e5b3c3e6bd | ||
|
|
4d7f63273e | ||
|
|
04553f6fc5 | ||
|
|
54676715c0 | ||
|
|
637d8e050a |
@@ -40,7 +40,8 @@ return $config->setRules(
|
||||
|
||||
[
|
||||
// rule sets
|
||||
'@PHP83Migration' => true,
|
||||
'@PHP8x3Migration' => true,
|
||||
'@PHP8x4Migration' => true,
|
||||
'@PhpCsFixer' => true,
|
||||
'@PhpCsFixer:risky' => true,
|
||||
'@PSR12' => true,
|
||||
|
||||
24
.ci/php-cs-fixer/composer.lock
generated
24
.ci/php-cs-fixer/composer.lock
generated
@@ -402,16 +402,16 @@
|
||||
},
|
||||
{
|
||||
"name": "friendsofphp/php-cs-fixer",
|
||||
"version": "v3.92.3",
|
||||
"version": "v3.92.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
|
||||
"reference": "2ba8f5a60f6f42fb65758cfb3768434fa2d1c7e8"
|
||||
"reference": "260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/2ba8f5a60f6f42fb65758cfb3768434fa2d1c7e8",
|
||||
"reference": "2ba8f5a60f6f42fb65758cfb3768434fa2d1c7e8",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58",
|
||||
"reference": "260cc8c4a1d2f6d2f22cd4f9c70aa72e55ebac58",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -443,17 +443,17 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"facile-it/paraunit": "^1.3.1 || ^2.7",
|
||||
"infection/infection": "^0.31.0",
|
||||
"justinrainbow/json-schema": "^6.5",
|
||||
"keradus/cli-executor": "^2.2",
|
||||
"infection/infection": "^0.31",
|
||||
"justinrainbow/json-schema": "^6.6",
|
||||
"keradus/cli-executor": "^2.3",
|
||||
"mikey179/vfsstream": "^1.6.12",
|
||||
"php-coveralls/php-coveralls": "^2.9",
|
||||
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6",
|
||||
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6",
|
||||
"phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34",
|
||||
"phpunit/phpunit": "^9.6.31 || ^10.5.60 || ^11.5.46",
|
||||
"symfony/polyfill-php85": "^1.33",
|
||||
"symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2 || ^8.0",
|
||||
"symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2 || ^8.0"
|
||||
"symfony/var-dumper": "^5.4.48 || ^6.4.26 || ^7.4.0 || ^8.0",
|
||||
"symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-dom": "For handling output formats in XML",
|
||||
@@ -494,7 +494,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
|
||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.92.3"
|
||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.92.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -502,7 +502,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-12-18T10:45:02+00:00"
|
||||
"time": "2026-01-08T21:57:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
Over time, many people have contributed to Firefly III. Their efforts are not always visible, but always remembered and appreciated.
|
||||
Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution.
|
||||
|
||||
## 2026
|
||||
- embedded
|
||||
|
||||
## 2025
|
||||
- Diego Algorta
|
||||
- Jihad
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\AccountTypeEnum;
|
||||
@@ -37,6 +36,7 @@ use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
@@ -51,7 +51,7 @@ class AccountController extends Controller
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
/** @var array<int, string> */
|
||||
private array $balanceTypes;
|
||||
private array $balanceTypes;
|
||||
private AccountRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
@@ -60,16 +60,14 @@ class AccountController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
$this->balanceTypes = [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value];
|
||||
}
|
||||
|
||||
@@ -83,24 +81,18 @@ class AccountController extends Controller
|
||||
public function accounts(AutocompleteApiRequest $request): JsonResponse
|
||||
{
|
||||
Log::debug('Before All.');
|
||||
[
|
||||
'types' => $types,
|
||||
'query' => $query,
|
||||
'date' => $date,
|
||||
'limit' => $limit,
|
||||
]
|
||||
= $request->attributes->all();
|
||||
['types' => $types, 'query' => $query, 'date' => $date, 'limit' => $limit] = $request->attributes->all();
|
||||
|
||||
$date ??= today(config('app.timezone'));
|
||||
|
||||
// set date to end-of-day for account balance. so it is at $date 23:59:59
|
||||
$date->endOfDay();
|
||||
|
||||
$return = [];
|
||||
$timer = Timer::getInstance();
|
||||
$return = [];
|
||||
$timer = Timer::getInstance();
|
||||
$timer->start(sprintf('AC accounts "%s"', $query));
|
||||
$result = $this->repository->searchAccount((string)$query, $types, $limit);
|
||||
$allBalances = Steam::accountsBalancesOptimized($result, $date, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$result = $this->repository->searchAccount((string) $query, $types, $limit);
|
||||
$allBalances = Steam::accountsBalancesOptimized($result, $date, $this->primaryCurrency, $this->convertToPrimary);
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($result as $account) {
|
||||
@@ -118,17 +110,17 @@ class AccountController extends Controller
|
||||
}
|
||||
|
||||
$return[] = [
|
||||
'id' => (string)$account->id,
|
||||
'id' => (string) $account->id,
|
||||
'name' => $account->name,
|
||||
'name_with_balance' => $nameWithBalance,
|
||||
'active' => $account->active,
|
||||
'type' => $account->accountType->type,
|
||||
'currency_id' => (string)$useCurrency->id,
|
||||
'currency_id' => (string) $useCurrency->id,
|
||||
'currency_name' => $useCurrency->name,
|
||||
'currency_code' => $useCurrency->code,
|
||||
'currency_symbol' => $useCurrency->symbol,
|
||||
'currency_decimal_places' => $useCurrency->decimal_places,
|
||||
'account_currency_id' => (string)$currency->id,
|
||||
'account_currency_id' => (string) $currency->id,
|
||||
'account_currency_name' => $currency->name,
|
||||
'account_currency_code' => $currency->code,
|
||||
'account_currency_symbol' => $currency->symbol,
|
||||
@@ -137,16 +129,13 @@ class AccountController extends Controller
|
||||
}
|
||||
|
||||
// custom order.
|
||||
usort(
|
||||
$return,
|
||||
static function (array $left, array $right): int {
|
||||
$order = [AccountTypeEnum::ASSET->value, AccountTypeEnum::REVENUE->value, AccountTypeEnum::EXPENSE->value];
|
||||
$posA = (int)array_search($left['type'], $order, true);
|
||||
$posB = (int)array_search($right['type'], $order, true);
|
||||
usort($return, static function (array $left, array $right): int {
|
||||
$order = [AccountTypeEnum::ASSET->value, AccountTypeEnum::REVENUE->value, AccountTypeEnum::EXPENSE->value];
|
||||
$posA = (int) array_search($left['type'], $order, true);
|
||||
$posB = (int) array_search($right['type'], $order, true);
|
||||
|
||||
return $posA - $posB;
|
||||
}
|
||||
);
|
||||
return $posA - $posB;
|
||||
});
|
||||
$timer->stop(sprintf('AC accounts "%s"', $query));
|
||||
|
||||
return response()->api($return);
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class BillController
|
||||
@@ -46,16 +46,14 @@ class BillController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(BillRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(BillRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,13 +63,7 @@ class BillController extends Controller
|
||||
public function bills(AutocompleteApiRequest $request): JsonResponse
|
||||
{
|
||||
$result = $this->repository->searchBill($request->attributes->get('query'), $request->attributes->get('limit'));
|
||||
$filtered = $result->map(
|
||||
static fn (Bill $item): array => [
|
||||
'id' => (string) $item->id,
|
||||
'name' => $item->name,
|
||||
'active' => $item->active,
|
||||
]
|
||||
);
|
||||
$filtered = $result->map(static fn (Bill $item): array => ['id' => (string) $item->id, 'name' => $item->name, 'active' => $item->active]);
|
||||
|
||||
return response()->api($filtered->toArray());
|
||||
}
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class BudgetController
|
||||
@@ -46,16 +46,14 @@ class BudgetController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(BudgetRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(BudgetRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,13 +63,7 @@ class BudgetController extends Controller
|
||||
public function budgets(AutocompleteApiRequest $request): JsonResponse
|
||||
{
|
||||
$result = $this->repository->searchBudget($request->attributes->get('query'), $request->attributes->get('limit'));
|
||||
$filtered = $result->map(
|
||||
static fn (Budget $item): array => [
|
||||
'id' => (string) $item->id,
|
||||
'name' => $item->name,
|
||||
'active' => $item->active,
|
||||
]
|
||||
);
|
||||
$filtered = $result->map(static fn (Budget $item): array => ['id' => (string) $item->id, 'name' => $item->name, 'active' => $item->active]);
|
||||
|
||||
return response()->api($filtered->toArray());
|
||||
}
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class CategoryController
|
||||
@@ -46,16 +46,14 @@ class CategoryController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(CategoryRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(CategoryRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,12 +63,7 @@ class CategoryController extends Controller
|
||||
public function categories(AutocompleteApiRequest $request): JsonResponse
|
||||
{
|
||||
$result = $this->repository->searchCategory($request->attributes->get('query'), $request->attributes->get('limit'));
|
||||
$filtered = $result->map(
|
||||
static fn (Category $item): array => [
|
||||
'id' => (string) $item->id,
|
||||
'name' => $item->name,
|
||||
]
|
||||
);
|
||||
$filtered = $result->map(static fn (Category $item): array => ['id' => (string) $item->id, 'name' => $item->name]);
|
||||
|
||||
return response()->api($filtered->toArray());
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Deprecated;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
@@ -33,6 +32,7 @@ use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class CurrencyController
|
||||
@@ -48,16 +48,14 @@ class CurrencyController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(CurrencyRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(CurrencyRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\ObjectGroup;
|
||||
use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class ObjectGroupController
|
||||
@@ -46,16 +46,14 @@ class ObjectGroupController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(ObjectGroupRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(ObjectGroupRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,11 +67,7 @@ class ObjectGroupController extends Controller
|
||||
|
||||
/** @var ObjectGroup $objectGroup */
|
||||
foreach ($result as $objectGroup) {
|
||||
$return[] = [
|
||||
'id' => (string) $objectGroup->id,
|
||||
'name' => $objectGroup->title,
|
||||
'title' => $objectGroup->title,
|
||||
];
|
||||
$return[] = ['id' => (string) $objectGroup->id, 'name' => $objectGroup->title, 'title' => $objectGroup->title];
|
||||
}
|
||||
|
||||
return response()->api($return);
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
@@ -34,13 +33,14 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class PiggyBankController
|
||||
*/
|
||||
class PiggyBankController extends Controller
|
||||
{
|
||||
private AccountRepositoryInterface $accountRepository;
|
||||
private AccountRepositoryInterface $accountRepository;
|
||||
private PiggyBankRepositoryInterface $piggyRepository;
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_PIGGY_BANKS];
|
||||
|
||||
@@ -50,19 +50,17 @@ class PiggyBankController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->piggyRepository = app(PiggyBankRepositoryInterface::class);
|
||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||
$this->piggyRepository->setUser($this->user);
|
||||
$this->piggyRepository->setUserGroup($this->userGroup);
|
||||
$this->accountRepository->setUser($this->user);
|
||||
$this->accountRepository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->piggyRepository = app(PiggyBankRepositoryInterface::class);
|
||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||
$this->piggyRepository->setUser($this->user);
|
||||
$this->piggyRepository->setUserGroup($this->userGroup);
|
||||
$this->accountRepository->setUser($this->user);
|
||||
$this->accountRepository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function piggyBanks(AutocompleteApiRequest $request): JsonResponse
|
||||
@@ -108,7 +106,7 @@ class PiggyBankController extends Controller
|
||||
'%s (%s / %s)',
|
||||
$piggy->name,
|
||||
Amount::formatAnything($currency, $currentAmount, false),
|
||||
Amount::formatAnything($currency, $piggy->target_amount, false),
|
||||
Amount::formatAnything($currency, $piggy->target_amount, false)
|
||||
),
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_name' => $currency->name,
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\Recurrence;
|
||||
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class RecurrenceController
|
||||
@@ -46,16 +46,14 @@ class RecurrenceController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(RecurringRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(RecurringRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function recurring(AutocompleteApiRequest $request): JsonResponse
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class RuleController
|
||||
@@ -38,7 +38,7 @@ use Illuminate\Http\JsonResponse;
|
||||
class RuleController extends Controller
|
||||
{
|
||||
private RuleRepositoryInterface $repository;
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_RULES];
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_RULES];
|
||||
|
||||
/**
|
||||
* RuleController constructor.
|
||||
@@ -46,16 +46,14 @@ class RuleController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(RuleRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(RuleRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function rules(AutocompleteApiRequest $request): JsonResponse
|
||||
@@ -66,7 +64,7 @@ class RuleController extends Controller
|
||||
/** @var Rule $rule */
|
||||
foreach ($rules as $rule) {
|
||||
$response[] = [
|
||||
'id' => (string)$rule->id,
|
||||
'id' => (string) $rule->id,
|
||||
'name' => $rule->title,
|
||||
'description' => $rule->description,
|
||||
'active' => $rule->active,
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class RuleGroupController
|
||||
@@ -46,16 +46,14 @@ class RuleGroupController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(RuleGroupRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(RuleGroupRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function ruleGroups(AutocompleteApiRequest $request): JsonResponse
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class TagController
|
||||
@@ -46,16 +46,14 @@ class TagController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(TagRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(TagRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function tags(AutocompleteApiRequest $request): JsonResponse
|
||||
@@ -65,11 +63,7 @@ class TagController extends Controller
|
||||
|
||||
/** @var Tag $tag */
|
||||
foreach ($result as $tag) {
|
||||
$array[] = [
|
||||
'id' => (string) $tag->id,
|
||||
'name' => $tag->tag,
|
||||
'tag' => $tag->tag,
|
||||
];
|
||||
$array[] = ['id' => (string) $tag->id, 'name' => $tag->tag, 'tag' => $tag->tag];
|
||||
}
|
||||
|
||||
return response()->api($array);
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteTransactionApiRequest;
|
||||
@@ -34,6 +33,7 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@@ -43,7 +43,7 @@ class TransactionController extends Controller
|
||||
{
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
private TransactionGroupRepositoryInterface $groupRepository;
|
||||
private JournalRepositoryInterface $repository;
|
||||
private JournalRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
* TransactionController constructor.
|
||||
@@ -51,19 +51,17 @@ class TransactionController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(JournalRepositoryInterface::class);
|
||||
$this->groupRepository = app(TransactionGroupRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->groupRepository->setUser($this->user);
|
||||
$this->groupRepository->setUserGroup($this->userGroup);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(JournalRepositoryInterface::class);
|
||||
$this->groupRepository = app(TransactionGroupRepositoryInterface::class);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->groupRepository->setUser($this->user);
|
||||
$this->groupRepository->setUserGroup($this->userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function transactions(AutocompleteTransactionApiRequest $request): JsonResponse
|
||||
|
||||
@@ -24,13 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteApiRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\TransactionType\TransactionTypeRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class TransactionTypeController
|
||||
@@ -46,14 +46,12 @@ class TransactionTypeController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(TransactionTypeRepositoryInterface::class);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(TransactionTypeRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function transactionTypes(AutocompleteApiRequest $request): JsonResponse
|
||||
@@ -64,11 +62,7 @@ class TransactionTypeController extends Controller
|
||||
/** @var TransactionType $type */
|
||||
foreach ($types as $type) {
|
||||
// different key for consistency.
|
||||
$array[] = [
|
||||
'id' => (string) $type->id,
|
||||
'name' => $type->type,
|
||||
'type' => $type->type,
|
||||
];
|
||||
$array[] = ['id' => (string) $type->id, 'name' => $type->type, 'type' => $type->type];
|
||||
}
|
||||
|
||||
return response()->api($array);
|
||||
|
||||
@@ -49,9 +49,9 @@ class AccountController extends Controller
|
||||
use CleansChartData;
|
||||
use CollectsAccountsFromFilter;
|
||||
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
private array $chartData = [];
|
||||
private array $chartData = [];
|
||||
private AccountRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
@@ -60,16 +60,14 @@ class AccountController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->repository->setUser($this->user);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,14 +113,14 @@ class AccountController extends Controller
|
||||
'label' => $account->name,
|
||||
|
||||
// the currency that belongs to the account.
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_name' => $currency->name,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
|
||||
// the primary currency
|
||||
'primary_currency_id' => (string)$this->primaryCurrency->id,
|
||||
'primary_currency_id' => (string) $this->primaryCurrency->id,
|
||||
|
||||
// the default currency of the user (could be the same!)
|
||||
'date' => $params['start']->toAtomString(),
|
||||
@@ -136,7 +134,7 @@ class AccountController extends Controller
|
||||
];
|
||||
if ($this->convertToPrimary) {
|
||||
$currentSet['pc_entries'] = [];
|
||||
$currentSet['primary_currency_id'] = (string)$this->primaryCurrency->id;
|
||||
$currentSet['primary_currency_id'] = (string) $this->primaryCurrency->id;
|
||||
$currentSet['primary_currency_code'] = $this->primaryCurrency->code;
|
||||
$currentSet['primary_currency_symbol'] = $this->primaryCurrency->symbol;
|
||||
$currentSet['primary_currency_decimal_places'] = $this->primaryCurrency->decimal_places;
|
||||
@@ -151,7 +149,6 @@ class AccountController extends Controller
|
||||
$previous = $balance;
|
||||
$currentSet['entries'][$label] = $balance;
|
||||
|
||||
|
||||
// do the same for the primary currency balance, if relevant:
|
||||
$pcBalance = null;
|
||||
if ($this->convertToPrimary) {
|
||||
@@ -160,6 +157,7 @@ class AccountController extends Controller
|
||||
$currentSet['pc_entries'][$label] = $pcBalance;
|
||||
}
|
||||
$currentStart = Navigation::addPeriod($currentStart, $period);
|
||||
|
||||
// $currentStart->addDay();
|
||||
}
|
||||
$this->chartData[] = $currentSet;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
|
||||
/*
|
||||
* BalanceController.php
|
||||
* Copyright (c) 2025 james@firefly-iii.org
|
||||
@@ -25,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Chart;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Chart\ChartRequest;
|
||||
use FireflyIII\Enums\TransactionTypeEnum;
|
||||
@@ -37,6 +35,7 @@ use FireflyIII\Support\Http\Api\AccountBalanceGrouped;
|
||||
use FireflyIII\Support\Http\Api\CleansChartData;
|
||||
use FireflyIII\Support\Http\Api\CollectsAccountsFromFilter;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class BalanceController
|
||||
@@ -45,10 +44,11 @@ class BalanceController extends Controller
|
||||
{
|
||||
use CleansChartData;
|
||||
use CollectsAccountsFromFilter;
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
private array $chartData = [];
|
||||
private GroupCollectorInterface $collector;
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
private array $chartData = [];
|
||||
private GroupCollectorInterface $collector;
|
||||
private AccountRepositoryInterface $repository;
|
||||
|
||||
// private TransactionCurrency $default;
|
||||
@@ -56,19 +56,17 @@ class BalanceController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->collector = app(GroupCollectorInterface::class);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->collector->setUserGroup($this->userGroup);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->collector->setUser($this->user);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->collector = app(GroupCollectorInterface::class);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->collector->setUserGroup($this->userGroup);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->collector->setUser($this->user);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,10 +87,16 @@ class BalanceController extends Controller
|
||||
|
||||
// get journals for entire period:
|
||||
|
||||
$this->collector->setRange($queryParameters['start'], $queryParameters['end'])
|
||||
$this->collector
|
||||
->setRange($queryParameters['start'], $queryParameters['end'])
|
||||
->withAccountInformation()
|
||||
->setXorAccounts($accounts)
|
||||
->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::RECONCILIATION->value, TransactionTypeEnum::TRANSFER->value])
|
||||
->setTypes([
|
||||
TransactionTypeEnum::WITHDRAWAL->value,
|
||||
TransactionTypeEnum::DEPOSIT->value,
|
||||
TransactionTypeEnum::RECONCILIATION->value,
|
||||
TransactionTypeEnum::TRANSFER->value,
|
||||
])
|
||||
;
|
||||
$journals = $this->collector->getExtractedJournals();
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Chart;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\DateRangeRequest;
|
||||
@@ -36,13 +35,14 @@ use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Http\Api\CleansChartData;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
|
||||
/**
|
||||
* Class BudgetController
|
||||
@@ -52,32 +52,30 @@ class BudgetController extends Controller
|
||||
use CleansChartData;
|
||||
use ValidatesUserGroupTrait;
|
||||
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
protected OperationsRepositoryInterface $opsRepository;
|
||||
private BudgetLimitRepositoryInterface $blRepository;
|
||||
private array $currencies = [];
|
||||
private BudgetRepositoryInterface $repository;
|
||||
private BudgetLimitRepositoryInterface $blRepository;
|
||||
private array $currencies = [];
|
||||
private BudgetRepositoryInterface $repository;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(BudgetRepositoryInterface::class);
|
||||
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
|
||||
$this->opsRepository = app(OperationsRepositoryInterface::class);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->opsRepository->setUserGroup($this->userGroup);
|
||||
$this->blRepository->setUserGroup($this->userGroup);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->opsRepository->setUser($this->user);
|
||||
$this->blRepository->setUser($this->user);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->repository = app(BudgetRepositoryInterface::class);
|
||||
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
|
||||
$this->opsRepository = app(OperationsRepositoryInterface::class);
|
||||
$this->repository->setUserGroup($this->userGroup);
|
||||
$this->opsRepository->setUserGroup($this->userGroup);
|
||||
$this->blRepository->setUserGroup($this->userGroup);
|
||||
$this->repository->setUser($this->user);
|
||||
$this->opsRepository->setUser($this->user);
|
||||
$this->blRepository->setUser($this->user);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,18 +156,17 @@ class BudgetController extends Controller
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
|
||||
// is always an array
|
||||
$return = [];
|
||||
foreach ($rows as $row) {
|
||||
$current = [
|
||||
'label' => $budget->name,
|
||||
'currency_id' => (string)$row['currency_id'],
|
||||
'currency_id' => (string) $row['currency_id'],
|
||||
'currency_name' => $row['currency_name'],
|
||||
'currency_code' => $row['currency_code'],
|
||||
'currency_decimal_places' => $row['currency_decimal_places'],
|
||||
|
||||
'primary_currency_id' => (string)$this->primaryCurrency->id,
|
||||
'primary_currency_id' => (string) $this->primaryCurrency->id,
|
||||
'primary_currency_name' => $this->primaryCurrency->name,
|
||||
'primary_currency_code' => $this->primaryCurrency->code,
|
||||
'primary_currency_symbol' => $this->primaryCurrency->symbol,
|
||||
@@ -181,12 +178,7 @@ class BudgetController extends Controller
|
||||
'end_date' => $row['end'],
|
||||
'yAxisID' => 0,
|
||||
'type' => 'bar',
|
||||
'entries' => [
|
||||
'budgeted' => $row['budgeted'],
|
||||
'spent' => $row['spent'],
|
||||
'left' => $row['left'],
|
||||
'overspent' => $row['overspent'],
|
||||
],
|
||||
'entries' => ['budgeted' => $row['budgeted'], 'spent' => $row['spent'], 'left' => $row['left'], 'overspent' => $row['overspent']],
|
||||
'pc_entries' => [
|
||||
'budgeted' => $row['pc_budgeted'],
|
||||
'spent' => $row['pc_spent'],
|
||||
@@ -233,11 +225,11 @@ class BudgetController extends Controller
|
||||
foreach ($spent as $currencyId => $block) {
|
||||
$this->currencies[$currencyId] ??= Amount::getTransactionCurrencyById($currencyId);
|
||||
$return[$currencyId] ??= [
|
||||
'currency_id' => (string)$currencyId,
|
||||
'currency_id' => (string) $currencyId,
|
||||
'currency_code' => $block['currency_code'],
|
||||
'currency_name' => $block['currency_name'],
|
||||
'currency_symbol' => $block['currency_symbol'],
|
||||
'currency_decimal_places' => (int)$block['currency_decimal_places'],
|
||||
'currency_decimal_places' => (int) $block['currency_decimal_places'],
|
||||
'start' => $start->toAtomString(),
|
||||
'end' => $end->toAtomString(),
|
||||
'budgeted' => '0',
|
||||
@@ -251,7 +243,7 @@ class BudgetController extends Controller
|
||||
/** @var array $journal */
|
||||
foreach ($currentBudgetArray['transaction_journals'] as $journal) {
|
||||
/** @var numeric-string $amount */
|
||||
$amount = (string)$journal['amount'];
|
||||
$amount = (string) $journal['amount'];
|
||||
$return[$currencyId]['spent'] = bcadd($return[$currencyId]['spent'], $amount);
|
||||
}
|
||||
}
|
||||
@@ -270,14 +262,21 @@ class BudgetController extends Controller
|
||||
if ($this->convertToPrimary) {
|
||||
if ($current->transaction_currency_id === $this->primaryCurrency->id) {
|
||||
// simply add it.
|
||||
$amount = bcadd($amount, (string)$current->amount);
|
||||
$amount = bcadd($amount, (string) $current->amount);
|
||||
Log::debug(sprintf('Set amount in limit to %s', $amount));
|
||||
}
|
||||
if ($current->transaction_currency_id !== $this->primaryCurrency->id) {
|
||||
// convert and then add it.
|
||||
$converted = $converter->convert($current->transactionCurrency, $this->primaryCurrency, $current->start_date, $current->amount);
|
||||
$amount = bcadd($amount, $converted);
|
||||
Log::debug(sprintf('Budgeted in limit #%d: %s %s, converted to %s %s', $current->id, $current->transactionCurrency->code, $current->amount, $this->primaryCurrency->code, $converted));
|
||||
Log::debug(sprintf(
|
||||
'Budgeted in limit #%d: %s %s, converted to %s %s',
|
||||
$current->id,
|
||||
$current->transactionCurrency->code,
|
||||
$current->amount,
|
||||
$this->primaryCurrency->code,
|
||||
$converted
|
||||
));
|
||||
Log::debug(sprintf('Set amount in limit to %s', $amount));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Chart;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\DateRangeRequest;
|
||||
@@ -40,6 +39,7 @@ use FireflyIII\Support\Http\Api\CleansChartData;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
@@ -52,25 +52,23 @@ class CategoryController extends Controller
|
||||
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
private AccountRepositoryInterface $accountRepos;
|
||||
private AccountRepositoryInterface $accountRepos;
|
||||
private CurrencyRepositoryInterface $currencyRepos;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$this->accountRepos->setUserGroup($this->userGroup);
|
||||
$this->currencyRepos->setUserGroup($this->userGroup);
|
||||
$this->accountRepos->setUser($this->user);
|
||||
$this->currencyRepos->setUser($this->user);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$this->accountRepos->setUserGroup($this->userGroup);
|
||||
$this->currencyRepos->setUserGroup($this->userGroup);
|
||||
$this->accountRepos->setUser($this->user);
|
||||
$this->currencyRepos->setUser($this->user);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,7 +86,12 @@ class CategoryController extends Controller
|
||||
|
||||
/** @var Carbon $end */
|
||||
$end = $request->attributes->get('end');
|
||||
$accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value]);
|
||||
$accounts = $this->accountRepos->getAccountsByType([
|
||||
AccountTypeEnum::DEBT->value,
|
||||
AccountTypeEnum::LOAN->value,
|
||||
AccountTypeEnum::MORTGAGE->value,
|
||||
AccountTypeEnum::ASSET->value,
|
||||
]);
|
||||
$currencies = [];
|
||||
$return = [];
|
||||
$converter = new ExchangeRateConverter();
|
||||
@@ -104,7 +107,7 @@ class CategoryController extends Controller
|
||||
/** @var array $journal */
|
||||
foreach ($journals as $journal) {
|
||||
// find journal:
|
||||
$journalCurrencyId = (int)$journal['currency_id'];
|
||||
$journalCurrencyId = (int) $journal['currency_id'];
|
||||
$type = $journal['transaction_type_type'];
|
||||
$currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId);
|
||||
$currencies[$journalCurrencyId] = $currency;
|
||||
@@ -113,7 +116,7 @@ class CategoryController extends Controller
|
||||
$currencyCode = $currency->code;
|
||||
$currencySymbol = $currency->symbol;
|
||||
$currencyDecimalPlaces = $currency->decimal_places;
|
||||
$amount = Steam::positive((string)$journal['amount']);
|
||||
$amount = Steam::positive((string) $journal['amount']);
|
||||
$pcAmount = null;
|
||||
|
||||
// overrule if necessary:
|
||||
@@ -130,18 +133,17 @@ class CategoryController extends Controller
|
||||
Log::debug(sprintf('Converted %s %s to %s %s', $journal['currency_code'], $amount, $this->primaryCurrency->code, $pcAmount));
|
||||
}
|
||||
|
||||
|
||||
$categoryName = $journal['category_name'] ?? (string)trans('firefly.no_category');
|
||||
$categoryName = $journal['category_name'] ?? (string) trans('firefly.no_category');
|
||||
$key = sprintf('%s-%s', $categoryName, $currencyCode);
|
||||
// create arrays
|
||||
$return[$key] ??= [
|
||||
'label' => $categoryName,
|
||||
'currency_id' => (string)$currencyId,
|
||||
'currency_id' => (string) $currencyId,
|
||||
'currency_name' => $currencyName,
|
||||
'currency_code' => $currencyCode,
|
||||
'currency_symbol' => $currencySymbol,
|
||||
'currency_decimal_places' => $currencyDecimalPlaces,
|
||||
'primary_currency_id' => (string)$this->primaryCurrency->id,
|
||||
'primary_currency_id' => (string) $this->primaryCurrency->id,
|
||||
'primary_currency_name' => $this->primaryCurrency->name,
|
||||
'primary_currency_code' => $this->primaryCurrency->code,
|
||||
'primary_currency_symbol' => $this->primaryCurrency->symbol,
|
||||
@@ -151,14 +153,8 @@ class CategoryController extends Controller
|
||||
'end_date' => $end->toAtomString(),
|
||||
'yAxisID' => 0,
|
||||
'type' => 'bar',
|
||||
'entries' => [
|
||||
'spent' => '0',
|
||||
'earned' => '0',
|
||||
],
|
||||
'pc_entries' => [
|
||||
'spent' => '0',
|
||||
'earned' => '0',
|
||||
],
|
||||
'entries' => ['spent' => '0', 'earned' => '0'],
|
||||
'pc_entries' => ['spent' => '0', 'earned' => '0'],
|
||||
];
|
||||
|
||||
// add monies
|
||||
@@ -182,7 +178,10 @@ class CategoryController extends Controller
|
||||
$return = array_values($return);
|
||||
|
||||
// order by amount
|
||||
usort($return, static fn (array $a, array $b): int => ((float)$a['entries']['spent'] + (float)$a['entries']['earned']) < ((float)$b['entries']['spent'] + (float)$b['entries']['earned']) ? 1 : -1);
|
||||
usort($return, static fn (array $a, array $b): int => ((float) $a['entries']['spent'] + (float) $a['entries']['earned'])
|
||||
< ((float) $b['entries']['spent'] + (float) $b['entries']['earned'])
|
||||
? 1
|
||||
: -1);
|
||||
|
||||
return response()->json($this->clean($return));
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers;
|
||||
|
||||
use Deprecated;
|
||||
use Carbon\Carbon;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use Deprecated;
|
||||
use FireflyIII\Exceptions\BadHttpHeaderException;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
@@ -63,15 +63,16 @@ abstract class Controller extends BaseController
|
||||
use ValidatesRequests;
|
||||
use ValidatesUserGroupTrait;
|
||||
|
||||
protected const string CONTENT_TYPE = 'application/vnd.api+json';
|
||||
protected const string JSON_CONTENT_TYPE = 'application/json';
|
||||
protected array $accepts = ['application/json', 'application/vnd.api+json'];
|
||||
protected const string CONTENT_TYPE = 'application/vnd.api+json';
|
||||
protected const string JSON_CONTENT_TYPE = 'application/json';
|
||||
|
||||
protected bool $convertToPrimary = false;
|
||||
protected array $accepts = ['application/json', 'application/vnd.api+json'];
|
||||
|
||||
protected bool $convertToPrimary = false;
|
||||
protected TransactionCurrency $primaryCurrency;
|
||||
|
||||
/** @deprecated use Request classes */
|
||||
protected ParameterBag $parameters;
|
||||
protected ParameterBag $parameters;
|
||||
|
||||
/**
|
||||
* Controller constructor.
|
||||
@@ -79,26 +80,22 @@ abstract class Controller extends BaseController
|
||||
public function __construct()
|
||||
{
|
||||
// get global parameters
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->parameters = $this->getParameters();
|
||||
if (auth()->check()) {
|
||||
$language = Steam::getLanguage();
|
||||
$this->convertToPrimary = Amount::convertToPrimary();
|
||||
$this->primaryCurrency = Amount::getPrimaryCurrency();
|
||||
app()->setLocale($language);
|
||||
}
|
||||
|
||||
|
||||
// filter down what this endpoint accepts.
|
||||
if (!$request->accepts($this->accepts)) {
|
||||
throw new BadHttpHeaderException(sprintf('Sorry, Accept header "%s" is not something this endpoint can provide.', $request->header('Accept')));
|
||||
}
|
||||
|
||||
|
||||
return $next($request);
|
||||
$this->middleware(function ($request, $next) {
|
||||
$this->parameters = $this->getParameters();
|
||||
if (auth()->check()) {
|
||||
$language = Steam::getLanguage();
|
||||
$this->convertToPrimary = Amount::convertToPrimary();
|
||||
$this->primaryCurrency = Amount::getPrimaryCurrency();
|
||||
app()->setLocale($language);
|
||||
}
|
||||
);
|
||||
|
||||
// filter down what this endpoint accepts.
|
||||
if (!$request->accepts($this->accepts)) {
|
||||
throw new BadHttpHeaderException(sprintf('Sorry, Accept header "%s" is not something this endpoint can provide.', $request->header('Accept')));
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
#[Deprecated(message: <<<'TXT'
|
||||
@@ -108,7 +105,7 @@ abstract class Controller extends BaseController
|
||||
private function getParameters(): ParameterBag
|
||||
{
|
||||
$bag = new ParameterBag();
|
||||
$page = (int)request()->get('page');
|
||||
$page = (int) request()->get('page');
|
||||
$page = min(max(1, $page), 2 ** 16);
|
||||
$bag->set('page', $page);
|
||||
|
||||
@@ -127,10 +124,10 @@ abstract class Controller extends BaseController
|
||||
$obj = null;
|
||||
if (null !== $date) {
|
||||
try {
|
||||
$obj = Carbon::parse((string)$date, config('app.timezone'));
|
||||
$obj = Carbon::parse((string) $date, config('app.timezone'));
|
||||
} catch (InvalidFormatException $e) {
|
||||
// don't care
|
||||
Log::warning(sprintf('Ignored invalid date "%s" in API controller parameter check: %s', substr((string)$date, 0, 20), $e->getMessage()));
|
||||
Log::warning(sprintf('Ignored invalid date "%s" in API controller parameter check: %s', substr((string) $date, 0, 20), $e->getMessage()));
|
||||
}
|
||||
}
|
||||
if ($obj instanceof Carbon) {
|
||||
@@ -150,24 +147,27 @@ abstract class Controller extends BaseController
|
||||
$value = null;
|
||||
}
|
||||
if (null !== $value) {
|
||||
$value = (int)$value;
|
||||
$value = (int) $value;
|
||||
$value = min(max(1, $value), 2 ** 16);
|
||||
$bag->set($integer, $value);
|
||||
}
|
||||
if (null === $value
|
||||
if (
|
||||
null === $value
|
||||
&& 'limit' === $integer // @phpstan-ignore-line
|
||||
&& auth()->check()) {
|
||||
&& auth()->check()
|
||||
) {
|
||||
// set default for user:
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$pageSize = (int)Preferences::getForUser($user, 'listPageSize', 50)->data;
|
||||
$pageSize = (int) Preferences::getForUser($user, 'listPageSize', 50)->data;
|
||||
$bag->set($integer, $pageSize);
|
||||
}
|
||||
}
|
||||
|
||||
// sort fields:
|
||||
return $bag;
|
||||
|
||||
// return $this->getSortParameters($bag);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Data;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Data\DestroyRequest;
|
||||
use FireflyIII\Enums\AccountTypeEnum;
|
||||
@@ -49,6 +48,7 @@ use FireflyIII\Services\Internal\Destroy\AccountDestroyService;
|
||||
use FireflyIII\Services\Internal\Destroy\JournalDestroyService;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
@@ -63,13 +63,11 @@ class DestroyController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function destroy(DestroyRequest $request): JsonResponse
|
||||
@@ -77,10 +75,40 @@ class DestroyController extends Controller
|
||||
$objects = $request->getObjects();
|
||||
$this->unused = $request->boolean('unused');
|
||||
|
||||
$allExceptAssets = [AccountTypeEnum::BENEFICIARY->value, AccountTypeEnum::CASH->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::EXPENSE->value, AccountTypeEnum::IMPORT->value, AccountTypeEnum::INITIAL_BALANCE->value, AccountTypeEnum::LIABILITY_CREDIT->value, AccountTypeEnum::RECONCILIATION->value, AccountTypeEnum::REVENUE->value];
|
||||
$all = [AccountTypeEnum::ASSET->value, AccountTypeEnum::BENEFICIARY->value, AccountTypeEnum::CASH->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::EXPENSE->value, AccountTypeEnum::IMPORT->value, AccountTypeEnum::INITIAL_BALANCE->value, AccountTypeEnum::LIABILITY_CREDIT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::RECONCILIATION->value];
|
||||
$allExceptAssets = [
|
||||
AccountTypeEnum::BENEFICIARY->value,
|
||||
AccountTypeEnum::CASH->value,
|
||||
AccountTypeEnum::CREDITCARD->value,
|
||||
AccountTypeEnum::DEFAULT->value,
|
||||
AccountTypeEnum::EXPENSE->value,
|
||||
AccountTypeEnum::IMPORT->value,
|
||||
AccountTypeEnum::INITIAL_BALANCE->value,
|
||||
AccountTypeEnum::LIABILITY_CREDIT->value,
|
||||
AccountTypeEnum::RECONCILIATION->value,
|
||||
AccountTypeEnum::REVENUE->value,
|
||||
];
|
||||
$all = [
|
||||
AccountTypeEnum::ASSET->value,
|
||||
AccountTypeEnum::BENEFICIARY->value,
|
||||
AccountTypeEnum::CASH->value,
|
||||
AccountTypeEnum::CREDITCARD->value,
|
||||
AccountTypeEnum::DEBT->value,
|
||||
AccountTypeEnum::DEFAULT->value,
|
||||
AccountTypeEnum::EXPENSE->value,
|
||||
AccountTypeEnum::IMPORT->value,
|
||||
AccountTypeEnum::INITIAL_BALANCE->value,
|
||||
AccountTypeEnum::LIABILITY_CREDIT->value,
|
||||
AccountTypeEnum::LOAN->value,
|
||||
AccountTypeEnum::MORTGAGE->value,
|
||||
AccountTypeEnum::RECONCILIATION->value,
|
||||
];
|
||||
$liabilities = [AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::CREDITCARD->value];
|
||||
$transactions = [TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value, TransactionTypeEnum::RECONCILIATION->value];
|
||||
$transactions = [
|
||||
TransactionTypeEnum::WITHDRAWAL->value,
|
||||
TransactionTypeEnum::DEPOSIT->value,
|
||||
TransactionTypeEnum::TRANSFER->value,
|
||||
TransactionTypeEnum::RECONCILIATION->value,
|
||||
];
|
||||
|
||||
match ($objects) {
|
||||
'budgets' => $this->destroyBudgets(),
|
||||
@@ -101,7 +129,7 @@ class DestroyController extends Controller
|
||||
'withdrawals' => $this->destroyTransactions([TransactionTypeEnum::WITHDRAWAL->value]),
|
||||
'deposits' => $this->destroyTransactions([TransactionTypeEnum::DEPOSIT->value]),
|
||||
'transfers' => $this->destroyTransactions([TransactionTypeEnum::TRANSFER->value]),
|
||||
default => throw new FireflyException(sprintf('200033: This endpoint can\'t handle object "%s"', $objects)),
|
||||
default => throw new FireflyException(sprintf('200033: This endpoint can\'t handle object "%s"', $objects))
|
||||
};
|
||||
|
||||
Preferences::mark();
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers\Data;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\Account;
|
||||
@@ -40,6 +39,7 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class PurgeController
|
||||
@@ -51,13 +51,11 @@ class PurgeController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
$this->middleware(function (Request $request, $next) {
|
||||
$this->validateUserGroup($request);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,6 +44,8 @@ use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use League\Fractal\Resource\Item;
|
||||
use Symfony\Component\HttpKernel\Exception\GoneHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
/**
|
||||
* Class StoreController
|
||||
@@ -82,7 +84,7 @@ class StoreController extends Controller
|
||||
*
|
||||
* Store a new transaction.
|
||||
*
|
||||
* @throws FireflyException|ValidationException
|
||||
* @throws FireflyException|GoneHttpException|ValidationException
|
||||
*/
|
||||
public function store(StoreRequest $request): JsonResponse
|
||||
{
|
||||
@@ -133,7 +135,7 @@ class StoreController extends Controller
|
||||
|
||||
$selectedGroup = $collector->getGroups()->first();
|
||||
if (null === $selectedGroup) {
|
||||
throw new FireflyException('200032: Cannot find transaction. Possibly, a rule deleted this transaction after its creation.');
|
||||
throw HttpException::fromStatusCode(410, '200032: Cannot find transaction. Possibly, a rule deleted this transaction after its creation.');
|
||||
}
|
||||
|
||||
// enrich
|
||||
|
||||
@@ -262,7 +262,7 @@ class ListController extends Controller
|
||||
// filter selection
|
||||
$collection = $unfiltered->filter(
|
||||
static function (Recurrence $recurrence) use ($currency): ?Recurrence { // @phpstan-ignore-line
|
||||
if (array_any($recurrence->recurrenceTransactions, fn ($transaction): bool => $transaction->transaction_currency_id === $currency->id || $transaction->foreign_currency_id === $currency->id)) {
|
||||
if (array_any($recurrence->recurrenceTransactions, static fn ($transaction): bool => $transaction->transaction_currency_id === $currency->id || $transaction->foreign_currency_id === $currency->id)) {
|
||||
return $recurrence;
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ class ListController extends Controller
|
||||
|
||||
$collection = $unfiltered->filter(
|
||||
static function (Rule $rule) use ($currency): ?Rule { // @phpstan-ignore-line
|
||||
if (array_any($rule->ruleTriggers, fn ($trigger): bool => 'currency_is' === $trigger->trigger_type && $currency->name === $trigger->trigger_value)) {
|
||||
if (array_any($rule->ruleTriggers, static fn ($trigger): bool => 'currency_is' === $trigger->trigger_type && $currency->name === $trigger->trigger_value)) {
|
||||
return $rule;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,11 +25,11 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Api\V1\Controllers\Search;
|
||||
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Search\TransactionSearchRequest;
|
||||
use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment;
|
||||
use FireflyIII\Support\Search\SearchInterface;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection;
|
||||
|
||||
@@ -42,12 +42,12 @@ class TransactionController extends Controller
|
||||
* This endpoint is documented at:
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/search/searchTransactions
|
||||
*/
|
||||
public function search(Request $request, SearchInterface $searcher): JsonResponse
|
||||
public function search(TransactionSearchRequest $request, SearchInterface $searcher): JsonResponse
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
$fullQuery = (string) $request->get('query');
|
||||
$page = 0 === (int) $request->get('page') ? 1 : (int) $request->get('page');
|
||||
$pageSize = $this->parameters->get('limit');
|
||||
$fullQuery = (string) $request->attributes->get('query');
|
||||
$page = $request->attributes->get('page');
|
||||
$pageSize = $request->attributes->get('limit');
|
||||
$searcher->parseQuery($fullQuery);
|
||||
$searcher->setPage($page);
|
||||
$searcher->setLimit($pageSize);
|
||||
|
||||
50
app/Api/V1/Requests/Search/SearchQueryRequest.php
Normal file
50
app/Api/V1/Requests/Search/SearchQueryRequest.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* SearchQueryRequest.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests\Search;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\ApiRequest;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
|
||||
class SearchQueryRequest extends ApiRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'query' => sprintf('min:0|max:500|%s', $this->required),
|
||||
];
|
||||
}
|
||||
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator): void {
|
||||
if ($validator->failed()) {
|
||||
return;
|
||||
}
|
||||
$query = $this->convertString('query');
|
||||
$this->attributes->set('query', $query);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
42
app/Api/V1/Requests/Search/TransactionSearchRequest.php
Normal file
42
app/Api/V1/Requests/Search/TransactionSearchRequest.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* SearchRequest.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests\Search;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\AggregateFormRequest;
|
||||
use FireflyIII\Api\V1\Requests\PaginationRequest;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Override;
|
||||
|
||||
class TransactionSearchRequest extends AggregateFormRequest
|
||||
{
|
||||
#[Override]
|
||||
protected function getRequests(): array
|
||||
{
|
||||
return [
|
||||
[PaginationRequest::class, 'sort_class' => TransactionJournal::class],
|
||||
SearchQueryRequest::class,
|
||||
// [ObjectTypeApiRequest::class, 'object_type' => Account::class],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ class ConvertsDatesToUTC extends Command
|
||||
}
|
||||
$this->friendlyInfo(sprintf('Converting field "%s" of model "%s" to UTC.', $field, $shortModel));
|
||||
$items->each(
|
||||
function ($item) use ($field, $timezoneField): void {
|
||||
static function ($item) use ($field, $timezoneField): void {
|
||||
$date = Carbon::parse($item->{$field}, $item->{$timezoneField}); // @phpstan-ignore-line
|
||||
$date->setTimezone('UTC');
|
||||
$item->{$field} = $date->format('Y-m-d H:i:s'); // @phpstan-ignore-line
|
||||
|
||||
@@ -103,7 +103,7 @@ class CorrectsPrimaryCurrencyAmounts extends Command
|
||||
|
||||
private function recalculateAccounts(UserGroup $userGroup): void
|
||||
{
|
||||
$set = $userGroup->accounts()->where(function (EloquentBuilder $q): void {
|
||||
$set = $userGroup->accounts()->where(static function (EloquentBuilder $q): void {
|
||||
$q->whereNotNull('virtual_balance');
|
||||
|
||||
// this needs a different piece of code for postgres.
|
||||
@@ -226,10 +226,10 @@ class CorrectsPrimaryCurrencyAmounts extends Command
|
||||
$set = DB::table('transactions')
|
||||
->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->where('transaction_journals.user_group_id', $userGroup->id)
|
||||
->where(function (DatabaseBuilder $q1) use ($currency): void {
|
||||
$q1->where(function (DatabaseBuilder $q2) use ($currency): void {
|
||||
->where(static function (DatabaseBuilder $q1) use ($currency): void {
|
||||
$q1->where(static function (DatabaseBuilder $q2) use ($currency): void {
|
||||
$q2->whereNot('transactions.transaction_currency_id', $currency->id)->whereNull('transactions.foreign_currency_id');
|
||||
})->orWhere(function (DatabaseBuilder $q3) use ($currency): void {
|
||||
})->orWhere(static function (DatabaseBuilder $q3) use ($currency): void {
|
||||
$q3->whereNot('transactions.transaction_currency_id', $currency->id)->whereNot('transactions.foreign_currency_id', $currency->id);
|
||||
});
|
||||
})
|
||||
|
||||
@@ -34,7 +34,7 @@ class RemovesBills extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Remove bills from transactions that shouldn\'t have one.';
|
||||
protected $description = 'Remove subscriptions from transactions that shouldn\'t have one.';
|
||||
protected $signature = 'correction:bills';
|
||||
|
||||
/**
|
||||
|
||||
91
app/Console/Commands/Explain/ExplainAvailableBudget.php
Normal file
91
app/Console/Commands/Explain/ExplainAvailableBudget.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* ExplainAvailableBudget.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Console\Commands\Explain;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use FireflyIII\Console\Commands\VerifiesAccessToken;
|
||||
use FireflyIII\Support\Facades\Navigation;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class ExplainAvailableBudget extends Command
|
||||
{
|
||||
use VerifiesAccessToken;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'explain:available-budget
|
||||
{--date=now : A date formatted YYYY-MM-DD or the word "now"}
|
||||
{--user=1 : The user ID.}
|
||||
{--token= : The user\'s access token.}
|
||||
';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Explains why the available budget amount is what it is.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$date = $this->getDate((string) $this->option('date'));
|
||||
$range = Preferences::getForUser($this->getUser(), 'viewRange', '1M')->data ?? '1M';
|
||||
$title = Navigation::periodShow($date, $range);
|
||||
$this->line('This command explains why the "available" budget bar at the top of your /budget bar means.');
|
||||
$this->line(sprintf(
|
||||
'You submitted date %s and your settings show a %s period, so this explanation concerns the period "%s".',
|
||||
$date->format('Y-m-d'),
|
||||
$range,
|
||||
$title
|
||||
));
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function getDate(string $param): Carbon
|
||||
{
|
||||
if ('now' === $param) {
|
||||
return today();
|
||||
}
|
||||
|
||||
try {
|
||||
$date = Carbon::parse($param);
|
||||
} catch (InvalidFormatException) {
|
||||
$this->warn('Invalid date given. Fall back to today\'s date.');
|
||||
|
||||
return today();
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
}
|
||||
@@ -198,15 +198,29 @@ class ApplyRules extends Command
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$accountRepository->setUser($this->getUser());
|
||||
foreach ($accountList as $accountId) {
|
||||
$accountId = (int) $accountId;
|
||||
$account = $accountRepository->find($accountId);
|
||||
if (null !== $account && in_array($account->accountType->type, $this->acceptedAccounts, true)) {
|
||||
$finalList->push($account);
|
||||
$accountId = (int)$accountId;
|
||||
if (0 === $accountId) {
|
||||
$this->friendlyWarning('You provided an account with ID 0 (zero). It will be ignored.');
|
||||
|
||||
continue;
|
||||
}
|
||||
$account = $accountRepository->find($accountId);
|
||||
if (null === $account) {
|
||||
$this->friendlyWarning(sprintf('There is no account with ID #%d, it cannot be added.', $accountId));
|
||||
|
||||
continue;
|
||||
}
|
||||
$type = $account->accountType->type;
|
||||
if (!in_array($account->accountType->type, $this->acceptedAccounts, true)) {
|
||||
$this->friendlyWarning(sprintf('Account "%s" with ID #%d is of type "%s" and cannot be added.', $account->name, $accountId, $type));
|
||||
|
||||
continue;
|
||||
}
|
||||
$finalList->push($account);
|
||||
}
|
||||
|
||||
if (0 === $finalList->count()) {
|
||||
$this->friendlyError('Please make sure all accounts in --accounts are asset accounts or liabilities.');
|
||||
$this->friendlyError('There are no accounts in the selection. Please make sure all accounts in --accounts are asset accounts or liabilities.');
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -225,13 +239,27 @@ class ApplyRules extends Command
|
||||
$ruleGroupList = explode(',', $ruleGroupString);
|
||||
|
||||
foreach ($ruleGroupList as $ruleGroupId) {
|
||||
$ruleGroup = $this->ruleGroupRepository->find((int) $ruleGroupId);
|
||||
if (true === $ruleGroup->active) {
|
||||
$this->ruleGroupSelection[] = $ruleGroup->id;
|
||||
$ruleGroupId = (int)$ruleGroupId;
|
||||
|
||||
if (0 === $ruleGroupId) {
|
||||
$this->friendlyWarning('You added a rule group with ID 0 (zero). It will be skipped.');
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$ruleGroup = $this->ruleGroupRepository->find($ruleGroupId);
|
||||
|
||||
if (null === $ruleGroup) {
|
||||
$this->friendlyWarning(sprintf('There is no rule group with ID #%d, this ID will be ignored.', $ruleGroupId));
|
||||
|
||||
continue;
|
||||
}
|
||||
if (false === $ruleGroup->active) {
|
||||
$this->friendlyWarning(sprintf('Will ignore inactive rule group #%d ("%s")', $ruleGroup->id, $ruleGroup->title));
|
||||
$this->friendlyWarning(sprintf('Rule group with ID #%d is not active, so this ID will be ignored.', $ruleGroupId));
|
||||
|
||||
continue;
|
||||
}
|
||||
$this->ruleGroupSelection[] = $ruleGroupId;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -247,10 +275,24 @@ class ApplyRules extends Command
|
||||
$ruleList = explode(',', $ruleString);
|
||||
|
||||
foreach ($ruleList as $ruleId) {
|
||||
$rule = $this->ruleRepository->find((int) $ruleId);
|
||||
if ($rule instanceof Rule && true === $rule->active) {
|
||||
$this->ruleSelection[] = $rule->id;
|
||||
$ruleId = (int)$ruleId;
|
||||
if (0 === $ruleId) {
|
||||
$this->friendlyWarning('You added a rule with ID 0 (zero). It will be skipped.');
|
||||
|
||||
continue;
|
||||
}
|
||||
$rule = $this->ruleRepository->find($ruleId);
|
||||
if (null === $rule) {
|
||||
$this->friendlyWarning(sprintf('There is no rule with ID #%d, this ID will be ignored.', $ruleId));
|
||||
|
||||
continue;
|
||||
}
|
||||
if (false === $rule->active) {
|
||||
$this->friendlyWarning(sprintf('Rule with ID #%d is not active, so this ID will be ignored.', $ruleId));
|
||||
|
||||
continue;
|
||||
}
|
||||
$this->ruleSelection[] = $ruleId;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -304,10 +346,12 @@ class ApplyRules extends Command
|
||||
|
||||
private function getRulesToApply(): Collection
|
||||
{
|
||||
Log::debug('getRulesToApply()');
|
||||
$rulesToApply = new Collection();
|
||||
|
||||
/** @var RuleGroup $group */
|
||||
foreach ($this->groups as $group) {
|
||||
Log::debug(sprintf('Scanning rule group #%d', $group->id));
|
||||
$rules = $this->ruleGroupRepository->getActiveStoreRules($group);
|
||||
|
||||
/** @var Rule $rule */
|
||||
@@ -318,16 +362,20 @@ class ApplyRules extends Command
|
||||
Log::debug(sprintf('Will include rule #%d "%s"', $rule->id, $rule->title));
|
||||
$rulesToApply->push($rule);
|
||||
}
|
||||
if (!$test) {
|
||||
Log::debug(sprintf('Will not include rule #%d', $rule->id));
|
||||
}
|
||||
}
|
||||
}
|
||||
Log::debug(sprintf('Found %d rules to apply.', $rulesToApply->count()));
|
||||
|
||||
return $rulesToApply;
|
||||
}
|
||||
|
||||
private function includeRule(Rule $rule, RuleGroup $group): bool
|
||||
{
|
||||
return in_array($group->id, $this->ruleGroupSelection, true)
|
||||
|| in_array($rule->id, $this->ruleSelection, true)
|
||||
return in_array((int)$group->id, $this->ruleGroupSelection, true)
|
||||
|| in_array((int)$rule->id, $this->ruleSelection, true)
|
||||
|| $this->allRules;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,15 +24,20 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Events\Model\TransactionGroup;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class TriggeredStoredTransactionGroup extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
public ?RuleGroup $ruleGroup = null;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct(public TransactionGroup $transactionGroup) {}
|
||||
public function __construct(public TransactionGroup $transactionGroup, ?RuleGroup $ruleGroup = null)
|
||||
{
|
||||
$this->ruleGroup = $ruleGroup;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace FireflyIII\Events;
|
||||
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* Class RequestedNewPassword.
|
||||
@@ -46,7 +47,7 @@ class RequestedNewPassword extends Event
|
||||
/**
|
||||
* Create a new event instance. This event is triggered when a users tries to reset his or her password.
|
||||
*/
|
||||
public function __construct(User $user, string $token, string $ipAddress)
|
||||
public function __construct(User $user, #[SensitiveParameter] string $token, string $ipAddress)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->token = $token;
|
||||
|
||||
@@ -45,6 +45,7 @@ use Override;
|
||||
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\GoneHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
@@ -71,6 +72,7 @@ class Handler extends ExceptionHandler
|
||||
AuthenticationException::class,
|
||||
LaravelValidationException::class,
|
||||
NotFoundHttpException::class,
|
||||
GoneHttpException::class,
|
||||
OAuthServerException::class,
|
||||
LaravelOAuthException::class,
|
||||
TokenMismatchException::class,
|
||||
|
||||
@@ -28,6 +28,7 @@ use FireflyIII\Events\Model\TransactionGroup\TriggeredStoredTransactionGroup;
|
||||
use FireflyIII\Events\RequestedSendWebhookMessages;
|
||||
use FireflyIII\Events\StoredTransactionGroup;
|
||||
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
|
||||
@@ -46,7 +47,7 @@ class StoredGroupEventHandler
|
||||
{
|
||||
public function runAllHandlers(StoredTransactionGroup $event): void
|
||||
{
|
||||
$this->processRules($event);
|
||||
$this->processRules($event, null);
|
||||
$this->recalculateCredit($event);
|
||||
$this->triggerWebhooks($event);
|
||||
$this->removePeriodStatistics($event);
|
||||
@@ -55,13 +56,13 @@ class StoredGroupEventHandler
|
||||
public function triggerRulesManually(TriggeredStoredTransactionGroup $event): void
|
||||
{
|
||||
$newEvent = new StoredTransactionGroup($event->transactionGroup, true, false);
|
||||
$this->processRules($newEvent);
|
||||
$this->processRules($newEvent, $event->ruleGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method grabs all the users rules and processes them.
|
||||
*/
|
||||
private function processRules(StoredTransactionGroup $storedGroupEvent): void
|
||||
private function processRules(StoredTransactionGroup $storedGroupEvent, ?RuleGroup $ruleGroup): void
|
||||
{
|
||||
if (false === $storedGroupEvent->applyRules) {
|
||||
Log::info(sprintf('Will not run rules on group #%d', $storedGroupEvent->transactionGroup->id));
|
||||
@@ -86,7 +87,14 @@ class StoredGroupEventHandler
|
||||
|
||||
// add the groups to the rule engine.
|
||||
// it should run the rules in the group and cancel the group if necessary.
|
||||
$groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal');
|
||||
if (null === $ruleGroup) {
|
||||
Log::debug('Fire processRules with ALL store-journal rule groups.');
|
||||
$groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal');
|
||||
}
|
||||
if (null !== $ruleGroup) {
|
||||
Log::debug(sprintf('Fire processRules with rule group #%d.', $ruleGroup->id));
|
||||
$groups = new Collection([$ruleGroup]);
|
||||
}
|
||||
|
||||
// create and fire rule engine.
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
|
||||
@@ -70,8 +70,12 @@ class UpdatedGroupEventHandler
|
||||
foreach ($event->transactionGroup->transactionJournals as $journal) {
|
||||
$source = $journal->transactions()->where('amount', '<', '0')->first();
|
||||
$dest = $journal->transactions()->where('amount', '>', '0')->first();
|
||||
$repository->deleteStatisticsForModel($source->account, $journal->date);
|
||||
$repository->deleteStatisticsForModel($dest->account, $journal->date);
|
||||
if (null !== $source) {
|
||||
$repository->deleteStatisticsForModel($source->account, $journal->date);
|
||||
}
|
||||
if (null !== $dest) {
|
||||
$repository->deleteStatisticsForModel($dest->account, $journal->date);
|
||||
}
|
||||
|
||||
$categories = $journal->categories;
|
||||
$tags = $journal->tags;
|
||||
|
||||
@@ -784,14 +784,23 @@ trait MetaCollection
|
||||
$filter = static function (array $object) use ($list): bool {
|
||||
Log::debug(sprintf('Now in setTags(%s) filter', implode(', ', $list)));
|
||||
foreach ($object['transactions'] as $transaction) {
|
||||
$total = count($transaction['tags']);
|
||||
$matched = 0;
|
||||
foreach ($transaction['tags'] as $tag) {
|
||||
Log::debug(sprintf('"%s" versus', strtolower((string) $tag['name'])), $list);
|
||||
if (in_array(strtolower((string) $tag['name']), $list, true)) {
|
||||
Log::debug(sprintf('Transaction has tag "%s" so return true.', $tag['name']));
|
||||
|
||||
return true;
|
||||
++$matched;
|
||||
if (1 === count($list)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count($list) > 1 && $total === $matched && $matched === count($list)) {
|
||||
Log::debug(sprintf('All %d searched tags are present.', $total));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Log::debug('Transaction has no tags from the list, so return false.');
|
||||
|
||||
|
||||
@@ -828,7 +828,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
*/
|
||||
foreach ($this->sorting as $field => $direction) {
|
||||
$func = 'ASC' === $direction ? 'sortBy' : 'sortByDesc';
|
||||
$collection = $collection->{$func}(function (array $product, int $key) use ($field) { // @phpstan-ignore-line
|
||||
$collection = $collection->{$func}(static function (array $product, int $key) use ($field) { // @phpstan-ignore-line
|
||||
// depends on $field:
|
||||
if ('description' === $field) {
|
||||
if (1 === count($product['transactions'])) {
|
||||
|
||||
@@ -37,6 +37,7 @@ use Illuminate\View\View;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* Class ResetPasswordController
|
||||
@@ -97,7 +98,7 @@ class ResetPasswordController extends Controller
|
||||
// database. Otherwise, we will parse the error and return the response.
|
||||
$response = $this->broker()->reset(
|
||||
$this->credentials($request),
|
||||
function ($user, $password): void {
|
||||
function ($user, #[SensitiveParameter] $password): void {
|
||||
$this->resetPassword($user, $password);
|
||||
}
|
||||
);
|
||||
@@ -123,7 +124,7 @@ class ResetPasswordController extends Controller
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function showResetForm(Request $request, $token = null)
|
||||
public function showResetForm(Request $request, #[SensitiveParameter] $token = null)
|
||||
{
|
||||
if ('web' !== config('firefly.authentication_guard')) {
|
||||
$message = sprintf('Cannot reset password when authenticating over "%s".', config('firefly.authentication_guard'));
|
||||
|
||||
@@ -74,6 +74,7 @@ class IndexController extends Controller
|
||||
{
|
||||
$this->cleanupObjectGroups();
|
||||
$this->repository->correctOrder();
|
||||
$this->repository->correctTransfers();
|
||||
$start = session('start');
|
||||
$end = session('end');
|
||||
$collection = $this->repository->getBills();
|
||||
|
||||
@@ -29,7 +29,6 @@ use FireflyIII\Support\Facades\Navigation;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Support\JsonApi\Enrichments\SubscriptionEnrichment;
|
||||
@@ -122,6 +121,7 @@ class ShowController extends Controller
|
||||
*/
|
||||
public function show(Request $request, Bill $bill): Factory|\Illuminate\Contracts\View\View
|
||||
{
|
||||
$this->repository->correctTransfers();
|
||||
// add info about rules:
|
||||
$rules = $this->repository->getRulesForBill($bill);
|
||||
$subTitle = $bill->name;
|
||||
@@ -184,7 +184,7 @@ class ShowController extends Controller
|
||||
/** @var AttachmentTransformer $transformer */
|
||||
$transformer = app(AttachmentTransformer::class);
|
||||
$attachments = $collection->each(
|
||||
static fn (Attachment $attachment) => $transformer->transform($attachment)
|
||||
$transformer->transform(...)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ class IndexController extends Controller
|
||||
|
||||
// translations:
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
static function ($request, $next) {
|
||||
app('view')->share('mainTitleIcon', 'fa-exchange');
|
||||
app('view')->share('title', (string) trans('firefly.header_exchange_rates'));
|
||||
|
||||
|
||||
@@ -212,6 +212,13 @@ class ReconcileController extends Controller
|
||||
|
||||
$startBalance = Steam::accountsBalancesOptimized(new Collection()->push($account), $startDate)[$account->id];
|
||||
$endBalance = Steam::accountsBalancesOptimized(new Collection()->push($account), $end)[$account->id];
|
||||
// round balances.
|
||||
foreach ($startBalance as $key => $value) {
|
||||
$startBalance[$key] = Steam::bcround($value, $currency->decimal_places);
|
||||
}
|
||||
foreach ($endBalance as $key => $value) {
|
||||
$endBalance[$key] = Steam::bcround($value, $currency->decimal_places);
|
||||
}
|
||||
|
||||
|
||||
// get the transactions
|
||||
|
||||
@@ -198,12 +198,14 @@ class PreferencesController extends Controller
|
||||
*/
|
||||
public function postIndex(PreferencesRequest $request): Redirector|RedirectResponse
|
||||
{
|
||||
Log::debug('postIndex for preferences.');
|
||||
// front page accounts
|
||||
$frontpageAccounts = [];
|
||||
if (is_array($request->get('frontpageAccounts')) && count($request->get('frontpageAccounts')) > 0) {
|
||||
foreach ($request->get('frontpageAccounts') as $id) {
|
||||
$frontpageAccounts[] = (int)$id;
|
||||
}
|
||||
Log::debug('Update frontpageAccounts', $frontpageAccounts);
|
||||
Preferences::set('frontpageAccounts', $frontpageAccounts);
|
||||
}
|
||||
|
||||
@@ -212,14 +214,17 @@ class PreferencesController extends Controller
|
||||
foreach (config('notifications.notifications.user') as $key => $info) {
|
||||
$key = sprintf('notification_%s', $key);
|
||||
if (array_key_exists($key, $all)) {
|
||||
Log::debug(sprintf('update notification to true: %s', $key));
|
||||
Preferences::set($key, true);
|
||||
}
|
||||
if (!array_key_exists($key, $all)) {
|
||||
Log::debug(sprintf('update notification to false: %s', $key));
|
||||
Preferences::set($key, false);
|
||||
}
|
||||
}
|
||||
|
||||
// view range:
|
||||
Log::debug(sprintf('Let viewRange to "%s"', $request->get('viewRange')));
|
||||
Preferences::set('viewRange', $request->get('viewRange'));
|
||||
// forget session values:
|
||||
session()->forget('start');
|
||||
@@ -319,6 +324,7 @@ class PreferencesController extends Controller
|
||||
// save and continue
|
||||
session()->flash('success', (string)trans('firefly.saved_preferences'));
|
||||
Preferences::mark();
|
||||
Log::debug('Done saving settings.');
|
||||
|
||||
return redirect(route('preferences.index'));
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ use Illuminate\View\View;
|
||||
use Laravel\Passport\ClientRepository;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* Class ProfileController.
|
||||
@@ -91,7 +92,7 @@ class ProfileController extends Controller
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function confirmEmailChange(UserRepositoryInterface $repository, string $token): Redirector|RedirectResponse
|
||||
public function confirmEmailChange(UserRepositoryInterface $repository, #[SensitiveParameter] string $token): Redirector|RedirectResponse
|
||||
{
|
||||
if (!$this->internalAuth) {
|
||||
throw new FireflyException(trans('firefly.external_user_mgt_disabled'));
|
||||
@@ -388,7 +389,7 @@ class ProfileController extends Controller
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function undoEmailChange(UserRepositoryInterface $repository, string $token, string $hash): Redirector|RedirectResponse
|
||||
public function undoEmailChange(UserRepositoryInterface $repository, #[SensitiveParameter] string $token, string $hash): Redirector|RedirectResponse
|
||||
{
|
||||
if (!$this->internalAuth) {
|
||||
throw new FireflyException(trans('firefly.external_user_mgt_disabled'));
|
||||
|
||||
@@ -35,6 +35,7 @@ use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\View\View;
|
||||
|
||||
@@ -70,13 +71,20 @@ class ExecutionController extends Controller
|
||||
*/
|
||||
public function execute(SelectTransactionsRequest $request, RuleGroup $ruleGroup): RedirectResponse
|
||||
{
|
||||
Log::debug(sprintf('You have selected rule group #%d', $ruleGroup->id));
|
||||
// Get parameters specified by the user
|
||||
$accounts = $request->get('accounts');
|
||||
$set = $this->repository->getAccountsById($accounts);
|
||||
$set = new Collection();
|
||||
if (is_array($accounts)) {
|
||||
$set = $this->repository->getAccountsById($accounts);
|
||||
}
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setAccounts($set);
|
||||
if (count($set) > 0) {
|
||||
$collector->setAccounts($set);
|
||||
}
|
||||
|
||||
// add date operators.
|
||||
if (null !== $request->get('start')) {
|
||||
$startDate = new Carbon($request->get('start'));
|
||||
@@ -96,7 +104,7 @@ class ExecutionController extends Controller
|
||||
/** @var TransactionGroup $group */
|
||||
foreach ($groups as $group) {
|
||||
Log::debug(sprintf('Processing group #%d.', $group->id));
|
||||
event(new TriggeredStoredTransactionGroup($group));
|
||||
event(new TriggeredStoredTransactionGroup($group, $ruleGroup));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ class AcceptHeaders
|
||||
// some routes are exempt from this.
|
||||
$exempt = [
|
||||
'api.v1.data.bulk.transactions',
|
||||
'api.v1.attachments.upload',
|
||||
];
|
||||
|
||||
if (('POST' === $method || 'PUT' === $method) && !$request->hasHeader('Content-Type') && !in_array($request->route()->getName(), $exempt, true)) {
|
||||
|
||||
@@ -157,7 +157,7 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
private function filterRecurrences(Collection $recurrences): Collection
|
||||
{
|
||||
return $recurrences->filter(
|
||||
fn (Recurrence $recurrence): bool => $this->validRecurrence($recurrence)
|
||||
$this->validRecurrence(...)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,6 @@ class AccountMeta extends Model
|
||||
|
||||
protected function data(): Attribute
|
||||
{
|
||||
return Attribute::make(get: fn (mixed $value): string => (string)json_decode((string)$value, true), set: fn (mixed $value): array => ['data' => json_encode($value)]);
|
||||
return Attribute::make(get: static fn (mixed $value): string => (string)json_decode((string)$value, true), set: static fn (mixed $value): array => ['data' => json_encode($value)]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,16 +103,16 @@ class AvailableBudget extends Model
|
||||
protected function endDate(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn (string $value): Carbon => Carbon::parse($value),
|
||||
set: fn (Carbon $value): string => $value->format('Y-m-d'),
|
||||
get: static fn (string $value): Carbon => Carbon::parse($value),
|
||||
set: static fn (Carbon $value): string => $value->format('Y-m-d'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function startDate(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn (string $value): Carbon => Carbon::parse($value),
|
||||
set: fn (Carbon $value): string => $value->format('Y-m-d'),
|
||||
get: static fn (string $value): Carbon => Carbon::parse($value),
|
||||
set: static fn (Carbon $value): string => $value->format('Y-m-d'),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,6 @@ class Configuration extends Model
|
||||
*/
|
||||
protected function data(): Attribute
|
||||
{
|
||||
return Attribute::make(get: fn ($value): mixed => json_decode((string)$value), set: fn ($value): array => ['data' => json_encode($value)]);
|
||||
return Attribute::make(get: static fn ($value): mixed => json_decode((string)$value), set: static fn ($value): array => ['data' => json_encode($value)]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ class Rule extends Model
|
||||
|
||||
protected function description(): Attribute
|
||||
{
|
||||
return Attribute::make(set: fn ($value): array => ['description' => e($value)]);
|
||||
return Attribute::make(set: static fn ($value): array => ['description' => e($value)]);
|
||||
}
|
||||
|
||||
protected function order(): Attribute
|
||||
|
||||
@@ -57,7 +57,7 @@ class TransactionJournalMeta extends Model
|
||||
|
||||
protected function data(): Attribute
|
||||
{
|
||||
return Attribute::make(get: fn ($value): mixed => json_decode((string)$value, false), set: function ($value): array {
|
||||
return Attribute::make(get: static fn ($value): mixed => json_decode((string)$value, false), set: static function ($value): array {
|
||||
$data = json_encode($value);
|
||||
|
||||
return ['data' => $data, 'hash' => hash('sha256', $data)];
|
||||
|
||||
@@ -45,7 +45,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
Schema::defaultStringLength(191);
|
||||
// Passport::$clientUuids = false;
|
||||
Response::macro('api', function (array $value) {
|
||||
Response::macro('api', static function (array $value) {
|
||||
$headers = [
|
||||
'Cache-Control' => 'no-store',
|
||||
];
|
||||
@@ -61,7 +61,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
});
|
||||
|
||||
// blade extension
|
||||
Blade::directive('activeXRoutePartial', function (string $route): string {
|
||||
Blade::directive('activeXRoutePartial', static function (string $route): string {
|
||||
$name = Route::getCurrentRoute()->getName() ?? '';
|
||||
if (str_contains($name, $route)) {
|
||||
return 'menu-open';
|
||||
@@ -69,7 +69,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
|
||||
return '';
|
||||
});
|
||||
Blade::if('partialroute', function (string $route, string $firstParam = ''): bool {
|
||||
Blade::if('partialroute', static function (string $route, string $firstParam = ''): bool {
|
||||
$name = Route::getCurrentRoute()->getName() ?? '';
|
||||
if ('' === $firstParam && str_contains($name, $route)) {
|
||||
return true;
|
||||
|
||||
@@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Providers;
|
||||
|
||||
use FireflyIII\Support\Search\OperatorQuerySearch;
|
||||
use FireflyIII\Support\Search\QueryParser\GdbotsQueryParser;
|
||||
use FireflyIII\Support\Search\QueryParser\QueryParser;
|
||||
use FireflyIII\Support\Search\QueryParser\QueryParserInterface;
|
||||
use FireflyIII\Support\Search\SearchInterface;
|
||||
@@ -49,16 +48,7 @@ class SearchServiceProvider extends ServiceProvider
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->bind(
|
||||
static function (): QueryParserInterface {
|
||||
return app(QueryParser::class);
|
||||
// 2025-12-20 ignore this setting.
|
||||
// $implementation = config('search.query_parser');
|
||||
//
|
||||
// return match ($implementation) {
|
||||
// 'new' => app(QueryParser::class),
|
||||
// default => app(GdbotsQueryParser::class),
|
||||
// };
|
||||
}
|
||||
static fn (): QueryParserInterface => app(QueryParser::class)
|
||||
);
|
||||
|
||||
$this->app->bind(
|
||||
|
||||
@@ -23,8 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Repositories\Bill;
|
||||
|
||||
use FireflyIII\Support\Facades\Navigation;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Enums\TransactionTypeEnum;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Factory\BillFactory;
|
||||
use FireflyIII\Models\Attachment;
|
||||
@@ -34,12 +34,14 @@ use FireflyIII\Models\ObjectGroup;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups;
|
||||
use FireflyIII\Services\Internal\Destroy\BillDestroyService;
|
||||
use FireflyIII\Services\Internal\Update\BillUpdateService;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\Navigation;
|
||||
use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface;
|
||||
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
@@ -48,6 +50,7 @@ use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Class BillRepository.
|
||||
@@ -244,7 +247,7 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
|
||||
/** @var null|Note $note */
|
||||
$note = $bill->notes()->first();
|
||||
|
||||
return (string) $note?->text;
|
||||
return (string)$note?->text;
|
||||
}
|
||||
|
||||
public function getOverallAverage(Bill $bill): array
|
||||
@@ -261,7 +264,7 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
|
||||
foreach ($journals as $journal) {
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $journal->transactions()->where('amount', '<', 0)->first();
|
||||
$currencyId = (int) $journal->transaction_currency_id;
|
||||
$currencyId = (int)$journal->transaction_currency_id;
|
||||
$currency = $journal->transactionCurrency;
|
||||
$result[$currencyId] ??= [
|
||||
'sum' => '0',
|
||||
@@ -274,10 +277,10 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
];
|
||||
$result[$currencyId]['sum'] = bcadd($result[$currencyId]['sum'], (string) $transaction->amount);
|
||||
$result[$currencyId]['sum'] = bcadd($result[$currencyId]['sum'], (string)$transaction->amount);
|
||||
$result[$currencyId]['pc_sum'] = bcadd($result[$currencyId]['pc_sum'], $transaction->native_amount ?? '0');
|
||||
if ($journal->foreign_currency_id === Amount::getPrimaryCurrency()->id) {
|
||||
$result[$currencyId]['pc_sum'] = bcadd($result[$currencyId]['pc_sum'], (string) $transaction->amount);
|
||||
$result[$currencyId]['pc_sum'] = bcadd($result[$currencyId]['pc_sum'], (string)$transaction->amount);
|
||||
}
|
||||
++$result[$currencyId]['count'];
|
||||
}
|
||||
@@ -288,8 +291,8 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
|
||||
* @var array $arr
|
||||
*/
|
||||
foreach ($result as $currencyId => $arr) {
|
||||
$result[$currencyId]['avg'] = bcdiv((string) $arr['sum'], (string) $arr['count']);
|
||||
$result[$currencyId]['pc_avg'] = bcdiv((string) $arr['pc_sum'], (string) $arr['count']);
|
||||
$result[$currencyId]['avg'] = bcdiv((string)$arr['sum'], (string)$arr['count']);
|
||||
$result[$currencyId]['pc_avg'] = bcdiv((string)$arr['pc_sum'], (string)$arr['count']);
|
||||
}
|
||||
|
||||
return $result;
|
||||
@@ -398,7 +401,7 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
|
||||
if (null === $transaction) {
|
||||
continue;
|
||||
}
|
||||
$currencyId = (int) $journal->transaction_currency_id;
|
||||
$currencyId = (int)$journal->transaction_currency_id;
|
||||
$currency = $journal->transactionCurrency;
|
||||
$result[$currencyId] ??= [
|
||||
'sum' => '0',
|
||||
@@ -410,10 +413,10 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
];
|
||||
$result[$currencyId]['sum'] = bcadd($result[$currencyId]['sum'], (string) $transaction->amount);
|
||||
$result[$currencyId]['sum'] = bcadd($result[$currencyId]['sum'], (string)$transaction->amount);
|
||||
$result[$currencyId]['pc_sum'] = bcadd($result[$currencyId]['pc_sum'], $transaction->native_amount ?? '0');
|
||||
if ($journal->foreign_currency_id === Amount::getPrimaryCurrency()->id) {
|
||||
$result[$currencyId]['pc_sum'] = bcadd($result[$currencyId]['pc_sum'], (string) $transaction->amount);
|
||||
$result[$currencyId]['pc_sum'] = bcadd($result[$currencyId]['pc_sum'], (string)$transaction->amount);
|
||||
}
|
||||
++$result[$currencyId]['count'];
|
||||
}
|
||||
@@ -424,8 +427,8 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
|
||||
* @var array $arr
|
||||
*/
|
||||
foreach ($result as $currencyId => $arr) {
|
||||
$result[$currencyId]['avg'] = bcdiv((string) $arr['sum'], (string) $arr['count']);
|
||||
$result[$currencyId]['pc_avg'] = bcdiv((string) $arr['pc_sum'], (string) $arr['count']);
|
||||
$result[$currencyId]['avg'] = bcdiv((string)$arr['sum'], (string)$arr['count']);
|
||||
$result[$currencyId]['pc_avg'] = bcdiv((string)$arr['pc_sum'], (string)$arr['count']);
|
||||
}
|
||||
|
||||
return $result;
|
||||
@@ -438,7 +441,7 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
|
||||
{
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$journal = $bill->user->transactionJournals()->find((int) $transaction['transaction_journal_id']);
|
||||
$journal = $bill->user->transactionJournals()->find((int)$transaction['transaction_journal_id']);
|
||||
$journal->bill_id = $bill->id;
|
||||
$journal->save();
|
||||
Log::debug(sprintf('Linked journal #%d to bill #%d', $journal->id, $bill->id));
|
||||
@@ -544,8 +547,8 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
|
||||
/** @var Collection $set */
|
||||
$set = $bill->transactionJournals()->after($start)->before($end)->get(['transaction_journals.*']);
|
||||
$currency = $convertToPrimary && $bill->transactionCurrency->id !== $primary->id ? $primary : $bill->transactionCurrency;
|
||||
$return[(int) $currency->id] ??= [
|
||||
'id' => (string) $currency->id,
|
||||
$return[(int)$currency->id] ??= [
|
||||
'id' => (string)$currency->id,
|
||||
'name' => $currency->name,
|
||||
'symbol' => $currency->symbol,
|
||||
'code' => $currency->code,
|
||||
@@ -557,9 +560,9 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
|
||||
/** @var TransactionJournal $transactionJournal */
|
||||
foreach ($set as $transactionJournal) {
|
||||
// grab currency from transaction.
|
||||
$transactionCurrency = $transactionJournal->transactionCurrency;
|
||||
$return[(int) $transactionCurrency->id] ??= [
|
||||
'id' => (string) $transactionCurrency->id,
|
||||
$transactionCurrency = $transactionJournal->transactionCurrency;
|
||||
$return[(int)$transactionCurrency->id] ??= [
|
||||
'id' => (string)$transactionCurrency->id,
|
||||
'name' => $transactionCurrency->name,
|
||||
'symbol' => $transactionCurrency->symbol,
|
||||
'code' => $transactionCurrency->code,
|
||||
@@ -568,7 +571,7 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
|
||||
];
|
||||
|
||||
// get currency from transaction as well.
|
||||
$return[(int) $transactionCurrency->id]['sum'] = bcadd($return[(int) $transactionCurrency->id]['sum'], Amount::getAmountFromJournalObject($transactionJournal));
|
||||
$return[(int)$transactionCurrency->id]['sum'] = bcadd($return[(int)$transactionCurrency->id]['sum'], Amount::getAmountFromJournalObject($transactionJournal));
|
||||
// $setAmount = bcadd($setAmount, Amount::getAmountFromJournalObject($transactionJournal));
|
||||
}
|
||||
// Log::debug(sprintf('Bill #%d ("%s") with %d transaction(s) and sum %s %s', $bill->id, $bill->name, $set->count(), $currency->code, $setAmount));
|
||||
@@ -622,14 +625,14 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
|
||||
$average = bcdiv(bcadd($bill->{$maxField} ?? '0', $bill->{$minField} ?? '0'), '2');
|
||||
Log::debug(sprintf('Amount to pay is %s %s (%d times)', $currency->code, $average, $total));
|
||||
$return[$currency->id] ??= [
|
||||
'id' => (string) $currency->id,
|
||||
'id' => (string)$currency->id,
|
||||
'name' => $currency->name,
|
||||
'symbol' => $currency->symbol,
|
||||
'code' => $currency->code,
|
||||
'decimal_places' => $currency->decimal_places,
|
||||
'sum' => '0',
|
||||
];
|
||||
$return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], bcmul($average, (string) $total));
|
||||
$return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], bcmul($average, (string)$total));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -704,4 +707,20 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
|
||||
|
||||
return $service->update($bill, $data);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function correctTransfers(): void
|
||||
{
|
||||
/** @var null|TransactionType $withdrawal */
|
||||
$withdrawal = TransactionType::where('type', TransactionTypeEnum::WITHDRAWAL->value)->first();
|
||||
if (null === $withdrawal) {
|
||||
return;
|
||||
}
|
||||
$this->user
|
||||
->transactionJournals()
|
||||
->whereNotNull('bill_id')
|
||||
->where('transaction_type_id', '!=', $withdrawal->id)
|
||||
->update(['bill_id' => null])
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,8 @@ interface BillRepositoryInterface
|
||||
*/
|
||||
public function correctOrder(): void;
|
||||
|
||||
public function correctTransfers(): void;
|
||||
|
||||
public function destroy(Bill $bill): bool;
|
||||
|
||||
public function destroyAll(): void;
|
||||
|
||||
@@ -55,12 +55,12 @@ class ExchangeRateRepository implements ExchangeRateRepositoryInterface, UserGro
|
||||
// orderBy('date', 'DESC')->toRawSql();
|
||||
return
|
||||
$this->userGroup->currencyExchangeRates()
|
||||
->where(function (Builder $q1) use ($from, $to): void {
|
||||
$q1->where(function (Builder $q) use ($from, $to): void {
|
||||
->where(static function (Builder $q1) use ($from, $to): void {
|
||||
$q1->where(static function (Builder $q) use ($from, $to): void {
|
||||
$q->where('from_currency_id', $from->id)
|
||||
->where('to_currency_id', $to->id)
|
||||
;
|
||||
})->orWhere(function (Builder $q) use ($from, $to): void {
|
||||
})->orWhere(static function (Builder $q) use ($from, $to): void {
|
||||
$q->where('from_currency_id', $to->id)
|
||||
->where('to_currency_id', $from->id)
|
||||
;
|
||||
|
||||
@@ -39,6 +39,7 @@ use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Override;
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* Class UserRepository.
|
||||
@@ -74,7 +75,7 @@ class UserRepository implements UserRepositoryInterface
|
||||
return true;
|
||||
}
|
||||
|
||||
public function changePassword(User $user, string $password): bool
|
||||
public function changePassword(User $user, #[SensitiveParameter] string $password): bool
|
||||
{
|
||||
$user->password = bcrypt($password);
|
||||
$user->save();
|
||||
|
||||
@@ -30,6 +30,7 @@ use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Support\Collection;
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* Interface UserRepositoryInterface.
|
||||
@@ -64,7 +65,7 @@ interface UserRepositoryInterface
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function changePassword(User $user, string $password);
|
||||
public function changePassword(User $user, #[SensitiveParameter] string $password);
|
||||
|
||||
public function changeStatus(User $user, bool $isBlocked, string $code): bool;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ use Illuminate\Support\Facades\Log;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* Class PwndVerifierV2.
|
||||
@@ -36,7 +37,7 @@ class PwndVerifierV2 implements Verifier
|
||||
/**
|
||||
* Verify the given password against (some) service.
|
||||
*/
|
||||
public function validPassword(string $password): bool
|
||||
public function validPassword(#[SensitiveParameter] string $password): bool
|
||||
{
|
||||
// Yes SHA1 is unsafe but in this context its fine.
|
||||
$hash = sha1($password);
|
||||
|
||||
@@ -23,6 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Services\Password;
|
||||
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* Interface Verifier.
|
||||
*/
|
||||
@@ -31,5 +33,5 @@ interface Verifier
|
||||
/**
|
||||
* Verify the given password against (some) service.
|
||||
*/
|
||||
public function validPassword(string $password): bool;
|
||||
public function validPassword(#[SensitiveParameter] string $password): bool;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Contracts\Auth\UserProvider;
|
||||
use Illuminate\Support\Str;
|
||||
use Override;
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* Class RemoteUserProvider
|
||||
@@ -100,7 +101,7 @@ class RemoteUserProvider implements UserProvider
|
||||
*
|
||||
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
|
||||
*/
|
||||
public function retrieveByToken($identifier, $token): ?Authenticatable
|
||||
public function retrieveByToken($identifier, #[SensitiveParameter] $token): ?Authenticatable
|
||||
{
|
||||
Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
|
||||
@@ -114,7 +115,7 @@ class RemoteUserProvider implements UserProvider
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function updateRememberToken(Authenticatable $user, $token): void
|
||||
public function updateRememberToken(Authenticatable $user, #[SensitiveParameter] $token): void
|
||||
{
|
||||
Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
|
||||
|
||||
@@ -331,7 +331,7 @@ trait PeriodOverview
|
||||
}
|
||||
|
||||
return $this->statistics->filter(
|
||||
fn (PeriodStatistic $statistic): bool => $statistic->start->eq($start) && $statistic->end->eq($end) && $statistic->type === $type
|
||||
static fn (PeriodStatistic $statistic): bool => $statistic->start->eq($start) && $statistic->end->eq($end) && $statistic->type === $type
|
||||
);
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ trait PeriodOverview
|
||||
}
|
||||
|
||||
return $this->statistics->filter(
|
||||
fn (PeriodStatistic $statistic): bool => $statistic->start->eq($start) && $statistic->end->eq($end) && str_starts_with($statistic->type, $prefix)
|
||||
static fn (PeriodStatistic $statistic): bool => $statistic->start->eq($start) && $statistic->end->eq($end) && str_starts_with($statistic->type, $prefix)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Support\Http\Controllers;
|
||||
|
||||
use FireflyIII\Models\Tag;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use FireflyIII\Enums\AccountTypeEnum;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
@@ -416,8 +417,21 @@ trait RenderPartialViews
|
||||
$repository = app(TagRepositoryInterface::class);
|
||||
$tags = $repository->get();
|
||||
|
||||
$grouped = [];
|
||||
|
||||
/** @var Tag $tag */
|
||||
foreach ($tags as $tag) {
|
||||
$year = (int) $tag->date?->year;
|
||||
$grouped[$year] ??= [
|
||||
'tags' => [],
|
||||
'year' => 0 === $year ? trans('firefly.no_date') : $year,
|
||||
];
|
||||
$grouped[$year]['tags'][] = $tag;
|
||||
}
|
||||
ksort($grouped);
|
||||
|
||||
try {
|
||||
$result = view('reports.options.tag', ['tags' => $tags])->render();
|
||||
$result = view('reports.options.tag', ['tags' => $grouped])->render();
|
||||
} catch (Throwable $e) {
|
||||
Log::error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage()));
|
||||
$result = 'Could not render view.';
|
||||
|
||||
@@ -179,7 +179,7 @@ class BudgetLimitEnrichment implements EnrichmentInterface
|
||||
|
||||
private function filterToBudget(array $expenses, int $budget): array
|
||||
{
|
||||
$result = array_filter($expenses, fn (array $item): bool => (int)$item['budget_id'] === $budget);
|
||||
$result = array_filter($expenses, static fn (array $item): bool => (int)$item['budget_id'] === $budget);
|
||||
Log::debug(sprintf('filterToBudget for budget #%d, from %d to %d items', $budget, count($expenses), count($result)));
|
||||
|
||||
return $result;
|
||||
@@ -187,13 +187,13 @@ class BudgetLimitEnrichment implements EnrichmentInterface
|
||||
|
||||
private function stringifyIds(): void
|
||||
{
|
||||
$this->expenses = array_map(fn ($first): array => array_map(function (array $second): array {
|
||||
$this->expenses = array_map(static fn ($first): array => array_map(static function (array $second): array {
|
||||
$second['currency_id'] = (string)($second['currency_id'] ?? 0);
|
||||
|
||||
return $second;
|
||||
}, $first), $this->expenses);
|
||||
|
||||
$this->pcExpenses = array_map(fn (array $first): array => array_map(function (array $second): array {
|
||||
$this->pcExpenses = array_map(static fn (array $first): array => array_map(static function (array $second): array {
|
||||
$second['currency_id'] ??= 0;
|
||||
|
||||
return $second;
|
||||
|
||||
@@ -339,7 +339,7 @@ class RecurringEnrichment implements EnrichmentInterface
|
||||
|
||||
/** @var RecurrenceRepetition $repetition */
|
||||
foreach ($set as $repetition) {
|
||||
$recurrence = $this->collection->filter(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;
|
||||
|
||||
@@ -173,7 +173,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
|
||||
*/
|
||||
protected function lastPaidDate(Bill $subscription, Collection $dates, Carbon $default): Carbon
|
||||
{
|
||||
$filtered = $dates->filter(fn (TransactionJournal $journal): bool => (int)$journal->bill_id === (int)$subscription->id);
|
||||
$filtered = $dates->filter(static fn (TransactionJournal $journal): bool => (int)$journal->bill_id === (int)$subscription->id);
|
||||
Log::debug(sprintf('Filtered down from %d to %d entries for bill #%d.', $dates->count(), $filtered->count(), $subscription->id));
|
||||
if (0 === $filtered->count()) {
|
||||
return $default;
|
||||
@@ -294,7 +294,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
|
||||
|
||||
// At this point the "next match" is exactly after the last time the bill was paid.
|
||||
$result = [];
|
||||
$filtered = $set->filter(fn (TransactionJournal $journal): bool => (int)$journal->bill_id === (int)$subscription->id);
|
||||
$filtered = $set->filter(static fn (TransactionJournal $journal): bool => (int)$journal->bill_id === (int)$subscription->id);
|
||||
foreach ($filtered as $entry) {
|
||||
$array = [
|
||||
'transaction_group_id' => (string)$entry->transaction_group_id,
|
||||
@@ -321,7 +321,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
|
||||
$array['foreign_currency_code'] = $entry->foreign_currency_code;
|
||||
$array['foreign_currency_symbol'] = $entry->foreign_currency_symbol;
|
||||
$array['foreign_currency_decimal_places'] = $entry->foreign_currency_decimal_places;
|
||||
$array['foreign_amount'] = Steam::bcround($entry->foreign_amount, $entry->foreign_currency_decimal_places);
|
||||
$array['foreign_amount'] = Steam::bcround((string) $entry->foreign_amount, $entry->foreign_currency_decimal_places);
|
||||
}
|
||||
// convert to primary, but is already primary.
|
||||
if ($this->convertToPrimary && (int)$entry->transaction_currency_id === $this->primaryCurrency->id) {
|
||||
@@ -329,7 +329,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
|
||||
}
|
||||
// convert to primary, but is NOT already primary.
|
||||
if ($this->convertToPrimary && (int)$entry->transaction_currency_id !== $this->primaryCurrency->id) {
|
||||
$array['pc_amount'] = $converter->convert($entry->transactionCurrency, $this->primaryCurrency, $entry->date, $entry->amount);
|
||||
$array['pc_amount'] = $converter->convert($entry->transactionCurrency, $this->primaryCurrency, $entry->date, (string) $entry->amount);
|
||||
}
|
||||
// convert to primary, but foreign is already primary.
|
||||
if ($this->convertToPrimary && (int)$entry->foreign_currency_id === $this->primaryCurrency->id) {
|
||||
@@ -340,7 +340,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
|
||||
// TODO this is very database intensive.
|
||||
/** @var TransactionCurrency $foreignCurrency */
|
||||
$foreignCurrency = Amount::getTransactionCurrencyById($entry->foreign_currency_id);
|
||||
$array['pc_foreign_amount'] = $converter->convert($foreignCurrency, $this->primaryCurrency, $entry->date, $entry->amount);
|
||||
$array['pc_foreign_amount'] = $converter->convert($foreignCurrency, $this->primaryCurrency, $entry->date, (string) $entry->amount);
|
||||
}
|
||||
$result[] = $array;
|
||||
}
|
||||
@@ -385,7 +385,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
|
||||
|
||||
private function filterPaidDates(array $entries): array
|
||||
{
|
||||
return array_map(function (array $entry): array {
|
||||
return array_map(static function (array $entry): array {
|
||||
unset($entry['date_object']);
|
||||
|
||||
return $entry;
|
||||
|
||||
@@ -202,7 +202,6 @@ class Navigation
|
||||
public function endOfPeriod(Carbon $end, string $repeatFreq): Carbon
|
||||
{
|
||||
$currentEnd = clone $end;
|
||||
|
||||
// Log::debug(sprintf('Now in endOfPeriod("%s", "%s").', $currentEnd->toIso8601String(), $repeatFreq));
|
||||
if ('MTD' === $repeatFreq && $end->isFuture()) {
|
||||
// fall back to a monthly schedule if the requested period is MTD.
|
||||
@@ -325,6 +324,7 @@ class Navigation
|
||||
}
|
||||
unset($result);
|
||||
|
||||
|
||||
if (!array_key_exists($repeatFreq, $functionMap)) {
|
||||
Log::error(sprintf('Cannot do endOfPeriod for $repeat_freq "%s"', $repeatFreq));
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ class Preferences
|
||||
|
||||
return Preference::where('user_id', $user->id)
|
||||
->where('name', '!=', 'currencyPreference')
|
||||
->where(function (Builder $q) use ($user): void {
|
||||
->where(static function (Builder $q) use ($user): void {
|
||||
$q->whereNull('user_group_id');
|
||||
$q->orWhere('user_group_id', $user->user_group_id);
|
||||
})
|
||||
@@ -108,7 +108,7 @@ class Preferences
|
||||
{
|
||||
$result = [];
|
||||
$preferences = Preference::where('user_id', $user->id)
|
||||
->where(function (Builder $q) use ($user): void {
|
||||
->where(static function (Builder $q) use ($user): void {
|
||||
$q->whereNull('user_group_id');
|
||||
$q->orWhere('user_group_id', $user->user_group_id);
|
||||
})
|
||||
|
||||
@@ -32,9 +32,9 @@ use Illuminate\Support\Facades\Log;
|
||||
|
||||
class TransactionSummarizer
|
||||
{
|
||||
private bool $convertToPrimary = false;
|
||||
private bool $convertToPrimary = false;
|
||||
private TransactionCurrency $default;
|
||||
private User $user;
|
||||
private User $user;
|
||||
|
||||
public function __construct(?User $user = null)
|
||||
{
|
||||
@@ -51,7 +51,7 @@ class TransactionSummarizer
|
||||
$field = 'amount';
|
||||
|
||||
// grab default currency information.
|
||||
$currencyId = (int)$journal['currency_id'];
|
||||
$currencyId = (int) $journal['currency_id'];
|
||||
$currencyName = $journal['currency_name'];
|
||||
$currencySymbol = $journal['currency_symbol'];
|
||||
$currencyCode = $journal['currency_code'];
|
||||
@@ -67,8 +67,8 @@ class TransactionSummarizer
|
||||
if ($this->convertToPrimary) {
|
||||
// Log::debug('convertToPrimary is true.');
|
||||
// if convert to primary currency, use the primary currency amount yes or no?
|
||||
$usePrimary = $this->default->id !== (int)$journal['currency_id'];
|
||||
$useForeign = $this->default->id === (int)$journal['foreign_currency_id'];
|
||||
$usePrimary = $this->default->id !== (int) $journal['currency_id'];
|
||||
$useForeign = $this->default->id === (int) $journal['foreign_currency_id'];
|
||||
if ($usePrimary) {
|
||||
// Log::debug(sprintf('Journal #%d switches to primary currency amount (original is %s)', $journal['transaction_journal_id'], $journal['currency_code']));
|
||||
$field = 'pc_amount';
|
||||
@@ -81,7 +81,7 @@ class TransactionSummarizer
|
||||
if ($useForeign) {
|
||||
// Log::debug(sprintf('Journal #%d switches to foreign amount (foreign is %s)', $journal['transaction_journal_id'], $journal['foreign_currency_code']));
|
||||
$field = 'foreign_amount';
|
||||
$currencyId = (int)$journal['foreign_currency_id'];
|
||||
$currencyId = (int) $journal['foreign_currency_id'];
|
||||
$currencyName = $journal['foreign_currency_name'];
|
||||
$currencySymbol = $journal['foreign_currency_symbol'];
|
||||
$currencyCode = $journal['foreign_currency_code'];
|
||||
@@ -91,9 +91,13 @@ class TransactionSummarizer
|
||||
if (!$this->convertToPrimary) {
|
||||
// Log::debug('convertToPrimary is false.');
|
||||
// use foreign amount?
|
||||
$foreignCurrencyId = (int)$journal['foreign_currency_id'];
|
||||
$foreignCurrencyId = (int) $journal['foreign_currency_id'];
|
||||
if (0 !== $foreignCurrencyId) {
|
||||
Log::debug(sprintf('Journal #%d also includes foreign amount (foreign is "%s")', $journal['transaction_journal_id'], $journal['foreign_currency_code']));
|
||||
Log::debug(sprintf(
|
||||
'Journal #%d also includes foreign amount (foreign is "%s")',
|
||||
$journal['transaction_journal_id'],
|
||||
$journal['foreign_currency_code']
|
||||
));
|
||||
$foreignCurrencyName = $journal['foreign_currency_name'];
|
||||
$foreignCurrencySymbol = $journal['foreign_currency_symbol'];
|
||||
$foreignCurrencyCode = $journal['foreign_currency_code'];
|
||||
@@ -102,7 +106,7 @@ class TransactionSummarizer
|
||||
}
|
||||
|
||||
// first process normal amount
|
||||
$amount = (string)($journal[$field] ?? '0');
|
||||
$amount = (string) ($journal[$field] ?? '0');
|
||||
$array[$currencyId] ??= [
|
||||
'sum' => '0',
|
||||
'currency_id' => $currencyId,
|
||||
@@ -121,7 +125,7 @@ class TransactionSummarizer
|
||||
|
||||
// then process foreign amount, if it exists.
|
||||
if (0 !== $foreignCurrencyId && $includeForeign) {
|
||||
$amount = (string)($journal['foreign_amount'] ?? '0');
|
||||
$amount = (string) ($journal['foreign_amount'] ?? '0');
|
||||
$array[$foreignCurrencyId] ??= [
|
||||
'sum' => '0',
|
||||
'currency_id' => $foreignCurrencyId,
|
||||
@@ -149,14 +153,12 @@ class TransactionSummarizer
|
||||
|
||||
public function groupByDirection(array $journals, string $method, string $direction): array
|
||||
{
|
||||
|
||||
$array = [];
|
||||
$idKey = sprintf('%s_account_id', $direction);
|
||||
$nameKey = sprintf('%s_account_name', $direction);
|
||||
$convertToPrimary = Amount::convertToPrimary($this->user);
|
||||
$primary = Amount::getPrimaryCurrencyByUserGroup($this->user->userGroup);
|
||||
|
||||
|
||||
Log::debug(sprintf('groupByDirection(array, %s, %s).', $direction, $method));
|
||||
foreach ($journals as $journal) {
|
||||
// currency
|
||||
@@ -193,13 +195,25 @@ class TransactionSummarizer
|
||||
];
|
||||
|
||||
// add the data from the $field to the array.
|
||||
$array[$key]['sum'] = bcadd($array[$key]['sum'], (string) Steam::{$method}((string)($journal[$field] ?? '0'))); // @phpstan-ignore-line
|
||||
Log::debug(sprintf('Field for transaction #%d is "%s" (%s). Sum: %s', $journal['transaction_group_id'], $currencyCode, $field, $array[$key]['sum']));
|
||||
$array[$key]['sum'] = bcadd($array[$key]['sum'], (string) Steam::{$method}((string) ($journal[$field] ?? '0'))); // @phpstan-ignore-line
|
||||
Log::debug(sprintf(
|
||||
'Field for transaction #%d is "%s" (%s). Sum: %s',
|
||||
$journal['transaction_group_id'],
|
||||
$currencyCode,
|
||||
$field,
|
||||
$array[$key]['sum']
|
||||
));
|
||||
|
||||
// also do foreign amount, but only when convertToPrimary is false (otherwise we have it already)
|
||||
// or when convertToPrimary is true and the foreign currency is ALSO not the default currency.
|
||||
if ((!$convertToPrimary || $journal['foreign_currency_id'] !== $primary->id) && 0 !== (int)$journal['foreign_currency_id']) {
|
||||
Log::debug(sprintf('Use foreign amount from transaction #%d: %s %s. Sum: %s', $journal['transaction_group_id'], $currencyCode, $journal['foreign_amount'], $array[$key]['sum']));
|
||||
if ((!$convertToPrimary || $journal['foreign_currency_id'] !== $primary->id) && 0 !== (int) $journal['foreign_currency_id']) {
|
||||
Log::debug(sprintf(
|
||||
'Use foreign amount from transaction #%d: %s %s. Sum: %s',
|
||||
$journal['transaction_group_id'],
|
||||
$currencyCode,
|
||||
$journal['foreign_amount'],
|
||||
$array[$key]['sum']
|
||||
));
|
||||
$key = sprintf('%s-%s', $journal[$idKey], $journal['foreign_currency_id']);
|
||||
$array[$key] ??= [
|
||||
'id' => $journal[$idKey],
|
||||
@@ -211,7 +225,7 @@ class TransactionSummarizer
|
||||
'currency_code' => $journal['foreign_currency_code'],
|
||||
'currency_decimal_places' => $journal['foreign_currency_decimal_places'],
|
||||
];
|
||||
$array[$key]['sum'] = bcadd($array[$key]['sum'], (string) Steam::{$method}((string)$journal['foreign_amount'])); // @phpstan-ignore-line
|
||||
$array[$key]['sum'] = bcadd($array[$key]['sum'], (string) Steam::{$method}((string) $journal['foreign_amount'])); // @phpstan-ignore-line
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ trait ValidatesWebhooks
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator): void {
|
||||
static function (Validator $validator): void {
|
||||
Log::debug('Validating webhook');
|
||||
if (count($validator->failed()) > 0) {
|
||||
return;
|
||||
|
||||
@@ -1245,9 +1245,9 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
return false;
|
||||
|
||||
//
|
||||
|
||||
// all account related searches:
|
||||
//
|
||||
|
||||
case 'account_is':
|
||||
$this->searchAccount($value, SearchDirection::BOTH, StringPosition::IS);
|
||||
|
||||
@@ -1608,9 +1608,9 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
break;
|
||||
|
||||
//
|
||||
|
||||
// cash account
|
||||
//
|
||||
|
||||
case 'source_is_cash':
|
||||
$account = $this->getCashAccount();
|
||||
$this->collector->setSourceAccounts(new Collection()->push($account));
|
||||
@@ -1647,9 +1647,9 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
break;
|
||||
|
||||
//
|
||||
|
||||
// description
|
||||
//
|
||||
|
||||
case 'description_starts':
|
||||
$this->collector->descriptionStarts([$value]);
|
||||
|
||||
@@ -1690,9 +1690,9 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
break;
|
||||
|
||||
//
|
||||
|
||||
// currency
|
||||
//
|
||||
|
||||
case 'currency_is':
|
||||
$currency = $this->findCurrency($value);
|
||||
if ($currency instanceof TransactionCurrency) {
|
||||
@@ -1741,9 +1741,9 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
break;
|
||||
|
||||
//
|
||||
|
||||
// attachments
|
||||
//
|
||||
|
||||
case 'has_attachments':
|
||||
case '-has_no_attachments':
|
||||
Log::debug('Set collector to filter on attachments.');
|
||||
@@ -1758,7 +1758,7 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
break;
|
||||
|
||||
//
|
||||
|
||||
// categories
|
||||
case '-has_any_category':
|
||||
case 'has_no_category':
|
||||
@@ -1866,9 +1866,9 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
break;
|
||||
|
||||
//
|
||||
|
||||
// budgets
|
||||
//
|
||||
|
||||
case '-has_any_budget':
|
||||
case 'has_no_budget':
|
||||
$this->collector->withoutBudget();
|
||||
@@ -1977,9 +1977,9 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
break;
|
||||
|
||||
//
|
||||
|
||||
// bill
|
||||
//
|
||||
|
||||
case '-has_any_bill':
|
||||
case 'has_no_bill':
|
||||
$this->collector->withoutBill();
|
||||
@@ -2088,9 +2088,9 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
break;
|
||||
|
||||
//
|
||||
|
||||
// tags
|
||||
//
|
||||
|
||||
case '-has_any_tag':
|
||||
case 'has_no_tag':
|
||||
$this->collector->withoutTags();
|
||||
@@ -2216,9 +2216,9 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
break;
|
||||
|
||||
//
|
||||
|
||||
// notes
|
||||
//
|
||||
|
||||
case 'notes_contains':
|
||||
$this->collector->notesContain($value);
|
||||
|
||||
@@ -2281,9 +2281,9 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
break;
|
||||
|
||||
//
|
||||
|
||||
// amount
|
||||
//
|
||||
|
||||
case 'amount_is':
|
||||
// strip comma's, make dots.
|
||||
Log::debug(sprintf('Original value "%s"', $value));
|
||||
@@ -2368,9 +2368,9 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
break;
|
||||
|
||||
//
|
||||
|
||||
// transaction type
|
||||
//
|
||||
|
||||
case 'transaction_type':
|
||||
$this->collector->setTypes([ucfirst($value)]);
|
||||
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
|
||||
@@ -2383,9 +2383,9 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
break;
|
||||
|
||||
//
|
||||
|
||||
// dates
|
||||
//
|
||||
|
||||
case '-date_on':
|
||||
case 'date_on':
|
||||
$range = $this->parseDateRange($operator, $value);
|
||||
@@ -2581,9 +2581,9 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
return false;
|
||||
|
||||
//
|
||||
|
||||
// external URL
|
||||
//
|
||||
|
||||
case '-any_external_url':
|
||||
case 'no_external_url':
|
||||
$this->collector->withoutExternalUrl();
|
||||
@@ -2648,9 +2648,9 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
break;
|
||||
|
||||
//
|
||||
|
||||
// other fields
|
||||
//
|
||||
|
||||
case 'external_id_is':
|
||||
$this->collector->setExternalId($value);
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Support\Search\QueryParser;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* Single-pass parser that processes query strings into structured nodes.
|
||||
@@ -202,7 +203,7 @@ class QueryParser implements QueryParserInterface
|
||||
return new NodeGroup($nodes, $prohibited);
|
||||
}
|
||||
|
||||
private function createNode(string $token, string $fieldName, bool $prohibited): Node
|
||||
private function createNode(#[SensitiveParameter] string $token, string $fieldName, bool $prohibited): Node
|
||||
{
|
||||
if ('' !== $fieldName) {
|
||||
// OK dus hoe trim je \" correct?
|
||||
|
||||
@@ -80,7 +80,7 @@ class Steam
|
||||
$currency = $currencies[$account->id];
|
||||
|
||||
// second array
|
||||
$accountSums = array_filter($arrayOfSums, fn (array $entry): bool => $entry['account_id'] === $account->id);
|
||||
$accountSums = array_filter($arrayOfSums, static fn (array $entry): bool => $entry['account_id'] === $account->id);
|
||||
if (0 === count($accountSums)) {
|
||||
$result[$account->id] = $return;
|
||||
|
||||
|
||||
@@ -392,7 +392,7 @@ class General extends AbstractExtension
|
||||
{
|
||||
return new TwigFunction(
|
||||
'phpdate',
|
||||
static fn (string $str): string => date($str)
|
||||
date(...)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -400,9 +400,7 @@ class General extends AbstractExtension
|
||||
{
|
||||
return new TwigFunction(
|
||||
'fireflyiiiconfig',
|
||||
static function (string $string, mixed $default): mixed {
|
||||
return FireflyConfig::get($string, $default)->data;
|
||||
}
|
||||
static fn (string $string, mixed $default): mixed => FireflyConfig::get($string, $default)->data
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,11 @@ class LinkToBill implements ActionInterface
|
||||
$billName = $this->action->getValue($journal);
|
||||
$bill = $repository->findByName($billName);
|
||||
|
||||
if (null !== $bill && TransactionTypeEnum::WITHDRAWAL->value === $journal['transaction_type_type']) {
|
||||
/** @var TransactionJournal $object */
|
||||
$object = TransactionJournal::with('transactionType')->find($journal['transaction_journal_id']);
|
||||
$type = $object->transactionType->type;
|
||||
|
||||
if (null !== $bill && TransactionTypeEnum::WITHDRAWAL->value === $type) {
|
||||
$count = DB::table('transaction_journals')->where('id', '=', $journal['transaction_journal_id'])->where('bill_id', $bill->id)->count();
|
||||
if (0 !== $count) {
|
||||
Log::error(sprintf('RuleAction LinkToBill could not set the bill of journal #%d to bill "%s": already set.', $journal['transaction_journal_id'], $billName));
|
||||
|
||||
@@ -24,6 +24,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\TransactionRules\Expressions;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\Component\ExpressionLanguage\SyntaxError;
|
||||
|
||||
@@ -141,6 +143,11 @@ class ActionExpression
|
||||
private function evaluateExpression(string $expr, array $journal): string
|
||||
{
|
||||
$result = $this->expressionLanguage->evaluate($expr, $journal);
|
||||
if (is_array($result)) {
|
||||
Log::error('Result of evaluating the expression is an array, please investigate', $result);
|
||||
|
||||
throw new FireflyException('Result of evaluating the expression is an array, please open a GitHub issue about this and include the error logs.');
|
||||
}
|
||||
|
||||
return (string) $result;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class ActionExpressionLanguageProvider implements ExpressionFunctionProviderInte
|
||||
{
|
||||
public function getFunctions(): array
|
||||
{
|
||||
$function = function ($arguments, $str): string {
|
||||
$function = static function ($arguments, $str): string {
|
||||
if (!is_string($str)) {
|
||||
return (string) $str;
|
||||
}
|
||||
|
||||
@@ -425,9 +425,7 @@ class TransactionGroupTransformer extends AbstractTransformer
|
||||
private function getSourceTransaction(TransactionJournal $journal): Transaction
|
||||
{
|
||||
$result = $journal->transactions->first(
|
||||
static function (Transaction $transaction): bool {
|
||||
return (float) $transaction->amount < 0; // lame but it works.
|
||||
}
|
||||
static fn (Transaction $transaction): bool => (float) $transaction->amount < 0
|
||||
);
|
||||
if (null === $result) {
|
||||
throw new FireflyException(sprintf('Journal #%d unexpectedly has no source transaction.', $journal->id));
|
||||
@@ -442,9 +440,7 @@ class TransactionGroupTransformer extends AbstractTransformer
|
||||
private function getDestinationTransaction(TransactionJournal $journal): Transaction
|
||||
{
|
||||
$result = $journal->transactions->first(
|
||||
static function (Transaction $transaction): bool {
|
||||
return (float) $transaction->amount > 0; // lame but it works
|
||||
}
|
||||
static fn (Transaction $transaction): bool => (float) $transaction->amount > 0
|
||||
);
|
||||
if (null === $result) {
|
||||
throw new FireflyException(sprintf('Journal #%d unexpectedly has no destination transaction.', $journal->id));
|
||||
|
||||
25
app/User.php
25
app/User.php
@@ -24,9 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII;
|
||||
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Deprecated;
|
||||
use Exception;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
@@ -57,6 +54,8 @@ use FireflyIII\Models\UserRole;
|
||||
use FireflyIII\Models\Webhook;
|
||||
use FireflyIII\Notifications\Admin\UserRegistration;
|
||||
use FireflyIII\Notifications\Admin\VersionCheckResult;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
@@ -66,10 +65,12 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Passport\HasApiTokens;
|
||||
use NotificationChannels\Pushover\PushoverReceiver;
|
||||
use SensitiveParameter;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class User extends Authenticatable
|
||||
@@ -77,6 +78,7 @@ class User extends Authenticatable
|
||||
use HasApiTokens;
|
||||
use Notifiable;
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
protected $fillable = ['email', 'password', 'blocked', 'blocked_code', 'user_group_id'];
|
||||
protected $hidden = ['password', 'remember_token'];
|
||||
protected $table = 'users';
|
||||
@@ -258,7 +260,12 @@ class User extends Authenticatable
|
||||
$dbRolesIds = $dbRoles->pluck('id')->toArray();
|
||||
$dbRolesTitles = $dbRoles->pluck('title')->toArray();
|
||||
|
||||
$groupMemberships = $this->groupMemberships()->whereIn('user_role_id', $dbRolesIds)->where('user_group_id', $userGroup->id)->get();
|
||||
$groupMemberships = $this
|
||||
->groupMemberships()
|
||||
->whereIn('user_role_id', $dbRolesIds)
|
||||
->where('user_group_id', $userGroup->id)
|
||||
->get()
|
||||
;
|
||||
if (0 === $groupMemberships->count()) {
|
||||
Log::error(sprintf(
|
||||
'User #%d "%s" does not have roles %s in user group #%d "%s"',
|
||||
@@ -370,7 +377,7 @@ class User extends Authenticatable
|
||||
|
||||
return match ($driver) {
|
||||
'mail' => $email,
|
||||
default => null,
|
||||
default => null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -452,7 +459,7 @@ class User extends Authenticatable
|
||||
*
|
||||
* @param string $token
|
||||
*/
|
||||
public function sendPasswordResetNotification($token): void
|
||||
public function sendPasswordResetNotification(#[SensitiveParameter] $token): void
|
||||
{
|
||||
$ipAddress = Request::ip();
|
||||
|
||||
@@ -528,10 +535,6 @@ class User extends Authenticatable
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'blocked' => 'boolean',
|
||||
];
|
||||
return ['created_at' => 'datetime', 'updated_at' => 'datetime', 'blocked' => 'boolean'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,8 +457,9 @@ class FireflyValidator extends Validator
|
||||
*
|
||||
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
|
||||
*/
|
||||
public function validateSecurePassword($attribute, string $value): bool
|
||||
public function validateSecurePassword($attribute, ?string $value): bool
|
||||
{
|
||||
$value = (string)$value;
|
||||
$verify = false;
|
||||
if (array_key_exists('verify_password', $this->data)) {
|
||||
$verify = 1 === (int) $this->data['verify_password'];
|
||||
|
||||
35
changelog.md
35
changelog.md
@@ -3,6 +3,41 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## v6.4.15 - 2026-01-07
|
||||
|
||||
### Added
|
||||
|
||||
- [Issue 11264](https://github.com/firefly-iii/firefly-iii/issues/11264) (Add GUI for some settings, replacing environment variables) reported by @jacobburrell
|
||||
- [Discussion 11433](https://github.com/orgs/firefly-iii/discussions/11433) (Updates to Date Range selection) started by @fett327
|
||||
|
||||
### Changed
|
||||
|
||||
- Moved some settings to your system settings
|
||||
|
||||
### Removed
|
||||
|
||||
- The following environment variables are removed and will no longer work. They are now in your settings.
|
||||
- `ENABLE_EXTERNAL_MAP`
|
||||
- `ENABLE_EXCHANGE_RATES`
|
||||
- `ENABLE_EXTERNAL_RATES`
|
||||
- `VALID_URL_PROTOCOLS`
|
||||
- `ALLOW_WEBHOOKS`
|
||||
- `USE_RUNNING_BALANCE`
|
||||
- Removed sentry.io code
|
||||
|
||||
### Fixed
|
||||
|
||||
- [Issue 11378](https://github.com/firefly-iii/firefly-iii/issues/11378) (Wrong account balance with initial transfer from different currency) reported by @bozho
|
||||
- [Issue 11383](https://github.com/firefly-iii/firefly-iii/issues/11383) (Login flow could redirect to javascript path) reported by @stefvonb
|
||||
- [Issue 11388](https://github.com/firefly-iii/firefly-iii/issues/11388) (TypeError bugs during upgrade to 6.4.14 + account_balances corruption) reported by @jaconde2
|
||||
- [Issue 11396](https://github.com/firefly-iii/firefly-iii/issues/11396) (Reconciliation adds extra digits) reported by @niklas2810
|
||||
- [Issue 11399](https://github.com/firefly-iii/firefly-iii/issues/11399) (Unusual behavior in audit logs (multi-currency)) reported by @jgmm81
|
||||
- [Issue 11403](https://github.com/firefly-iii/firefly-iii/issues/11403) (Error 404 when trying to view the details (Piggy banks section)) reported by @jgmm81
|
||||
- [Issue 11410](https://github.com/firefly-iii/firefly-iii/issues/11410) (nitpick: Bulk edit tags should keep the option chosen instead of always changing back to "replace") reported by @jxtxzzw
|
||||
- [Issue 11443](https://github.com/firefly-iii/firefly-iii/issues/11443) (Exception thrown, when subscription is in foreign currency) reported by @ajgon
|
||||
- [Issue 11445](https://github.com/firefly-iii/firefly-iii/issues/11445) (“Reconcile” screen breaks when Preferences → Layout is set to “Year to date”) reported by @semonsir
|
||||
- [Issue 11449](https://github.com/firefly-iii/firefly-iii/issues/11449) (Non-strict rules break with "Apply rule" and "Apply rule group") reported by @Bytenka
|
||||
|
||||
## v6.4.14 - 2025-12-17
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
"transfers",
|
||||
"management"
|
||||
],
|
||||
"platform": {
|
||||
"php": "8.4"
|
||||
},
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"homepage": "https://github.com/firefly-iii/firefly-iii",
|
||||
"type": "project",
|
||||
|
||||
76
composer.lock
generated
76
composer.lock
generated
@@ -1878,16 +1878,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v12.44.0",
|
||||
"version": "v12.47.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "592bbf1c036042958332eb98e3e8131b29102f33"
|
||||
"reference": "ab8114c2e78f32e64eb238fc4b495bea3f8b80ec"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/592bbf1c036042958332eb98e3e8131b29102f33",
|
||||
"reference": "592bbf1c036042958332eb98e3e8131b29102f33",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/ab8114c2e78f32e64eb238fc4b495bea3f8b80ec",
|
||||
"reference": "ab8114c2e78f32e64eb238fc4b495bea3f8b80ec",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2096,7 +2096,7 @@
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2025-12-23T15:29:43+00:00"
|
||||
"time": "2026-01-13T15:29:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/passport",
|
||||
@@ -2176,16 +2176,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/prompts",
|
||||
"version": "v0.3.8",
|
||||
"version": "v0.3.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/prompts.git",
|
||||
"reference": "096748cdfb81988f60090bbb839ce3205ace0d35"
|
||||
"reference": "5c41bf0555b7cfefaad4e66d3046675829581ac4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/prompts/zipball/096748cdfb81988f60090bbb839ce3205ace0d35",
|
||||
"reference": "096748cdfb81988f60090bbb839ce3205ace0d35",
|
||||
"url": "https://api.github.com/repos/laravel/prompts/zipball/5c41bf0555b7cfefaad4e66d3046675829581ac4",
|
||||
"reference": "5c41bf0555b7cfefaad4e66d3046675829581ac4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2229,22 +2229,22 @@
|
||||
"description": "Add beautiful and user-friendly forms to your command-line applications.",
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/prompts/issues",
|
||||
"source": "https://github.com/laravel/prompts/tree/v0.3.8"
|
||||
"source": "https://github.com/laravel/prompts/tree/v0.3.9"
|
||||
},
|
||||
"time": "2025-11-21T20:52:52+00:00"
|
||||
"time": "2026-01-07T21:00:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/sanctum",
|
||||
"version": "v4.2.1",
|
||||
"version": "v4.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/sanctum.git",
|
||||
"reference": "f5fb373be39a246c74a060f2cf2ae2c2145b3664"
|
||||
"reference": "47d26f1d310879ff757b971f5a6fc631d18663fd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/sanctum/zipball/f5fb373be39a246c74a060f2cf2ae2c2145b3664",
|
||||
"reference": "f5fb373be39a246c74a060f2cf2ae2c2145b3664",
|
||||
"url": "https://api.github.com/repos/laravel/sanctum/zipball/47d26f1d310879ff757b971f5a6fc631d18663fd",
|
||||
"reference": "47d26f1d310879ff757b971f5a6fc631d18663fd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2294,20 +2294,20 @@
|
||||
"issues": "https://github.com/laravel/sanctum/issues",
|
||||
"source": "https://github.com/laravel/sanctum"
|
||||
},
|
||||
"time": "2025-11-21T13:59:03+00:00"
|
||||
"time": "2026-01-11T18:20:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/serializable-closure",
|
||||
"version": "v2.0.7",
|
||||
"version": "v2.0.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/serializable-closure.git",
|
||||
"reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd"
|
||||
"reference": "7581a4407012f5f53365e11bafc520fd7f36bc9b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/cb291e4c998ac50637c7eeb58189c14f5de5b9dd",
|
||||
"reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd",
|
||||
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/7581a4407012f5f53365e11bafc520fd7f36bc9b",
|
||||
"reference": "7581a4407012f5f53365e11bafc520fd7f36bc9b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2355,7 +2355,7 @@
|
||||
"issues": "https://github.com/laravel/serializable-closure/issues",
|
||||
"source": "https://github.com/laravel/serializable-closure"
|
||||
},
|
||||
"time": "2025-11-21T20:52:36+00:00"
|
||||
"time": "2026-01-08T16:22:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/slack-notification-channel",
|
||||
@@ -3621,16 +3621,16 @@
|
||||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
"version": "3.9.0",
|
||||
"version": "3.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Seldaek/monolog.git",
|
||||
"reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6"
|
||||
"reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6",
|
||||
"reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0",
|
||||
"reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3648,7 +3648,7 @@
|
||||
"graylog2/gelf-php": "^1.4.2 || ^2.0",
|
||||
"guzzlehttp/guzzle": "^7.4.5",
|
||||
"guzzlehttp/psr7": "^2.2",
|
||||
"mongodb/mongodb": "^1.8",
|
||||
"mongodb/mongodb": "^1.8 || ^2.0",
|
||||
"php-amqplib/php-amqplib": "~2.4 || ^3",
|
||||
"php-console/php-console": "^3.1.8",
|
||||
"phpstan/phpstan": "^2",
|
||||
@@ -3708,7 +3708,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Seldaek/monolog/issues",
|
||||
"source": "https://github.com/Seldaek/monolog/tree/3.9.0"
|
||||
"source": "https://github.com/Seldaek/monolog/tree/3.10.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3720,7 +3720,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-03-24T10:02:05+00:00"
|
||||
"time": "2026-01-02T08:56:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nesbot/carbon",
|
||||
@@ -10081,12 +10081,12 @@
|
||||
"version": "v3.16.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/barryvdh/laravel-debugbar.git",
|
||||
"url": "https://github.com/fruitcake/laravel-debugbar.git",
|
||||
"reference": "c91e57ea113edd6526f5b8cd6b1c6ee02c67b28e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/c91e57ea113edd6526f5b8cd6b1c6ee02c67b28e",
|
||||
"url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/c91e57ea113edd6526f5b8cd6b1c6ee02c67b28e",
|
||||
"reference": "c91e57ea113edd6526f5b8cd6b1c6ee02c67b28e",
|
||||
"shasum": ""
|
||||
},
|
||||
@@ -10146,8 +10146,8 @@
|
||||
"webprofiler"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/barryvdh/laravel-debugbar/issues",
|
||||
"source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.16.3"
|
||||
"issues": "https://github.com/fruitcake/laravel-debugbar/issues",
|
||||
"source": "https://github.com/fruitcake/laravel-debugbar/tree/v3.16.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -11889,16 +11889,16 @@
|
||||
},
|
||||
{
|
||||
"name": "rector/rector",
|
||||
"version": "2.3.0",
|
||||
"version": "2.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/rectorphp/rector.git",
|
||||
"reference": "f7166355dcf47482f27be59169b0825995f51c7d"
|
||||
"reference": "9afc1bb43571b25629f353c61a9315b5ef31383a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/rectorphp/rector/zipball/f7166355dcf47482f27be59169b0825995f51c7d",
|
||||
"reference": "f7166355dcf47482f27be59169b0825995f51c7d",
|
||||
"url": "https://api.github.com/repos/rectorphp/rector/zipball/9afc1bb43571b25629f353c61a9315b5ef31383a",
|
||||
"reference": "9afc1bb43571b25629f353c61a9315b5ef31383a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -11937,7 +11937,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/rectorphp/rector/issues",
|
||||
"source": "https://github.com/rectorphp/rector/tree/2.3.0"
|
||||
"source": "https://github.com/rectorphp/rector/tree/2.3.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -11945,7 +11945,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-12-25T22:00:18+00:00"
|
||||
"time": "2026-01-13T15:13:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
|
||||
@@ -78,8 +78,8 @@ return [
|
||||
'running_balance_column' => (bool)envNonEmpty('USE_RUNNING_BALANCE', true), // this is only the default value, is not used.
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => 'develop/2026-01-02',
|
||||
'build_time' => 1767337875,
|
||||
'version' => 'develop/2026-01-13',
|
||||
'build_time' => 1768334619,
|
||||
'api_version' => '2.1.0', // field is no longer used.
|
||||
'db_version' => 28, // field is no longer used.
|
||||
|
||||
|
||||
@@ -87,6 +87,7 @@ class TransactionCurrencySeeder extends Seeder
|
||||
$currencies[] = ['code' => 'CZK', 'name' => 'Czech koruna', 'symbol' => 'Kč', 'decimal_places' => 2];
|
||||
$currencies[] = ['code' => 'KZT', 'name' => 'Kazakhstani tenge', 'symbol' => '₸', 'decimal_places' => 2];
|
||||
$currencies[] = ['code' => 'SAR', 'name' => 'Saudi Riyal', 'symbol' => 'SAR', 'decimal_places' => 2];
|
||||
$currencies[] = ['code' => 'RSD', 'name' => 'Serbian Dinar', 'symbol' => 'RSD', 'decimal_places' => 2];
|
||||
|
||||
foreach ($currencies as $currency) {
|
||||
if (null === TransactionCurrency::where('code', $currency['code'])->first()) {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* WebhookDataSeeder.php
|
||||
|
||||
42
mago.toml
Normal file
42
mago.toml
Normal file
@@ -0,0 +1,42 @@
|
||||
# Welcome to Mago!
|
||||
# For full documentation, see https://mago.carthage.software/tools/overview
|
||||
php-version = "8.4.0"
|
||||
|
||||
[source]
|
||||
workspace = "."
|
||||
paths = ["app/", "database/factories/", "database/seeders/", "tests/"]
|
||||
includes = ["vendor"]
|
||||
excludes = []
|
||||
|
||||
[formatter]
|
||||
print-width = 160
|
||||
tab-width = 4
|
||||
use-tabs = false
|
||||
trailing-comma = false
|
||||
method-chain-breaking-style = "same_line"
|
||||
preserve-breaking-array-like = false
|
||||
align-assignment-like = true
|
||||
null-type-hint = "null_pipe"
|
||||
|
||||
[linter]
|
||||
integrations = ["symfony", "laravel", "phpunit"]
|
||||
|
||||
[linter.rules]
|
||||
ambiguous-function-call = { enabled = false }
|
||||
literal-named-argument = { enabled = false }
|
||||
halstead = { effort-threshold = 7000 }
|
||||
prefer-early-continue = { enabled = false }
|
||||
|
||||
[analyzer]
|
||||
find-unused-definitions = true
|
||||
find-unused-expressions = true
|
||||
analyze-dead-code = false
|
||||
memoize-properties = true
|
||||
allow-possibly-undefined-array-keys = true
|
||||
check-throws = true
|
||||
check-missing-override = false
|
||||
find-unused-parameters = false
|
||||
strict-list-index-checks = false
|
||||
no-boolean-literal-comparison = false
|
||||
check-missing-type-hints = false
|
||||
register-super-globals = true
|
||||
942
package-lock.json
generated
942
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,8 @@
|
||||
* Skin: Firefly III dark
|
||||
* Built upon the code below with some extra things.
|
||||
* https://raw.githubusercontent.com/anvyst/adminlte-skin-midnight/master/build/less/skins/skin-midnight.less
|
||||
*
|
||||
* To minify, just throw it in cyber chef.
|
||||
* ------------
|
||||
*/
|
||||
.force-background-tags-input {
|
||||
@@ -163,10 +165,17 @@
|
||||
.skin-firefly-iii .ti-input {
|
||||
border: 1px solid #353c42 !important;
|
||||
}
|
||||
.skin-firefly-iii code {
|
||||
background-color: #343941;
|
||||
color: #c9d1d9;
|
||||
.skin-firefly-iii pre {
|
||||
background-color: #1f2327;
|
||||
border: 1px solid #3a4046;
|
||||
}
|
||||
|
||||
.skin-firefly-iii code {
|
||||
background-color:transparent;
|
||||
color:#eee;
|
||||
}
|
||||
|
||||
|
||||
.skin-firefly-iii .modal-content {
|
||||
position: relative;
|
||||
background-color: #353c42;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Skin: Blue
|
||||
* Slight changes for FF3
|
||||
* Slight changes for FF3.
|
||||
* ----------
|
||||
*/
|
||||
.skin-firefly-iii ::-moz-selection {
|
||||
|
||||
@@ -68,8 +68,8 @@ export default {
|
||||
props: ['source', 'destination', 'transactionType', 'value', 'error', 'no_currency', 'title',],
|
||||
mounted() {
|
||||
this.liability = false;
|
||||
// console.log('I am mounted with a ' + this.transactionType + ' transaction type and currency id!');
|
||||
// console.log(this.value);
|
||||
console.log('ForeignAmountSelect is mounted with a ' + this.transactionType + ' transaction type and currency id!');
|
||||
console.log(this.value);
|
||||
this.loadCurrencies();
|
||||
},
|
||||
data() {
|
||||
@@ -124,23 +124,25 @@ export default {
|
||||
let sourceIsLiability = liabilities.indexOf(srcType) !== -1;
|
||||
let destIsLiability = liabilities.indexOf(destType) !== -1;
|
||||
|
||||
|
||||
// console.log(srcType + ' (source) is a liability: ' + sourceIsLiability);
|
||||
// console.log(destType + ' (dest) is a liability: ' + destIsLiability);
|
||||
// console.log('tType: ' + tType);
|
||||
if (tType === 'transfer' || destIsLiability || sourceIsLiability) {
|
||||
// console.log('Source is liability OR dest is liability, OR transfer. Lock list on currency of destination.');
|
||||
console.log('Source or dest is a liability.')
|
||||
console.log('Source is liability OR dest is liability, OR transfer. Lock list on currency of destination.');
|
||||
// console.log('Length of currencies is ' + this.currencies.length);
|
||||
// console.log(this.currencies);
|
||||
this.liability = true;
|
||||
// lock dropdown list on currencyID of destination.
|
||||
// lock dropdown list on currencyID of destination UNLESS dest is not liab
|
||||
for (const key in this.currencies) {
|
||||
if (this.currencies.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
||||
if (
|
||||
parseInt(this.currencies[key].id) === parseInt(this.destination.currency_id)
|
||||
parseInt(this.currencies[key].id) === parseInt(this.destination.currency_id) || !destIsLiability
|
||||
) {
|
||||
// console.log('Enable currency!!');
|
||||
console.log('Enable currency!!');
|
||||
console.log(this.currencies[key]);
|
||||
// console.log(this.destination);
|
||||
// console.log(this.currencies[key]);
|
||||
this.enabledCurrencies.push(this.currencies[key]);
|
||||
}
|
||||
}
|
||||
@@ -151,6 +153,7 @@ export default {
|
||||
|
||||
// if type is withdrawal, list all but skip the source account ID.
|
||||
if (tType === 'withdrawal' && this.source && false === sourceIsLiability) {
|
||||
console.log('Type is withdrawal, there is a source, it is not a liability.')
|
||||
for (const key in this.currencies) {
|
||||
if (this.currencies.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
||||
if (this.source.currency_id !== this.currencies[key].id) {
|
||||
|
||||
@@ -107,18 +107,18 @@
|
||||
"multi_account_warning_withdrawal": "Pami\u0119taj, \u017ce konto \u017ar\u00f3d\u0142owe kolejnych podzia\u0142\u00f3w zostanie ustawione na konto zdefiniowane w pierwszym podziale wyp\u0142aty.",
|
||||
"multi_account_warning_deposit": "Pami\u0119taj, \u017ce konto docelowe kolejnych podzia\u0142\u00f3w zostanie ustawione na konto zdefiniowane w pierwszym podziale wp\u0142aty.",
|
||||
"multi_account_warning_transfer": "Pami\u0119taj, \u017ce konta \u017ar\u00f3d\u0142owe i docelowe kolejnych podzia\u0142\u00f3w zostan\u0105 ustawione na konto zdefiniowane w pierwszym podziale transferu.",
|
||||
"webhook_trigger_ANY": "After any event",
|
||||
"webhook_trigger_ANY": "Po ka\u017cdym wydarzeniu",
|
||||
"webhook_trigger_STORE_TRANSACTION": "Po utworzeniu transakcji",
|
||||
"webhook_trigger_UPDATE_TRANSACTION": "Po zmodyfikowaniu transakcji",
|
||||
"webhook_trigger_DESTROY_TRANSACTION": "Po usuni\u0119ciu transakcji",
|
||||
"webhook_trigger_STORE_BUDGET": "After budget creation",
|
||||
"webhook_trigger_UPDATE_BUDGET": "After budget update",
|
||||
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
|
||||
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
|
||||
"webhook_trigger_STORE_BUDGET": "Po utworzeniu bud\u017cetu",
|
||||
"webhook_trigger_UPDATE_BUDGET": "Po aktualizacji bud\u017cetu",
|
||||
"webhook_trigger_DESTROY_BUDGET": "Po usuni\u0119ciu bud\u017cetu",
|
||||
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "Po zmianie kwoty bud\u017cetu",
|
||||
"webhook_response_TRANSACTIONS": "Szczeg\u00f3\u0142y transakcji",
|
||||
"webhook_response_RELEVANT": "Relevant details",
|
||||
"webhook_response_ACCOUNTS": "Szczeg\u00f3\u0142y konta",
|
||||
"webhook_response_NONE": "No details",
|
||||
"webhook_response_NONE": "Brak szczeg\u00f3\u0142\u00f3w",
|
||||
"webhook_delivery_JSON": "JSON",
|
||||
"actions": "Akcje",
|
||||
"meta_data": "Metadane",
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"administrations_page_title": "Administra\u00e7\u00e3o financeira",
|
||||
"administrations_index_menu": "Administra\u00e7\u00e3o financeira",
|
||||
"expires_at": "Expira em",
|
||||
"temp_administrations_introduction": "Firefly III will soon get the ability to manage multiple financial administrations. Right now, you only have the one. You can set the title of this administration and its primary currency. This replaces the previous setting where you would set your \"default currency\". This setting is now tied to the financial administration and can be different per administration.",
|
||||
"administration_currency_form_help": "It may take a long time for the page to load if you change the primary currency because transaction may need to be converted to your (new) primary currency.",
|
||||
"temp_administrations_introduction": "O Firefly III ter\u00e1 em breve a capacidade de gerir m\u00faltiplas administra\u00e7\u00f5es financeiras. Neste momento, voc\u00ea tem apenas um. Voc\u00ea pode definir o t\u00edtulo desta administra\u00e7\u00e3o e sua moeda prim\u00e1ria. Isso substitui a configura\u00e7\u00e3o anterior onde voc\u00ea iria definir sua \"moeda padr\u00e3o\". Esta defini\u00e7\u00e3o est\u00e1 agora ligada \u00e0 administra\u00e7\u00e3o financeira e pode ser diferente por administra\u00e7\u00e3o.",
|
||||
"administration_currency_form_help": "Pode demorar muito tempo para a p\u00e1gina carregar, se voc\u00ea alterar a moeda principal, porque a transa\u00e7\u00e3o pode precisar ser convertida para a sua (nova) moeda principal.",
|
||||
"administrations_page_edit_sub_title_js": "Editar administra\u00e7\u00e3o financeira \"{title}\"",
|
||||
"table": "Tabela",
|
||||
"welcome_back": "O que est\u00e1 acontecendo?",
|
||||
@@ -102,23 +102,23 @@
|
||||
"profile_oauth_client_secret_title": "Segredo do cliente",
|
||||
"profile_oauth_client_secret_expl": "Aqui est\u00e1 o seu novo segredo de cliente. Esta \u00e9 a \u00fanica vez que ela ser\u00e1 mostrada, ent\u00e3o n\u00e3o o perca! Agora voc\u00ea pode usar este segredo para fazer requisi\u00e7\u00f5es de API.",
|
||||
"profile_oauth_confidential": "Confidencial",
|
||||
"profile_oauth_confidential_help": "Require the client to authenticate with a secret. Confidential clients can hold credentials in a secure way without exposing them to unauthorized parties. Public applications, such as native desktop or JavaScript SPA applications, are unable to hold secrets securely.",
|
||||
"profile_oauth_confidential_help": "Exigir que o cliente se autentique com um segredo. Clientes confidenciais podem segurar credenciais de forma segura sem expor a partes n\u00e3o autorizadas. Aplica\u00e7\u00f5es p\u00fablicas, como aplica\u00e7\u00f5es de \u00e1rea de trabalho nativas ou JavaScript SPA, s\u00e3o incapazes de manter segredos com seguran\u00e7a.",
|
||||
"multi_account_warning_unknown": "Dependendo do tipo de transa\u00e7\u00e3o que voc\u00ea criar, a conta de origem e\/ou de destino das divis\u00f5es subsequentes pode ser sobrescrita pelo que estiver definido na primeira divis\u00e3o da transa\u00e7\u00e3o.",
|
||||
"multi_account_warning_withdrawal": "Tenha em mente que a conta de origem das divis\u00f5es subsequentes ser\u00e1 sobrescrita pelo que estiver definido na primeira divis\u00e3o da sa\u00edda.",
|
||||
"multi_account_warning_deposit": "Tenha em mente que a conta de destino das divis\u00f5es subsequentes ser\u00e1 sobrescrita pelo que estiver definido na primeira divis\u00e3o da entrada.",
|
||||
"multi_account_warning_transfer": "Tenha em mente que a conta de origem + de destino das divis\u00f5es subsequentes ser\u00e3o sobrescritas pelo que for definido na primeira divis\u00e3o da transfer\u00eancia.",
|
||||
"webhook_trigger_ANY": "After any event",
|
||||
"webhook_trigger_ANY": "Ap\u00f3s qualquer evento",
|
||||
"webhook_trigger_STORE_TRANSACTION": "Ap\u00f3s cria\u00e7\u00e3o da transa\u00e7\u00e3o",
|
||||
"webhook_trigger_UPDATE_TRANSACTION": "Ap\u00f3s atualiza\u00e7\u00e3o da transa\u00e7\u00e3o",
|
||||
"webhook_trigger_DESTROY_TRANSACTION": "Ap\u00f3s exclus\u00e3o da transa\u00e7\u00e3o",
|
||||
"webhook_trigger_STORE_BUDGET": "After budget creation",
|
||||
"webhook_trigger_UPDATE_BUDGET": "After budget update",
|
||||
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
|
||||
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
|
||||
"webhook_trigger_STORE_BUDGET": "Ap\u00f3s cria\u00e7\u00e3o do or\u00e7amento",
|
||||
"webhook_trigger_UPDATE_BUDGET": "Ap\u00f3s atualiza\u00e7\u00e3o do or\u00e7amento",
|
||||
"webhook_trigger_DESTROY_BUDGET": "Ap\u00f3s exclus\u00e3o do or\u00e7amento",
|
||||
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "Ap\u00f3s mudan\u00e7a de valor or\u00e7ado",
|
||||
"webhook_response_TRANSACTIONS": "Detalhes da transa\u00e7\u00e3o",
|
||||
"webhook_response_RELEVANT": "Relevant details",
|
||||
"webhook_response_RELEVANT": "Detalhes relevantes",
|
||||
"webhook_response_ACCOUNTS": "Detalhes da conta",
|
||||
"webhook_response_NONE": "No details",
|
||||
"webhook_response_NONE": "Sem detalhes",
|
||||
"webhook_delivery_JSON": "JSON",
|
||||
"actions": "A\u00e7\u00f5es",
|
||||
"meta_data": "Meta dados",
|
||||
@@ -160,7 +160,7 @@
|
||||
"url": "URL",
|
||||
"active": "Ativo",
|
||||
"interest_date": "Data do juros",
|
||||
"administration_currency": "Primary currency",
|
||||
"administration_currency": "Moeda principal",
|
||||
"title": "T\u00edtulo",
|
||||
"date": "Data",
|
||||
"book_date": "Data de lan\u00e7amento",
|
||||
@@ -180,7 +180,7 @@
|
||||
"list": {
|
||||
"title": "T\u00edtulo",
|
||||
"active": "Est\u00e1 ativo?",
|
||||
"primary_currency": "Primary currency",
|
||||
"primary_currency": "Moeda principal",
|
||||
"trigger": "Gatilho",
|
||||
"response": "Resposta",
|
||||
"delivery": "Entrega",
|
||||
|
||||
@@ -2480,6 +2480,7 @@ return [
|
||||
'balanceFor' => 'Balance for :name',
|
||||
'no_tags' => '(no tags)',
|
||||
'nothing_found' => '(nothing found)',
|
||||
'no_date' => '(no date)',
|
||||
|
||||
// page settings and wizard dialogs
|
||||
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
<label for="inputTags" class="col-sm-3 control-label">{{ 'select_tag'|_ }}</label>
|
||||
<div class="col-sm-9">
|
||||
<select id="inputTags" name="tag[]" multiple="multiple" class="form-control">
|
||||
{% for tag in tags %}
|
||||
<option value="{{ tag.id }}" label="{{ tag.tag|e('html') }}">{{ tag.tag|e('html') }}</option>
|
||||
{% for year in tags %}
|
||||
<optgroup label="{{ year.year }}">
|
||||
{% for tag in year.tags %}
|
||||
<option value="{{ tag.id }}" label="{{ tag.tag|e('html') }}">{{ tag.tag|e('html') }}</option>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user