Compare commits

...

53 Commits

Author SHA1 Message Date
github-actions[bot]
35447f5eee Merge pull request #10843 from firefly-iii/release-1756727799
🤖 Automatically merge the PR into the develop branch.
2025-09-01 13:56:46 +02:00
JC5
7c6fcb5731 🤖 Auto commit for release 'develop' on 2025-09-01 2025-09-01 13:56:39 +02:00
Sander Dorigo
fabd9bf765 Merge branch 'develop' of https://github.com/firefly-iii/firefly-iii into develop 2025-09-01 13:38:39 +02:00
Sander Dorigo
6352d26633 fix argument 2025-09-01 13:38:32 +02:00
github-actions[bot]
ebef145bd6 Merge pull request #10842 from firefly-iii/release-1756723300
🤖 Automatically merge the PR into the develop branch.
2025-09-01 12:41:48 +02:00
JC5
acc89eb5f9 🤖 Auto commit for release 'develop' on 2025-09-01 2025-09-01 12:41:40 +02:00
Sander Dorigo
6523596415 fix bad call 2025-09-01 12:36:36 +02:00
github-actions[bot]
b6c2d23116 Merge pull request #10838 from firefly-iii/release-1756697534
🤖 Automatically merge the PR into the develop branch.
2025-09-01 05:32:22 +02:00
JC5
2a123354f9 🤖 Auto commit for release 'develop' on 2025-09-01 2025-09-01 05:32:14 +02:00
James Cole
1e7ea4b76c Improve account list and view. 2025-08-31 19:20:02 +02:00
James Cole
d959526eb3 Fix #10837 2025-08-31 19:07:45 +02:00
James Cole
8846ee9091 Fix #10827 2025-08-30 07:51:28 +02:00
github-actions[bot]
6eb8d0fc8c Merge pull request #10826 from firefly-iii/release-1756445011
🤖 Automatically merge the PR into the develop branch.
2025-08-29 07:23:42 +02:00
JC5
1b0e16b6a5 🤖 Auto commit for release 'develop' on 2025-08-29 2025-08-29 07:23:32 +02:00
James Cole
2e4df28288 Less logging. 2025-08-29 06:51:20 +02:00
James Cole
f3b7a3015d Fix #10824 2025-08-27 19:00:12 +02:00
James Cole
5de5e08b1d Fix #10820 2025-08-26 16:05:36 +02:00
James Cole
0a116cd04c Add API autocomplete tests. 2025-08-25 17:17:51 +02:00
James Cole
fd32a692c1 Add some API tests. 2025-08-25 15:31:51 +02:00
James Cole
1ac762aba8 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-08-25 15:31:15 +02:00
James Cole
315dc532b6 Fix #10819 2025-08-25 15:31:07 +02:00
github-actions[bot]
e19ed1be15 Merge pull request #10817 from firefly-iii/release-1756092462
🤖 Automatically merge the PR into the develop branch.
2025-08-25 05:27:50 +02:00
JC5
cbb0621fd9 🤖 Auto commit for release 'develop' on 2025-08-25 2025-08-25 05:27:42 +02:00
James Cole
049cbab861 Remove superfluous logging. 2025-08-24 20:31:12 +02:00
James Cole
28b620fb5c Remove superfluous logging. 2025-08-24 20:30:40 +02:00
James Cole
c183f91ff6 Add issue to changelog 2025-08-24 20:29:39 +02:00
github-actions[bot]
172efae41c Merge pull request #10816 from firefly-iii/release-1756060004
🤖 Automatically merge the PR into the develop branch.
2025-08-24 20:26:54 +02:00
JC5
756e857ba0 🤖 Auto commit for release 'develop' on 2025-08-24 2025-08-24 20:26:44 +02:00
James Cole
1cde7aab0c Fix #10815 2025-08-24 20:14:02 +02:00
James Cole
2d67eece5d Various code cleanup. 2025-08-24 17:14:07 +02:00
James Cole
b1f79c4c0f Fix #10814 2025-08-24 13:31:00 +02:00
James Cole
33bd2ceae8 Fix amounts in transaction show. 2025-08-24 13:30:41 +02:00
James Cole
e68850f192 Fix changelog. 2025-08-24 06:51:48 +02:00
James Cole
450ac7e6ee Fix #10813 2025-08-24 06:51:28 +02:00
James Cole
91f52b5dbc Add copyright statements. 2025-08-23 11:26:04 +02:00
James Cole
eed2405d76 Update changelog before I forget about it. 2025-08-23 11:17:05 +02:00
github-actions[bot]
c956df7790 Merge pull request #10809 from firefly-iii/release-1755940492
🤖 Automatically merge the PR into the develop branch.
2025-08-23 11:15:02 +02:00
JC5
0fdccec6a8 🤖 Auto commit for release 'develop' on 2025-08-23 2025-08-23 11:14:52 +02:00
James Cole
8ded54d7a8 Fix #10808 2025-08-23 08:56:25 +02:00
James Cole
bb1b4ca5ca Fix #10807 2025-08-23 08:54:50 +02:00
James Cole
e90d60113b Extra validation and a new config variable for #10806 2025-08-23 08:47:34 +02:00
James Cole
d95dada0e0 Update changelog for #10804 2025-08-22 20:20:07 +02:00
James Cole
8722456595 Fix #10804 2025-08-22 20:19:52 +02:00
James Cole
b5ad226451 Remove unused headers. 2025-08-22 15:45:39 +02:00
James Cole
ddb0e66651 Update changelog. 2025-08-22 15:45:05 +02:00
James Cole
e802899608 Pre-filter expenses, fixes #10803 2025-08-22 11:53:08 +02:00
James Cole
0894d3bf42 Fix #10802 2025-08-22 11:09:17 +02:00
James Cole
80bcfd3bcd Small issues to fix #5532 2025-08-22 09:52:27 +02:00
James Cole
8c410f42bd Expand changelog. 2025-08-22 09:15:34 +02:00
James Cole
7a1021dffc Add spent + earned info to category chart. 2025-08-22 09:15:13 +02:00
github-actions[bot]
63883c9a84 Merge pull request #10801 from firefly-iii/release-1755840718
🤖 Automatically merge the PR into the develop branch.
2025-08-22 07:32:06 +02:00
JC5
50d7f9d1ec 🤖 Auto commit for release 'develop' on 2025-08-22 2025-08-22 07:31:58 +02:00
James Cole
ebc7ea0eb6 Fix amount display 2025-08-22 07:28:09 +02:00
95 changed files with 2969 additions and 1324 deletions

View File

@@ -1253,16 +1253,16 @@
},
{
"name": "symfony/console",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "5f360ebc65c55265a74d23d7fe27f957870158a1"
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1",
"reference": "5f360ebc65c55265a74d23d7fe27f957870158a1",
"url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
"shasum": ""
},
"require": {
@@ -1327,7 +1327,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.3.2"
"source": "https://github.com/symfony/console/tree/v7.3.3"
},
"funding": [
{
@@ -1347,7 +1347,7 @@
"type": "tidelift"
}
],
"time": "2025-07-30T17:13:41+00:00"
"time": "2025-08-25T06:35:40+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -1418,16 +1418,16 @@
},
{
"name": "symfony/event-dispatcher",
"version": "v7.3.0",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "497f73ac996a598c92409b44ac43b6690c4f666d"
"reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d",
"reference": "497f73ac996a598c92409b44ac43b6690c4f666d",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191",
"reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191",
"shasum": ""
},
"require": {
@@ -1478,7 +1478,7 @@
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/event-dispatcher/tree/v7.3.0"
"source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3"
},
"funding": [
{
@@ -1489,12 +1489,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-04-22T09:11:45+00:00"
"time": "2025-08-13T11:49:31+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
@@ -1712,16 +1716,16 @@
},
{
"name": "symfony/options-resolver",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37"
"reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37",
"reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d",
"reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d",
"shasum": ""
},
"require": {
@@ -1759,7 +1763,7 @@
"options"
],
"support": {
"source": "https://github.com/symfony/options-resolver/tree/v7.3.2"
"source": "https://github.com/symfony/options-resolver/tree/v7.3.3"
},
"funding": [
{
@@ -1779,7 +1783,7 @@
"type": "tidelift"
}
],
"time": "2025-07-15T11:36:08+00:00"
"time": "2025-08-05T10:16:07+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -2282,16 +2286,16 @@
},
{
"name": "symfony/process",
"version": "v7.3.0",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af"
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af",
"reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af",
"url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1",
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1",
"shasum": ""
},
"require": {
@@ -2323,7 +2327,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v7.3.0"
"source": "https://github.com/symfony/process/tree/v7.3.3"
},
"funding": [
{
@@ -2334,12 +2338,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-04-17T09:11:12+00:00"
"time": "2025-08-18T09:42:54+00:00"
},
{
"name": "symfony/service-contracts",
@@ -2488,16 +2496,16 @@
},
{
"name": "symfony/string",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "42f505aff654e62ac7ac2ce21033818297ca89ca"
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca",
"reference": "42f505aff654e62ac7ac2ce21033818297ca89ca",
"url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
"shasum": ""
},
"require": {
@@ -2555,7 +2563,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v7.3.2"
"source": "https://github.com/symfony/string/tree/v7.3.3"
},
"funding": [
{
@@ -2575,7 +2583,7 @@
"type": "tidelift"
}
],
"time": "2025-07-10T08:47:49+00:00"
"time": "2025-08-25T06:35:40+00:00"
}
],
"packages-dev": [],

View File

@@ -114,6 +114,7 @@ class AccountController extends Controller
'id' => (string) $account->id,
'name' => $account->name,
'name_with_balance' => $nameWithBalance,
'active' => $account->active,
'type' => $account->accountType->type,
'currency_id' => (string) $useCurrency->id,
'currency_name' => $useCurrency->name,

View File

@@ -67,8 +67,9 @@ class BudgetController extends Controller
$result = $this->repository->searchBudget($data['query'], $this->parameters->get('limit'));
$filtered = $result->map(
static fn (Budget $item) => [
'id' => (string) $item->id,
'name' => $item->name,
'id' => (string) $item->id,
'name' => $item->name,
'active' => $item->active,
]
);

View File

@@ -69,6 +69,7 @@ class RecurrenceController extends Controller
'id' => (string) $recurrence->id,
'name' => $recurrence->title,
'description' => $recurrence->description,
'active' => $recurrence->active,
];
}

View File

@@ -37,7 +37,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.
@@ -66,9 +66,10 @@ 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,
];
}

View File

@@ -69,6 +69,7 @@ class RuleGroupController extends Controller
'id' => (string) $group->id,
'name' => $group->title,
'description' => $group->description,
'active' => $group->active,
];
}

View File

@@ -1,5 +1,26 @@
<?php
/*
* BalanceController.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Chart;

View File

@@ -97,22 +97,23 @@ class CategoryController extends Controller
$collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end)->withAccountInformation();
$collector->setXorAccounts($accounts)->withCategoryInformation();
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::RECONCILIATION->value]);
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value]);
$journals = $collector->getExtractedJournals();
/** @var array $journal */
foreach ($journals as $journal) {
// find journal:
$journalCurrencyId = (int)$journal['currency_id'];
$currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId);
$currencies[$journalCurrencyId] = $currency;
$currencyId = (int)$currency->id;
$currencyName = (string)$currency->name;
$currencyCode = (string)$currency->code;
$currencySymbol = (string)$currency->symbol;
$currencyDecimalPlaces = (int)$currency->decimal_places;
$amount = Steam::positive($journal['amount']);
$pcAmount = null;
$journalCurrencyId = (int)$journal['currency_id'];
$type = $journal['transaction_type_type'];
$currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId);
$currencies[$journalCurrencyId] = $currency;
$currencyId = (int)$currency->id;
$currencyName = (string)$currency->name;
$currencyCode = (string)$currency->code;
$currencySymbol = (string)$currency->symbol;
$currencyDecimalPlaces = (int)$currency->decimal_places;
$amount = Steam::positive((string)$journal['amount']);
$pcAmount = null;
// overrule if necessary:
if ($this->convertToPrimary && $journalCurrencyId === $this->primaryCurrency->id) {
@@ -129,8 +130,8 @@ class CategoryController extends Controller
}
$categoryName = $journal['category_name'] ?? (string)trans('firefly.no_category');
$key = sprintf('%s-%s', $categoryName, $currencyCode);
$categoryName = $journal['category_name'] ?? (string)trans('firefly.no_category');
$key = sprintf('%s-%s', $categoryName, $currencyCode);
// create arrays
$return[$key] ??= [
'label' => $categoryName,
@@ -150,23 +151,37 @@ class CategoryController extends Controller
'yAxisID' => 0,
'type' => 'bar',
'entries' => [
'spent' => '0',
'spent' => '0',
'earned' => '0',
],
'pc_entries' => [
'spent' => '0',
'spent' => '0',
'earned' => '0',
],
];
// add monies
$return[$key]['entries']['spent'] = bcadd($return[$key]['entries']['spent'], (string)$amount);
if (null !== $pcAmount) {
$return[$key]['pc_entries']['spent'] = bcadd($return[$key]['pc_entries']['spent'], (string)$pcAmount);
// expenses to spent
if (TransactionTypeEnum::WITHDRAWAL->value === $type) {
$return[$key]['entries']['spent'] = bcadd($return[$key]['entries']['spent'], $amount);
if (null !== $pcAmount) {
$return[$key]['pc_entries']['spent'] = bcadd($return[$key]['pc_entries']['spent'], $pcAmount);
}
continue;
}
// positive amount = earned
if (TransactionTypeEnum::DEPOSIT->value === $type) {
$return[$key]['entries']['earned'] = bcadd($return[$key]['entries']['earned'], $amount);
if (null !== $pcAmount) {
$return[$key]['pc_entries']['earned'] = bcadd($return[$key]['pc_entries']['earned'], $pcAmount);
}
}
}
$return = array_values($return);
// order by amount
usort($return, static fn (array $a, array $b) => (float)$a['entries']['spent'] < (float)$b['entries']['spent'] ? 1 : -1);
usort($return, static fn (array $a, array $b) => ((float)$a['entries']['spent'] + (float)$a['entries']['earned']) < ((float)$b['entries']['spent'] + (float)$b['entries']['earned']) ? 1 : -1);
return response()->json($this->clean($return));
}

View File

@@ -108,12 +108,7 @@ abstract class Controller extends BaseController
{
$bag = new ParameterBag();
$page = (int)request()->get('page');
if ($page < 1) {
$page = 1;
}
if ($page > 2 ** 16) {
$page = 2 ** 16;
}
$page = min(max(1, $page), 2 ** 16);
$bag->set('page', $page);
// some date fields:
@@ -131,19 +126,15 @@ abstract class Controller extends BaseController
$obj = null;
if (null !== $date) {
try {
$obj = Carbon::parse((string)$date);
$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()));
}
}
$bag->set($field, $obj);
if ($obj instanceof Carbon) {
$bag->set($field, $obj);
}
}
// integer fields:

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Models\Account;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\Account\ShowRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
@@ -33,7 +34,6 @@ use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
@@ -71,23 +71,23 @@ class ShowController extends Controller
*
* @throws FireflyException
*/
public function index(Request $request): JsonResponse
public function index(ShowRequest $request): JsonResponse
{
$manager = $this->getManager();
$type = $request->get('type') ?? 'all';
$this->parameters->set('type', $type);
$params = $request->getParameters();
$this->parameters->set('type', $params['type']);
// types to get, page size:
$types = $this->mapAccountTypes($this->parameters->get('type'));
$pageSize = $this->parameters->get('limit');
$types = $this->mapAccountTypes($params['type']);
// get list of accounts. Count it and split it.
$this->repository->resetAccountOrder();
$collection = $this->repository->getAccountsByType($types, $this->parameters->get('sort') ?? []);
// TODO fix sort.
$collection = $this->repository->getAccountsByType($types, null);
$count = $collection->count();
// continue sort:
$accounts = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
$accounts = $collection->slice(($this->parameters->get('page') - 1) * $params['limit'], $params['limit']);
// enrich
/** @var User $admin */
@@ -98,7 +98,7 @@ class ShowController extends Controller
$accounts = $enrichment->enrich($accounts);
// make paginator:
$paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page'));
$paginator = new LengthAwarePaginator($accounts, $count, $params['limit'], $this->parameters->get('page'));
$paginator->setPath(route('api.v1.accounts.index').$this->buildParams());
/** @var AccountTransformer $transformer */

View File

@@ -86,7 +86,7 @@ class UpdateController extends Controller
$admin = auth()->user();
$enrichment = new BudgetLimitEnrichment();
$enrichment->setUser($admin);
$budgetLimit = $enrichment->enrich($budgetLimit);
$budgetLimit = $enrichment->enrichSingle($budgetLimit);
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);

View File

