Compare commits

...

8 Commits

Author SHA1 Message Date
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
9 changed files with 143 additions and 32 deletions

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

@@ -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

@@ -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) {

24
composer.lock generated
View File

@@ -10549,16 +10549,16 @@
},
{
"name": "driftingly/rector-laravel",
"version": "2.0.7",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/driftingly/rector-laravel.git",
"reference": "625dc02cee08d47ecf0ac86de2f02a55026cf34e"
"reference": "efb636a08dfddfa2a3f4527b1dd970a898a075a4"
},
"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/efb636a08dfddfa2a3f4527b1dd970a898a075a4",
"reference": "efb636a08dfddfa2a3f4527b1dd970a898a075a4",
"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.0"
},
"time": "2025-08-19T20:49:47+00:00"
"time": "2025-10-12T21:51:39+00:00"
},
{
"name": "fakerphp/faker",
@@ -11920,16 +11920,16 @@
},
{
"name": "rector/rector",
"version": "2.2.2",
"version": "2.2.3",
"source": {
"type": "git",
"url": "https://github.com/rectorphp/rector.git",
"reference": "5b353f7457b9a0c63fc91ef340f5d119a40991ed"
"reference": "d27f976a332a87b5d03553c2e6f04adbe5da034f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/5b353f7457b9a0c63fc91ef340f5d119a40991ed",
"reference": "5b353f7457b9a0c63fc91ef340f5d119a40991ed",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/d27f976a332a87b5d03553c2e6f04adbe5da034f",
"reference": "d27f976a332a87b5d03553c2e6f04adbe5da034f",
"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.3"
},
"funding": [
{
@@ -11976,7 +11976,7 @@
"type": "github"
}
],
"time": "2025-10-09T19:50:20+00:00"
"time": "2025-10-11T21:50:23+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-13',
'build_time' => 1760325798,
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 28, // field is no longer used.

18
package-lock.json generated
View File

@@ -3173,9 +3173,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.7.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.1.tgz",
"integrity": "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q==",
"version": "24.7.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.2.tgz",
"integrity": "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4521,9 +4521,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001749",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001749.tgz",
"integrity": "sha512-0rw2fJOmLfnzCRbkm8EyHL8SvI2Apu5UbnQuTsJ0ClgrH8hcwFooJ1s5R0EP8o8aVrFu8++ae29Kt9/gZAZp/Q==",
"version": "1.0.30001750",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001750.tgz",
"integrity": "sha512-cuom0g5sdX6rw00qOoLNSFCJ9/mYIsuSOA+yzpDw8eopiFqcVwQvZHqov0vmEighRxX++cfC0Vg1G+1Iy/mSpQ==",
"dev": true,
"funding": [
{
@@ -5820,9 +5820,9 @@
}
},
"node_modules/envinfo": {
"version": "7.17.0",
"resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.17.0.tgz",
"integrity": "sha512-GpfViocsFM7viwClFgxK26OtjMlKN67GCR5v6ASFkotxtpBWd9d+vNy+AH7F2E1TUkMDZ8P/dDPZX71/NG8xnQ==",
"version": "7.18.0",
"resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.18.0.tgz",
"integrity": "sha512-02QGCLRW+Jb8PC270ic02lat+N57iBaWsvHjcJViqp6UVupRB+Vsg7brYPTqEFXvsdTql3KnSczv5ModZFpl8Q==",
"dev": true,
"license": "MIT",
"bin": {

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

@@ -28,7 +28,6 @@ namespace Tests\integration\Api\Models\Account;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Models\Account;
use FireflyIII\User;
use Override;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\integration\TestCase;