Compare commits

..

17 Commits

Author SHA1 Message Date
github-actions[bot]
f89f50c2db Merge pull request #11101 from firefly-iii/release-1761383546
🤖 Automatically merge the PR into the develop branch.
2025-10-25 11:12:35 +02:00
JC5
394df46961 🤖 Auto commit for release 'develop' on 2025-10-25 2025-10-25 11:12:26 +02:00
James Cole
535e5e4f50 Catch null pointer. 2025-10-25 11:08:07 +02:00
James Cole
00ba2a46d2 Fix #11063 2025-10-25 09:20:21 +02:00
James Cole
a3ff26e3e4 Fix https://github.com/firefly-iii/firefly-iii/issues/11096 2025-10-23 16:54:13 +02:00
github-actions[bot]
54aeb4b4ef Merge pull request #11084 from firefly-iii/release-1760930902
🤖 Automatically merge the PR into the develop branch.
2025-10-20 05:28:30 +02:00
JC5
9eb3ad62dd 🤖 Auto commit for release 'develop' on 2025-10-20 2025-10-20 05:28:22 +02:00
github-actions[bot]
4f0e978687 Merge pull request #11060 from firefly-iii/release-1760325917
🤖 Automatically merge the PR into the develop branch.
2025-10-13 05:25:26 +02:00
JC5
e1cf9f7a79 🤖 Auto commit for release 'develop' on 2025-10-13 2025-10-13 05:25:17 +02:00
James Cole
7ce055a22c Merge pull request #11056 from ctrl-f5/feat/account-attachment-list-request
account/attachments endpoint use request object for pagination, add test
2025-10-12 20:15:00 +02:00
Nicky De Maeyer
7bd915930c account/attachments endpoint use request object for pagination, add test 2025-10-12 19:54:47 +02:00
github-actions[bot]
75aa2d99fd Merge pull request #11055 from firefly-iii/release-1760278017
🤖 Automatically merge the PR into the develop branch.
2025-10-12 16:07:07 +02:00
JC5
f52bc0e242 🤖 Auto commit for release 'develop' on 2025-10-12 2025-10-12 16:06:57 +02:00
James Cole
55cf924794 Another fix for #11054 2025-10-12 16:02:14 +02:00
James Cole
df3e4a6554 Fix #11054 2025-10-12 12:25:21 +02:00
James Cole
7c4ada458e Merge pull request #11052 from ctrl-f5/fix/incorrect-validator-function
correct validator function to check for errors + `Account\ShowControllerTest`
2025-10-12 06:47:19 +02:00
Nicky De Maeyer
2a4a98dd10 use the correct validator function to check for errors, add a test for ShowController 2025-10-11 23:02:54 +02:00
19 changed files with 688 additions and 454 deletions

View File

@@ -402,16 +402,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.88.2",
"version": "v3.89.1",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99"
"reference": "f34967da2866ace090a2b447de1f357356474573"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a8d15584bafb0f0d9d938827840060fd4a3ebc99",
"reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/f34967da2866ace090a2b447de1f357356474573",
"reference": "f34967da2866ace090a2b447de1f357356474573",
"shasum": ""
},
"require": {
@@ -426,7 +426,6 @@
"php": "^7.4 || ^8.0",
"react/child-process": "^0.6.6",
"react/event-loop": "^1.5",
"react/promise": "^3.3",
"react/socket": "^1.16",
"react/stream": "^1.4",
"sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0",
@@ -494,7 +493,7 @@
],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.88.2"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.89.1"
},
"funding": [
{
@@ -502,7 +501,7 @@
"type": "github"
}
],
"time": "2025-09-27T00:24:15+00:00"
"time": "2025-10-24T12:05:10+00:00"
},
{
"name": "psr/container",

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\PaginationRequest;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
@@ -69,22 +70,25 @@ class ListController extends Controller
);
}
public function attachments(Account $account): JsonResponse
public function attachments(Account $account, PaginationRequest $request): JsonResponse
{
$manager = $this->getManager();
$pageSize = $this->parameters->get('limit');
[
'limit' => $limit,
'offset' => $offset,
'page' => $page,
] = $request->attributes->all();
$collection = $this->repository->getAttachments($account);
$count = $collection->count();
$attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
$attachments = $collection->slice($offset, $limit);
// make paginator:
$paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page'));
$paginator = new LengthAwarePaginator($attachments, $count, $limit, $page);
$paginator->setPath(route('api.v1.accounts.attachments', [$account->id]).$this->buildParams());
/** @var AttachmentTransformer $transformer */
$transformer = app(AttachmentTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($attachments, $transformer, 'attachments');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));