@@ -0,0 +1,126 @@
<?php
declare(strict_types=1);
/*
* TriggerController.php
* Copyright (c) 2025 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\Controllers\Models\Recurrence;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Generic\SingleDateRequest;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Jobs\CreateRecurringTransactions;
use FireflyIII\Models\Recurrence;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment;
use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
class TriggerController extends Controller
{
private RecurringRepositoryInterface $repository;
/**
* RecurrenceController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(RecurringRepositoryInterface::class);
$this->repository->setUser(auth()->user());
return $next($request);
}
);
}
public function trigger(SingleDateRequest $request, Recurrence $recurrence): JsonResponse
{
// find recurrence occurrence for this date and trigger it.
// grab the date from the last time the recurrence fired:
$backupDate = $recurrence->latest_date;
$date = $request->getDate();
// fire the recurring cron job on the given date, then post-date the created transaction.
Log::info(sprintf('Trigger: will now fire recurring cron job task for date "%s".', $date->format('Y-m-d H:i:s')));
/** @var CreateRecurringTransactions $job */
$job = app(CreateRecurringTransactions::class);
$job->setRecurrences(new Collection()->push($recurrence));
$job->setDate($date);
$job->setForce(false);
$job->handle();
Log::debug('Done with recurrence.');
$groups = $job->getGroups();
$this->repository->markGroupsAsNow($groups);
$recurrence->latest_date = $backupDate;
$recurrence->latest_date_tz = $backupDate?->format('e');
$recurrence->save();
Preferences::mark();
// enrich groups and return them:
if (0 === $groups->count()) {
$paginator = new LengthAwarePaginator(new Collection(), 0, 1);
}
if ($groups->count() > 0) {
/** @var User $admin */
$admin = auth()->user();
// use new group collector:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setUser($admin)
->setIds($groups->pluck('id')->toArray())
->withAPIInformation()
;
$paginator = $collector->getPaginatedGroups();
}
$manager = $this->getManager();
$paginator->setPath(route('api.v1.recurrences.trigger', [$recurrence->id]).$this->buildParams());
// enrich
$admin = auth()->user();
$enrichment = new TransactionGroupEnrichment();
$enrichment->setUser($admin);
$transactions = $enrichment->enrich($paginator->getCollection());
/** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
}
}

View File

@@ -64,6 +64,7 @@ class ChartRequest extends FormRequest
'end' => 'required|date|after:1970-01-02|before:2038-01-17|after_or_equal:start',
'preselected' => sprintf('nullable|in:%s', implode(',', config('firefly.preselected_accounts'))),
'period' => sprintf('nullable|in:%s', implode(',', config('firefly.valid_view_ranges'))),
'accounts' => 'nullable|array',
'accounts.*' => 'exists:accounts,id',
];

View File

@@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
/*
* ShowRequest.php
* Copyright (c) 2025 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\Models\Account;
use Carbon\Carbon;
use FireflyIII\Models\Preference;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\User;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
class ShowRequest extends FormRequest
{
use AccountFilter;
use ConvertsDataTypes;
public function getParameters(): array
{
$limit = $this->convertInteger('limit');
if (0 === $limit) {
// get default for user:
/** @var User $user */
$user = auth()->user();
/** @var Preference $pageSize */
$limit = (int)Preferences::getForUser($user, 'listPageSize', 50)->data;
}
$page = $this->convertInteger('page');
$page = min(max(1, $page), 2 ** 16);
return [
'type' => $this->convertString('type', 'all'),
'limit' => $limit,
'sort' => $this->convertString('sort', 'order'),
'page' => $page,
];
}
public function rules(): array
{
$keys = implode(',', array_keys($this->types));
return [
'date' => 'date',
'start' => 'date|present_with:end|before_or_equal:end|before:2038-01-17|after:1970-01-02',
'end' => 'date|present_with:start|after_or_equal:start|before:2038-01-17|after:1970-01-02',
'sort' => 'in:active,iban,name,order,-active,-iban,-name,-order', // TODO improve me.
'type' => sprintf('in:%s', $keys),
'limit' => 'numeric|min:1|max:131337',
'page' => 'numeric|min:1|max:131337',
];
}
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator): void {
if ($validator->failed()) {
return;
}
$data = $validator->getData();
if (array_key_exists('date', $data) && array_key_exists('start', $data) && array_key_exists('end', $data)) {
// assume valid dates, before we got here.
$start = Carbon::parse($data['start'], config('app.timezone'))->startOfDay();
$end = Carbon::parse($data['end'], config('app.timezone'))->endOfDay();
$date = Carbon::parse($data['date'], config('app.timezone'));
if (!$date->between($start, $end)) {
$validator->errors()->add('date', (string)trans('validation.between_date'));
}
}
}
);
}
}

View File

@@ -78,7 +78,7 @@ class StoreRequest extends FormRequest
'object_group_id' => 'numeric|belongsToUser:object_groups,id',
'object_group_title' => ['min:1', 'max:255'],
'target_amount' => ['required', new IsValidZeroOrMoreAmount()],
'start_date' => 'date|nullable',
'start_date' => 'required|date|after:1970-01-01|before:2038-01-17',
'transaction_currency_id' => 'exists:transaction_currencies,id|required_without:transaction_currency_code',
'transaction_currency_code' => 'exists:transaction_currencies,code|required_without:transaction_currency_id',
'target_date' => 'date|nullable|after:start_date',

View File

@@ -1,9 +1,9 @@
<?php
declare(strict_types=1);
/*
* ValidatesEnvironmentVariables.php
* Copyright (c) 2025 james@firefly-iii.org.
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -18,9 +18,11 @@ declare(strict_types=1);
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Integrity;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;

View File

@@ -133,6 +133,9 @@ class OutputsInstructions extends Command
if ('03-31' === $today) {
$colors = ['bright-blue', 'bright-red', 'white', 'white', 'bright-red', 'bright-blue', 'default', 'default'];
}
if ('ru_RU' === config('firefly.default_language')) {
$colors = ['blue', 'blue', 'blue', 'yellow', 'yellow', 'yellow', 'default', 'default'];
}
$this->line(sprintf('<fg=%s> ______ _ __ _ _____ _____ _____ </>', $colors[0]));
$this->line(sprintf('<fg=%s> | ____(_) / _| | |_ _|_ _|_ _| </>', $colors[1]));
@@ -238,14 +241,38 @@ class OutputsInstructions extends Command
private function someQuote(): void
{
$lines = [
$lines = [
'Forgive yourself for not being at peace.',
'Doesn\'t look like anything to me.',
'Be proud of what you make.',
'Be there or forever wonder.',
'A year from now you will wish you had started today.',
];
$random = random_int(0, count($lines) - 1);
$this->line(sprintf(' "%s"', $lines[$random]));
$addQuotes = true;
// fuck the Russian aggression in Ukraine.
// There is no point even trying to be neutral, because you cant. When I say you cant be neutral on
// a moving train, it means the world is already moving in certain directions. Children are going
// hungry, wars are taking place. In a situation like that, to be neutral or to try to be neutral,
// to stand aside, not to take a stand, not to participate, is to collaborate with whatever is
// going on, to allow that to happen.
if ('ru_RU' === config('firefly.default_language')) {
$addQuotes = false;
$lines = [
'🇺🇦 Слава Україні!',
'🇺🇦 Slava Ukraini!',
];
}
$random = random_int(0, count($lines) - 1);
if ($addQuotes) {
$this->line(sprintf(' "%s"', $lines[$random]));
return;
}
$this->line(sprintf(' %s', $lines[$random]));
}
}

View File

@@ -1,9 +1,9 @@
<?php
declare(strict_types=1);
/*
* RecalculatesRunningBalance.php
* Copyright (c) 2025 james@firefly-iii.org.
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -18,9 +18,11 @@ declare(strict_types=1);
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\System;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;

View File

@@ -1,5 +1,26 @@
<?php
/*
* ResetsErrorMailLimit.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\System;

View File

@@ -1,5 +1,26 @@
<?php
/*
* UpgradesWebhooks.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Upgrade;

View File

@@ -1,6 +1,27 @@
<?php
/*
* WarnUserAboutBill.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events\Model\Bill;

View File

@@ -1,5 +1,26 @@
<?php
/*
* WarnUserAboutOverdueSubscriptions.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events\Model\Bill;

View File

@@ -1,5 +1,26 @@
<?php
/*
* ChangedName.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events\Model\PiggyBank;

View File

@@ -108,10 +108,10 @@ class BillEventHandler
{
Log::debug(sprintf('Now in %s', __METHOD__));
$bill = $event->bill;
$bill = $event->bill;
/** @var bool $preference */
Preferences::getForUser($bill->user, 'notification_bill_reminder', true)->data;
$preference = Preferences::getForUser($bill->user, 'notification_bill_reminder', true)->data;
if (true === $preference) {
Log::debug('Bill reminder is true!');

View File

@@ -40,7 +40,7 @@ class WebhookEventHandler
{
Log::debug(sprintf('Now in %s', __METHOD__));
if (false === config('firefly.feature_flags.webhooks') || false === config('firefly.allow_webhooks')) {
Log::info('Webhook event handler is disabled, do not run sendWebhookMessages().');
Log::debug('Webhook event handler is disabled, do not run sendWebhookMessages().');
return;
}

View File

@@ -72,7 +72,7 @@ class TransactionObserver
}
$transaction->saveQuietly();
Log::debug('Transaction primary currency amounts are updated.');
Log::debug(sprintf('Transaction #%d primary currency amounts are updated.', $transaction->id));
}
public function deleting(?Transaction $transaction): void

View File

@@ -851,7 +851,7 @@ class GroupCollector implements GroupCollectorInterface
*/
public function getPaginatedGroups(): LengthAwarePaginator
{
$set = $this->getGroups();
$set = $this->getGroups();
if (0 === $this->limit) {
$this->setLimit(50);
}
@@ -861,8 +861,9 @@ class GroupCollector implements GroupCollectorInterface
return new LengthAwarePaginator($set, $this->total, $total, 1);
}
$limit = $this->limit ?? 1;
return new LengthAwarePaginator($set, $this->total, $this->limit, $this->page);
return new LengthAwarePaginator($set, $this->total, $limit, $this->page);
}
/**

View File

@@ -158,18 +158,8 @@ class ShowController extends Controller
Log::debug('End collect transactions');
$timer->stop('collection');
// enrich data in arrays.
// enrich
// $enrichment = new TransactionGroupEnrichment();
// $enrichment->setUser(auth()->user());
// $groups->setCollection($enrichment->enrich($groups->getCollection()));
$groups->setPath(route('accounts.show', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]));
$showAll = false;
// correct
$now = today()->endOfDay();
if ($now->gt($end) || $now->lt($start)) {
$now = $end;

View File

@@ -37,6 +37,7 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Http\Controllers\AugumentData;
use FireflyIII\Support\Http\Controllers\ChartGeneration;
use FireflyIII\Support\Http\Controllers\DateCalculation;
@@ -504,6 +505,7 @@ class AccountController extends Controller
Log::debug(sprintf('Step is %s', $step));
$locale = Steam::getLocale();
$return = [];
$converter = new ExchangeRateConverter();
// fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041
// have to make sure this chart is always based on the balance at the END of the period.
@@ -512,10 +514,10 @@ class AccountController extends Controller
$current = app('navigation')->endOfX($current, $step, null);
$format = (string)trans('config.month_and_day_js', [], $locale);
$accountCurrency = $this->accountRepository->getAccountCurrency($account);
Log::debug('Get and filter balance for entire range start');
$range = Steam::finalAccountBalanceInRange($account, $start, $end, $this->convertToPrimary);
$range = Steam::filterAccountBalances($range, $account, $this->convertToPrimary, $accountCurrency);
Log::debug('Get and filter balance for entire range end');
// temp, get end balance.
Log::debug(sprintf('period: Call finalAccountBalance with date/time "%s"', $end->toIso8601String()));
Steam::finalAccountBalance($account, $end);
@@ -552,7 +554,15 @@ class AccountController extends Controller
$carbon = Carbon::createFromFormat('Y-m-d', $newRange[$expectedIndex]['date'])->endOfDay();
}
}
Log::debug(sprintf('momentBalance is now %s', json_encode($momentBalance)));
Log::debug(sprintf('momentBalance[%s] is now %s', $current->format('Y-m-d H:i:s'), json_encode($momentBalance)));
// check, perhaps recalculate the amount in currency X if the
if ($accountCurrency->id !== $this->primaryCurrency->id && $this->convertToPrimary && array_key_exists($accountCurrency->code, $momentBalance)) {
$converted = $converter->convert($accountCurrency, $this->primaryCurrency, $current, $momentBalance[$accountCurrency->code]);
$momentBalance['pc_balance'] = $converted;
}
$return = $this->updateChartKeys($return, $momentBalance);
$previous = $momentBalance;

View File

@@ -28,8 +28,7 @@ use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\TriggerRecurrenceRequest;
use FireflyIII\Jobs\CreateRecurringTransactions;
use FireflyIII\Models\Recurrence;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Support\Facades\Preferences;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Collection;
@@ -58,20 +57,11 @@ class TriggerController extends Controller
app('log')->debug('Done with recurrence.');
$groups = $job->getGroups();
/** @var TransactionGroup $group */
foreach ($groups as $group) {
/** @var TransactionJournal $journal */
foreach ($group->transactionJournals as $journal) {
app('log')->debug(sprintf('Set date of journal #%d to today!', $journal->id));
$journal->date = today(config('app.timezone'));
$journal->save();
}
}
$this->repository->markGroupsAsNow($groups);
$recurrence->latest_date = $backupDate;
$recurrence->latest_date_tz = $backupDate?->format('e');
$recurrence->save();
app('preferences')->mark();
Preferences::mark();
if (0 === $groups->count()) {
$request->session()->flash('info', (string) trans('firefly.no_new_transaction_in_recurrence'));

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Rule;
use Throwable;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\SelectTransactionsRequest;
@@ -74,20 +73,16 @@ class SelectController extends Controller
/** @var User $user */
$user = auth()->user();
$accounts = implode(',', $request->get('accounts'));
$startDate = new Carbon($request->get('start'));
$endDate = new Carbon($request->get('end'));
// create new rule engine:
$newRuleEngine = app(RuleEngineInterface::class);
$newRuleEngine->setUser($user);
// add extra operators:
$newRuleEngine->addOperator(['type' => 'date_after', 'value' => $startDate->format('Y-m-d')]);
$newRuleEngine->addOperator(['type' => 'date_before', 'value' => $endDate->format('Y-m-d')]);
$newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]);
// set rules:
$newRuleEngine->setRules(new Collection([$rule]));
$newRuleEngine->setRules(new Collection()->push($rule));
$newRuleEngine->fire();
$resultCount = $newRuleEngine->getResults();
@@ -107,11 +102,9 @@ class SelectController extends Controller
return redirect(route('rules.index'));
}
// does the user have shared accounts?
$first = session('first', today(config('app.timezone'))->subYear())->format('Y-m-d');
$today = today(config('app.timezone'))->format('Y-m-d');
$subTitle = (string) trans('firefly.apply_rule_selection', ['title' => $rule->title]);
return view('rules.rule.select-transactions', compact('first', 'today', 'rule', 'subTitle'));
return view('rules.rule.select-transactions', compact('rule', 'subTitle'));
}
/**

View File

@@ -25,11 +25,9 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\RuleGroup;
use Exception;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\SelectTransactionsRequest;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
use FireflyIII\User;
use Illuminate\Contracts\View\Factory;
@@ -42,8 +40,6 @@ use Illuminate\View\View;
*/
class ExecutionController extends Controller
{
private RuleGroupRepositoryInterface $ruleGroupRepository;
/**
* ExecutionController constructor.
*/
@@ -56,7 +52,6 @@ class ExecutionController extends Controller
app('view')->share('title', (string) trans('firefly.rules'));
app('view')->share('mainTitleIcon', 'fa-random');
$this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
return $next($request);
}
@@ -74,15 +69,11 @@ class ExecutionController extends Controller
/** @var User $user */
$user = auth()->user();
$accounts = implode(',', $request->get('accounts'));
$startDate = new Carbon($request->get('start'));
$endDate = new Carbon($request->get('end'));
// create new rule engine:
$newRuleEngine = app(RuleEngineInterface::class);
$newRuleEngine->setUser($user);
// add extra operators:
$newRuleEngine->addOperator(['type' => 'date_after', 'value' => $startDate->format('Y-m-d')]);
$newRuleEngine->addOperator(['type' => 'date_before', 'value' => $endDate->format('Y-m-d')]);
$newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]);
// set rules:
@@ -104,10 +95,8 @@ class ExecutionController extends Controller
*/
public function selectTransactions(RuleGroup $ruleGroup)
{
$first = session('first')->format('Y-m-d');
$today = today(config('app.timezone'))->format('Y-m-d');
$subTitle = (string) trans('firefly.apply_rule_group_selection', ['title' => $ruleGroup->title]);
return view('rules.rule-group.select-transactions', compact('first', 'today', 'ruleGroup', 'subTitle'));
return view('rules.rule-group.select-transactions', compact('ruleGroup', 'subTitle'));
}
}

View File

