mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-02-22 11:27:00 +00:00
Compare commits
6 Commits
develop-20
...
develop-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80823cdfe3 | ||
|
|
defaef171e | ||
|
|
81f6f22efb | ||
|
|
4ad2508675 | ||
|
|
35f997be45 | ||
|
|
ad3fec1458 |
6
.github/ISSUE_TEMPLATE/bug.yml
vendored
6
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -4,9 +4,9 @@ body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Support guidelines
|
||||
description: Please read the support guidelines before proceeding.
|
||||
description: Thank you for reading the support guidelines before proceeding.
|
||||
options:
|
||||
- label: I've read the <!-- MZ2udTpin6FL --> [support guidelines](https://github.com/firefly-iii/firefly-iii/blob/main/.github/support.md)
|
||||
- label: I'm smart and I read the <!-- MZ2udTpin6FL --> [support guidelines](https://github.com/firefly-iii/firefly-iii/blob/main/.github/support.md)
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
@@ -29,7 +29,7 @@ body:
|
||||
attributes:
|
||||
label: Debug information
|
||||
description: Please provide the table from the /debug page. Do not add backticks or quotes.
|
||||
placeholder: The output from the /debug page
|
||||
placeholder: The output from the /debug page or "N/A"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
18
.github/pull_request_template.md
vendored
18
.github/pull_request_template.md
vendored
@@ -1,14 +1,14 @@
|
||||
<!--
|
||||
Thank you for submitting new code to Firefly III, or any of the related projects. Please read the following rules carefully.
|
||||
|
||||
- Please do not submit solutions for problems that are not already reported in an issue.
|
||||
- Unfortunately, Firefly III can't be your learning experience. If you're new to all of this, please open an issue first.
|
||||
- Please do not open PRs to "discuss" possible solutions or to "get feedback" on your code. I simply don't have time for that.
|
||||
- Pull requests for the MAIN branch will be closed.
|
||||
- DO NOT include translated strings in your PR.
|
||||
- PRs (or parts thereof) that only fix issues inside code comments will not be accepted.
|
||||
Please TALK TO ME FIRST before you open a PR.
|
||||
|
||||
If it feels necessary to open an issue first, please do so, before you open a PR.
|
||||
1. If you fix a problem that has no ticket, talk to me FIRST.
|
||||
2. If you introduce new financial solutions or concepts, talk to me FIRST.
|
||||
3. If your PR is more than 25 lines, talk to me FIRST.
|
||||
4. If you used AI to write your PR, talk to me FIRST.
|
||||
5. If you fix spelling or code comments, talk to me FIRST.
|
||||
|
||||
Wanna talk to me? Open a GitHub Issue, Discussion, or send me an email: james@firefly-iii.org
|
||||
|
||||
See also: https://docs.firefly-iii.org/explanation/support/#contributing-code
|
||||
|
||||
@@ -16,7 +16,7 @@ See also: https://docs.firefly-iii.org/explanation/support/#contributing-code
|
||||
|
||||
@JC5
|
||||
|
||||
This PR fixes issue # (if relevant).
|
||||
This PR fixes issue # <!-- mandatory field! -->.
|
||||
|
||||
Changes in this pull request:
|
||||
|
||||
|
||||
@@ -25,11 +25,15 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Api\V1\Controllers\Search;
|
||||
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Search\CountRequest;
|
||||
use FireflyIII\Api\V1\Requests\Search\TransactionSearchRequest;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment;
|
||||
use FireflyIII\Support\Search\SearchInterface;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection;
|
||||
|
||||
@@ -38,6 +42,51 @@ use League\Fractal\Resource\Collection;
|
||||
*/
|
||||
class TransactionController extends Controller
|
||||
{
|
||||
private JournalRepositoryInterface $repository;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(function ($request, $next) {
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
|
||||
$this->repository = app(JournalRepositoryInterface::class);
|
||||
$this->repository->setUser($admin);
|
||||
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function count(CountRequest $request, SearchInterface $searcher): JsonResponse
|
||||
{
|
||||
$count = 0;
|
||||
$includeDeleted = $request->attributes->get('include_deleted', false);
|
||||
$externalId = (string) $request->attributes->get('external_identifier');
|
||||
$internalRef = (string) $request->attributes->get('internal_reference');
|
||||
$notes = (string) $request->attributes->get('notes');
|
||||
$description = (string) $request->attributes->get('description');
|
||||
Log::debug(sprintf('Include deleted? %s', var_export($includeDeleted, true)));
|
||||
if ('' !== $externalId) {
|
||||
$count += $this->repository->countByMeta('external_identifier', $externalId, $includeDeleted);
|
||||
Log::debug(sprintf('Search for transactions with external_identifier "%s", count is now %d', $externalId, $count));
|
||||
}
|
||||
if ('' !== $internalRef) {
|
||||
$count += $this->repository->countByMeta('internal_reference', $internalRef, $includeDeleted);
|
||||
Log::debug(sprintf('Search for transactions with internal_reference "%s", count is now %d', $internalRef, $count));
|
||||
}
|
||||
if ('' !== $notes) {
|
||||
$count += $this->repository->countByNotes($notes, $includeDeleted);
|
||||
Log::debug(sprintf('Search for transactions with notes LIKE "%s", count is now %d', $notes, $count));
|
||||
}
|
||||
if ('' !== $description) {
|
||||
$count += $this->repository->countByDescription($description, $includeDeleted);
|
||||
Log::debug(sprintf('Search for transactions with description "%s", count is now %d', $description, $count));
|
||||
}
|
||||
|
||||
return response()->json(['count' => $count]);
|
||||
}
|
||||
|
||||
/**
|
||||
* This endpoint is documented at:
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/search/searchTransactions
|
||||
|
||||
64
app/Api/V1/Requests/Search/CountRequest.php
Normal file
64
app/Api/V1/Requests/Search/CountRequest.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* SearchRequest.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests\Search;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\AggregateFormRequest;
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Override;
|
||||
|
||||
class CountRequest extends AggregateFormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'notes' => 'string|min:1|max:255',
|
||||
'external_identifier' => 'string|min:1|max:255',
|
||||
'description' => 'string|min:1|max:255',
|
||||
'internal_reference' => 'string|min:1|max:255',
|
||||
'include_deleted' => new IsBoolean(),
|
||||
];
|
||||
}
|
||||
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(function (Validator $validator): void {
|
||||
if ($validator->failed()) {
|
||||
return;
|
||||
}
|
||||
$this->attributes->set('include_deleted', $this->convertBoolean($this->input('include_deleted', 'false')));
|
||||
$this->attributes->set('notes', $this->convertString('notes'));
|
||||
$this->attributes->set('external_identifier', $this->convertString('external_identifier'));
|
||||
$this->attributes->set('description', $this->convertString('description'));
|
||||
$this->attributes->set('internal_reference', $this->convertString('internal_reference'));
|
||||
});
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function getRequests(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,47 @@ class JournalRepository implements JournalRepositoryInterface, UserGroupInterfac
|
||||
{
|
||||
use UserGroupTrait;
|
||||
|
||||
#[Override]
|
||||
public function countByDescription(string $value, bool $includeDeleted): int
|
||||
{
|
||||
$search = $this->user->transactionJournals()->where('description', $value);
|
||||
if ($includeDeleted) {
|
||||
$search->withTrashed();
|
||||
}
|
||||
|
||||
return $search->count();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function countByMeta(string $field, string $value, bool $includeDeleted): int
|
||||
{
|
||||
$search = TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
|
||||
->where('name', $field)
|
||||
->where('data', json_encode($value))
|
||||
->where('transaction_journals.user_id', $this->user->id)
|
||||
;
|
||||
if ($includeDeleted) {
|
||||
$search->withTrashed();
|
||||
}
|
||||
|
||||
return $search->count();
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function countByNotes(string $value, bool $includeDeleted): int
|
||||
{
|
||||
$search = Note::where('noteable_type', TransactionJournal::class)
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'notes.noteable_id')
|
||||
->where('transaction_journals.user_id', $this->user->id)
|
||||
->where('text', 'LIKE', sprintf('%%%s%%', $value))
|
||||
;
|
||||
if ($includeDeleted) {
|
||||
$search->withTrashed();
|
||||
}
|
||||
|
||||
return $search->count();
|
||||
}
|
||||
|
||||
public function destroyGroup(TransactionGroup $transactionGroup): void
|
||||
{
|
||||
/** @var TransactionGroupDestroyService $service */
|
||||
|
||||
@@ -47,6 +47,12 @@ use Illuminate\Support\Collection;
|
||||
*/
|
||||
interface JournalRepositoryInterface
|
||||
{
|
||||
public function countByDescription(string $value, bool $includeDeleted): int;
|
||||
|
||||
public function countByMeta(string $field, string $value, bool $includeDeleted): int;
|
||||
|
||||
public function countByNotes(string $value, bool $includeDeleted): int;
|
||||
|
||||
/**
|
||||
* Deletes a transaction group.
|
||||
*/
|
||||
|
||||
@@ -182,6 +182,10 @@ class ExportDataGenerator
|
||||
|
||||
// @phpstan-ignore-line
|
||||
|
||||
// @phpstan-ignore-line
|
||||
|
||||
// @phpstan-ignore-line
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->accounts = new Collection();
|
||||
|
||||
@@ -14,15 +14,15 @@ And yes, despite my goal not to change things, some very clever users (that's yo
|
||||
- Support for PHP 8.5
|
||||
|
||||
### Changed
|
||||
- #11776
|
||||
- [PR 11776](https://github.com/firefly-iii/firefly-iii/pull/11776) (Convert to primary currency for charts) reported by @dakennguyen
|
||||
- The update check now contacts GitHub directly.
|
||||
|
||||
### Removed
|
||||
- Support for PHP 8.4 and earlier
|
||||
|
||||
### Fixed
|
||||
- #11685
|
||||
- #11778
|
||||
- [Discussion 11685](https://github.com/orgs/firefly-iii/discussions/11685) (Yearly budget best practices) started by @molnarti
|
||||
- [Issue 11778](https://github.com/firefly-iii/firefly-iii/issues/11778) (API update rule trigger only accepts "store-journal") reported by @jhns-de
|
||||
- Test notification was broken for system owners.
|
||||
|
||||
## v6.4.23 - 2026-02-20
|
||||
|
||||
@@ -78,8 +78,8 @@ return [
|
||||
'running_balance_column' => (bool)envNonEmpty('USE_RUNNING_BALANCE', true), // this is only the default value, is not used.
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => 'develop/2026-02-21',
|
||||
'build_time' => 1771686731,
|
||||
'version' => 'develop/2026-02-22',
|
||||
'build_time' => 1771741082,
|
||||
'api_version' => '2.1.0', // field is no longer used.
|
||||
'db_version' => 28, // field is no longer used.
|
||||
|
||||
|
||||
12
package-lock.json
generated
12
package-lock.json
generated
@@ -4597,9 +4597,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001770",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz",
|
||||
"integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==",
|
||||
"version": "1.0.30001772",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001772.tgz",
|
||||
"integrity": "sha512-mIwLZICj+ntVTw4BT2zfp+yu/AqV6GMKfJVJMx3MwPxs+uk/uj2GLl2dH8LQbjiLDX66amCga5nKFyDgRR43kg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -8328,9 +8328,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz",
|
||||
"integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
|
||||
@@ -699,6 +699,7 @@ Route::group(
|
||||
],
|
||||
static function (): void {
|
||||
Route::get('transactions', ['uses' => 'TransactionController@search', 'as' => 'transactions']);
|
||||
Route::get('transactions/count', ['uses' => 'TransactionController@count', 'as' => 'count']);
|
||||
Route::get('accounts', ['uses' => 'AccountController@search', 'as' => 'accounts']);
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user