View File

@@ -172,7 +172,6 @@ class ShowController extends Controller
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($budgetLimit, $transformer, 'budget_limits');

View File

@@ -39,7 +39,7 @@ class DateRangeRequest extends ApiRequest
{
$validator->after(
function (Validator $validator): void {
if (!$validator->valid()) {
if ($validator->failed()) {
// set null values
$this->attributes->set('start', null);
$this->attributes->set('end', null);

View File

@@ -39,8 +39,7 @@ class DateRequest extends ApiRequest
{
$validator->after(
function (Validator $validator): void {
$this->attributes->set('date', null);
if (!$validator->valid()) {
if ($validator->failed()) {
return;
}
$date = $this->getCarbonDate('date')?->endOfDay();

View File

@@ -42,7 +42,7 @@ class AccountTypeApiRequest extends ApiRequest
{
$validator->after(
function (Validator $validator): void {
if (!$validator->valid()) {
if ($validator->failed()) {
return;
}

View File

@@ -57,7 +57,7 @@ class PaginationRequest extends ApiRequest
{
$validator->after(
function (Validator $validator): void {
if (!$validator->valid()) {
if ($validator->failed()) {
return;
}

View File

@@ -84,8 +84,10 @@ class AttachmentFactory
return $attachment;
}
public function setUser(User $user): void
public function setUser(User $user): static
{
$this->user = $user;
return $this;
}
}

View File

@@ -221,10 +221,26 @@ class TransactionJournalFactory
];
Log::debug('Source info:', $sourceInfo);
Log::debug('Destination info:', $destInfo);
$sourceAccount = $this->getAccount($type->type, 'source', $sourceInfo);
$destinationAccount = $this->getAccount($type->type, 'destination', $destInfo, $sourceAccount);
$destinationAccount = null;
$sourceAccount = null;
if (TransactionTypeEnum::DEPOSIT->value === $type->type) {
Log::debug('Transaction type is deposit, start with destination first.');
$destinationAccount = $this->getAccount($type->type, 'destination', $destInfo);
$sourceAccount = $this->getAccount($type->type, 'source', $sourceInfo, $destinationAccount);
}
if (TransactionTypeEnum::DEPOSIT->value !== $type->type) {
Log::debug('Transaction type is not deposit, start with source first.');
$sourceAccount = $this->getAccount($type->type, 'source', $sourceInfo);
$destinationAccount = $this->getAccount($type->type, 'destination', $destInfo, $sourceAccount);
}
Log::debug('Done with getAccount(2x)');
// there is a safety catch here. If either account is NULL, they will be replaced with the cash account.
if (null === $destinationAccount) {
Log::warning('Destination account is NULL, will replace with cash account.');
$destinationAccount = $this->accountRepository->getCashAccount();
}
// this is the moment for a reconciliation sanity check (again).
if (TransactionTypeEnum::RECONCILIATION->value === $type->type) {

View File

@@ -27,6 +27,7 @@ use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Events\RequestedSendWebhookMessages;
use FireflyIII\Events\StoredTransactionGroup;
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
@@ -105,10 +106,18 @@ class StoredGroupEventHandler
/** @var TransactionJournal $journal */
foreach ($event->transactionGroup->transactionJournals as $journal) {
/** @var null|Transaction $source */
$source = $journal->transactions()->where('amount', '<', '0')->first();
/** @var null|Transaction $dest */
$dest = $journal->transactions()->where('amount', '>', '0')->first();
$repository->deleteStatisticsForModel($source->account, $journal->date);
$repository->deleteStatisticsForModel($dest->account, $journal->date);
if (null !== $source) {
$repository->deleteStatisticsForModel($source->account, $journal->date);
}
if (null !== $dest) {
$repository->deleteStatisticsForModel($dest->account, $journal->date);
}
$categories = $journal->categories;
$tags = $journal->tags;
$budgets = $journal->budgets;

View File

@@ -88,6 +88,7 @@ trait JournalServiceTrait
// the account that Firefly III creates must be "creatable", aka select the one we can create from the list just in case
$creatableType = $this->getCreatableType($expectedTypes[$transactionType]);
Log::debug(sprintf('Creatable type is "%s"', $creatableType), $expectedTypes[$transactionType]);
// if the result is NULL but the ID is set, an account could exist of the wrong type.
// that data can be used to create a new account of the right type.
@@ -227,9 +228,11 @@ trait JournalServiceTrait
}
// find by preferred type.
Log::debug('Find by preferred type.');
$result = $this->accountRepository->findByName($data['name'], [$types[0]]);
// or any expected type.
Log::debug('Find by any expected type.');
$result ??= $this->accountRepository->findByName($data['name'], $types);
if (null !== $result) {

View File

@@ -634,10 +634,10 @@ trait PeriodOverview
$currencySymbol = $journal['currency_symbol'];
$currencyDecimalPlaces = $journal['currency_decimal_places'];
$foreignCurrencyId = $journal['foreign_currency_id'];
$amount = $journal['amount'] ?? '0';
$amount = (string) ($journal['amount'] ?? '0');
if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId !== $this->primaryCurrency->id) {
$amount = $journal['pc_amount'] ?? '0';
$amount = (string) ($journal['pc_amount'] ?? '0');
$currencyId = $this->primaryCurrency->id;
$currencyCode = $this->primaryCurrency->code;
$currencyName = $this->primaryCurrency->name;
@@ -650,7 +650,7 @@ trait PeriodOverview
$currencyName = $journal['foreign_currency_name'];
$currencySymbol = $journal['foreign_currency_symbol'];
$currencyDecimalPlaces = $journal['foreign_currency_decimal_places'];
$amount = $journal['foreign_amount'] ?? '0';
$amount = (string) ($journal['foreign_amount'] ?? '0');
}
$return[$currencyId] ??= [
'amount' => '0',

View File

@@ -154,6 +154,9 @@ class BudgetLimitEnrichment implements EnrichmentInterface
$this->start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth();
$this->end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth();
// #11096 make sure that the max end date is also at the end of the day,
$this->end->endOfDay();
/** @var BudgetLimit $limit */
foreach ($this->collection as $limit) {
$id = (int)$limit->id;

142
composer.lock generated
View File

@@ -1878,16 +1878,16 @@
},
{
"name": "laravel/framework",
"version": "v12.33.0",
"version": "v12.35.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "124efc5f09d4668a4dc13f94a1018c524a58bcb1"
"reference": "d6d6e3cb68238e2fb25b440f222442adef5a8a15"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/124efc5f09d4668a4dc13f94a1018c524a58bcb1",
"reference": "124efc5f09d4668a4dc13f94a1018c524a58bcb1",
"url": "https://api.github.com/repos/laravel/framework/zipball/d6d6e3cb68238e2fb25b440f222442adef5a8a15",
"reference": "d6d6e3cb68238e2fb25b440f222442adef5a8a15",
"shasum": ""
},
"require": {
@@ -1999,7 +1999,7 @@
"league/flysystem-sftp-v3": "^3.25.1",
"mockery/mockery": "^1.6.10",
"opis/json-schema": "^2.4.1",
"orchestra/testbench-core": "^10.6.5",
"orchestra/testbench-core": "^10.7.0",
"pda/pheanstalk": "^5.0.6|^7.0.0",
"php-http/discovery": "^1.15",
"phpstan/phpstan": "^2.0",
@@ -2093,7 +2093,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2025-10-07T14:30:39+00:00"
"time": "2025-10-23T15:25:03+00:00"
},
{
"name": "laravel/passport",
@@ -2296,16 +2296,16 @@
},
{
"name": "laravel/serializable-closure",
"version": "v2.0.5",
"version": "v2.0.6",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
"reference": "3832547db6e0e2f8bb03d4093857b378c66eceed"
"reference": "038ce42edee619599a1debb7e81d7b3759492819"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/3832547db6e0e2f8bb03d4093857b378c66eceed",
"reference": "3832547db6e0e2f8bb03d4093857b378c66eceed",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/038ce42edee619599a1debb7e81d7b3759492819",
"reference": "038ce42edee619599a1debb7e81d7b3759492819",
"shasum": ""
},
"require": {
@@ -2353,7 +2353,7 @@
"issues": "https://github.com/laravel/serializable-closure/issues",
"source": "https://github.com/laravel/serializable-closure"
},
"time": "2025-09-22T17:29:40+00:00"
"time": "2025-10-09T13:42:30+00:00"
},
{
"name": "laravel/slack-notification-channel",
@@ -2549,22 +2549,22 @@
},
{
"name": "lcobucci/jwt",
"version": "5.5.0",
"version": "5.6.0",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/jwt.git",
"reference": "a835af59b030d3f2967725697cf88300f579088e"
"reference": "bb3e9f21e4196e8afc41def81ef649c164bca25e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/a835af59b030d3f2967725697cf88300f579088e",
"reference": "a835af59b030d3f2967725697cf88300f579088e",
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/bb3e9f21e4196e8afc41def81ef649c164bca25e",
"reference": "bb3e9f21e4196e8afc41def81ef649c164bca25e",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"ext-sodium": "*",
"php": "~8.2.0 || ~8.3.0 || ~8.4.0",
"php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
"psr/clock": "^1.0"
},
"require-dev": {
@@ -2606,7 +2606,7 @@
],
"support": {
"issues": "https://github.com/lcobucci/jwt/issues",
"source": "https://github.com/lcobucci/jwt/tree/5.5.0"
"source": "https://github.com/lcobucci/jwt/tree/5.6.0"
},
"funding": [
{
@@ -2618,7 +2618,7 @@
"type": "patreon"
}
],
"time": "2025-01-26T21:29:45+00:00"
"time": "2025-10-17T11:30:53+00:00"
},
{
"name": "league/commonmark",
@@ -2811,16 +2811,16 @@
},
{
"name": "league/csv",
"version": "9.26.0",
"version": "9.27.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/csv.git",
"reference": "7fce732754d043f3938899e5183e2d0f3d31b571"
"reference": "26de738b8fccf785397d05ee2fc07b6cd8749797"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/7fce732754d043f3938899e5183e2d0f3d31b571",
"reference": "7fce732754d043f3938899e5183e2d0f3d31b571",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/26de738b8fccf785397d05ee2fc07b6cd8749797",
"reference": "26de738b8fccf785397d05ee2fc07b6cd8749797",
"shasum": ""
},
"require": {
@@ -2898,7 +2898,7 @@
"type": "github"
}
],
"time": "2025-10-01T11:24:54+00:00"
"time": "2025-10-25T08:35:20+00:00"
},
{
"name": "league/event",
@@ -2956,16 +2956,16 @@
},
{
"name": "league/flysystem",
"version": "3.30.0",
"version": "3.30.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
"reference": "2203e3151755d874bb2943649dae1eb8533ac93e"
"reference": "c139fd65c1f796b926f4aec0df37f6caa959a8da"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/2203e3151755d874bb2943649dae1eb8533ac93e",
"reference": "2203e3151755d874bb2943649dae1eb8533ac93e",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/c139fd65c1f796b926f4aec0df37f6caa959a8da",
"reference": "c139fd65c1f796b926f4aec0df37f6caa959a8da",
"shasum": ""
},
"require": {
@@ -3033,9 +3033,9 @@
],
"support": {
"issues": "https://github.com/thephpleague/flysystem/issues",
"source": "https://github.com/thephpleague/flysystem/tree/3.30.0"
"source": "https://github.com/thephpleague/flysystem/tree/3.30.1"
},
"time": "2025-06-25T13:29:59+00:00"
"time": "2025-10-20T15:35:26+00:00"
},
{
"name": "league/flysystem-local",
@@ -4069,31 +4069,31 @@
},
{
"name": "nunomaduro/termwind",
"version": "v2.3.1",
"version": "v2.3.2",
"source": {
"type": "git",
"url": "https://github.com/nunomaduro/termwind.git",
"reference": "dfa08f390e509967a15c22493dc0bac5733d9123"
"reference": "eb61920a53057a7debd718a5b89c2178032b52c0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nunomaduro/termwind/zipball/dfa08f390e509967a15c22493dc0bac5733d9123",
"reference": "dfa08f390e509967a15c22493dc0bac5733d9123",
"url": "https://api.github.com/repos/nunomaduro/termwind/zipball/eb61920a53057a7debd718a5b89c2178032b52c0",
"reference": "eb61920a53057a7debd718a5b89c2178032b52c0",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": "^8.2",
"symfony/console": "^7.2.6"
"symfony/console": "^7.3.4"
},
"require-dev": {
"illuminate/console": "^11.44.7",
"laravel/pint": "^1.22.0",
"illuminate/console": "^11.46.1",
"laravel/pint": "^1.25.1",
"mockery/mockery": "^1.6.12",
"pestphp/pest": "^2.36.0 || ^3.8.2",
"phpstan/phpstan": "^1.12.25",
"pestphp/pest": "^2.36.0 || ^3.8.4",
"phpstan/phpstan": "^1.12.32",
"phpstan/phpstan-strict-rules": "^1.6.2",
"symfony/var-dumper": "^7.2.6",
"symfony/var-dumper": "^7.3.4",
"thecodingmachine/phpstan-strict-rules": "^1.0.0"
},
"type": "library",
@@ -4136,7 +4136,7 @@
],
"support": {
"issues": "https://github.com/nunomaduro/termwind/issues",
"source": "https://github.com/nunomaduro/termwind/tree/v2.3.1"
"source": "https://github.com/nunomaduro/termwind/tree/v2.3.2"
},
"funding": [
{
@@ -4152,7 +4152,7 @@
"type": "github"
}
],
"time": "2025-05-08T08:14:37+00:00"
"time": "2025-10-18T11:10:27+00:00"
},
{
"name": "nyholm/psr7",
@@ -10052,28 +10052,28 @@
},
{
"name": "webmozart/assert",
"version": "1.11.0",
"version": "1.12.0",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
"reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991"
"reference": "541057574806f942c94662b817a50f63f7345360"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991",
"reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/541057574806f942c94662b817a50f63f7345360",
"reference": "541057574806f942c94662b817a50f63f7345360",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"ext-date": "*",
"ext-filter": "*",
"php": "^7.2 || ^8.0"
},
"conflict": {
"phpstan/phpstan": "<0.12.20",
"vimeo/psalm": "<4.6.1 || 4.6.2"
},
"require-dev": {
"phpunit/phpunit": "^8.5.13"
"suggest": {
"ext-intl": "",
"ext-simplexml": "",
"ext-spl": ""
},
"type": "library",
"extra": {
@@ -10104,9 +10104,9 @@
],
"support": {
"issues": "https://github.com/webmozarts/assert/issues",
"source": "https://github.com/webmozarts/assert/tree/1.11.0"
"source": "https://github.com/webmozarts/assert/tree/1.12.0"
},
"time": "2022-06-03T18:03:27+00:00"
"time": "2025-10-20T12:43:39+00:00"
}
],
"packages-dev": [
@@ -10549,16 +10549,16 @@
},
{
"name": "driftingly/rector-laravel",
"version": "2.0.7",
"version": "2.1.1",
"source": {
"type": "git",
"url": "https://github.com/driftingly/rector-laravel.git",
"reference": "625dc02cee08d47ecf0ac86de2f02a55026cf34e"
"reference": "abc336cbf06f53d90ab74cecfd319379fc55d408"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/625dc02cee08d47ecf0ac86de2f02a55026cf34e",
"reference": "625dc02cee08d47ecf0ac86de2f02a55026cf34e",
"url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/abc336cbf06f53d90ab74cecfd319379fc55d408",
"reference": "abc336cbf06f53d90ab74cecfd319379fc55d408",
"shasum": ""
},
"require": {
@@ -10578,9 +10578,9 @@
"description": "Rector upgrades rules for Laravel Framework",
"support": {
"issues": "https://github.com/driftingly/rector-laravel/issues",
"source": "https://github.com/driftingly/rector-laravel/tree/2.0.7"
"source": "https://github.com/driftingly/rector-laravel/tree/2.1.1"
},
"time": "2025-08-19T20:49:47+00:00"
"time": "2025-10-23T13:53:44+00:00"
},
{
"name": "fakerphp/faker",
@@ -11036,16 +11036,16 @@
},
{
"name": "nikic/php-parser",
"version": "v5.6.1",
"version": "v5.6.2",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2"
"reference": "3a454ca033b9e06b63282ce19562e892747449bb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2",
"reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb",
"reference": "3a454ca033b9e06b63282ce19562e892747449bb",
"shasum": ""
},
"require": {
@@ -11088,9 +11088,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1"
"source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2"
},
"time": "2025-08-13T20:13:15+00:00"
"time": "2025-10-21T19:32:17+00:00"
},
{
"name": "phar-io/manifest",
@@ -11920,16 +11920,16 @@
},
{
"name": "rector/rector",
"version": "2.2.2",
"version": "2.2.5",
"source": {
"type": "git",
"url": "https://github.com/rectorphp/rector.git",
"reference": "5b353f7457b9a0c63fc91ef340f5d119a40991ed"
"reference": "fb9418af7777dfb1c87a536dc58398b5b07c74b9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/5b353f7457b9a0c63fc91ef340f5d119a40991ed",
"reference": "5b353f7457b9a0c63fc91ef340f5d119a40991ed",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/fb9418af7777dfb1c87a536dc58398b5b07c74b9",
"reference": "fb9418af7777dfb1c87a536dc58398b5b07c74b9",
"shasum": ""
},
"require": {
@@ -11968,7 +11968,7 @@
],
"support": {
"issues": "https://github.com/rectorphp/rector/issues",
"source": "https://github.com/rectorphp/rector/tree/2.2.2"
"source": "https://github.com/rectorphp/rector/tree/2.2.5"
},
"funding": [
{
@@ -11976,7 +11976,7 @@
"type": "github"
}
],
"time": "2025-10-09T19:50:20+00:00"
"time": "2025-10-23T11:22:37+00:00"
},
{
"name": "sebastian/cli-parser",

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-10-11',
'build_time' => 1760188898,
'version' => 'develop/2025-10-25',
'build_time' => 1761383422,
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 28, // field is no longer used.

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Database\Factories;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Models\AccountType;
use Illuminate\Database\Eloquent\Factories\Factory;
class AccountFactory extends Factory
{
public function definition(): array
{
return [
'name' => $this->faker->name(),
'active' => true,
];
}
public function withType(AccountTypeEnum $type): static
{
return $this->for(AccountType::where('type', $type->value)->first());
}
}

710
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
<?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\Models\Account;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Factory\AttachmentFactory;
use FireflyIII\Models\Account;
use FireflyIII\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\integration\TestCase;
/**
* @internal
*
* @covers \FireflyIII\Api\V1\Controllers\Models\Account\ListController
*/
final class ListControllerTest extends TestCase
{
use RefreshDatabase;
private User $user;
private Account $account;
protected function setUp(): void
{
parent::setUp();
$this->user = $this->createAuthenticatedUser();
$this->actingAs($this->user);
$this->account = Account::factory()->for($this->user)->withType(AccountTypeEnum::ASSET)->create();
app(AttachmentFactory::class)->setUser($this->user)->create([
'filename' => 'test 1',
'title' => 'test 1',
'attachable_type' => Account::class,
'attachable_id' => $this->account->id,
]);
app(AttachmentFactory::class)->setUser($this->user)->create([
'filename' => 'test 2',
'title' => 'test 2',
'attachable_type' => Account::class,
'attachable_id' => $this->account->id,
]);
}
public function testIndex(): void
{
$this->actingAs($this->user);
$response = $this->getJson(route('api.v1.accounts.attachments', ['account' => $this->account->id]));
$response->assertStatus(200);
$response->assertJson([
'meta' => ['pagination' => ['total' => 2, 'total_pages' => 1]],
]);
}
public function testIndexCanChangePageSize(): void
{
$this->actingAs($this->user);
$response = $this->getJson(route('api.v1.accounts.attachments', ['account' => $this->account->id, 'limit' => 1]));
$response->assertStatus(200);
$response->assertJson([
'meta' => ['pagination' => ['total' => 2, 'total_pages' => 2]],
]);
}
}

View File

@@ -0,0 +1,89 @@
<?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\Models\Account;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Models\Account;
use FireflyIII\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\integration\TestCase;
/**
* @internal
*
* @covers \FireflyIII\Api\V1\Controllers\Models\Account\ShowController
*/
final class ShowControllerTest extends TestCase
{
use RefreshDatabase;
private User $user;
protected function setUp(): void
{
parent::setUp();
$this->user = $this->createAuthenticatedUser();
$this->actingAs($this->user);
Account::factory()->for($this->user)->withType(AccountTypeEnum::ASSET)->create();
Account::factory()->for($this->user)->withType(AccountTypeEnum::REVENUE)->create();
Account::factory()->for($this->user)->withType(AccountTypeEnum::EXPENSE)->create();
Account::factory()->for($this->user)->withType(AccountTypeEnum::DEBT)->create();
Account::factory()->for($this->user)->withType(AccountTypeEnum::ASSET)->create();
}
public function testIndex(): void
{
$this->actingAs($this->user);
$response = $this->getJson(route('api.v1.accounts.index'));
$response->assertStatus(200);
$response->assertJson([
'meta' => ['pagination' => ['total' => 5]],
]);
}
public function testIndexFailsOnUnknownAccountType(): void
{
$this->actingAs($this->user);
$response = $this->getJson(route('api.v1.accounts.index').'?type=foobar');
$response->assertStatus(422);
$response->assertJson(['errors' => ['type' => ['The selected type is invalid.']]]);
}
public function testIndexCanFilterOnAccountType(): void
{
$this->actingAs($this->user);
$response = $this->getJson(route('api.v1.accounts.index').'?type=asset');
$response->assertStatus(200);
$response->assertJson([
'data' => [
['attributes' => ['type' => 'asset']],
['attributes' => ['type' => 'asset']],
],
'meta' => ['pagination' => ['total' => 2]],
]);
}
}