@@ -303,22 +303,22 @@ class ConvertController extends Controller
private function convertJournal(TransactionJournal $journal, TransactionType $transactionType, array $data): TransactionJournal
{
/** @var AccountValidator $validator */
$validator = app(AccountValidator::class);
$validator = app(AccountValidator::class);
$validator->setUser(auth()->user());
$validator->setTransactionType($transactionType->type);
$sourceId = $data['source_id'][$journal->id] ?? null;
$sourceName = $data['source_name'][$journal->id] ?? null;
$destinationId = $data['destination_id'][$journal->id] ?? null;
$destinationName = $data['destination_name'][$journal->id] ?? null;
$sourceId = $data['source_id'][$journal->id] ?? null;
$sourceName = $data['source_name'][$journal->id] ?? null;
$destinationId = $data['destination_id'][$journal->id] ?? null;
$destinationName = $data['destination_name'][$journal->id] ?? null;
// double check it's not an empty string.
$sourceId = '' === $sourceId || null === $sourceId ? null : (int) $sourceId;
$sourceName = '' === $sourceName ? null : (string) $sourceName;
$destinationId = '' === $destinationId || null === $destinationId ? null : (int) $destinationId;
$destinationName = '' === $destinationName ? null : (string) $destinationName;
$validSource = $validator->validateSource(['id' => $sourceId, 'name' => $sourceName]);
$validDestination = $validator->validateDestination(['id' => $destinationId, 'name' => $destinationName]);
$sourceId = '' === $sourceId || null === $sourceId ? null : (int) $sourceId;
$sourceName = '' === $sourceName ? null : (string) $sourceName;
$destinationId = '' === $destinationId || null === $destinationId ? null : (int) $destinationId;
$destinationName = '' === $destinationName ? null : (string) $destinationName;
$validSource = $validator->validateSource(['id' => $sourceId, 'name' => $sourceName]);
$validDestination = $validator->validateDestination(['id' => $destinationId, 'name' => $destinationName]);
if (false === $validSource) {
throw new FireflyException(sprintf(trans('firefly.convert_invalid_source'), $journal->id));
@@ -329,7 +329,7 @@ class ConvertController extends Controller
// TODO typeOverrule: the account validator may have another opinion on the transaction type.
$update = [
$update = [
'source_id' => $sourceId,
'source_name' => $sourceName,
'destination_id' => $destinationId,
@@ -337,6 +337,9 @@ class ConvertController extends Controller
'type' => $transactionType->type,
];
$sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first();
$amount = $sourceTransaction?->amount ?? '0';
// also set the currency to the currency of the source account, in case you're converting a deposit into a transfer.
if (TransactionTypeEnum::TRANSFER->value === $transactionType->type && TransactionTypeEnum::DEPOSIT->value === $journal->transactionType->type) {
$source = $this->accountRepository->find((int) $sourceId);
@@ -346,12 +349,25 @@ class ConvertController extends Controller
if ($sourceCurrency instanceof TransactionCurrency && $destCurrency instanceof TransactionCurrency && $sourceCurrency->code !== $destCurrency->code) {
$update['currency_id'] = $sourceCurrency->id;
$update['foreign_currency_id'] = $destCurrency->id;
$update['foreign_amount'] = '1'; // not the best solution but at this point the amount is hard to get.
$update['foreign_amount'] = Steam::positive($amount); // not the best solution but at this point the amount is hard to get.
}
}
// same thing for converting a withdrawal into a transfer, but with the currency of the destination account.
if (TransactionTypeEnum::TRANSFER->value === $transactionType->type && TransactionTypeEnum::WITHDRAWAL->value === $journal->transactionType->type) {
$source = $this->accountRepository->find((int) $sourceId);
$sourceCurrency = $this->accountRepository->getAccountCurrency($source);
$dest = $this->accountRepository->find((int) $destinationId);
$destCurrency = $this->accountRepository->getAccountCurrency($dest);
if ($sourceCurrency instanceof TransactionCurrency && $destCurrency instanceof TransactionCurrency && $sourceCurrency->code !== $destCurrency->code) {
$update['currency_id'] = $sourceCurrency->id;
$update['foreign_currency_id'] = $destCurrency->id;
$update['foreign_amount'] = Steam::positive($amount); // not the best solution but at this point the amount is hard to get.
}
}
/** @var JournalUpdateService $service */
$service = app(JournalUpdateService::class);
$service = app(JournalUpdateService::class);
$service->setTransactionJournal($journal);
$service->setData($update);
$service->update();

View File

@@ -41,8 +41,6 @@ class SelectTransactionsRequest extends FormRequest
public function rules(): array
{
return [
'start' => 'required|date|after:1970-01-02|before:2038-01-17|before:end|required_with:end',
'end' => 'required|date|after:1970-01-02|before:2038-01-17|after:start|required_with:start',
'accounts' => 'required',
'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts',
];

View File

@@ -37,6 +37,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* Class CreateAutoBudgetLimits
@@ -59,7 +60,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
$newDate = clone $date;
$newDate->startOfDay();
$this->date = $newDate;
app('log')->debug(sprintf('Created new CreateAutoBudgetLimits("%s")', $this->date->format('Y-m-d')));
Log::debug(sprintf('Created new CreateAutoBudgetLimits("%s")', $this->date->format('Y-m-d')));
}
}
@@ -70,9 +71,9 @@ class CreateAutoBudgetLimits implements ShouldQueue
*/
public function handle(): void
{
app('log')->debug(sprintf('Now at start of CreateAutoBudgetLimits() job for %s.', $this->date->format('D d M Y')));
Log::debug(sprintf('Now at start of CreateAutoBudgetLimits() job for %s.', $this->date->format('D d M Y')));
$autoBudgets = AutoBudget::get();
app('log')->debug(sprintf('Found %d auto budgets.', $autoBudgets->count()));
Log::debug(sprintf('Found %d auto budgets.', $autoBudgets->count()));
foreach ($autoBudgets as $autoBudget) {
$this->handleAutoBudget($autoBudget);
}
@@ -84,18 +85,18 @@ class CreateAutoBudgetLimits implements ShouldQueue
private function handleAutoBudget(AutoBudget $autoBudget): void
{
if (null === $autoBudget->budget) {
app('log')->info(sprintf('Auto budget #%d is associated with a deleted budget.', $autoBudget->id));
Log::info(sprintf('Auto budget #%d is associated with a deleted budget.', $autoBudget->id));
$autoBudget->delete();
return;
}
if (false === $autoBudget->budget->active) {
app('log')->info(sprintf('Auto budget #%d is associated with an inactive budget.', $autoBudget->id));
Log::info(sprintf('Auto budget #%d is associated with an inactive budget.', $autoBudget->id));
return;
}
if (!$this->isMagicDay($autoBudget)) {
app('log')->info(
Log::info(
sprintf(
'Today (%s) is not a magic day for %s auto-budget #%d (part of budget #%d "%s")',
$this->date->format('Y-m-d'),
@@ -105,11 +106,11 @@ class CreateAutoBudgetLimits implements ShouldQueue
$autoBudget->budget->name
)
);
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
return;
}
app('log')->info(
Log::info(
sprintf(
'Today (%s) is a magic day for %s auto-budget #%d (part of budget #%d "%s")',
$this->date->format('Y-m-d'),
@@ -131,7 +132,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
// that's easy: create one.
// do nothing else.
$this->createBudgetLimit($autoBudget, $start, $end);
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
return;
}
@@ -139,18 +140,18 @@ class CreateAutoBudgetLimits implements ShouldQueue
if (!$budgetLimit instanceof BudgetLimit && AutoBudgetType::AUTO_BUDGET_ROLLOVER->value === (int) $autoBudget->auto_budget_type) {
// budget limit exists already,
$this->createRollover($autoBudget);
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
return;
}
if (!$budgetLimit instanceof BudgetLimit && AutoBudgetType::AUTO_BUDGET_ADJUSTED->value === (int) $autoBudget->auto_budget_type) {
// budget limit exists already,
$this->createAdjustedLimit($autoBudget);
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
return;
}
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
}
/**
@@ -193,7 +194,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
private function findBudgetLimit(Budget $budget, Carbon $start, Carbon $end): ?BudgetLimit
{
app('log')->debug(
Log::debug(
sprintf(
'Going to find a budget limit for budget #%d ("%s") between %s and %s',
$budget->id,
@@ -212,21 +213,21 @@ class CreateAutoBudgetLimits implements ShouldQueue
private function createBudgetLimit(AutoBudget $autoBudget, Carbon $start, Carbon $end, ?string $amount = null): void
{
app('log')->debug(sprintf('No budget limit exist. Must create one for auto-budget #%d', $autoBudget->id));
Log::debug(sprintf('No budget limit exist. Must create one for auto-budget #%d', $autoBudget->id));
if (null !== $amount) {
app('log')->debug(sprintf('Amount is overruled and will be set to %s', $amount));
Log::debug(sprintf('Amount is overruled and will be set to %s', $amount));
}
$budgetLimit = new BudgetLimit();
$budgetLimit->budget()->associate($autoBudget->budget);
$budgetLimit->transactionCurrency()->associate($autoBudget->transactionCurrency);
$budgetLimit->start_date = $start;
$budgetLimit->end_date = $end;
$budgetLimit->start_date = clone $start;
$budgetLimit->end_date = clone $end;
$budgetLimit->amount = $amount ?? $autoBudget->amount;
$budgetLimit->period = $autoBudget->period;
$budgetLimit->generated = 1;
$budgetLimit->save();
app('log')->debug(sprintf('Created budget limit #%d.', $budgetLimit->id));
Log::debug(sprintf('Created budget limit #%d.', $budgetLimit->id));
}
/**
@@ -234,7 +235,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
*/
private function createRollover(AutoBudget $autoBudget): void
{
app('log')->debug(sprintf('Will now manage rollover for auto budget #%d', $autoBudget->id));
Log::debug(sprintf('Will now manage rollover for auto budget #%d', $autoBudget->id));
// current period:
$start = app('navigation')->startOfPeriod($this->date, $autoBudget->period);
$end = app('navigation')->endOfPeriod($start, $autoBudget->period);
@@ -243,7 +244,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
$previousStart = app('navigation')->subtractPeriod($start, $autoBudget->period);
$previousEnd = app('navigation')->endOfPeriod($previousStart, $autoBudget->period);
app('log')->debug(
Log::debug(
sprintf(
'Current period is %s-%s, so previous period is %s-%s',
$start->format('Y-m-d'),
@@ -257,44 +258,44 @@ class CreateAutoBudgetLimits implements ShouldQueue
$budgetLimit = $this->findBudgetLimit($autoBudget->budget, $previousStart, $previousEnd);
if (!$budgetLimit instanceof BudgetLimit) {
app('log')->debug('No budget limit exists in previous period, so create one.');
Log::debug('No budget limit exists in previous period, so create one.');
// if not, create it and we're done.
$this->createBudgetLimit($autoBudget, $start, $end);
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
return;
}
app('log')->debug('Budget limit exists for previous period.');
Log::debug('Budget limit exists for previous period.');
// if has one, calculate expenses and use that as a base.
$repository = app(OperationsRepositoryInterface::class);
$repository->setUser($autoBudget->budget->user);
$spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection([$autoBudget->budget]), $autoBudget->transactionCurrency);
$currencyId = $autoBudget->transaction_currency_id;
$spentAmount = $spent[$currencyId]['sum'] ?? '0';
app('log')->debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount));
Log::debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount));
// if you spent more in previous budget period, than whatever you had previous budget period, the amount resets
// previous budget limit + spent
$budgetLeft = bcadd($budgetLimit->amount, $spentAmount);
$totalAmount = $autoBudget->amount;
app('log')->debug(sprintf('Total amount left for previous budget period is %s', $budgetLeft));
Log::debug(sprintf('Total amount left for previous budget period is %s', $budgetLeft));
if (-1 !== bccomp('0', $budgetLeft)) {
app('log')->info(sprintf('The amount left is negative, so it will be reset to %s.', $totalAmount));
Log::info(sprintf('The amount left is negative, so it will be reset to %s.', $totalAmount));
}
if (1 !== bccomp('0', $budgetLeft)) {
$totalAmount = bcadd($budgetLeft, $totalAmount);
app('log')->info(sprintf('The amount left is positive, so the new amount will be %s.', $totalAmount));
Log::info(sprintf('The amount left is positive, so the new amount will be %s.', $totalAmount));
}
// create budget limit:
$this->createBudgetLimit($autoBudget, $start, $end, $totalAmount);
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
}
private function createAdjustedLimit(AutoBudget $autoBudget): void
{
app('log')->debug(sprintf('Will now manage rollover for auto budget #%d', $autoBudget->id));
Log::debug(sprintf('Will now manage rollover for auto budget #%d', $autoBudget->id));
// current period:
$start = app('navigation')->startOfPeriod($this->date, $autoBudget->period);
$end = app('navigation')->endOfPeriod($start, $autoBudget->period);
@@ -303,7 +304,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
$previousStart = app('navigation')->subtractPeriod($start, $autoBudget->period);
$previousEnd = app('navigation')->endOfPeriod($previousStart, $autoBudget->period);
app('log')->debug(
Log::debug(
sprintf(
'Current period is %s-%s, so previous period is %s-%s',
$start->format('Y-m-d'),
@@ -317,13 +318,13 @@ class CreateAutoBudgetLimits implements ShouldQueue
$budgetLimit = $this->findBudgetLimit($autoBudget->budget, $previousStart, $previousEnd);
if (!$budgetLimit instanceof BudgetLimit) {
app('log')->debug('No budget limit exists in previous period, so create one.');
Log::debug('No budget limit exists in previous period, so create one.');
// if not, create standard amount, and we're done.
$this->createBudgetLimit($autoBudget, $start, $end);
return;
}
app('log')->debug('Budget limit exists for previous period.');
Log::debug('Budget limit exists for previous period.');
// if has one, calculate expenses and use that as a base.
$repository = app(OperationsRepositoryInterface::class);
@@ -331,31 +332,31 @@ class CreateAutoBudgetLimits implements ShouldQueue
$spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection([$autoBudget->budget]), $autoBudget->transactionCurrency);
$currencyId = $autoBudget->transaction_currency_id;
$spentAmount = $spent[$currencyId]['sum'] ?? '0';
app('log')->debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount));
Log::debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount));
// what you spent in previous period PLUS the amount for the current period,
// if that is more than zero, that's the amount that will be set.
$budgetAvailable = bcadd(bcadd($budgetLimit->amount, $autoBudget->amount), $spentAmount);
$totalAmount = $autoBudget->amount;
app('log')->debug(sprintf('Total amount available for current budget period is %s', $budgetAvailable));
Log::debug(sprintf('Total amount available for current budget period is %s', $budgetAvailable));
if (-1 !== bccomp($budgetAvailable, $totalAmount)) {
app('log')->info(sprintf('There is no overspending, no need to adjust. Budget limit amount will be %s.', $budgetAvailable));
Log::info(sprintf('There is no overspending, no need to adjust. Budget limit amount will be %s.', $budgetAvailable));
// create budget limit:
$this->createBudgetLimit($autoBudget, $start, $end, $budgetAvailable);
}
if (1 !== bccomp($budgetAvailable, $totalAmount) && 1 === bccomp($budgetAvailable, '0')) {
app('log')->info(sprintf('There was overspending, so the new amount will be %s.', $budgetAvailable));
Log::info(sprintf('There was overspending, so the new amount will be %s.', $budgetAvailable));
// create budget limit:
$this->createBudgetLimit($autoBudget, $start, $end, $budgetAvailable);
}
if (1 !== bccomp($budgetAvailable, $totalAmount) && -1 === bccomp($budgetAvailable, '0')) {
app('log')->info('There was overspending, but so much even this period cant fix that. Reset it to 1.');
Log::info('There was overspending, but so much even this period cant fix that. Reset it to 1.');
// create budget limit:
$this->createBudgetLimit($autoBudget, $start, $end, '1');
}
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
}
public function setDate(Carbon $date): void

View File

@@ -1,5 +1,26 @@
<?php
/*
* WebhookDelivery.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Models;

View File

@@ -1,5 +1,26 @@
<?php
/*
* WebhookResponse.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Models;

View File

@@ -1,5 +1,26 @@
<?php
/*
* WebhookTrigger.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Models;

View File

@@ -1,5 +1,26 @@
<?php
/*
* SubscriptionsOverdueReminder.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Notifications\User;

View File

@@ -35,6 +35,7 @@ use FireflyIII\Models\RecurrenceMeta;
use FireflyIII\Models\RecurrenceRepetition;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\RecurrenceTransactionMeta;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionJournalMeta;
use FireflyIII\Services\Internal\Destroy\RecurrenceDestroyService;
@@ -582,4 +583,17 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte
return $service->update($recurrence, $data);
}
public function markGroupsAsNow(Collection $groups): void
{
/** @var TransactionGroup $group */
foreach ($groups as $group) {
/** @var TransactionJournal $journal */
foreach ($group->transactionJournals as $journal) {
Log::debug(sprintf('Set date of journal #%d to today!', $journal->id));
$journal->date = now(config('app.timezone'));
$journal->save();
}
}
}
}

View File

@@ -139,6 +139,8 @@ interface RecurringRepositoryInterface
*/
public function getXOccurrencesSince(RecurrenceRepetition $repetition, Carbon $date, Carbon $afterDate, int $count): array;
public function markGroupsAsNow(Collection $groups): void;
/**
* Parse the repetition in a string that is user readable.
*/

View File

@@ -52,6 +52,7 @@ class EitherConfigKey
'firefly.languages',
'app.timezone',
'firefly.valid_view_ranges',
'firefly.preselected_accounts',
// triggers and actions:
'firefly.rule-actions',

View File

@@ -103,7 +103,7 @@ class ExchangeRateConverter
// find in cache
if (null !== $res) {
Log::debug(sprintf('ExchangeRateConverter: Return cached rate from %s to %s on %s.', $from->code, $to->code, $date->format('Y-m-d')));
Log::debug(sprintf('ExchangeRateConverter: Return cached rate (%s) from %s to %s on %s.', $res, $from->code, $to->code, $date->format('Y-m-d')));
return $res;
}

View File

@@ -234,9 +234,9 @@ class AccountEnrichment implements EnrichmentInterface
private function appendCollectedData(): void
{
$this->collection = $this->collection->map(function (Account $item) {
$id = (int)$item->id;
$item->full_account_type = $this->accountTypes[(int)$item->account_type_id] ?? null;
$meta = [
$id = (int)$item->id;
$item->full_account_type = $this->accountTypes[(int)$item->account_type_id] ?? null;
$meta = [
'currency' => null,
'location' => [
'latitude' => null,
@@ -249,14 +249,14 @@ class AccountEnrichment implements EnrichmentInterface
'opening_balance_date' => null,
'opening_balance_amount' => null,
'account_number' => null,
'notes' => $notes[$id] ?? null,
'notes' => $this->notes[$id] ?? null,
'last_activity' => $this->lastActivities[$id] ?? null,
];
// add object group if available
if (array_key_exists($id, $this->mappedObjects)) {
$key = $this->mappedObjects[$id];
$meta['object_group_id'] = $this->objectGroups[$key]['id'];
$meta['object_group_id'] = (string) $this->objectGroups[$key]['id'];
$meta['object_group_title'] = $this->objectGroups[$key]['title'];
$meta['object_group_order'] = $this->objectGroups[$key]['order'];
}
@@ -283,28 +283,28 @@ class AccountEnrichment implements EnrichmentInterface
// add balances
// get currencies:
$currency = $this->primaryCurrency; // assume primary currency
$currency = $this->primaryCurrency; // assume primary currency
if (null !== $meta['currency']) {
$currency = $meta['currency'];
}
// get the current balance:
$date = $this->getDate();
$date = $this->getDate();
// $finalBalance = Steam::finalAccountBalance($item, $date, $this->primaryCurrency, $this->convertToPrimary);
$finalBalance = $this->balances[$id];
$finalBalance = $this->balances[$id];
Log::debug(sprintf('Call finalAccountBalance(%s) with date/time "%s"', var_export($this->convertToPrimary, true), $date->toIso8601String()), $finalBalance);
// collect current balances:
$currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places);
$openingBalance = Steam::bcround($meta['opening_balance_amount'] ?? '0', $currency->decimal_places);
$virtualBalance = Steam::bcround($account->virtual_balance ?? '0', $currency->decimal_places);
$debtAmount = $meta['current_debt'] ?? null;
$currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places);
$openingBalance = Steam::bcround($meta['opening_balance_amount'] ?? '0', $currency->decimal_places);
$virtualBalance = Steam::bcround($account->virtual_balance ?? '0', $currency->decimal_places);
$debtAmount = $meta['current_debt'] ?? null;
// set some pc_ default values to NULL:
$pcCurrentBalance = null;
$pcOpeningBalance = null;
$pcVirtualBalance = null;
$pcDebtAmount = null;
$pcCurrentBalance = null;
$pcOpeningBalance = null;
$pcVirtualBalance = null;
$pcDebtAmount = null;
// convert to primary currency if needed:
if ($this->convertToPrimary && $currency->id !== $this->primaryCurrency->id) {
@@ -327,7 +327,8 @@ class AccountEnrichment implements EnrichmentInterface
$openingBalance = null;
$pcOpeningBalance = null;
}
$meta['balances'] = [
$meta['current_balance_date'] = $this->getDate();
$meta['balances'] = [
'current_balance' => $currentBalance,
'pc_current_balance' => $pcCurrentBalance,
'opening_balance' => $openingBalance,
@@ -338,7 +339,7 @@ class AccountEnrichment implements EnrichmentInterface
'pc_debt_amount' => $pcDebtAmount,
];
// end add balances
$item->meta = $meta;
$item->meta = $meta;
return $item;
});
@@ -378,13 +379,17 @@ class AccountEnrichment implements EnrichmentInterface
public function setDate(?Carbon $date): void
{
if (null !== $date) {
$date->endOfDay();
Log::debug(sprintf('Date is now %s', $date->toW3cString()));
}
$this->date = $date;
}
public function getDate(): Carbon
{
if (null === $this->date) {
return today();
return now();
}
return $this->date;

View File

@@ -1,5 +1,26 @@
<?php
/*
* BudgetEnrichment.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments;
@@ -117,7 +138,7 @@ class BudgetEnrichment implements EnrichmentInterface
// add object group if available
if (array_key_exists($id, $this->mappedObjects)) {
$key = $this->mappedObjects[$id];
$meta['object_group_id'] = $this->objectGroups[$key]['id'];
$meta['object_group_id'] = (string) $this->objectGroups[$key]['id'];
$meta['object_group_title'] = $this->objectGroups[$key]['title'];
$meta['object_group_order'] = $this->objectGroups[$key]['order'];
}

View File

@@ -1,5 +1,26 @@
<?php
/*
* BudgetLimitEnrichment.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments;
@@ -135,6 +156,7 @@ class BudgetLimitEnrichment implements EnrichmentInterface
/** @var BudgetLimit $budgetLimit */
foreach ($this->collection as $budgetLimit) {
$id = (int)$budgetLimit->id;
$filteredExpenses = $this->filterToBudget($expenses, $budgetLimit->budget_id);
$filteredExpenses = $repository->sumCollectedExpenses($expenses, $budgetLimit->start_date, $budgetLimit->end_date, $budgetLimit->transactionCurrency, false);
$this->expenses[$id] = array_values($filteredExpenses);
@@ -175,4 +197,11 @@ class BudgetLimitEnrichment implements EnrichmentInterface
}, $first);
}, $this->expenses);
}
private function filterToBudget(array $expenses, int $budget): array
{
return array_filter($expenses, function (array $item) use ($budget) {
return (int)$item['budget_id'] === $budget;
});
}
}

View File

@@ -1,5 +1,26 @@
<?php
/*
* CategoryEnrichment.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments;

View File

@@ -1,5 +1,26 @@
<?php
/*
* PiggyBankEnrichment.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments;
@@ -105,7 +126,7 @@ class PiggyBankEnrichment implements EnrichmentInterface
'pc_current_amount' => '0',
];
}
$this->amounts[$id][$accountId]['current_amount'] = bcadd($this->amounts[$id][$accountId]['current_amount'], $item->current_amount);
$this->amounts[$id][$accountId]['current_amount'] = bcadd($this->amounts[$id][$accountId]['current_amount'], (string) $item->current_amount);
if (null !== $this->amounts[$id][$accountId]['pc_current_amount'] && null !== $item->native_current_amount) {
$this->amounts[$id][$accountId]['pc_current_amount'] = bcadd($this->amounts[$id][$accountId]['pc_current_amount'], $item->native_current_amount);
}

View File

@@ -1,5 +1,26 @@
<?php
/*
* PiggyBankEventEnrichment.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments;

View File

@@ -1,5 +1,26 @@
<?php
/*
* RecurringEnrichment.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments;
@@ -38,7 +59,7 @@ class RecurringEnrichment implements EnrichmentInterface
private Collection $collection;
private array $ids = [];
private array $transactionTypeIds = [];
private array $transactionTypes = [];
// private array $transactionTypes = [];
private array $notes = [];
private array $repetitions = [];
private array $transactions = [];
@@ -107,14 +128,14 @@ class RecurringEnrichment implements EnrichmentInterface
$this->ids[] = $id;
$this->transactionTypeIds[$id] = $typeId;
}
$this->ids = array_unique($this->ids);
$this->ids = array_unique($this->ids);
// collect transaction types.
$transactionTypes = TransactionType::whereIn('id', array_unique($this->transactionTypeIds))->get();
foreach ($transactionTypes as $transactionType) {
$id = (int)$transactionType->id;
$this->transactionTypes[$id] = TransactionTypeEnum::from($transactionType->type);
}
// $transactionTypes = TransactionType::whereIn('id', array_unique($this->transactionTypeIds))->get();
// foreach ($transactionTypes as $transactionType) {
// $id = (int)$transactionType->id;
// $this->transactionTypes[$id] = TransactionTypeEnum::from($transactionType->type);
// }
}
private function collectRepetitions(): void
@@ -379,7 +400,7 @@ class RecurringEnrichment implements EnrichmentInterface
private function collectTransactionMetaData(): void
{
$ids = array_keys($this->transactions);
$meta = RecurrenceTransactionMeta::whereIn('rt_id', $ids)->get();
$meta = RecurrenceTransactionMeta::whereNull('deleted_at')->whereIn('rt_id', $ids)->get();
// other meta-data to be collected:
$billIds = [];
$piggyBankIds = [];
@@ -389,8 +410,15 @@ class RecurringEnrichment implements EnrichmentInterface
foreach ($meta as $entry) {
$id = (int)$entry->id;
$transactionId = (int)$entry->rt_id;
$recurrenceId = $this->recurrenceIds[$transactionId];
$name = (string)$entry->name;
// this should refer to another array, were rtIds can be used to find the recurrence.
$recurrenceId = $this->recurrenceIds[$transactionId] ?? 0;
$name = (string)$entry->name ?? '';
if (0 === $recurrenceId) {
Log::error(sprintf('Could not find recurrence ID for recurrence transaction ID %d', $transactionId));
continue;
}
switch ($name) {
default:

View File

@@ -1,5 +1,26 @@
<?php
/*
* SubscriptionEnrichment.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments;
@@ -101,7 +122,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
// add object group if available
if (array_key_exists($id, $this->mappedObjects)) {
$key = $this->mappedObjects[$id];
$meta['object_group_id'] = $objectGroups[$key]['id'];
$meta['object_group_id'] = (string) $objectGroups[$key]['id'];
$meta['object_group_title'] = $objectGroups[$key]['title'];
$meta['object_group_order'] = $objectGroups[$key]['order'];
}
@@ -362,7 +383,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
Log::debug(sprintf('[b] Last paid date is: %s', $return->format('Y-m-d')));
}
}
Log::debug(sprintf('[c] Last paid date is: "%s"', $return?->format('Y-m-d')));
// Log::debug(sprintf('[c] Last paid date is: "%s"', $return?->format('Y-m-d')));
return $return;
}

View File

@@ -143,9 +143,9 @@ class TransactionGroupEnrichment implements EnrichmentInterface
continue;
}
if (in_array($name, $this->dateFields, true)) {
Log::debug(sprintf('Meta data for "%s" is a date : "%s"', $name, $data));
// Log::debug(sprintf('Meta data for "%s" is a date : "%s"', $name, $data));
$this->metaData[$entry['transaction_journal_id']][$name] = Carbon::parse($data, config('app.timezone'));
Log::debug(sprintf('Meta data for "%s" converts to: "%s"', $name, $this->metaData[$entry['transaction_journal_id']][$name]->toW3CString()));
// Log::debug(sprintf('Meta data for "%s" converts to: "%s"', $name, $this->metaData[$entry['transaction_journal_id']][$name]->toW3CString()));
continue;
}

View File

@@ -1,5 +1,26 @@
<?php
/*
* WebhookEnrichment.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments;

View File

@@ -1,5 +1,26 @@
<?php
/*
* RecalculatesAvailableBudgetsTrait.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Support\Observers;

View File

@@ -1,5 +1,26 @@
<?php
/*
* ValidatesWebhooks.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Support\Request;

View File

@@ -1,5 +1,26 @@
<?php
/*
* PreferencesSingleton.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Support\Singleton;

View File

@@ -224,6 +224,7 @@ class Steam
*/
$request = clone $start;
$request->subDay()->endOfDay();
Log::debug('Get first balance to start.');
Log::debug(sprintf('finalAccountBalanceInRange: Call finalAccountBalance with date/time "%s"', $request->toIso8601String()));
$startBalance = $this->finalAccountBalance($account, $request);
$primaryCurrency = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup);
@@ -315,7 +316,7 @@ class Steam
Log::debug(sprintf('Updated entry [%s]', $carbonKey), $currentBalance);
}
$cache->store($balances);
Log::debug('End of method');
Log::debug('End of method finalAccountBalanceInRange');
return $balances;
}

View File

@@ -78,10 +78,6 @@ class AccountTransformer extends AbstractTransformer
$zoomLevel = $account->meta['location']['zoom_level'] ?? null;
$order = $account->order;
// date (for balance etc.)
$date = $this->getDate();
$date->endOfDay();
// get primary currency as fallback:
$currency = $this->primary; // assume primary currency
if ($hasCurrencySettings) {
@@ -141,7 +137,7 @@ class AccountTransformer extends AbstractTransformer
'debt_amount' => $account->meta['balances']['debt_amount'],
'pc_debt_amount' => $account->meta['balances']['pc_debt_amount'],
'current_balance_date' => $date->toAtomString(),
'current_balance_date' => $account->meta['current_balance_date']->toAtomString(),
'notes' => $account->meta['notes'] ?? null,
'monthly_payment_date' => $monthlyPaymentDate,
'credit_card_type' => $creditCardType,

View File

@@ -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/).
## 6.4.0 - 2025-09-01
### Added
- [Issue 5532](https://github.com/firefly-iii/firefly-iii/issues/5532) (Asset prices and exchange rates) reported by @svozniuk
- [Discussion 10725](https://github.com/orgs/firefly-iii/discussions/10725) (New webhook triggers) started by @Billos. See the [documentation](https://docs.firefly-iii.org/how-to/firefly-iii/features/webhooks/).
### Changed
- Initial release.
### Fixed
- [Issue 10790](https://github.com/firefly-iii/firefly-iii/issues/10790) (Undefined variable $occurrences) reported by @senna1992
- [Issue 10791](https://github.com/firefly-iii/firefly-iii/issues/10791) (Clone and edit a transaction with a different currency doesn't clear the foreign transaction amount) reported by @jxtxzzw
- [Issue 10794](https://github.com/firefly-iii/firefly-iii/issues/10794) (Error with recurring transaction) reported by @MaximSN
- [Issue 10799](https://github.com/firefly-iii/firefly-iii/issues/10799) (Budget - "Left (per day)" not showing the correct value) reported by @GensHaze
- [Issue 10802](https://github.com/firefly-iii/firefly-iii/issues/10802) (Crash when trying to update a budget limit) reported by @Billos
- [Issue 10803](https://github.com/firefly-iii/firefly-iii/issues/10803) (Issue in /v1/budget-limits spent attribute) reported by @Billos
- [Issue 10804](https://github.com/firefly-iii/firefly-iii/issues/10804) (No notes information included in the "List all accounts" API call) reported by @gpampuro
- [Issue 10808](https://github.com/firefly-iii/firefly-iii/issues/10808) (cron job Error: Undefined variable $preference) reported by @MexerSam
- [Issue 10813](https://github.com/firefly-iii/firefly-iii/issues/10813) (Error "Argument #2 ($symbol) must be of type string" while try open subscriptions section) reported by @mrResident
- [Issue 10819](https://github.com/firefly-iii/firefly-iii/issues/10819) (Internal Server Error when trying to open piggy banks) reported by @noantiq
- [Issue 10820](https://github.com/firefly-iii/firefly-iii/issues/10820) (Unable to search date 1970-01-01 to apply rule.) reported by @Kage1
- [Issue 10824](https://github.com/firefly-iii/firefly-iii/issues/10824) (Converting withdrawal to transfer to account in different currency doesn't allow setting correct currencies) reported by @avee87
### API
- [Issue 8345](https://github.com/firefly-iii/firefly-iii/issues/8345) (API: Distinguish spent & earned at `/v2/chart/category/dashboard` (or future `v2/categories`)) reported by @dreautall
- [Issue 10804](https://github.com/firefly-iii/firefly-iii/issues/10804) (No notes information included in the "List all accounts" API call) reported by @gpampuro
- [Issue 10806](https://github.com/firefly-iii/firefly-iii/issues/10806) (API: `/v1/chart/balance/balance` has undocumented `period` parameter) reported by @dreautall
- [Issue 10807](https://github.com/firefly-iii/firefly-iii/issues/10807) (API: `/v1/bills` field `object_group_id` returns int, should be string) reported by @dreautall
- [Issue 10815](https://github.com/firefly-iii/firefly-iii/issues/10815) (API: `/v1/accounts` balance is off by a day) reported by @dreautall
- [Issue 10827](https://github.com/firefly-iii/firefly-iii/issues/10827) (Trigger Recurrence by API) reported by @MexerSam
## 6.3.2 - 2025-08-20
### Fixed

324
composer.lock generated
View File

@@ -1244,22 +1244,22 @@
},
{
"name": "guzzlehttp/guzzle",
"version": "7.9.3",
"version": "7.10.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77"
"reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77",
"reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4",
"reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/promises": "^1.5.3 || ^2.0.3",
"guzzlehttp/psr7": "^2.7.0",
"guzzlehttp/promises": "^2.3",
"guzzlehttp/psr7": "^2.8",
"php": "^7.2.5 || ^8.0",
"psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.2 || ^3.0"
@@ -1350,7 +1350,7 @@
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/7.9.3"
"source": "https://github.com/guzzle/guzzle/tree/7.10.0"
},
"funding": [
{
@@ -1366,20 +1366,20 @@
"type": "tidelift"
}
],
"time": "2025-03-27T13:37:11+00:00"
"time": "2025-08-23T22:36:01+00:00"
},
{
"name": "guzzlehttp/promises",
"version": "2.2.0",
"version": "2.3.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c"
"reference": "481557b130ef3790cf82b713667b43030dc9c957"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c",
"reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c",
"url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957",
"reference": "481557b130ef3790cf82b713667b43030dc9c957",
"shasum": ""
},
"require": {
@@ -1387,7 +1387,7 @@
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
"phpunit/phpunit": "^8.5.44 || ^9.6.25"
},
"type": "library",
"extra": {
@@ -1433,7 +1433,7 @@
],
"support": {
"issues": "https://github.com/guzzle/promises/issues",
"source": "https://github.com/guzzle/promises/tree/2.2.0"
"source": "https://github.com/guzzle/promises/tree/2.3.0"
},
"funding": [
{
@@ -1449,20 +1449,20 @@
"type": "tidelift"
}
],
"time": "2025-03-27T13:27:01+00:00"
"time": "2025-08-22T14:34:08+00:00"
},
{
"name": "guzzlehttp/psr7",
"version": "2.7.1",
"version": "2.8.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16"
"reference": "21dc724a0583619cd1652f673303492272778051"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16",
"reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051",
"reference": "21dc724a0583619cd1652f673303492272778051",
"shasum": ""
},
"require": {
@@ -1478,7 +1478,7 @@
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"http-interop/http-factory-tests": "0.9.0",
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
"phpunit/phpunit": "^8.5.44 || ^9.6.25"
},
"suggest": {
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
@@ -1549,7 +1549,7 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/2.7.1"
"source": "https://github.com/guzzle/psr7/tree/2.8.0"
},
"funding": [
{
@@ -1565,20 +1565,20 @@
"type": "tidelift"
}
],
"time": "2025-03-27T12:30:47+00:00"
"time": "2025-08-23T21:21:41+00:00"
},
{
"name": "guzzlehttp/uri-template",
"version": "v1.0.4",
"version": "v1.0.5",
"source": {
"type": "git",
"url": "https://github.com/guzzle/uri-template.git",
"reference": "30e286560c137526eccd4ce21b2de477ab0676d2"
"reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/uri-template/zipball/30e286560c137526eccd4ce21b2de477ab0676d2",
"reference": "30e286560c137526eccd4ce21b2de477ab0676d2",
"url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1",
"reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1",
"shasum": ""
},
"require": {
@@ -1587,7 +1587,7 @@
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"phpunit/phpunit": "^8.5.36 || ^9.6.15",
"phpunit/phpunit": "^8.5.44 || ^9.6.25",
"uri-template/tests": "1.0.0"
},
"type": "library",
@@ -1635,7 +1635,7 @@
],
"support": {
"issues": "https://github.com/guzzle/uri-template/issues",
"source": "https://github.com/guzzle/uri-template/tree/v1.0.4"
"source": "https://github.com/guzzle/uri-template/tree/v1.0.5"
},
"funding": [
{
@@ -1651,7 +1651,7 @@
"type": "tidelift"
}
],
"time": "2025-02-03T10:55:03+00:00"
"time": "2025-08-22T14:27:06+00:00"
},
{
"name": "jc5/google2fa-laravel",
@@ -1878,16 +1878,16 @@
},
{
"name": "laravel/framework",
"version": "v12.25.0",
"version": "v12.26.4",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "2ee2ba94ae60efd24c7a787cbb1a2f82f714bb20"
"reference": "085a367a32ba86fcfa647bfc796098ae6f795b09"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/2ee2ba94ae60efd24c7a787cbb1a2f82f714bb20",
"reference": "2ee2ba94ae60efd24c7a787cbb1a2f82f714bb20",
"url": "https://api.github.com/repos/laravel/framework/zipball/085a367a32ba86fcfa647bfc796098ae6f795b09",
"reference": "085a367a32ba86fcfa647bfc796098ae6f795b09",
"shasum": ""
},
"require": {
@@ -1927,9 +1927,9 @@
"symfony/http-kernel": "^7.2.0",
"symfony/mailer": "^7.2.0",
"symfony/mime": "^7.2.0",
"symfony/polyfill-php83": "^1.31",
"symfony/polyfill-php84": "^1.31",
"symfony/polyfill-php85": "^1.31",
"symfony/polyfill-php83": "^1.33",
"symfony/polyfill-php84": "^1.33",
"symfony/polyfill-php85": "^1.33",
"symfony/process": "^7.2.0",
"symfony/routing": "^7.2.0",
"symfony/uid": "^7.2.0",
@@ -1997,7 +1997,7 @@
"league/flysystem-read-only": "^3.25.1",
"league/flysystem-sftp-v3": "^3.25.1",
"mockery/mockery": "^1.6.10",
"orchestra/testbench-core": "^10.6.0",
"orchestra/testbench-core": "^10.6.3",
"pda/pheanstalk": "^5.0.6|^7.0.0",
"php-http/discovery": "^1.15",
"phpstan/phpstan": "^2.0",
@@ -2091,7 +2091,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2025-08-18T22:20:52+00:00"
"time": "2025-08-29T14:15:53+00:00"
},
{
"name": "laravel/passport",
@@ -5827,23 +5827,23 @@
},
{
"name": "rcrowe/twigbridge",
"version": "v0.14.5",
"version": "v0.14.6",
"source": {
"type": "git",
"url": "https://github.com/rcrowe/TwigBridge.git",
"reference": "88c83c9658a2c029c64ec80dd8a15d5a67433ac4"
"reference": "0798ee4b5e5b943d0200850acaa87ccd82e2fe45"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/88c83c9658a2c029c64ec80dd8a15d5a67433ac4",
"reference": "88c83c9658a2c029c64ec80dd8a15d5a67433ac4",
"url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/0798ee4b5e5b943d0200850acaa87ccd82e2fe45",
"reference": "0798ee4b5e5b943d0200850acaa87ccd82e2fe45",
"shasum": ""
},
"require": {
"illuminate/support": "^9|^10|^11|^12",
"illuminate/view": "^9|^10|^11|^12",
"php": "^8.1",
"twig/twig": "~3.12"
"twig/twig": "~3.21"
},
"require-dev": {
"ext-json": "*",
@@ -5893,22 +5893,22 @@
],
"support": {
"issues": "https://github.com/rcrowe/TwigBridge/issues",
"source": "https://github.com/rcrowe/TwigBridge/tree/v0.14.5"
"source": "https://github.com/rcrowe/TwigBridge/tree/v0.14.6"
},
"time": "2025-04-18T18:48:57+00:00"
"time": "2025-08-20T11:25:49+00:00"
},
{
"name": "spatie/backtrace",
"version": "1.7.4",
"version": "1.8.1",
"source": {
"type": "git",
"url": "https://github.com/spatie/backtrace.git",
"reference": "cd37a49fce7137359ac30ecc44ef3e16404cccbe"
"reference": "8c0f16a59ae35ec8c62d85c3c17585158f430110"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/backtrace/zipball/cd37a49fce7137359ac30ecc44ef3e16404cccbe",
"reference": "cd37a49fce7137359ac30ecc44ef3e16404cccbe",
"url": "https://api.github.com/repos/spatie/backtrace/zipball/8c0f16a59ae35ec8c62d85c3c17585158f430110",
"reference": "8c0f16a59ae35ec8c62d85c3c17585158f430110",
"shasum": ""
},
"require": {
@@ -5946,7 +5946,8 @@
"spatie"
],
"support": {
"source": "https://github.com/spatie/backtrace/tree/1.7.4"
"issues": "https://github.com/spatie/backtrace/issues",
"source": "https://github.com/spatie/backtrace/tree/1.8.1"
},
"funding": [
{
@@ -5958,7 +5959,7 @@
"type": "other"
}
],
"time": "2025-05-08T15:41:09+00:00"
"time": "2025-08-26T08:22:30+00:00"
},
{
"name": "spatie/error-solutions",
@@ -6663,16 +6664,16 @@
},
{
"name": "symfony/console",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "5f360ebc65c55265a74d23d7fe27f957870158a1"
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1",
"reference": "5f360ebc65c55265a74d23d7fe27f957870158a1",
"url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
"shasum": ""
},
"require": {
@@ -6737,7 +6738,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.3.2"
"source": "https://github.com/symfony/console/tree/v7.3.3"
},
"funding": [
{
@@ -6757,7 +6758,7 @@
"type": "tidelift"
}
],
"time": "2025-07-30T17:13:41+00:00"
"time": "2025-08-25T06:35:40+00:00"
},
{
"name": "symfony/css-selector",
@@ -6974,16 +6975,16 @@
},
{
"name": "symfony/event-dispatcher",
"version": "v7.3.0",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "497f73ac996a598c92409b44ac43b6690c4f666d"
"reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d",
"reference": "497f73ac996a598c92409b44ac43b6690c4f666d",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191",
"reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191",
"shasum": ""
},
"require": {
@@ -7034,7 +7035,7 @@
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/event-dispatcher/tree/v7.3.0"
"source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3"
},
"funding": [
{
@@ -7045,12 +7046,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-04-22T09:11:45+00:00"
"time": "2025-08-13T11:49:31+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
@@ -7266,16 +7271,16 @@
},
{
"name": "symfony/http-client",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
"reference": "1c064a0c67749923483216b081066642751cc2c7"
"reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client/zipball/1c064a0c67749923483216b081066642751cc2c7",
"reference": "1c064a0c67749923483216b081066642751cc2c7",
"url": "https://api.github.com/repos/symfony/http-client/zipball/333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019",
"reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019",
"shasum": ""
},
"require": {
@@ -7283,6 +7288,7 @@
"psr/log": "^1|^2|^3",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/http-client-contracts": "~3.4.4|^3.5.2",
"symfony/polyfill-php83": "^1.29",
"symfony/service-contracts": "^2.5|^3"
},
"conflict": {
@@ -7341,7 +7347,7 @@
"http"
],
"support": {
"source": "https://github.com/symfony/http-client/tree/v7.3.2"
"source": "https://github.com/symfony/http-client/tree/v7.3.3"
},
"funding": [
{
@@ -7361,7 +7367,7 @@
"type": "tidelift"
}
],
"time": "2025-07-15T11:36:08+00:00"
"time": "2025-08-27T07:45:05+00:00"
},
{
"name": "symfony/http-client-contracts",
@@ -7443,16 +7449,16 @@
},
{
"name": "symfony/http-foundation",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6"
"reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/6877c122b3a6cc3695849622720054f6e6fa5fa6",
"reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/7475561ec27020196c49bb7c4f178d33d7d3dc00",
"reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00",
"shasum": ""
},
"require": {
@@ -7502,7 +7508,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v7.3.2"
"source": "https://github.com/symfony/http-foundation/tree/v7.3.3"
},
"funding": [
{
@@ -7522,20 +7528,20 @@
"type": "tidelift"
}
],
"time": "2025-07-10T08:47:49+00:00"
"time": "2025-08-20T08:04:18+00:00"
},
{
"name": "symfony/http-kernel",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "6ecc895559ec0097e221ed2fd5eb44d5fede083c"
"reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/6ecc895559ec0097e221ed2fd5eb44d5fede083c",
"reference": "6ecc895559ec0097e221ed2fd5eb44d5fede083c",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/72c304de37e1a1cec6d5d12b81187ebd4850a17b",
"reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b",
"shasum": ""
},
"require": {
@@ -7620,7 +7626,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-kernel/tree/v7.3.2"
"source": "https://github.com/symfony/http-kernel/tree/v7.3.3"
},
"funding": [
{
@@ -7640,20 +7646,20 @@
"type": "tidelift"
}
],
"time": "2025-07-31T10:45:04+00:00"
"time": "2025-08-29T08:23:45+00:00"
},
{
"name": "symfony/mailer",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/mailer.git",
"reference": "d43e84d9522345f96ad6283d5dfccc8c1cfc299b"
"reference": "a32f3f45f1990db8c4341d5122a7d3a381c7e575"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mailer/zipball/d43e84d9522345f96ad6283d5dfccc8c1cfc299b",
"reference": "d43e84d9522345f96ad6283d5dfccc8c1cfc299b",
"url": "https://api.github.com/repos/symfony/mailer/zipball/a32f3f45f1990db8c4341d5122a7d3a381c7e575",
"reference": "a32f3f45f1990db8c4341d5122a7d3a381c7e575",
"shasum": ""
},
"require": {
@@ -7704,7 +7710,7 @@
"description": "Helps sending emails",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/mailer/tree/v7.3.2"
"source": "https://github.com/symfony/mailer/tree/v7.3.3"
},
"funding": [
{
@@ -7724,7 +7730,7 @@
"type": "tidelift"
}
],
"time": "2025-07-15T11:36:08+00:00"
"time": "2025-08-13T11:49:31+00:00"
},
{
"name": "symfony/mailgun-mailer",
@@ -7885,16 +7891,16 @@
},
{
"name": "symfony/options-resolver",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37"
"reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37",
"reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d",
"reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d",
"shasum": ""
},
"require": {
@@ -7932,7 +7938,7 @@
"options"
],
"support": {
"source": "https://github.com/symfony/options-resolver/tree/v7.3.2"
"source": "https://github.com/symfony/options-resolver/tree/v7.3.3"
},
"funding": [
{
@@ -7952,7 +7958,7 @@
"type": "tidelift"
}
],
"time": "2025-07-15T11:36:08+00:00"
"time": "2025-08-05T10:16:07+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -8785,16 +8791,16 @@
},
{
"name": "symfony/process",
"version": "v7.3.0",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af"
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af",
"reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af",
"url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1",
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1",
"shasum": ""
},
"require": {
@@ -8826,7 +8832,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v7.3.0"
"source": "https://github.com/symfony/process/tree/v7.3.3"
},
"funding": [
{
@@ -8837,12 +8843,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-04-17T09:11:12+00:00"
"time": "2025-08-18T09:42:54+00:00"
},
{
"name": "symfony/psr-http-message-bridge",
@@ -9097,16 +9107,16 @@
},
{
"name": "symfony/string",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "42f505aff654e62ac7ac2ce21033818297ca89ca"
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca",
"reference": "42f505aff654e62ac7ac2ce21033818297ca89ca",
"url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
"shasum": ""
},
"require": {
@@ -9164,7 +9174,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v7.3.2"
"source": "https://github.com/symfony/string/tree/v7.3.3"
},
"funding": [
{
@@ -9184,20 +9194,20 @@
"type": "tidelift"
}
],
"time": "2025-07-10T08:47:49+00:00"
"time": "2025-08-25T06:35:40+00:00"
},
{
"name": "symfony/translation",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
"reference": "81b48f4daa96272efcce9c7a6c4b58e629df3c90"
"reference": "e0837b4cbcef63c754d89a4806575cada743a38d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/81b48f4daa96272efcce9c7a6c4b58e629df3c90",
"reference": "81b48f4daa96272efcce9c7a6c4b58e629df3c90",
"url": "https://api.github.com/repos/symfony/translation/zipball/e0837b4cbcef63c754d89a4806575cada743a38d",
"reference": "e0837b4cbcef63c754d89a4806575cada743a38d",
"shasum": ""
},
"require": {
@@ -9264,7 +9274,7 @@
"description": "Provides tools to internationalize your application",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/translation/tree/v7.3.2"
"source": "https://github.com/symfony/translation/tree/v7.3.3"
},
"funding": [
{
@@ -9284,7 +9294,7 @@
"type": "tidelift"
}
],
"time": "2025-07-30T17:31:46+00:00"
"time": "2025-08-01T21:02:37+00:00"
},
{
"name": "symfony/translation-contracts",
@@ -9440,16 +9450,16 @@
},
{
"name": "symfony/var-dumper",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "53205bea27450dc5c65377518b3275e126d45e75"
"reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/53205bea27450dc5c65377518b3275e126d45e75",
"reference": "53205bea27450dc5c65377518b3275e126d45e75",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/34d8d4c4b9597347306d1ec8eb4e1319b1e6986f",
"reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f",
"shasum": ""
},
"require": {
@@ -9503,7 +9513,7 @@
"dump"
],
"support": {
"source": "https://github.com/symfony/var-dumper/tree/v7.3.2"
"source": "https://github.com/symfony/var-dumper/tree/v7.3.3"
},
"funding": [
{
@@ -9523,20 +9533,20 @@
"type": "tidelift"
}
],
"time": "2025-07-29T20:02:46+00:00"
"time": "2025-08-13T11:49:31+00:00"
},
{
"name": "symfony/var-exporter",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-exporter.git",
"reference": "05b3e90654c097817325d6abd284f7938b05f467"
"reference": "d4dfcd2a822cbedd7612eb6fbd260e46f87b7137"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/05b3e90654c097817325d6abd284f7938b05f467",
"reference": "05b3e90654c097817325d6abd284f7938b05f467",
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/d4dfcd2a822cbedd7612eb6fbd260e46f87b7137",
"reference": "d4dfcd2a822cbedd7612eb6fbd260e46f87b7137",
"shasum": ""
},
"require": {
@@ -9584,7 +9594,7 @@
"serialize"
],
"support": {
"source": "https://github.com/symfony/var-exporter/tree/v7.3.2"
"source": "https://github.com/symfony/var-exporter/tree/v7.3.3"
},
"funding": [
{
@@ -9604,7 +9614,7 @@
"type": "tidelift"
}
],
"time": "2025-07-10T08:47:49+00:00"
"time": "2025-08-18T13:10:53+00:00"
},
{
"name": "thecodingmachine/safe",
@@ -10726,16 +10736,16 @@
},
{
"name": "larastan/larastan",
"version": "v3.6.0",
"version": "v3.6.1",
"source": {
"type": "git",
"url": "https://github.com/larastan/larastan.git",
"reference": "6431d010dd383a9279eb8874a76ddb571738564a"
"reference": "3c223047e374befd1b64959784685d6ecccf66aa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/larastan/larastan/zipball/6431d010dd383a9279eb8874a76ddb571738564a",
"reference": "6431d010dd383a9279eb8874a76ddb571738564a",
"url": "https://api.github.com/repos/larastan/larastan/zipball/3c223047e374befd1b64959784685d6ecccf66aa",
"reference": "3c223047e374befd1b64959784685d6ecccf66aa",
"shasum": ""
},
"require": {
@@ -10803,7 +10813,7 @@
],
"support": {
"issues": "https://github.com/larastan/larastan/issues",
"source": "https://github.com/larastan/larastan/tree/v3.6.0"
"source": "https://github.com/larastan/larastan/tree/v3.6.1"
},
"funding": [
{
@@ -10811,7 +10821,7 @@
"type": "github"
}
],
"time": "2025-07-11T06:52:52+00:00"
"time": "2025-08-25T07:24:56+00:00"
},
{
"name": "laravel-json-api/testing",
@@ -11473,34 +11483,34 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "12.3.2",
"version": "12.3.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "086553c5b2e0e1e20293d782d788ab768202b621"
"reference": "96dc0466673e215bf5536301039017f03cd45c6b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/086553c5b2e0e1e20293d782d788ab768202b621",
"reference": "086553c5b2e0e1e20293d782d788ab768202b621",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/96dc0466673e215bf5536301039017f03cd45c6b",
"reference": "96dc0466673e215bf5536301039017f03cd45c6b",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"ext-xmlwriter": "*",
"nikic/php-parser": "^5.4.0",
"nikic/php-parser": "^5.6.1",
"php": ">=8.3",
"phpunit/php-file-iterator": "^6.0",
"phpunit/php-text-template": "^5.0",
"sebastian/complexity": "^5.0",
"sebastian/environment": "^8.0",
"sebastian/environment": "^8.0.3",
"sebastian/lines-of-code": "^4.0",
"sebastian/version": "^6.0",
"theseer/tokenizer": "^1.2.3"
},
"require-dev": {
"phpunit/phpunit": "^12.1"
"phpunit/phpunit": "^12.3.7"
},
"suggest": {
"ext-pcov": "PHP extension that provides line coverage",
@@ -11538,7 +11548,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.2"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.5"
},
"funding": [
{
@@ -11558,7 +11568,7 @@
"type": "tidelift"
}
],
"time": "2025-07-29T06:19:24+00:00"
"time": "2025-09-01T08:07:42+00:00"
},
{
"name": "phpunit/php-file-iterator",
@@ -11807,16 +11817,16 @@
},
{
"name": "phpunit/phpunit",
"version": "12.3.6",
"version": "12.3.7",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "a2cab3224f687150ac2f3cc13d99b64ba1e1d088"
"reference": "b8fa997c49682979ad6bfaa0d7fb25f54954965e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a2cab3224f687150ac2f3cc13d99b64ba1e1d088",
"reference": "a2cab3224f687150ac2f3cc13d99b64ba1e1d088",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b8fa997c49682979ad6bfaa0d7fb25f54954965e",
"reference": "b8fa997c49682979ad6bfaa0d7fb25f54954965e",
"shasum": ""
},
"require": {
@@ -11830,7 +11840,7 @@
"phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1",
"php": ">=8.3",
"phpunit/php-code-coverage": "^12.3.2",
"phpunit/php-code-coverage": "^12.3.3",
"phpunit/php-file-iterator": "^6.0.0",
"phpunit/php-invoker": "^6.0.0",
"phpunit/php-text-template": "^5.0.0",
@@ -11884,7 +11894,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.6"
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.7"
},
"funding": [
{
@@ -11908,7 +11918,7 @@
"type": "tidelift"
}
],
"time": "2025-08-20T14:43:23+00:00"
"time": "2025-08-28T05:15:46+00:00"
},
{
"name": "rector/rector",
@@ -12400,16 +12410,16 @@
},
{
"name": "sebastian/global-state",
"version": "8.0.0",
"version": "8.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc"
"reference": "ef1377171613d09edd25b7816f05be8313f9115d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/570a2aeb26d40f057af686d63c4e99b075fb6cbc",
"reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d",
"reference": "ef1377171613d09edd25b7816f05be8313f9115d",
"shasum": ""
},
"require": {
@@ -12450,15 +12460,27 @@
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
"security": "https://github.com/sebastianbergmann/global-state/security/policy",
"source": "https://github.com/sebastianbergmann/global-state/tree/8.0.0"
"source": "https://github.com/sebastianbergmann/global-state/tree/8.0.2"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
},
{
"url": "https://liberapay.com/sebastianbergmann",
"type": "liberapay"
},
{
"url": "https://thanks.dev/u/gh/sebastianbergmann",
"type": "thanks_dev"
},
{
"url": "https://tidelift.com/funding/github/packagist/sebastian/global-state",
"type": "tidelift"
}
],
"time": "2025-02-07T04:56:59+00:00"
"time": "2025-08-29T11:29:25+00:00"
},
{
"name": "sebastian/lines-of-code",

View File

@@ -78,8 +78,8 @@ return [
'running_balance_column' => env('USE_RUNNING_BALANCE', false),
// see cer.php for exchange rates feature flag.
],
'version' => 'develop/2025-08-22',
'build_time' => 1755838953,
'version' => 'develop/2025-09-01',
'build_time' => 1756727696,
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 26,

View File

@@ -1,5 +1,26 @@
<?php
/*
* webhooks.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
// this is hard coded, which is unfortunate.

View File

@@ -1,5 +1,26 @@
<?php
/*
* 2025_07_10_065736_rename_tag_mode.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Log;

View File

@@ -1,5 +1,26 @@
<?php
/*
* 2025_08_19_180459_create_webhook_details_tables.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\QueryException;
use Illuminate\Database\Schema\Blueprint;

View File

@@ -1,5 +1,26 @@
<?php
/*
* WebhookDataSeeder.php
* Copyright (c) 2025 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 Database\Seeders;
use FireflyIII\Enums\WebhookTrigger;

297
package-lock.json generated
View File

@@ -2592,9 +2592,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.47.1.tgz",
"integrity": "sha512-lTahKRJip0knffA/GTNFJMrToD+CM+JJ+Qt5kjzBK/sFQ0EWqfKW3AYQSlZXN98tX0lx66083U9JYIMioMMK7g==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.0.tgz",
"integrity": "sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==",
"cpu": [
"arm"
],
@@ -2606,9 +2606,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.47.1.tgz",
"integrity": "sha512-uqxkb3RJLzlBbh/bbNQ4r7YpSZnjgMgyoEOY7Fy6GCbelkDSAzeiogxMG9TfLsBbqmGsdDObo3mzGqa8hps4MA==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.0.tgz",
"integrity": "sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw==",
"cpu": [
"arm64"
],
@@ -2620,9 +2620,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.47.1.tgz",
"integrity": "sha512-tV6reObmxBDS4DDyLzTDIpymthNlxrLBGAoQx6m2a7eifSNEZdkXQl1PE4ZjCkEDPVgNXSzND/k9AQ3mC4IOEQ==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.0.tgz",
"integrity": "sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg==",
"cpu": [
"arm64"
],
@@ -2634,9 +2634,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.47.1.tgz",
"integrity": "sha512-XuJRPTnMk1lwsSnS3vYyVMu4x/+WIw1MMSiqj5C4j3QOWsMzbJEK90zG+SWV1h0B1ABGCQ0UZUjti+TQK35uHQ==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.0.tgz",
"integrity": "sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw==",
"cpu": [
"x64"
],
@@ -2648,9 +2648,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.47.1.tgz",
"integrity": "sha512-79BAm8Ag/tmJ5asCqgOXsb3WY28Rdd5Lxj8ONiQzWzy9LvWORd5qVuOnjlqiWWZJw+dWewEktZb5yiM1DLLaHw==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.0.tgz",
"integrity": "sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ==",
"cpu": [
"arm64"
],
@@ -2662,9 +2662,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.47.1.tgz",
"integrity": "sha512-OQ2/ZDGzdOOlyfqBiip0ZX/jVFekzYrGtUsqAfLDbWy0jh1PUU18+jYp8UMpqhly5ltEqotc2miLngf9FPSWIA==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.0.tgz",
"integrity": "sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA==",
"cpu": [
"x64"
],
@@ -2676,9 +2676,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.47.1.tgz",
"integrity": "sha512-HZZBXJL1udxlCVvoVadstgiU26seKkHbbAMLg7680gAcMnRNP9SAwTMVet02ANA94kXEI2VhBnXs4e5nf7KG2A==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.0.tgz",
"integrity": "sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w==",
"cpu": [
"arm"
],
@@ -2690,9 +2690,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.47.1.tgz",
"integrity": "sha512-sZ5p2I9UA7T950JmuZ3pgdKA6+RTBr+0FpK427ExW0t7n+QwYOcmDTK/aRlzoBrWyTpJNlS3kacgSlSTUg6P/Q==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.0.tgz",
"integrity": "sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg==",
"cpu": [
"arm"
],
@@ -2704,9 +2704,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.47.1.tgz",
"integrity": "sha512-3hBFoqPyU89Dyf1mQRXCdpc6qC6At3LV6jbbIOZd72jcx7xNk3aAp+EjzAtN6sDlmHFzsDJN5yeUySvorWeRXA==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.0.tgz",
"integrity": "sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g==",
"cpu": [
"arm64"
],
@@ -2718,9 +2718,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.47.1.tgz",
"integrity": "sha512-49J4FnMHfGodJWPw73Ve+/hsPjZgcXQGkmqBGZFvltzBKRS+cvMiWNLadOMXKGnYRhs1ToTGM0sItKISoSGUNA==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.0.tgz",
"integrity": "sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ==",
"cpu": [
"arm64"
],
@@ -2732,9 +2732,9 @@
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.47.1.tgz",
"integrity": "sha512-4yYU8p7AneEpQkRX03pbpLmE21z5JNys16F1BZBZg5fP9rIlb0TkeQjn5du5w4agConCCEoYIG57sNxjryHEGg==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.0.tgz",
"integrity": "sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ==",
"cpu": [
"loong64"
],
@@ -2746,9 +2746,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.47.1.tgz",
"integrity": "sha512-fAiq+J28l2YMWgC39jz/zPi2jqc0y3GSRo1yyxlBHt6UN0yYgnegHSRPa3pnHS5amT/efXQrm0ug5+aNEu9UuQ==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.0.tgz",
"integrity": "sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg==",
"cpu": [
"ppc64"
],
@@ -2760,9 +2760,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.47.1.tgz",
"integrity": "sha512-daoT0PMENNdjVYYU9xec30Y2prb1AbEIbb64sqkcQcSaR0zYuKkoPuhIztfxuqN82KYCKKrj+tQe4Gi7OSm1ow==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.0.tgz",
"integrity": "sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA==",
"cpu": [
"riscv64"
],
@@ -2774,9 +2774,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.47.1.tgz",
"integrity": "sha512-JNyXaAhWtdzfXu5pUcHAuNwGQKevR+6z/poYQKVW+pLaYOj9G1meYc57/1Xv2u4uTxfu9qEWmNTjv/H/EpAisw==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.0.tgz",
"integrity": "sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ==",
"cpu": [
"riscv64"
],
@@ -2788,9 +2788,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.47.1.tgz",
"integrity": "sha512-U/CHbqKSwEQyZXjCpY43/GLYcTVKEXeRHw0rMBJP7fP3x6WpYG4LTJWR3ic6TeYKX6ZK7mrhltP4ppolyVhLVQ==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.0.tgz",
"integrity": "sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ==",
"cpu": [
"s390x"
],
@@ -2802,9 +2802,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.47.1.tgz",
"integrity": "sha512-uTLEakjxOTElfeZIGWkC34u2auLHB1AYS6wBjPGI00bWdxdLcCzK5awjs25YXpqB9lS8S0vbO0t9ZcBeNibA7g==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.0.tgz",
"integrity": "sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA==",
"cpu": [
"x64"
],
@@ -2816,9 +2816,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.47.1.tgz",
"integrity": "sha512-Ft+d/9DXs30BK7CHCTX11FtQGHUdpNDLJW0HHLign4lgMgBcPFN3NkdIXhC5r9iwsMwYreBBc4Rho5ieOmKNVQ==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.0.tgz",
"integrity": "sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw==",
"cpu": [
"x64"
],
@@ -2829,10 +2829,24 @@
"linux"
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.0.tgz",
"integrity": "sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.47.1.tgz",
"integrity": "sha512-N9X5WqGYzZnjGAFsKSfYFtAShYjwOmFJoWbLg3dYixZOZqU7hdMq+/xyS14zKLhFhZDhP9VfkzQnsdk0ZDS9IA==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.0.tgz",
"integrity": "sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg==",
"cpu": [
"arm64"
],
@@ -2844,9 +2858,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.47.1.tgz",
"integrity": "sha512-O+KcfeCORZADEY8oQJk4HK8wtEOCRE4MdOkb8qGZQNun3jzmj2nmhV/B/ZaaZOkPmJyvm/gW9n0gsB4eRa1eiQ==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.0.tgz",
"integrity": "sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw==",
"cpu": [
"ia32"
],
@@ -2858,9 +2872,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.47.1.tgz",
"integrity": "sha512-CpKnYa8eHthJa3c+C38v/E+/KZyF1Jdh2Cz3DyKZqEWYgrM1IHFArXNWvBLPQCKUEsAqqKX27tTqVEFbDNUcOA==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.0.tgz",
"integrity": "sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg==",
"cpu": [
"x64"
],
@@ -3256,42 +3270,42 @@
}
},
"node_modules/@vue/compiler-core": {
"version": "3.5.19",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.19.tgz",
"integrity": "sha512-/afpyvlkrSNYbPo94Qu8GtIOWS+g5TRdOvs6XZNw6pWQQmj5pBgSZvEPOIZlqWq0YvoUhDDQaQ2TnzuJdOV4hA==",
"version": "3.5.20",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.20.tgz",
"integrity": "sha512-8TWXUyiqFd3GmP4JTX9hbiTFRwYHgVL/vr3cqhr4YQ258+9FADwvj7golk2sWNGHR67QgmCZ8gz80nQcMokhwg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.3",
"@vue/shared": "3.5.19",
"@vue/shared": "3.5.20",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.5.19",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.19.tgz",
"integrity": "sha512-Drs6rPHQZx/pN9S6ml3Z3K/TWCIRPvzG2B/o5kFK9X0MNHt8/E+38tiRfojufrYBfA6FQUFB2qBBRXlcSXWtOA==",
"version": "3.5.20",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.20.tgz",
"integrity": "sha512-whB44M59XKjqUEYOMPYU0ijUV0G+4fdrHVKDe32abNdX/kJe1NUEMqsi4cwzXa9kyM9w5S8WqFsrfo1ogtBZGQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-core": "3.5.19",
"@vue/shared": "3.5.19"
"@vue/compiler-core": "3.5.20",
"@vue/shared": "3.5.20"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.5.19",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.19.tgz",
"integrity": "sha512-YWCm1CYaJ+2RvNmhCwI7t3I3nU+hOrWGWMsn+Z/kmm1jy5iinnVtlmkiZwbLlbV1SRizX7vHsc0/bG5dj0zRTg==",
"version": "3.5.20",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.20.tgz",
"integrity": "sha512-SFcxapQc0/feWiSBfkGsa1v4DOrnMAQSYuvDMpEaxbpH5dKbnEM5KobSNSgU+1MbHCl+9ftm7oQWxvwDB6iBfw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.3",
"@vue/compiler-core": "3.5.19",
"@vue/compiler-dom": "3.5.19",
"@vue/compiler-ssr": "3.5.19",
"@vue/shared": "3.5.19",
"@vue/compiler-core": "3.5.20",
"@vue/compiler-dom": "3.5.20",
"@vue/compiler-ssr": "3.5.20",
"@vue/shared": "3.5.20",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.17",
"postcss": "^8.5.6",
@@ -3299,14 +3313,14 @@
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.5.19",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.19.tgz",
"integrity": "sha512-/wx0VZtkWOPdiQLWPeQeqpHWR/LuNC7bHfSX7OayBTtUy8wur6vT6EQIX6Et86aED6J+y8tTw43qo2uoqGg5sw==",
"version": "3.5.20",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.20.tgz",
"integrity": "sha512-RSl5XAMc5YFUXpDQi+UQDdVjH9FnEpLDHIALg5J0ITHxkEzJ8uQLlo7CIbjPYqmZtt6w0TsIPbo1izYXwDG7JA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.19",
"@vue/shared": "3.5.19"
"@vue/compiler-dom": "3.5.20",
"@vue/shared": "3.5.20"
}
},
"node_modules/@vue/component-compiler-utils": {
@@ -3388,9 +3402,9 @@
"license": "MIT"
},
"node_modules/@vue/shared": {
"version": "3.5.19",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.19.tgz",
"integrity": "sha512-IhXCOn08wgKrLQxRFKKlSacWg4Goi1BolrdEeLYn6tgHjJNXVrWJ5nzoxZqNwl5p88aLlQ8LOaoMa3AYvaKJ/Q==",
"version": "3.5.20",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.20.tgz",
"integrity": "sha512-SoRGP596KU/ig6TfgkCMbXkr4YJ91n/QSdMuqeP5r3hVIYA3CPHUBCc7Skak0EAKV+5lL4KyIh61VA/pK1CIAA==",
"dev": true,
"license": "MIT"
},
@@ -4170,9 +4184,9 @@
"license": "ISC"
},
"node_modules/bootstrap": {
"version": "5.3.7",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.7.tgz",
"integrity": "sha512-7KgiD8UHjfcPBHEpDNg+zGz8L3LqR3GVwqZiBRFX04a1BCArZOz1r2kjly2HQ0WokqTO0v1nF+QAt8dsW4lKlw==",
"version": "5.3.8",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz",
"integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==",
"funding": [
{
"type": "github",
@@ -4326,9 +4340,9 @@
}
},
"node_modules/browserslist": {
"version": "4.25.3",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz",
"integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==",
"version": "4.25.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz",
"integrity": "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==",
"dev": true,
"funding": [
{
@@ -4346,8 +4360,8 @@
],
"license": "MIT",
"dependencies": {
"caniuse-lite": "^1.0.30001735",
"electron-to-chromium": "^1.5.204",
"caniuse-lite": "^1.0.30001737",
"electron-to-chromium": "^1.5.211",
"node-releases": "^2.0.19",
"update-browserslist-db": "^1.1.3"
},
@@ -4486,9 +4500,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001736",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001736.tgz",
"integrity": "sha512-ImpN5gLEY8gWeqfLUyEF4b7mYWcYoR2Si1VhnrbM4JizRFmfGaAQ12PhNykq6nvI4XvKLrsp8Xde74D5phJOSw==",
"version": "1.0.30001739",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz",
"integrity": "sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==",
"dev": true,
"funding": [
{
@@ -5700,9 +5714,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
"version": "1.5.208",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.208.tgz",
"integrity": "sha512-ozZyibehoe7tOhNaf16lKmljVf+3npZcJIEbJRVftVsmAg5TeA1mGS9dVCZzOwr2xT7xK15V0p7+GZqSPgkuPg==",
"version": "1.5.211",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.211.tgz",
"integrity": "sha512-IGBvimJkotaLzFnwIVgW9/UD/AOJ2tByUmeOrtqBfACSbAw5b1G0XpvdaieKyc7ULmbwXVx+4e4Be8pOPBrYkw==",
"dev": true,
"license": "ISC"
},
@@ -6159,9 +6173,9 @@
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
"integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
"dev": true,
"funding": [
{
@@ -7052,9 +7066,9 @@
}
},
"node_modules/i18next": {
"version": "25.4.0",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.4.0.tgz",
"integrity": "sha512-UH5aiamXsO3cfrZFurCHiB6YSs3C+s+XY9UaJllMMSbmaoXILxFgqDEZu4NbfzJFjmUo3BNMa++Rjkr3ofjfLw==",
"version": "25.4.2",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.4.2.tgz",
"integrity": "sha512-gD4T25a6ovNXsfXY1TwHXXXLnD/K2t99jyYMCSimSCBnBRJVQr5j+VAaU83RJCPzrTGhVQ6dqIga66xO2rtd5g==",
"funding": [
{
"type": "individual",
@@ -7818,9 +7832,9 @@
}
},
"node_modules/laravel-vite-plugin": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-2.0.0.tgz",
"integrity": "sha512-pnaKHInJgiWpG/g+LmaISHl7D/1s5wnOXnrGiBdt4NOs+tYZRw0v/ZANELGX2/dGgHyEzO+iZ6x4idpoK04z/Q==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-2.0.1.tgz",
"integrity": "sha512-zQuvzWfUKQu9oNVi1o0RZAJCwhGsdhx4NEOyrVQwJHaWDseGP9tl7XUPLY2T8Cj6+IrZ6lmyxlR1KC8unf3RLA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10123,9 +10137,9 @@
}
},
"node_modules/rollup": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.47.1.tgz",
"integrity": "sha512-iasGAQoZ5dWDzULEUX3jiW0oB1qyFOepSyDyoU6S/OhVlDIwj5knI5QBa5RRQ0sK7OE0v+8VIi2JuV+G+3tfNg==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.0.tgz",
"integrity": "sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10139,26 +10153,27 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.47.1",
"@rollup/rollup-android-arm64": "4.47.1",
"@rollup/rollup-darwin-arm64": "4.47.1",
"@rollup/rollup-darwin-x64": "4.47.1",
"@rollup/rollup-freebsd-arm64": "4.47.1",
"@rollup/rollup-freebsd-x64": "4.47.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.47.1",
"@rollup/rollup-linux-arm-musleabihf": "4.47.1",
"@rollup/rollup-linux-arm64-gnu": "4.47.1",
"@rollup/rollup-linux-arm64-musl": "4.47.1",
"@rollup/rollup-linux-loongarch64-gnu": "4.47.1",
"@rollup/rollup-linux-ppc64-gnu": "4.47.1",
"@rollup/rollup-linux-riscv64-gnu": "4.47.1",
"@rollup/rollup-linux-riscv64-musl": "4.47.1",
"@rollup/rollup-linux-s390x-gnu": "4.47.1",
"@rollup/rollup-linux-x64-gnu": "4.47.1",
"@rollup/rollup-linux-x64-musl": "4.47.1",
"@rollup/rollup-win32-arm64-msvc": "4.47.1",
"@rollup/rollup-win32-ia32-msvc": "4.47.1",
"@rollup/rollup-win32-x64-msvc": "4.47.1",
"@rollup/rollup-android-arm-eabi": "4.50.0",
"@rollup/rollup-android-arm64": "4.50.0",
"@rollup/rollup-darwin-arm64": "4.50.0",
"@rollup/rollup-darwin-x64": "4.50.0",
"@rollup/rollup-freebsd-arm64": "4.50.0",
"@rollup/rollup-freebsd-x64": "4.50.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.50.0",
"@rollup/rollup-linux-arm-musleabihf": "4.50.0",
"@rollup/rollup-linux-arm64-gnu": "4.50.0",
"@rollup/rollup-linux-arm64-musl": "4.50.0",
"@rollup/rollup-linux-loongarch64-gnu": "4.50.0",
"@rollup/rollup-linux-ppc64-gnu": "4.50.0",
"@rollup/rollup-linux-riscv64-gnu": "4.50.0",
"@rollup/rollup-linux-riscv64-musl": "4.50.0",
"@rollup/rollup-linux-s390x-gnu": "4.50.0",
"@rollup/rollup-linux-x64-gnu": "4.50.0",
"@rollup/rollup-linux-x64-musl": "4.50.0",
"@rollup/rollup-openharmony-arm64": "4.50.0",
"@rollup/rollup-win32-arm64-msvc": "4.50.0",
"@rollup/rollup-win32-ia32-msvc": "4.50.0",
"@rollup/rollup-win32-x64-msvc": "4.50.0",
"fsevents": "~2.3.2"
}
},
@@ -10214,9 +10229,9 @@
"license": "MIT"
},
"node_modules/sass": {
"version": "1.90.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.90.0.tgz",
"integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==",
"version": "1.91.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.91.0.tgz",
"integrity": "sha512-aFOZHGf+ur+bp1bCHZ+u8otKGh77ZtmFyXDo4tlYvT7PWql41Kwd8wdkPqhhT+h2879IVblcHFglIMofsFd1EA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11005,13 +11020,17 @@
}
},
"node_modules/tapable": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz",
"integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==",
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz",
"integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/terser": {
@@ -11527,9 +11546,9 @@
}
},
"node_modules/vite": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz",
"integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==",
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.4.tgz",
"integrity": "sha512-X5QFK4SGynAeeIt+A7ZWnApdUyHYm+pzv/8/A57LqSGcI88U6R6ipOs3uCesdc6yl7nl+zNO0t8LmqAdXcQihw==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -1,4 +1,25 @@
<?php
/*
* index.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
use Illuminate\Contracts\Http\Kernel;

View File

@@ -285,4 +285,4 @@
<glyph unicode="&#x1f511;" d="M250 1200h600q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-500l-255 -178q-19 -9 -32 -1t-13 29v650h-150q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM400 1100v-100h300v100h-300z" />
<glyph unicode="&#x1f6aa;" d="M250 1200h750q39 0 69.5 -40.5t30.5 -84.5v-933l-700 -117v950l600 125h-700v-1000h-100v1025q0 23 15.5 49t34.5 26zM500 525v-100l100 20v100z" />
</font>
</defs></svg>
</defs></svg>

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 434 KiB

After

Width:  |  Height:  |  Size: 433 KiB

View File

@@ -107,7 +107,7 @@
"multi_account_warning_withdrawal": "Bedenken Sie, dass das Quellkonto nachfolgender Aufteilungen von dem, was in der ersten Aufteilung der Abhebung definiert ist, au\u00dfer Kraft gesetzt wird.",
"multi_account_warning_deposit": "Bedenken Sie, dass das Zielkonto nachfolgender Aufteilungen von dem, was in der ersten Aufteilung der Einnahmen definiert ist, au\u00dfer Kraft gesetzt wird.",
"multi_account_warning_transfer": "Bedenken Sie, dass das Quell- und Zielkonto nachfolgender Aufteilungen durch das, was in der ersten Aufteilung der \u00dcbertragung definiert ist, au\u00dfer Kraft gesetzt wird.",
"webhook_trigger_ANY": "After any event",
"webhook_trigger_ANY": "Nach jedem Ereignis",
"webhook_trigger_STORE_TRANSACTION": "Nach Erstellen einer Buchung",
"webhook_trigger_UPDATE_TRANSACTION": "Nach Aktualisierung einer Buchung",
"webhook_trigger_DESTROY_TRANSACTION": "Nach dem L\u00f6schen einer Buchung",
@@ -116,7 +116,7 @@
"webhook_trigger_DESTROY_BUDGET": "Nach dem L\u00f6schen des Budgets",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "Nach dem \u00c4ndern des budgetierten Betrags",
"webhook_response_TRANSACTIONS": "Buchungsdetails",
"webhook_response_RELEVANT": "Relevant details",
"webhook_response_RELEVANT": "Relevante Details",
"webhook_response_ACCOUNTS": "Kontodetails",
"webhook_response_NONE": "Keine Details",
"webhook_delivery_JSON": "JSON",

View File

@@ -107,7 +107,7 @@
"multi_account_warning_withdrawal": "Gardez en t\u00eate que le compte source des s\u00e9parations suivantes peut \u00eatre remplac\u00e9 par celui de la premi\u00e8re s\u00e9paration de la d\u00e9pense.",
"multi_account_warning_deposit": "Gardez en t\u00eate que le compte de destination des s\u00e9parations suivantes peut \u00eatre remplac\u00e9 par celui de la premi\u00e8re s\u00e9paration du d\u00e9p\u00f4t.",
"multi_account_warning_transfer": "Gardez en t\u00eate que les comptes source et de destination des s\u00e9parations suivantes peuvent \u00eatre remplac\u00e9s par ceux de la premi\u00e8re s\u00e9paration du transfert.",
"webhook_trigger_ANY": "After any event",
"webhook_trigger_ANY": "Apr\u00e8s n'importe quel \u00e9v\u00e9nement",
"webhook_trigger_STORE_TRANSACTION": "Apr\u00e8s la cr\u00e9ation de l'op\u00e9ration",
"webhook_trigger_UPDATE_TRANSACTION": "Apr\u00e8s la mise \u00e0 jour de l'op\u00e9ration",
"webhook_trigger_DESTROY_TRANSACTION": "Apr\u00e8s la suppression de l'op\u00e9ration",
@@ -116,7 +116,7 @@
"webhook_trigger_DESTROY_BUDGET": "Apr\u00e8s la suppression du budget",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "Apr\u00e8s le changement du montant budg\u00e9tis\u00e9",
"webhook_response_TRANSACTIONS": "D\u00e9tails de l'op\u00e9ration",
"webhook_response_RELEVANT": "Relevant details",
"webhook_response_RELEVANT": "D\u00e9tails pertinents",
"webhook_response_ACCOUNTS": "D\u00e9tails du compte",
"webhook_response_NONE": "Aucun d\u00e9tail",
"webhook_delivery_JSON": "JSON",

View File

@@ -160,7 +160,7 @@
"url": "URL",
"active": "Actief",
"interest_date": "Rentedatum",
"administration_currency": "Primary currency",
"administration_currency": "Standaardvaluta",
"title": "Titel",
"date": "Datum",
"book_date": "Boekdatum",
@@ -180,7 +180,7 @@
"list": {
"title": "Titel",
"active": "Actief?",
"primary_currency": "Primary currency",
"primary_currency": "Standaardvaluta",
"trigger": "Trigger",
"response": "Reactie",
"delivery": "Bericht",

View File

@@ -3,8 +3,8 @@
"administrations_page_title": "Finan\u010dne administracije",
"administrations_index_menu": "Finan\u010dne administracije",
"expires_at": "Pote\u010de ob",
"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": "Firefly III bo kmalu dobil mo\u017enost upravljanja ve\u010d finan\u010dnih administracij. Trenutno imate samo eno. Nastavite lahko naziv te administracije in njeno glavno valuto. To nadome\u0161\u010da prej\u0161njo nastavitev, kjer bi nastavili svojo \"privzeto valuto\". Ta nastavitev je zdaj vezana na finan\u010dno administracijo in se lahko razlikuje glede na administracijo.",
"administration_currency_form_help": "\u010ce spremenite doma\u010do valuto, lahko traja dolgo \u010dasa, da se stran nalo\u017ei, ker bo transakcijo morda treba pretvoriti v va\u0161o (novo) doma\u010do valuto.",
"administrations_page_edit_sub_title_js": "Uredi finan\u010dno administracijo \"{title}\"",
"table": "Tabela",
"welcome_back": "Kaj vse se dogaja?",
@@ -102,23 +102,23 @@
"profile_oauth_client_secret_title": "Skrivna koda odjemalca",
"profile_oauth_client_secret_expl": "Tukaj je skrivna koda va\u0161ega odjemalca. To je edini \u010das, da bo prikazana, zato je ne izgubite! Zdaj lahko uporabite to skrivno kodo za po\u0161iljanje zahtev API.",
"profile_oauth_confidential": "Zaupno",
"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": "Od odjemalca zahtevajte avtentikacijo s skrivno kodo. Zaupni odjemalci imajo lahko poverilnice na varen na\u010din, ne da bi jih izpostavili nepoobla\u0161\u010denim osebam. Javne aplikacije, kot so izvorne namizne aplikacije ali aplikacije JavaScript SPA, ne morejo varno hraniti skrivnih kod.",
"multi_account_warning_unknown": "Odvisno od vrste transakcije, ki jo ustvarite, lahko izvorni in\/ali ciljni ra\u010dun poznej\u0161ih razdelitev preglasi tisto, kar je definirano v prvi razdelitvi transakcije.",
"multi_account_warning_withdrawal": "Upo\u0161tevajte, da bo izvorni ra\u010dun poznej\u0161ih razdelitev preglasilo tisto, kar je definirano v prvi razdelitvi odliva.",
"multi_account_warning_deposit": "Upo\u0161tevajte, da bo ciljni ra\u010dun poznej\u0161ih delitev preglasilo tisto, kar je opredeljeno v prvi delitvi priliva.",
"multi_account_warning_transfer": "Upo\u0161tevajte, da bo izvorni + ciljni ra\u010dun poznej\u0161ih razdelitev preglasilo tisto, kar je definirano v prvi razdelitvi prenosa.",
"webhook_trigger_ANY": "After any event",
"webhook_trigger_ANY": "Po katerem koli dogodku",
"webhook_trigger_STORE_TRANSACTION": "Po ustvarjanju transakcije",
"webhook_trigger_UPDATE_TRANSACTION": "Po posodabljanju transakcije",
"webhook_trigger_DESTROY_TRANSACTION": "Po brisanju transakcije",
"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 izdelavi prora\u010duna",
"webhook_trigger_UPDATE_BUDGET": "Po posodobitvi prora\u010duna",
"webhook_trigger_DESTROY_BUDGET": "Po izbrisu prora\u010duna",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "Po spremembi zneska v prora\u010dunu",
"webhook_response_TRANSACTIONS": "Podrobnosti transakcije",
"webhook_response_RELEVANT": "Relevant details",
"webhook_response_RELEVANT": "Relevantni podatki",
"webhook_response_ACCOUNTS": "Podrobnosti ra\u010duna",
"webhook_response_NONE": "No details",
"webhook_response_NONE": "Ni podrobnosti",
"webhook_delivery_JSON": "JSON",
"actions": "Dejanja",
"meta_data": "Meta podatki",
@@ -160,7 +160,7 @@
"url": "URL",
"active": "Aktivno",
"interest_date": "Datum obresti",
"administration_currency": "Primary currency",
"administration_currency": "Primarna valuta",
"title": "Naslov",
"date": "Datum",
"book_date": "Datum knji\u017eenja",
@@ -180,7 +180,7 @@
"list": {
"title": "Naslov",
"active": "Aktiviran?",
"primary_currency": "Primary currency",
"primary_currency": "Primarna valuta",
"trigger": "Spro\u017eilec",
"response": "Odziv",
"delivery": "Dostava",

View File

@@ -32,6 +32,7 @@ import {showWizardButton} from "../../support/page-settings/show-wizard-button.j
import {setVariable} from "../../store/set-variable.js";
import {getVariables} from "../../store/get-variables.js";
import pageNavigation from "../../support/page-navigation.js";
import {getVariable} from "../../store/get-variable.js";
// set type from URL
@@ -56,12 +57,12 @@ if(sortingColumn[0] === '-') {
page = parseInt(params.page ?? 1);
showInternalsButton();
showWizardButton();
// TODO currency conversion
// TODO page cleanup and recycle for transaction lists.
// TODO process startPeriod and endPeriod.
let index = function () {
return {
@@ -73,9 +74,9 @@ let index = function () {
show: false, text: '', url: '',
}, wait: {
show: false, text: '',
}
},
convertToPrimary: false,
totalPages: 1,
page: 1,
pageUrl: '',
@@ -259,6 +260,7 @@ let index = function () {
{name: this.getPreferenceKey('sd'), default: ''},
{name: this.getPreferenceKey('filters'), default: this.filters},
{name: this.getPreferenceKey('grouped'), default: true},
{name: 'convert_to_primary', default: false},
]).then((res) => {
// process columns:
for (let k in res[0]) {
@@ -283,6 +285,9 @@ let index = function () {
// group accounts
this.pageOptions.groupedAccounts = res[4];
// convert to primary?
this.convertToPrimary = res[5];
this.loadAccounts();
});
},
@@ -348,7 +353,6 @@ let index = function () {
if('asc' === this.pageOptions.sortDirection && '' !== sorting) {
sorting = '-' + sorting;
}
//const sorting = [{column: this.pageOptions.sortingColumn, direction: this.pageOptions.sortDirection}];
// filter instructions
let filters = {};
@@ -371,20 +375,20 @@ let index = function () {
sort: sorting,
filter: filters,
active: active,
currentMoment: today,
date: format(today,'yyyy-MM-dd'),
type: type,
page: this.page,
startPeriod: start,
endPeriod: end
// startPeriod: start,
// endPeriod: end
};
if (!this.tableColumns.balance_difference.enabled) {
delete params.startPeriod;
delete params.endPeriod;
// delete params.startPeriod;
// delete params.endPeriod;
}
this.accounts = [];
let groupedAccounts = {};
// one page only.o
// one page only
(new Get()).index(params).then(response => {
this.totalPages = response.meta.pagination.total_pages;
for (let i = 0; i < response.data.length; i++) {
@@ -406,9 +410,9 @@ let index = function () {
liability_direction: current.attributes.liability_direction,
interest: current.attributes.interest,
interest_period: current.attributes.interest_period,
balance: current.attributes.balance,
pc_balance: current.attributes.pc_balance,
balances: current.attributes.balances,
// balance: current.attributes.balance,
// pc_balance: current.attributes.pc_balance,
// balances: current.attributes.balances,
};
// get group info:
let groupId = current.attributes.object_group_id;
@@ -426,10 +430,9 @@ let index = function () {
}
}
groupedAccounts[groupId].accounts.push(account);
//this.accounts.push(account);
}
}
// order grouped accounts by order.
let sortable = [];
for (let set in groupedAccounts) {

View File

@@ -119,6 +119,7 @@ return [
'between.file' => 'The :attribute must be between :min and :max kilobytes.',
'between.string' => 'The :attribute must be between :min and :max characters.',
'between.array' => 'The :attribute must have between :min and :max items.',
'between_date' => 'The date must be between the given start and end date.',
'boolean' => 'The :attribute field must be true or false.',
'confirmed' => 'The :attribute confirmation does not match.',
'date' => 'The :attribute is not a valid date.',

View File

@@ -147,7 +147,7 @@
<div class="box-header with-border">
<h3 class="box-title">{{ 'transactions'|_ }}
{% if balances.balance %}
({{ formatAmountBySymbol(balances.balance, currency.symbol, currency.decimal_places, true)|raw }})
({{ formatAmountBySymbol(balances.balance, currency.symbol, currency.decimal_places, true)|raw }})
{% elseif balances.pc_balance %}
({{ formatAmountBySymbol(balances.pc_balance, primaryCurrency.symbol, primaryCurrency.decimal_places, true)|raw }})
{% endif %}

View File

@@ -68,8 +68,8 @@
>
~ {{ formatAmountBySymbol((entry.amount_max + entry.amount_min)/2, entry.currency_symbol, entry.currency_decimal_places) }}
{% if '0' != entry.pc_amount_max %}
(~ {{ formatAmountBySymbol((entry.pc_amount_max + entry.pc_amount_min)/2, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
{% if '0' != entry.pc_amount_max and null != entry.pc_amount_max %}
(~ {{ formatAmountBySymbol((entry.pc_amount_max + entry.pc_amount_min)/2, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
{% endif %}
</span>
</td>

View File

@@ -24,8 +24,6 @@
</p>
<div class="row">
<div class="col-lg-6 col-md-8 col-sm-12 col-xs-12">
{{ ExpandedForm.date('start', first) }}
{{ ExpandedForm.date('end', today) }}
{{ AccountForm.assetAccountCheckList('accounts', {'select_all': true,'class': 'account-checkbox', 'label': trans('firefly.include_transactions_from_accounts') }) }}
</div>
</div>

View File

@@ -23,8 +23,6 @@
</p>
<div class="row">
<div class="col-lg-6 col-md-8 col-sm-12 col-xs-12">
{{ ExpandedForm.date('start', first) }}
{{ ExpandedForm.date('end', today) }}
{{ AccountForm.assetAccountCheckList('accounts', {'select_all': true, 'class': 'account-checkbox', 'label': trans('firefly.include_transactions_from_accounts') }) }}
</div>
</div>

View File

@@ -160,12 +160,13 @@
<td>
{% for amount in amounts %}
{% if first.transaction_type_type == 'Withdrawal' %}
{{ formatAmountBySymbol(amount.amount*-1,amount.symbol, amount.decimal_places) }}{% if loop.index0 != amounts|length -1 %}, {% endif %}
{% elseif first.transaction_type_type == 'Deposit' %}
{{ formatAmountBySymbol(amount.amount,amount.symbol, amount.decimal_places) }}{% if loop.index0 != amounts|length -1 %}, {% endif %}
{% elseif first.transaction_type_type == 'Deposit' %}
{{ formatAmountBySymbol(amount.amount*-1,amount.symbol, amount.decimal_places) }}{% if loop.index0 != amounts|length -1 %}, {% endif %}
{% elseif first.transaction_type_type == 'Transfer' %}
<span class="text-info money-transfer">
{{ formatAmountBySymbol(amount.amount, amount.symbol, amount.decimal_places, false) }}{% if loop.index0 != amounts|length -1 %}, {% endif %}
{{ formatAmountBySymbol(amount.amount*-1, amount.symbol, amount.decimal_places, false) }}{% if loop.index0 != amounts|length -1 %}, {% endif %}
</span>
{% elseif first.transaction_type_type == 'Opening balance' %}
{# Opening balance stored amount is always negative: find out which way the money goes #}
@@ -289,7 +290,6 @@
<table class="table">
<tr>
<td colspan="2">
<!-- type is: "{{ first.transaction_type_type }}" -->
{% if 'Cash account' == journal.source_account_type %}
<span class="text-success">({{ 'cash'|_ }})</span>
{% else %}
@@ -298,12 +298,12 @@
{% endif %}
{% if first.transaction_type_type == 'Withdrawal' %}
{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_decimal_places) }}
{% elseif first.transaction_type_type == 'Deposit' %}
{{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_decimal_places) }}
{% elseif first.transaction_type_type == 'Deposit' %}
{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_decimal_places) }}
{% elseif first.transaction_type_type == 'Transfer' or first.transaction_type_type == 'Opening balance' %}
<span class="text-info money-transfer">
{{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_decimal_places, false) }}
{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_decimal_places, false) }}
</span>
{% elseif first.transaction_type_type == 'Liability credit' %}
<span class="text-info money-transfer">
@@ -314,12 +314,12 @@
<!-- do primary currency amount -->
{% if null != journal.pc_amount and primaryCurrency.id != journal.currency_id %}
{% if first.transaction_type_type == 'Withdrawal' %}
({{ formatAmountBySymbol(journal.pc_amount*-1, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
{% elseif first.transaction_type_type == 'Deposit' %}
({{ formatAmountBySymbol(journal.pc_amount, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
{% elseif first.transaction_type_type == 'Deposit' %}
({{ formatAmountBySymbol(journal.pc_amount*-1, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
{% elseif first.transaction_type_type == 'Transfer' %}
<span class="text-info money-transfer">
({{ formatAmountBySymbol(journal.pc_amount, primaryCurrency.symbol, primaryCurrency.decimal_places, false) }})
({{ formatAmountBySymbol(journal.pc_amount*-1, primaryCurrency.symbol, primaryCurrency.decimal_places, false) }})
</span>
{% endif %}
{% endif %}
@@ -327,12 +327,12 @@
<!-- do foreign amount -->
{% if null != journal.foreign_amount %}
{% if first.transaction_type_type == 'Withdrawal' %}
({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }})
{% elseif first.transaction_type_type == 'Deposit' %}
({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }})
{% elseif first.transaction_type_type == 'Deposit' %}
({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }})
{% elseif first.transaction_type_type == 'Transfer' %}
<span class="text-info money-transfer">
({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }})
({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }})
</span>
{% endif %}
{% endif %}
@@ -340,12 +340,12 @@
<!-- do foreign PC amount -->
{% if null != journal.pc_foreign_amount %}
{% if first.transaction_type_type == 'Withdrawal' %}
({{ formatAmountBySymbol(journal.pc_foreign_amount*-1, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
{% elseif first.transaction_type_type == 'Deposit' %}
({{ formatAmountBySymbol(journal.pc_foreign_amount, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
{% elseif first.transaction_type_type == 'Deposit' %}
({{ formatAmountBySymbol(journal.pc_foreign_amount*-1, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
{% elseif first.transaction_type_type == 'Transfer' %}
<span class="text-info money-transfer">
({{ formatAmountBySymbol(journal.pc_foreign_amount, primaryCurrency.symbol, primaryCurrency.decimal_places, false) }})
({{ formatAmountBySymbol(journal.pc_foreign_amount*-1, primaryCurrency.symbol, primaryCurrency.decimal_places, false) }})
</span>
{% endif %}
{% endif %}
@@ -441,7 +441,7 @@
<td>
{% for tag in journal.tags %}
<h4 style="display: inline;"><a class="label label-success"
href="{{ route('tags.show', tag.id) }}">
href="{{ route('tags.show', [tag.id]) }}">
<span class="fa fa-fw fa-tag"></span>{{ tag.tag }}</a>
</h4>
{% endfor %}

View File

@@ -10,7 +10,7 @@
<h3 class="card-title">{{ __('firefly.net_worth') }}</h3>
</div>
<div class="card-body">
TODO
Not yet implemented.
</div>
</div>
</div>
@@ -20,17 +20,17 @@
<h3 class="card-title">{{ __('firefly.in_out_period') }}</h3>
</div>
<div class="card-body">
TODO
Not yet implemented.
</div>
</div>
</div>
<div class="col-xl-4 col-lg-6 col-md-12 col-sm-12 col-xs-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">TODO</h3>
<h3 class="card-title">Not yet implemented.</h3>
</div>
<div class="card-body">
TODO
Not yet implemented.
</div>
</div>
</div>
@@ -262,17 +262,14 @@
</template>
</td>
<td x-show="tableColumns.current_balance.visible && tableColumns.current_balance.enabled">
<template x-if="null !== account.balance">
<template x-for="balance in account.balance">
<span>
<span x-show="parseFloat(balance.balance) < 0.0" class="text-danger"
x-text="formatMoney(balance.balance, balance.currency_code)"></span>
<span x-show="parseFloat(balance.balance) === 0.0" class="text-muted"
x-text="formatMoney(balance.balance, balance.currency_code)"></span>
<span x-show="parseFloat(balance.balance) > 0.0" class="text-success"
x-text="formatMoney(balance.balance, balance.currency_code)"></span>
</span>
</template>
<span x-show="parseFloat(account.current_balance) < 0.0" class="text-danger"
x-text="formatMoney(account.current_balance, account.currency_code)"></span>
<span x-show="parseFloat(account.current_balance) === 0.0" class="text-muted"
x-text="formatMoney(account.current_balance, account.currency_code)"></span>
<span x-show="parseFloat(account.current_balance) > 0.0" class="text-success"
x-text="formatMoney(account.current_balance, account.currency_code)"></span>
<template x-if="null !== account.pc_current_balance">
<span>PC current balance TODO.</span>
</template>
</td>
<td x-show="tableColumns.amount_due.visible && tableColumns.amount_due.enabled">

View File

@@ -75,7 +75,7 @@
<ul class="list-unstyled list-no-margin">
<template x-for="transaction in group.transactions">
<li>
@include('partials.elements.amount', ['convertToPrimary' => true,'type' => 'transaction.type','amount' => 'transaction.amount','primary' => 'transaction.amount'])
@include('partials.elements.amount', ['convertToPrimary' => $convertToPrimary,'type' => 'transaction.type','amount' => 'transaction.amount', 'primary' => 'transaction.pc_amount'])
</li>
</template>
</ul>

View File

@@ -57,20 +57,31 @@
</template>
@else
<template x-if="{{ $amount }}_raw < 0">
<span class="text-danger">
<span x-text="{{ $amount }}"></span>
</span>
<span>
<template x-if="'transfer' === {{ $type }}">
<span class="text-primary">
<span x-text="{{ $amount }}"></span>
</span>
</template>
<template x-if="'transfer' !== {{ $type }}">
<span class="text-danger">
<span x-text="{{ $amount }}"></span>
</span>
</template>
</span>
</template>
<template x-if="{{ $amount }}_raw >= 0">
<template x-if="'transfer' === {{ $type }}">
<span class="text-primary">
<span x-text="{{ $amount }}"></span>
<span>
<template x-if="'transfer' === {{ $type }}">
<span class="text-primary">
<span x-text="{{ $amount }}"></span>
</span>
</template>
<template x-if="'transfer' !== {{ $type }}">
<span class="text-success">
<span x-text="{{ $amount }}"></span>
</span>
</template>
</span>
</template>
<template x-if="'transfer' !== {{ $type }}">
<span class="text-success">
<span x-text="{{ $amount }}"></span>
</span>
</template>
</template>
@endif

View File

@@ -48,6 +48,7 @@ Route::group(
// Auto complete routes
Route::get('accounts', ['uses' => 'AccountController@accounts', 'as' => 'accounts']);
Route::get('bills', ['uses' => 'BillController@bills', 'as' => 'bills']);
Route::get('subscriptions', ['uses' => 'BillController@bills', 'as' => 'subscriptions']);
Route::get('budgets', ['uses' => 'BudgetController@budgets', 'as' => 'budgets']);
Route::get('categories', ['uses' => 'CategoryController@categories', 'as' => 'categories']);
Route::get('currencies', ['uses' => 'CurrencyController@currencies', 'as' => 'currencies']);
@@ -504,6 +505,7 @@ Route::group(
Route::get('', ['uses' => 'ShowController@index', 'as' => 'index']);
Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']);
Route::get('{recurrence}', ['uses' => 'ShowController@show', 'as' => 'show']);
Route::post('{recurrence}/trigger', ['uses' => 'TriggerController@trigger', 'as' => 'trigger']);
Route::put('{recurrence}', ['uses' => 'UpdateController@update', 'as' => 'update']);
Route::delete('{recurrence}', ['uses' => 'DestroyController@destroy', 'as' => 'delete']);

View File

@@ -0,0 +1,147 @@
<?php
/*
* BillControllerTest.php
* Copyright (c) 2024 tasnim0tantawi
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Tests\integration\Api\Autocomplete;
use FireflyIII\Models\Recurrence;
use FireflyIII\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\integration\TestCase;
/**
* Class BillControllerTest
*
* @internal
*
* @coversNothing
*/
final class RecurrenceControllerTest extends TestCase
{
/**
* @covers \FireflyIII\Api\V1\Controllers\Autocomplete\RecurrenceController
*/
use RefreshDatabase;
private function createTestRecurrences(int $count, User $user): void
{
for ($i = 1; $i <= $count; ++$i) {
$recurrence = Recurrence::create([
'user_id' => $user->id,
'user_group_id' => $user->user_group_id,
'transaction_type_id' => 1,
'title' => 'Recurrence '.$i,
'description' => 'Recurrence '.$i,
'first_date' => today(),
'apply_rules' => 1,
'active' => 1,
'repetitions' => 5,
]);
}
}
public function testUnAuthenticatedCall(): void
{
// test API
$response = $this->get(route('api.v1.autocomplete.recurring'), ['Accept' => 'application/json']);
$response->assertStatus(401);
$response->assertHeader('Content-Type', 'application/json');
$response->assertContent('{"message":"Unauthenticated.","exception":"AuthenticationException"}');
}
public function testAuthenticatedCall(): void
{
// act as a user
$user = $this->createAuthenticatedUser();
$this->actingAs($user);
$response = $this->get(route('api.v1.autocomplete.recurring'), ['Accept' => 'application/json']);
$response->assertStatus(200);
$response->assertHeader('Content-Type', 'application/json');
}
public function testGivenAuthenticatedRequestWithItems(): void
{
$user = $this->createAuthenticatedUser();
$this->actingAs($user);
$this->createTestRecurrences(5, $user);
$response = $this->get(route('api.v1.autocomplete.recurring'), ['Accept' => 'application/json']);
$response->assertStatus(200);
$response->assertHeader('Content-Type', 'application/json');
$response->assertJsonCount(5);
$response->assertJsonFragment(['name' => 'Recurrence 1']);
$response->assertJsonStructure([
'*' => [
'id',
'name',
'active',
],
]);
}
public function testGivenAuthenticatedRequestWithItemsLimited(): void
{
$user = $this->createAuthenticatedUser();
$this->actingAs($user);
$this->createTestRecurrences(5, $user);
$response = $this->get(route('api.v1.autocomplete.recurring', [
'query' => 'Recurrence',
'limit' => 3,
]), ['Accept' => 'application/json']);
$response->assertStatus(200);
$response->assertHeader('Content-Type', 'application/json');
$response->assertJsonCount(3);
$response->assertJsonFragment(['name' => 'Recurrence 1']);
$response->assertJsonStructure([
'*' => [
'id',
'name',
'active',
],
]);
}
public function testGivenAuthenticatedRequestWithItemsLots(): void
{
$user = $this->createAuthenticatedUser();
$this->actingAs($user);
$this->createTestRecurrences(20, $user);
$response = $this->get(route('api.v1.autocomplete.recurring', [
'query' => 'Recurrence 1',
'limit' => 20,
]), ['Accept' => 'application/json']);
$response->assertStatus(200);
$response->assertHeader('Content-Type', 'application/json');
// Bill 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 (11)
$response->assertJsonCount(11);
$response->assertJsonMissing(['name' => 'Recurrence 2']);
}
}

View File

@@ -0,0 +1,156 @@
<?php
/*
* BillControllerTest.php
* Copyright (c) 2024 tasnim0tantawi
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Tests\integration\Api\Autocomplete;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\integration\TestCase;
/**
* @internal
*
* @coversNothing
*/
final class RuleControllerTest extends TestCase
{
/**
* @covers \FireflyIII\Api\V1\Controllers\Autocomplete\RecurrenceController
*/
use RefreshDatabase;
private function createTestRules(int $count, User $user): void
{
$ruleGroup = RuleGroup::create(
[
'user_id' => $user->id,
'user_group_id' => $user->user_group_id,
'title' => 'RuleGroup 1',
'description' => 'RuleGroup 1',
'order' => 1,
'active' => 1,
'stop_processing' => 0,
]
);
for ($i = 1; $i <= $count; ++$i) {
$rule = Rule::create([
'user_id' => $user->id,
'user_group_id' => $user->user_group_id,
'rule_group_id' => $ruleGroup->id,
'title' => 'Rule '.$i,
'description' => 'Rule '.$i,
'order' => 1,
'active' => 1,
'stop_processing' => 0,
'strict' => 0,
]);
}
}
public function testUnauthenticatedCall(): void
{
// test API
$response = $this->get(route('api.v1.autocomplete.rules'), ['Accept' => 'application/json']);
$response->assertStatus(401);
$response->assertHeader('Content-Type', 'application/json');
$response->assertContent('{"message":"Unauthenticated.","exception":"AuthenticationException"}');
}
public function testAuthenticatedCall(): void
{
// act as a user
$user = $this->createAuthenticatedUser();
$this->actingAs($user);
$response = $this->get(route('api.v1.autocomplete.rules'), ['Accept' => 'application/json']);
$response->assertStatus(200);
$response->assertHeader('Content-Type', 'application/json');
}
public function testGivenAuthenticatedRequestWithItems(): void
{
$user = $this->createAuthenticatedUser();
$this->actingAs($user);
$this->createTestRules(5, $user);
$response = $this->get(route('api.v1.autocomplete.rules'), ['Accept' => 'application/json']);
$response->assertStatus(200);
$response->assertHeader('Content-Type', 'application/json');
$response->assertJsonCount(5);
$response->assertJsonFragment(['name' => 'Rule 1']);
$response->assertJsonStructure([
'*' => [
'id',
'name',
'active',
],
]);
}
public function testGivenAuthenticatedRequestWithItemsLimited(): void
{
$user = $this->createAuthenticatedUser();
$this->actingAs($user);
$this->createTestRules(5, $user);
$response = $this->get(route('api.v1.autocomplete.rules', [
'query' => 'Rule',
'limit' => 3,
]), ['Accept' => 'application/json']);
$response->assertStatus(200);
$response->assertHeader('Content-Type', 'application/json');
$response->assertJsonCount(3);
$response->assertJsonFragment(['name' => 'Rule 1']);
$response->assertJsonStructure([
'*' => [
'id',
'name',
'active',
],
]);
}
public function testGivenAuthenticatedRequestWithItemsLots(): void
{
$user = $this->createAuthenticatedUser();
$this->actingAs($user);
$this->createTestRules(20, $user);
$response = $this->get(route('api.v1.autocomplete.rules', [
'query' => 'Rule 1',
'limit' => 20,
]), ['Accept' => 'application/json']);
$response->assertStatus(200);
$response->assertHeader('Content-Type', 'application/json');
// Bill 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 (11)
$response->assertJsonCount(11);
$response->assertJsonMissing(['name' => 'Rule 2']);
}
}

View File

@@ -0,0 +1,145 @@
<?php
/*
* BillControllerTest.php
* Copyright (c) 2024 tasnim0tantawi
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Tests\integration\Api\Autocomplete;
use FireflyIII\Models\RuleGroup;
use FireflyIII\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\integration\TestCase;
/**
* @internal
*
* @coversNothing
*/
final class RuleGroupControllerTest extends TestCase
{
/**
* @covers \FireflyIII\Api\V1\Controllers\Autocomplete\RecurrenceController
*/
use RefreshDatabase;
private function createTestRuleGroups(int $count, User $user): void
{
for ($i = 1; $i <= $count; ++$i) {
$ruleGroup = RuleGroup::create(
[
'user_id' => $user->id,
'user_group_id' => $user->user_group_id,
'title' => 'RuleGroup '.$i,
'description' => 'RuleGroup '.$i,
'order' => 1,
'active' => 1,
'stop_processing' => 0,
]
);
}
}
public function testUnauthenticatedCall(): void
{
// test API
$response = $this->get(route('api.v1.autocomplete.rule-groups'), ['Accept' => 'application/json']);
$response->assertStatus(401);
$response->assertHeader('Content-Type', 'application/json');
$response->assertContent('{"message":"Unauthenticated.","exception":"AuthenticationException"}');
}
public function testAuthenticatedCall(): void
{
// act as a user
$user = $this->createAuthenticatedUser();
$this->actingAs($user);
$response = $this->get(route('api.v1.autocomplete.rule-groups'), ['Accept' => 'application/json']);
$response->assertStatus(200);
$response->assertHeader('Content-Type', 'application/json');
}
public function testGivenAuthenticatedRequestWithItems(): void
{
$user = $this->createAuthenticatedUser();
$this->actingAs($user);
$this->createTestRuleGroups(5, $user);
$response = $this->get(route('api.v1.autocomplete.rule-groups'), ['Accept' => 'application/json']);
$response->assertStatus(200);
$response->assertHeader('Content-Type', 'application/json');
$response->assertJsonCount(5);
$response->assertJsonFragment(['name' => 'RuleGroup 1']);
$response->assertJsonStructure([
'*' => [
'id',
'name',
'active',
],
]);
}
public function testGivenAuthenticatedRequestWithItemsLimited(): void
{
$user = $this->createAuthenticatedUser();
$this->actingAs($user);
$this->createTestRuleGroups(5, $user);
$response = $this->get(route('api.v1.autocomplete.rule-groups', [
'query' => 'RuleGroup',
'limit' => 3,
]), ['Accept' => 'application/json']);
$response->assertStatus(200);
$response->assertHeader('Content-Type', 'application/json');
$response->assertJsonCount(3);
$response->assertJsonFragment(['name' => 'RuleGroup 1']);
$response->assertJsonStructure([
'*' => [
'id',
'name',
'active',
],
]);
}
public function testGivenAuthenticatedRequestWithItemsLots(): void
{
$user = $this->createAuthenticatedUser();
$this->actingAs($user);
$this->createTestRuleGroups(20, $user);
$response = $this->get(route('api.v1.autocomplete.rule-groups', [
'query' => 'RuleGroup 1',
'limit' => 20,
]), ['Accept' => 'application/json']);
$response->assertStatus(200);
$response->assertHeader('Content-Type', 'application/json');
// Bill 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 (11)
$response->assertJsonCount(11);
$response->assertJsonMissing(['name' => 'RuleGroup 2']);
}
}

View File

@@ -0,0 +1,142 @@
<?php
/*
* BillControllerTest.php
* Copyright (c) 2024 tasnim0tantawi
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Tests\integration\Api\Autocomplete;
use FireflyIII\Models\Tag;
use FireflyIII\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\integration\TestCase;
/**
* @internal
*
* @coversNothing
*/
final class TagControllerTest extends TestCase
{
/**
* @covers \FireflyIII\Api\V1\Controllers\Autocomplete\RecurrenceController
*/
use RefreshDatabase;
private function createTestTags(int $count, User $user): void
{
for ($i = 1; $i <= $count; ++$i) {
$tag = Tag::create(
[
'user_id' => $user->id,
'user_group_id' => $user->user_group_id,
'tag' => 'Tag '.$i,
'tag_mode' => 'nothing',
]
);
}
}
public function testUnauthenticatedCall(): void
{
// test API
$response = $this->get(route('api.v1.autocomplete.tags'), ['Accept' => 'application/json']);
$response->assertStatus(401);
$response->assertHeader('Content-Type', 'application/json');
$response->assertContent('{"message":"Unauthenticated.","exception":"AuthenticationException"}');
}
public function testAuthenticatedCall(): void
{
// act as a user
$user = $this->createAuthenticatedUser();
$this->actingAs($user);
$response = $this->get(route('api.v1.autocomplete.tags'), ['Accept' => 'application/json']);
$response->assertStatus(200);
$response->assertHeader('Content-Type', 'application/json');
}
public function testGivenAuthenticatedRequestWithItems(): void
{
$user = $this->createAuthenticatedUser();
$this->actingAs($user);
$this->createTestTags(5, $user);
$response = $this->get(route('api.v1.autocomplete.tags'), ['Accept' => 'application/json']);
$response->assertStatus(200);
$response->assertHeader('Content-Type', 'application/json');
$response->assertJsonCount(5);
$response->assertJsonFragment(['name' => 'Tag 1']);
$response->assertJsonStructure([
'*' => [
'id',
'name',
'tag',
],
]);
}
public function testGivenAuthenticatedRequestWithItemsLimited(): void
{
$user = $this->createAuthenticatedUser();
$this->actingAs($user);
$this->createTestTags(5, $user);
$response = $this->get(route('api.v1.autocomplete.tags', [
'query' => 'Tag',
'limit' => 3,
]), ['Accept' => 'application/json']);
$response->assertStatus(200);
$response->assertHeader('Content-Type', 'application/json');
$response->assertJsonCount(3);
$response->assertJsonFragment(['name' => 'Tag 1']);
$response->assertJsonStructure([
'*' => [
'id',
'name',
'tag',
],
]);
}
public function testGivenAuthenticatedRequestWithItemsLots(): void
{
$user = $this->createAuthenticatedUser();
$this->actingAs($user);
$this->createTestTags(20, $user);
$response = $this->get(route('api.v1.autocomplete.tags', [
'query' => 'Tag 1',
'limit' => 20,
]), ['Accept' => 'application/json']);
$response->assertStatus(200);
$response->assertHeader('Content-Type', 'application/json');
// Bill 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 (11)
$response->assertJsonCount(11);
$response->assertJsonMissing(['name' => 'Tag 2']);
}
}

View File

@@ -1,5 +1,26 @@
<?php
/*
* AccountControllerTest.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Tests\integration\Api\Chart;

View File

@@ -1,5 +1,26 @@
<?php
/*
* BalanceControllerTest.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Tests\integration\Api\Chart;

View File

@@ -1,5 +1,26 @@
<?php
/*
* BudgetControllerTest.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Tests\integration\Api\Chart;

View File

@@ -1,5 +1,26 @@
<?php
/*
* CategoryControllerTest.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Tests\integration\Api\Chart;

View File

@@ -22,7 +22,7 @@
declare(strict_types=1);
namespace Tests\integration\Api\About;
namespace Tests\integration\Api\System;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Testing\Fluent\AssertableJson;

View File

@@ -1,5 +1,26 @@
<?php
/*
* AbstractQueryParserInterfaceParseQueryTester.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Tests\unit\Support\Search\QueryParser;

View File

@@ -1,5 +1,26 @@
<?php
/*
* QueryParserParseQueryTest.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Tests\unit\Support\Search\QueryParser;