Compare commits

..

20 Commits

Author SHA1 Message Date
github-actions
40de147611 Auto commit for release 'develop' on 2024-10-28 2024-10-28 04:15:04 +01:00
James Cole
bb4f90d730 Fix nullpointer 2024-10-27 10:49:55 +01:00
github-actions
d89d46aaec Auto commit for release 'develop' on 2024-10-23 2024-10-23 06:53:06 +02:00
James Cole
304d720c4c Merge branch 'main' into develop 2024-10-21 20:16:12 +02:00
James Cole
7eff160190 Fix https://github.com/firefly-iii/firefly-iii/issues/9303 2024-10-21 20:15:43 +02:00
James Cole
8b2e18ed9d Merge pull request #9383 from firefly-iii/dependabot/github_actions/github/command-1.2.2 2024-10-21 05:46:04 +02:00
github-actions
7001051833 Auto commit for release 'develop' on 2024-10-21 2024-10-21 05:15:16 +02:00
dependabot[bot]
b4b9752c05 Bump github/command from 1.2.1 to 1.2.2
Bumps [github/command](https://github.com/github/command) from 1.2.1 to 1.2.2.
- [Release notes](https://github.com/github/command/releases)
- [Commits](https://github.com/github/command/compare/v1.2.1...v1.2.2)

---
updated-dependencies:
- dependency-name: github/command
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-21 03:13:11 +00:00
James Cole
acadc89eaa Clean up API routes 2024-10-20 17:55:06 +02:00
James Cole
6ff84b8e90 Clean up autocomplete controller (accounts) 2024-10-20 10:16:54 +02:00
James Cole
7f3e3fc3bf Fix https://github.com/firefly-iii/firefly-iii/issues/9360 2024-10-18 07:35:19 +02:00
James Cole
02233fd7a4 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2024-10-14 19:55:54 +02:00
James Cole
50d3db0643 Minor update 2024-10-14 19:55:43 +02:00
James Cole
3751831779 Merge pull request #9349 from firefly-iii/dependabot/composer/develop/phpunit/phpunit-10.5.36
Bump phpunit/phpunit from 10.5.35 to 10.5.36
2024-10-14 09:33:49 +02:00
James Cole
14a24e47fb Merge pull request #9350 from firefly-iii/dependabot/composer/develop/laravel/framework-11.27.2 2024-10-14 09:18:24 +02:00
James Cole
b7e78cb0e6 Merge pull request #9351 from firefly-iii/dependabot/npm_and_yarn/develop/chartjs-chart-sankey-0.13.0 2024-10-14 09:18:06 +02:00
dependabot[bot]
a8f65f42fc Bump chartjs-chart-sankey from 0.12.1 to 0.13.0
Bumps [chartjs-chart-sankey](https://github.com/kurkle/chartjs-chart-sankey) from 0.12.1 to 0.13.0.
- [Release notes](https://github.com/kurkle/chartjs-chart-sankey/releases)
- [Commits](https://github.com/kurkle/chartjs-chart-sankey/compare/v0.12.1...v0.13.0)

---
updated-dependencies:
- dependency-name: chartjs-chart-sankey
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-14 03:22:09 +00:00
dependabot[bot]
d3385a116d Bump laravel/framework from 11.26.0 to 11.27.2
Bumps [laravel/framework](https://github.com/laravel/framework) from 11.26.0 to 11.27.2.
- [Release notes](https://github.com/laravel/framework/releases)
- [Changelog](https://github.com/laravel/framework/blob/11.x/CHANGELOG.md)
- [Commits](https://github.com/laravel/framework/compare/v11.26.0...v11.27.2)

---
updated-dependencies:
- dependency-name: laravel/framework
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-14 03:14:55 +00:00
dependabot[bot]
33d11b4780 Bump phpunit/phpunit from 10.5.35 to 10.5.36
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 10.5.35 to 10.5.36.
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/10.5.36/ChangeLog-10.5.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/10.5.35...10.5.36)

---
updated-dependencies:
- dependency-name: phpunit/phpunit
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-14 03:14:44 +00:00
James Cole
07c49d1d04 Update stale action. 2024-10-10 07:20:34 +02:00
31 changed files with 1363 additions and 1361 deletions

View File

@@ -1259,16 +1259,16 @@
},
{
"name": "symfony/console",
"version": "v7.1.5",
"version": "v7.1.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee"
"reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/0fa539d12b3ccf068a722bbbffa07ca7079af9ee",
"reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee",
"url": "https://api.github.com/repos/symfony/console/zipball/bb5192af6edc797cbab5c8e8ecfea2fe5f421e57",
"reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57",
"shasum": ""
},
"require": {
@@ -1332,7 +1332,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.1.5"
"source": "https://github.com/symfony/console/tree/v7.1.6"
},
"funding": [
{
@@ -1348,7 +1348,7 @@
"type": "tidelift"
}
],
"time": "2024-09-20T08:28:38+00:00"
"time": "2024-10-09T08:46:59+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -1419,16 +1419,16 @@
},
{
"name": "symfony/event-dispatcher",
"version": "v7.1.1",
"version": "v7.1.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7"
"reference": "87254c78dd50721cfd015b62277a8281c5589702"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7",
"reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87254c78dd50721cfd015b62277a8281c5589702",
"reference": "87254c78dd50721cfd015b62277a8281c5589702",
"shasum": ""
},
"require": {
@@ -1479,7 +1479,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.1.1"
"source": "https://github.com/symfony/event-dispatcher/tree/v7.1.6"
},
"funding": [
{
@@ -1495,7 +1495,7 @@
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
"time": "2024-09-25T14:20:29+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
@@ -1575,16 +1575,16 @@
},
{
"name": "symfony/filesystem",
"version": "v7.1.5",
"version": "v7.1.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a"
"reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/61fe0566189bf32e8cfee78335d8776f64a66f5a",
"reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/c835867b3c62bb05c7fe3d637c871c7ae52024d4",
"reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4",
"shasum": ""
},
"require": {
@@ -1621,7 +1621,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/filesystem/tree/v7.1.5"
"source": "https://github.com/symfony/filesystem/tree/v7.1.6"
},
"funding": [
{
@@ -1637,20 +1637,20 @@
"type": "tidelift"
}
],
"time": "2024-09-17T09:16:35+00:00"
"time": "2024-10-25T15:11:02+00:00"
},
{
"name": "symfony/finder",
"version": "v7.1.4",
"version": "v7.1.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "d95bbf319f7d052082fb7af147e0f835a695e823"
"reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/d95bbf319f7d052082fb7af147e0f835a695e823",
"reference": "d95bbf319f7d052082fb7af147e0f835a695e823",
"url": "https://api.github.com/repos/symfony/finder/zipball/2cb89664897be33f78c65d3d2845954c8d7a43b8",
"reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8",
"shasum": ""
},
"require": {
@@ -1685,7 +1685,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/finder/tree/v7.1.4"
"source": "https://github.com/symfony/finder/tree/v7.1.6"
},
"funding": [
{
@@ -1701,20 +1701,20 @@
"type": "tidelift"
}
],
"time": "2024-08-13T14:28:19+00:00"
"time": "2024-10-01T08:31:23+00:00"
},
{
"name": "symfony/options-resolver",
"version": "v7.1.1",
"version": "v7.1.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55"
"reference": "85e95eeede2d41cd146146e98c9c81d9214cae85"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/47aa818121ed3950acd2b58d1d37d08a94f9bf55",
"reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/85e95eeede2d41cd146146e98c9c81d9214cae85",
"reference": "85e95eeede2d41cd146146e98c9c81d9214cae85",
"shasum": ""
},
"require": {
@@ -1752,7 +1752,7 @@
"options"
],
"support": {
"source": "https://github.com/symfony/options-resolver/tree/v7.1.1"
"source": "https://github.com/symfony/options-resolver/tree/v7.1.6"
},
"funding": [
{
@@ -1768,7 +1768,7 @@
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
"time": "2024-09-25T14:20:29+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -2246,16 +2246,16 @@
},
{
"name": "symfony/process",
"version": "v7.1.5",
"version": "v7.1.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "5c03ee6369281177f07f7c68252a280beccba847"
"reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/5c03ee6369281177f07f7c68252a280beccba847",
"reference": "5c03ee6369281177f07f7c68252a280beccba847",
"url": "https://api.github.com/repos/symfony/process/zipball/6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e",
"reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e",
"shasum": ""
},
"require": {
@@ -2287,7 +2287,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v7.1.5"
"source": "https://github.com/symfony/process/tree/v7.1.6"
},
"funding": [
{
@@ -2303,7 +2303,7 @@
"type": "tidelift"
}
],
"time": "2024-09-19T21:48:23+00:00"
"time": "2024-09-25T14:20:29+00:00"
},
{
"name": "symfony/service-contracts",
@@ -2390,16 +2390,16 @@
},
{
"name": "symfony/stopwatch",
"version": "v7.1.1",
"version": "v7.1.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/stopwatch.git",
"reference": "5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d"
"reference": "8b4a434e6e7faf6adedffb48783a5c75409a1a05"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/stopwatch/zipball/5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d",
"reference": "5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d",
"url": "https://api.github.com/repos/symfony/stopwatch/zipball/8b4a434e6e7faf6adedffb48783a5c75409a1a05",
"reference": "8b4a434e6e7faf6adedffb48783a5c75409a1a05",
"shasum": ""
},
"require": {
@@ -2432,7 +2432,7 @@
"description": "Provides a way to profile code",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/stopwatch/tree/v7.1.1"
"source": "https://github.com/symfony/stopwatch/tree/v7.1.6"
},
"funding": [
{
@@ -2448,20 +2448,20 @@
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
"time": "2024-09-25T14:20:29+00:00"
},
{
"name": "symfony/string",
"version": "v7.1.5",
"version": "v7.1.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "d66f9c343fa894ec2037cc928381df90a7ad4306"
"reference": "61b72d66bf96c360a727ae6232df5ac83c71f626"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/d66f9c343fa894ec2037cc928381df90a7ad4306",
"reference": "d66f9c343fa894ec2037cc928381df90a7ad4306",
"url": "https://api.github.com/repos/symfony/string/zipball/61b72d66bf96c360a727ae6232df5ac83c71f626",
"reference": "61b72d66bf96c360a727ae6232df5ac83c71f626",
"shasum": ""
},
"require": {
@@ -2519,7 +2519,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v7.1.5"
"source": "https://github.com/symfony/string/tree/v7.1.6"
},
"funding": [
{
@@ -2535,7 +2535,7 @@
"type": "tidelift"
}
],
"time": "2024-09-20T08:28:38+00:00"
"time": "2024-09-25T14:20:29+00:00"
}
],
"packages-dev": [],

View File

@@ -13,7 +13,7 @@ jobs:
close_duplicates:
runs-on: ubuntu-latest
steps:
- uses: github/command@v1.2.1
- uses: github/command@v1.2.2
id: command
with:
allowed_contexts: "issue"

View File

@@ -35,4 +35,5 @@ jobs:
Thank you for your contributions.
days-before-stale: 14
days-before-close: 7
exempt-issue-labels: 'enhancement,feature,bug,announcement,epic,triage'
exempt-all-milestones: true
exempt-issue-labels: 'triage'

View File

@@ -68,9 +68,9 @@ class AccountController extends Controller
*/
public function accounts(AutocompleteRequest $request): JsonResponse
{
$queryParameters = $request->getParameters();
$result = $this->repository->searchAccount($queryParameters['query'], $queryParameters['account_types'], $queryParameters['size']);
$return = [];
$params = $request->getParameters();
$result = $this->repository->searchAccount($params['query'], $params['account_types'], $params['page'], $params['size']);
$return = [];
/** @var Account $account */
foreach ($result as $account) {
@@ -89,6 +89,7 @@ class AccountController extends Controller
'title' => $account->name,
'meta' => [
'type' => $account->accountType->type,
// TODO is multi currency property.
'currency_id' => null === $currency ? null : (string) $currency->id,
'currency_code' => $currency?->code,
'currency_symbol' => $currency?->symbol,

View File

@@ -23,15 +23,12 @@ declare(strict_types=1);
namespace FireflyIII\Api\V2\Request\Autocomplete;
use FireflyIII\JsonApi\Rules\IsValidFilter;
use FireflyIII\JsonApi\Rules\IsValidPage;
use FireflyIII\Models\AccountType;
use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\Http\Api\ParsesQueryFilters;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
use LaravelJsonApi\Core\Query\QueryParameters;
use LaravelJsonApi\Validation\Rule as JsonApiRule;
/**
* Class AutocompleteRequest
@@ -51,35 +48,46 @@ class AutocompleteRequest extends FormRequest
*/
public function getParameters(): array
{
$queryParameters = QueryParameters::cast($this->all());
return [
'date' => $this->dateOrToday($queryParameters, 'date'),
'query' => $this->arrayOfStrings($queryParameters, 'query'),
'size' => $this->integerFromQueryParams($queryParameters, 'size', 50),
'account_types' => $this->getAccountTypeParameter($this->arrayOfStrings($queryParameters, 'account_types')),
$array = [
'date' => $this->convertDateTime('date'),
'query' => $this->clearString((string) $this->get('query')),
'size' => $this->integerFromValue('size'),
'page' => $this->integerFromValue('page'),
'account_types' => $this->arrayFromValue($this->get('account_types')),
'transaction_types' => $this->arrayFromValue($this->get('transaction_types')),
];
$array['size'] = $array['size'] < 1 || $array['size'] > 100 ? 15 : $array['size'];
$array['page'] = max($array['page'], 1);
if (null === $array['account_types']) {
$array['account_types'] = [];
}
if (null === $array['transaction_types']) {
$array['transaction_types'] = [];
}
// remove 'initial balance' from allowed types. its internal
$array['account_types'] = array_diff($array['account_types'], [AccountType::INITIAL_BALANCE, AccountType::RECONCILIATION, AccountType::CREDITCARD]);
$array['account_types'] = $this->getAccountTypeParameter($array['account_types']);
return $array;
}
public function rules(): array
{
$valid = array_keys($this->types);
return [
'fields' => JsonApiRule::notSupported(),
'filter' => ['nullable', 'array', new IsValidFilter(['query', 'date', 'account_types'])],
'include' => JsonApiRule::notSupported(),
'page' => ['nullable', 'array', new IsValidPage(['size'])],
'sort' => JsonApiRule::notSupported(),
'date' => 'nullable|date|after:1900-01-01|before:2100-01-01',
'query' => 'nullable|string',
'size' => 'nullable|integer|min:1|max:100',
'page' => 'nullable|integer|min:1',
'account_types' => sprintf('nullable|in:%s', implode(',', $valid)),
'transaction_types' => 'nullable|in:todo',
];
}
private function getAccountTypeParameter(mixed $types): array
private function getAccountTypeParameter(array $types): array
{
if (is_string($types) && str_contains($types, ',')) {
$types = explode(',', $types);
}
if (!is_iterable($types)) {
$types = [$types];
}
$return = [];
foreach ($types as $type) {
$return = array_merge($return, $this->mapAccountTypes($type));

View File

@@ -24,8 +24,6 @@ declare(strict_types=1);
namespace FireflyIII\Api\V2\Request\Chart;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\JsonApi\Rules\IsValidFilter;
use FireflyIII\Rules\IsFilterValueIn;
use FireflyIII\Support\Http\Api\ParsesQueryFilters;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use FireflyIII\Support\Request\ChecksLogin;
@@ -33,8 +31,6 @@ use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Validator;
use LaravelJsonApi\Core\Query\QueryParameters;
use LaravelJsonApi\Validation\Rule as JsonApiRule;
/**
* Class ChartRequest
@@ -50,52 +46,29 @@ class ChartRequest extends FormRequest
public function getParameters(): array
{
$queryParameters = QueryParameters::cast($this->all());
// $queryParameters = QueryParameters::cast($this->all());
return [
'start' => $this->dateOrToday($queryParameters, 'start')->startOfDay(),
'end' => $this->dateOrToday($queryParameters, 'end')->endOfDay(),
'preselected' => $this->stringFromQueryParams($queryParameters, 'preselected', 'empty'),
'period' => $this->stringFromFilterParams($queryParameters, 'period', '1M'),
'accounts' => $this->arrayOfStrings($queryParameters, 'accounts'),
// preselected heeft maar een paar toegestane waardes, dat moet ook goed gaan.
// 'query' => $this->arrayOfStrings($queryParameters, 'query'),
// 'size' => $this->integerFromQueryParams($queryParameters,'size', 50),
// 'account_types' => $this->getAccountTypeParameter($this->arrayOfStrings($queryParameters, 'account_types')),
'start' => $this->convertDateTime('start')?->startOfDay(),
'end' => $this->convertDateTime('end')?->endOfDay(),
'preselected' => $this->convertString('preselected', 'empty'),
'period' => $this->convertString('period', '1M'),
'accounts' => $this->arrayFromValue($this->get('accounts')),
];
// collect accounts based on this list?
}
// return [
// 'accounts' => $this->getAccountList(),
// 'preselected' => $this->convertString('preselected'),
// ];
// }
/**
* The rules that the incoming request must be matched against.
*/
public function rules(): array
{
return [
'fields' => JsonApiRule::notSupported(),
'filter' => ['nullable', 'array',
new IsValidFilter(['start', 'end', 'preselected', 'accounts', 'period']),
new IsFilterValueIn('preselected', config('firefly.preselected_accounts')),
],
'include' => JsonApiRule::notSupported(),
'page' => JsonApiRule::notSupported(),
'sort' => JsonApiRule::notSupported(),
// 'start' => 'required|date|after:1900-01-01|before:2099-12-31',
// 'end' => 'required|date|after_or_equal:start|before:2099-12-31|after:1900-01-01',
'start' => 'required|date|after:1900-01-01|before:2099-12-31|before_or_equal:end',
'end' => 'required|date|after:1900-01-01|before:2099-12-31|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.*' => 'exists:accounts,id',
];
// return [
// 'start' => 'required|date|after:1900-01-01|before:2099-12-31',
// 'end' => 'required|date|after_or_equal:start|before:2099-12-31|after:1900-01-01',
// 'preselected' => sprintf('in:%s', implode(',', config('firefly.preselected_accounts'))),
// 'accounts.*' => 'exists:accounts,id',
// ];
}
public function withValidator(Validator $validator): void

View File

@@ -51,7 +51,11 @@ class FixUnevenAmount extends Command
$this->convertOldStyleTransfers();
$this->fixUnevenAmounts();
$this->matchCurrencies();
AccountBalanceCalculator::forceRecalculateAll();
if (config('firefly.feature_flags.running_balance_column')) {
$this->friendlyInfo('Will recalculate transaction running balance columns. This may take a LONG time. Please be patient.');
AccountBalanceCalculator::recalculateAll(true);
$this->friendlyInfo('Done recalculating transaction running balance columns.');
}
return 0;
}

View File

@@ -33,6 +33,7 @@ use Illuminate\Console\Command;
class CorrectAccountBalance extends Command
{
use ShowsFriendlyMessages;
public const string CONFIG_NAME = '610_correct_balances';
protected $description = 'Recalculate all account balance amounts';
protected $signature = 'firefly-iii:correct-account-balance {--F|force : Force the execution of this command.}';
@@ -44,23 +45,30 @@ class CorrectAccountBalance extends Command
return 0;
}
$this->friendlyWarning('This command has been disabled.');
$this->markAsExecuted();
if (config('firefly.feature_flags.running_balance_column')) {
$this->friendlyInfo('Will recalculate account balances. This may take a LONG time. Please be patient.');
$this->markAsExecuted();
$this->correctBalanceAmounts();
$this->friendlyInfo('Done recalculating account balances.');
return 0;
}
$this->friendlyWarning('This command has been disabled.');
// $this->correctBalanceAmounts();
return 0;
}
private function correctBalanceAmounts(): void
{
AccountBalanceCalculator::recalculateAll();
return;
AccountBalanceCalculator::recalculateAll(true);
}
private function isExecuted(): bool
{
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
return (bool)$configVar?->data;
return (bool) $configVar?->data;
}
private function markAsExecuted(): void

View File

@@ -54,6 +54,7 @@ class MigrateRuleActions extends Command
}
$this->replaceEqualSign();
$this->replaceObsoleteActions();
$this->markAsExecuted();
return 0;
}
@@ -179,4 +180,9 @@ class MigrateRuleActions extends Command
}
}
}
private function markAsExecuted(): void
{
app('fireflyconfig')->set(self::CONFIG_NAME, true);
}
}

View File

@@ -95,7 +95,7 @@ class DebugController extends Controller
// also do some recalculations.
Artisan::call('firefly-iii:trigger-credit-recalculation');
AccountBalanceCalculator::forceRecalculateAll();
AccountBalanceCalculator::recalculateAll(true);
try {
Artisan::call('twig:clean');

View File

@@ -107,7 +107,6 @@ class JavascriptController extends Controller
$lang = $pref->data;
$dateRange = $this->getDateRangeConfig();
$uid = substr(hash('sha256', sprintf('%s-%s-%s', (string)config('app.key'), auth()->user()->id, auth()->user()->email)), 0, 12);
$data = [
'currencyCode' => $currency->code,
'currencySymbol' => $currency->symbol,

View File

@@ -298,34 +298,38 @@ class AccountRepository implements AccountRepositoryInterface
return $query->get(['accounts.*']);
}
public function searchAccount(array $query, array $types, int $limit): Collection
public function searchAccount(string $query, array $types, int $page, int $limit): Collection
{
// search by group, not by user
$dbQuery = $this->userGroup->accounts()
->where('active', true)
->orderBy('accounts.updated_at', 'ASC')
->orderBy('accounts.order', 'ASC')
->orderBy('accounts.account_type_id', 'ASC')
->orderBy('accounts.name', 'ASC')
->with(['accountType'])
;
if (count($query) > 0) {
// split query on spaces just in case:
// split query on spaces just in case:
if ('' !== trim($query)) {
$dbQuery->where(function (EloquentBuilder $q) use ($query): void {
foreach ($query as $line) {
$parts = explode(' ', $line);
foreach ($parts as $part) {
$search = sprintf('%%%s%%', $part);
$q->orWhereLike('name', $search);
}
$parts = explode(' ', $query);
foreach ($parts as $part) {
$search = sprintf('%%%s%%', $part);
$q->orWhereLike('name', $search);
}
});
}
if (0 !== count($types)) {
$dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$dbQuery->whereIn('account_types.type', $types);
}
return $dbQuery->take($limit)->get(['accounts.*']);
$dbQuery->skip(($page - 1) * $limit)->take($limit);
return $dbQuery->get(['accounts.*']);
}
#[\Override]

View File

@@ -80,7 +80,7 @@ interface AccountRepositoryInterface
*/
public function resetAccountOrder(): void;
public function searchAccount(array $query, array $types, int $limit): Collection;
public function searchAccount(string $query, array $types, int $page, int $limit): Collection;
public function setUser(User $user): void;

View File

@@ -161,7 +161,7 @@ class CreditRecalculateService
app('log')->debug(sprintf('Now processing account #%d ("%s"). All amounts with 2 decimals!', $account->id, $account->name));
// get opening balance (if present)
$this->repository->setUser($account->user);
$direction = (string)$this->repository->getMetaValue($account, 'liability_direction');
$direction = (string) $this->repository->getMetaValue($account, 'liability_direction');
$openingBalance = $this->repository->getOpeningBalance($account);
if (null !== $openingBalance) {
app('log')->debug(sprintf('Found opening balance transaction journal #%d', $openingBalance->id));
@@ -242,6 +242,15 @@ class CreditRecalculateService
private function processTransaction(Account $account, string $direction, Transaction $transaction, string $leftOfDebt): string
{
$journal = $transaction->transactionJournal;
// here be null pointers.
if (null === $journal) {
app('log')->warning(sprintf('Transaction #%d has no journal.', $transaction->id));
return $leftOfDebt;
}
$foreignCurrency = $transaction->foreignCurrency;
$accountCurrency = $this->repository->getAccountCurrency($account);
$type = $journal->transactionType->type;

View File

@@ -36,10 +36,12 @@ trait CollectsAccountsFromFilter
$collection = new Collection();
// always collect from the query parameter, even when it's empty.
foreach ($queryParameters['accounts'] as $accountId) {
$account = $this->repository->find((int) $accountId);
if (null !== $account) {
$collection->push($account);
if (null !== $queryParameters['accounts']) {
foreach ($queryParameters['accounts'] as $accountId) {
$account = $this->repository->find((int) $accountId);
if (null !== $account) {
$collection->push($account);
}
}
}

View File

@@ -24,10 +24,10 @@ declare(strict_types=1);
namespace FireflyIII\Support\Models;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountBalance;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
@@ -47,20 +47,15 @@ class AccountBalanceCalculator
}
/**
* Recalculate all balances.
* Recalculate all account and transaction balances.
*/
public static function forceRecalculateAll(): void
{
Transaction::whereNull('deleted_at')->update(['balance_dirty' => true]);
$object = new self();
$object->optimizedCalculation(new Collection());
}
/**
* Recalculate all balances.
*/
public static function recalculateAll(): void
public static function recalculateAll(bool $forced): void
{
if ($forced) {
Transaction::whereNull('deleted_at')->update(['balance_dirty' => true]);
// also delete account balances.
AccountBalance::whereNotNull('created_at')->delete();
}
$object = new self();
$object->optimizedCalculation(new Collection());
}
@@ -130,12 +125,6 @@ class AccountBalanceCalculator
private function optimizedCalculation(Collection $accounts, ?Carbon $notBefore = null): void
{
Log::debug('start of optimizedCalculation');
if (false === config('firefly.feature_flags.running_balance_column')) {
Log::debug('optimizedCalculation is disabled, return.');
return;
}
if ($accounts->count() > 0) {
Log::debug(sprintf('Limited to %d account(s)', $accounts->count()));
}
@@ -162,14 +151,17 @@ class AccountBalanceCalculator
$set = $query->get(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount']);
// the balance value is an array.
// first entry is the balance, second is the date.
/** @var Transaction $entry */
foreach ($set as $entry) {
// start with empty array:
$balances[$entry->account_id] ??= [];
$balances[$entry->account_id][$entry->transaction_currency_id] ??= $this->getLatestBalance($entry->account_id, $entry->transaction_currency_id, $notBefore);
$balances[$entry->account_id][$entry->transaction_currency_id] ??= [$this->getLatestBalance($entry->account_id, $entry->transaction_currency_id, $notBefore), null];
// before and after are easy:
$before = $balances[$entry->account_id][$entry->transaction_currency_id];
$before = $balances[$entry->account_id][$entry->transaction_currency_id][0];
$after = bcadd($before, $entry->amount);
if (true === $entry->balance_dirty || $accounts->count() > 0) {
// update the transaction:
@@ -181,12 +173,13 @@ class AccountBalanceCalculator
}
// then update the array:
$balances[$entry->account_id][$entry->transaction_currency_id] = $after;
$balances[$entry->account_id][$entry->transaction_currency_id] = [$after, $entry->date];
}
Log::debug(sprintf('end of optimizedCalculation, corrected %d balance(s)', $count));
// then update all transactions.
// ?? something with accounts?
// save all collected balances in their respective account objects.
$this->storeAccountBalances($balances);
}
private function getAccountBalanceByJournal(string $title, int $account, int $journal, int $currency): AccountBalance
@@ -208,145 +201,182 @@ class AccountBalanceCalculator
return $entry;
}
private function recalculateLatest(?Account $account): void
// private function recalculateLatest(?Account $account): void
// {
// $query = Transaction::groupBy(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']);
//
// if (null !== $account) {
// $query->where('transactions.account_id', $account->id);
// }
// $result = $query->get(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id', \DB::raw('SUM(transactions.amount) as sum_amount'), \DB::raw('SUM(transactions.foreign_amount) as sum_foreign_amount')]);
//
// // reset account balances:
// $this->resetAccountBalancesByAccount('balance', $account);
//
// /** @var \stdClass $row */
// foreach ($result as $row) {
// $account = (int) $row->account_id;
// $transactionCurrency = (int) $row->transaction_currency_id;
// $foreignCurrency = (int) $row->foreign_currency_id;
// $sumAmount = (string) $row->sum_amount;
// $sumForeignAmount = (string) $row->sum_foreign_amount;
// $sumAmount = '' === $sumAmount ? '0' : $sumAmount;
// $sumForeignAmount = '' === $sumForeignAmount ? '0' : $sumForeignAmount;
//
// // at this point SQLite may return scientific notation because why not. Terrible.
// $sumAmount = app('steam')->floatalize($sumAmount);
// $sumForeignAmount = app('steam')->floatalize($sumForeignAmount);
//
// // first create for normal currency:
// $entry = $this->getAccountBalanceByAccount($account, $transactionCurrency);
//
// try {
// $entry->balance = bcadd((string) $entry->balance, $sumAmount);
// } catch (\ValueError $e) {
// $message = sprintf('[a] Could not add "%s" to "%s": %s', $entry->balance, $sumAmount, $e->getMessage());
// Log::error($message);
//
// throw new FireflyException($message, 0, $e);
// }
// $entry->save();
//
// // then do foreign amount, if present:
// if ($foreignCurrency > 0) {
// $entry = $this->getAccountBalanceByAccount($account, $foreignCurrency);
//
// try {
// $entry->balance = bcadd((string) $entry->balance, $sumForeignAmount);
// } catch (\ValueError $e) {
// $message = sprintf('[b] Could not add "%s" to "%s": %s', $entry->balance, $sumForeignAmount, $e->getMessage());
// Log::error($message);
//
// throw new FireflyException($message, 0, $e);
// }
// $entry->save();
// }
// }
// Log::debug(sprintf('Recalculated %d account balance(s)', $result->count()));
// }
// private function resetAccountBalancesByAccount(string $title, ?Account $account): void
// {
// if (null === $account) {
// $count = AccountBalance::whereNotNull('updated_at')->where('title', $title)->update(['balance' => '0']);
// Log::debug(sprintf('Set %d account balance(s) to zero.', $count));
//
// return;
// }
// $count = AccountBalance::where('account_id', $account->id)->where('title', $title)->update(['balance' => '0']);
// Log::debug(sprintf('Set %d account balance(s) of account #%d to zero.', $count, $account->id));
// }
// /**
// * Als je alles opnieuw doet, verzamel je alle transactions en het bedrag en zet je dat neer als "balance after
// * journal". Dat betekent, netjes op volgorde van datum en doorrekenen.
// *
// * Zodra je een transaction journal verplaatst (datum) moet je dat journal en alle latere journals opnieuw doen.
// * Maar dan moet je van de account wel een beginnetje hebben, namelijk de balance tot en met dat moment.
// *
// * 1. Dus dan search je eerst naar die SUM, som alle transactions eerder dan (niet inclusief) de journal.
// * 2. En vanaf daar pak je alle journals op of na de journal (dus ook de journal zelf) en begin je door te tellen.
// * 3. Elke voorbij gaande journal entry "balance_after_journal" geef je een update of voeg je toe.
// */
// private function recalculateJournals(?Account $account, ?TransactionJournal $transactionJournal): void
// {
// $query = Transaction::groupBy(['transactions.account_id', 'transaction_journals.id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']);
// $query->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id');
// $query->orderBy('transaction_journals.date', 'asc');
// $amounts = [];
// if (null !== $account) {
// $query->where('transactions.account_id', $account->id);
// }
// if (null !== $account && null !== $transactionJournal) {
// $query->where('transaction_journals.date', '>=', $transactionJournal->date);
// $amounts = $this->getStartAmounts($account, $transactionJournal);
// }
// $result = $query->get(['transactions.account_id', 'transaction_journals.id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id', \DB::raw('SUM(transactions.amount) as sum_amount'), \DB::raw('SUM(transactions.foreign_amount) as sum_foreign_amount')]);
//
// /** @var \stdClass $row */
// foreach ($result as $row) {
// $account = (int) $row->account_id;
// $transactionCurrency = (int) $row->transaction_currency_id;
// $foreignCurrency = (int) $row->foreign_currency_id;
// $sumAmount = (string) $row->sum_amount;
// $sumForeignAmount = (string) $row->sum_foreign_amount;
// $journalId = (int) $row->id;
//
// // check for empty strings
// $sumAmount = '' === $sumAmount ? '0' : $sumAmount;
// $sumForeignAmount = '' === $sumForeignAmount ? '0' : $sumForeignAmount;
//
// // new amounts:
// $amounts[$account][$transactionCurrency] = bcadd($amounts[$account][$transactionCurrency] ?? '0', $sumAmount);
// $amounts[$account][$foreignCurrency] = bcadd($amounts[$account][$foreignCurrency] ?? '0', $sumForeignAmount);
//
// // first create for normal currency:
// $entry = self::getAccountBalanceByJournal('balance_after_journal', $account, $journalId, $transactionCurrency);
// $entry->balance = $amounts[$account][$transactionCurrency];
// $entry->save();
//
// // then do foreign amount, if present:
// if ($foreignCurrency > 0) {
// $entry = self::getAccountBalanceByJournal('balance_after_journal', $account, $journalId, $foreignCurrency);
// $entry->balance = $amounts[$account][$foreignCurrency];
// $entry->save();
// }
// }
//
// // select transactions.account_id, transaction_journals.id, transactions.transaction_currency_id, transactions.foreign_currency_id, sum(transactions.amount), sum(transactions.foreign_amount)
// //
// // from transactions
// //
// // left join transaction_journals ON transaction_journals.id = transactions.transaction_journal_id
// //
// // group by account_id, transaction_journals.id, transaction_currency_id, foreign_currency_id
// // order by transaction_journals.date desc
// }
// private function getStartAmounts(Account $account, TransactionJournal $journal): array
// {
// exit('here we are 1');
//
// return [];
// }
private function storeAccountBalances(array $balances): void
{
$query = Transaction::groupBy(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']);
/**
* @var int $accountId
* @var array $currencies
*/
foreach ($balances as $accountId => $currencies) {
/** @var Account $account */
$account = Account::find($accountId);
if (null === $account) {
Log::error(sprintf('Could not find account #%d, will not save account balance.', $accountId));
if (null !== $account) {
$query->where('transactions.account_id', $account->id);
}
$result = $query->get(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id', \DB::raw('SUM(transactions.amount) as sum_amount'), \DB::raw('SUM(transactions.foreign_amount) as sum_foreign_amount')]);
// reset account balances:
$this->resetAccountBalancesByAccount('balance', $account);
/** @var \stdClass $row */
foreach ($result as $row) {
$account = (int) $row->account_id;
$transactionCurrency = (int) $row->transaction_currency_id;
$foreignCurrency = (int) $row->foreign_currency_id;
$sumAmount = (string) $row->sum_amount;
$sumForeignAmount = (string) $row->sum_foreign_amount;
$sumAmount = '' === $sumAmount ? '0' : $sumAmount;
$sumForeignAmount = '' === $sumForeignAmount ? '0' : $sumForeignAmount;
// at this point SQLite may return scientific notation because why not. Terrible.
$sumAmount = app('steam')->floatalize($sumAmount);
$sumForeignAmount = app('steam')->floatalize($sumForeignAmount);
// first create for normal currency:
$entry = $this->getAccountBalanceByAccount($account, $transactionCurrency);
try {
$entry->balance = bcadd((string) $entry->balance, $sumAmount);
} catch (\ValueError $e) {
$message = sprintf('[a] Could not add "%s" to "%s": %s', $entry->balance, $sumAmount, $e->getMessage());
Log::error($message);
throw new FireflyException($message, 0, $e);
continue;
}
$entry->save();
// then do foreign amount, if present:
if ($foreignCurrency > 0) {
$entry = $this->getAccountBalanceByAccount($account, $foreignCurrency);
/**
* @var int $currencyId
* @var array $balance
*/
foreach ($currencies as $currencyId => $balance) {
/** @var TransactionCurrency $currency */
$currency = TransactionCurrency::find($currencyId);
if (null === $currency) {
Log::error(sprintf('Could not find currency #%d, will not save account balance.', $currencyId));
try {
$entry->balance = bcadd((string) $entry->balance, $sumForeignAmount);
} catch (\ValueError $e) {
$message = sprintf('[b] Could not add "%s" to "%s": %s', $entry->balance, $sumForeignAmount, $e->getMessage());
Log::error($message);
throw new FireflyException($message, 0, $e);
continue;
}
$entry->save();
/** @var AccountBalance $object */
$object = $account->accountBalances()->firstOrCreate(['title' => 'running_balance', 'balance' => '0', 'transaction_currency_id' => $currencyId, 'date' => $balance[1]]);
$object->balance = $balance[0];
$object->date = $balance[1];
$object->save();
}
}
Log::debug(sprintf('Recalculated %d account balance(s)', $result->count()));
}
private function resetAccountBalancesByAccount(string $title, ?Account $account): void
{
if (null === $account) {
$count = AccountBalance::whereNotNull('updated_at')->where('title', $title)->update(['balance' => '0']);
Log::debug(sprintf('Set %d account balance(s) to zero.', $count));
return;
}
$count = AccountBalance::where('account_id', $account->id)->where('title', $title)->update(['balance' => '0']);
Log::debug(sprintf('Set %d account balance(s) of account #%d to zero.', $count, $account->id));
}
/**
* Als je alles opnieuw doet, verzamel je alle transactions en het bedrag en zet je dat neer als "balance after
* journal". Dat betekent, netjes op volgorde van datum en doorrekenen.
*
* Zodra je een transaction journal verplaatst (datum) moet je dat journal en alle latere journals opnieuw doen.
* Maar dan moet je van de account wel een beginnetje hebben, namelijk de balance tot en met dat moment.
*
* 1. Dus dan search je eerst naar die SUM, som alle transactions eerder dan (niet inclusief) de journal.
* 2. En vanaf daar pak je alle journals op of na de journal (dus ook de journal zelf) en begin je door te tellen.
* 3. Elke voorbij gaande journal entry "balance_after_journal" geef je een update of voeg je toe.
*/
private function recalculateJournals(?Account $account, ?TransactionJournal $transactionJournal): void
{
$query = Transaction::groupBy(['transactions.account_id', 'transaction_journals.id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']);
$query->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id');
$query->orderBy('transaction_journals.date', 'asc');
$amounts = [];
if (null !== $account) {
$query->where('transactions.account_id', $account->id);
}
if (null !== $account && null !== $transactionJournal) {
$query->where('transaction_journals.date', '>=', $transactionJournal->date);
$amounts = $this->getStartAmounts($account, $transactionJournal);
}
$result = $query->get(['transactions.account_id', 'transaction_journals.id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id', \DB::raw('SUM(transactions.amount) as sum_amount'), \DB::raw('SUM(transactions.foreign_amount) as sum_foreign_amount')]);
/** @var \stdClass $row */
foreach ($result as $row) {
$account = (int) $row->account_id;
$transactionCurrency = (int) $row->transaction_currency_id;
$foreignCurrency = (int) $row->foreign_currency_id;
$sumAmount = (string) $row->sum_amount;
$sumForeignAmount = (string) $row->sum_foreign_amount;
$journalId = (int) $row->id;
// check for empty strings
$sumAmount = '' === $sumAmount ? '0' : $sumAmount;
$sumForeignAmount = '' === $sumForeignAmount ? '0' : $sumForeignAmount;
// new amounts:
$amounts[$account][$transactionCurrency] = bcadd($amounts[$account][$transactionCurrency] ?? '0', $sumAmount);
$amounts[$account][$foreignCurrency] = bcadd($amounts[$account][$foreignCurrency] ?? '0', $sumForeignAmount);
// first create for normal currency:
$entry = self::getAccountBalanceByJournal('balance_after_journal', $account, $journalId, $transactionCurrency);
$entry->balance = $amounts[$account][$transactionCurrency];
$entry->save();
// then do foreign amount, if present:
if ($foreignCurrency > 0) {
$entry = self::getAccountBalanceByJournal('balance_after_journal', $account, $journalId, $foreignCurrency);
$entry->balance = $amounts[$account][$foreignCurrency];
$entry->save();
}
}
// select transactions.account_id, transaction_journals.id, transactions.transaction_currency_id, transactions.foreign_currency_id, sum(transactions.amount), sum(transactions.foreign_amount)
//
// from transactions
//
// left join transaction_journals ON transaction_journals.id = transactions.transaction_journal_id
//
// group by account_id, transaction_journals.id, transaction_currency_id, foreign_currency_id
// order by transaction_journals.date desc
}
private function getStartAmounts(Account $account, TransactionJournal $journal): array
{
exit('here we are 1');
return [];
}
}

View File

@@ -105,11 +105,11 @@ trait ConvertsDataTypes
/**
* Return string value.
*/
public function convertString(string $field): string
public function convertString(string $field, string $default = ''): string
{
$entry = $this->get($field);
if (!is_scalar($entry)) {
return '';
return $default;
}
return (string)$this->clearString((string)$entry);

View File

@@ -847,7 +847,8 @@ class FireflyValidator extends Validator
->where('trigger', $trigger)
->where('response', $response)
->where('delivery', $delivery)
->where('url', $url)->count();
->where('url', $url)->count()
;
}
return false;

520
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -110,7 +110,7 @@ return [
'running_balance_column' => env('USE_RUNNING_BALANCE', false),
// see cer.php for exchange rates feature flag.
],
'version' => 'develop/2024-10-14',
'version' => 'develop/2024-10-28',
'api_version' => '2.1.0',
'db_version' => 24,

1420
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -131,7 +131,7 @@ function initRevenueACField(fieldName) {
}
});
sourceNames.initialize();
$('input[name="' + fieldName + '"]').typeahead({hint: true, highlight: true,}, {source: sourceNames, displayKey: 'name', autoSelect: false});
$('input[name="' + fieldName + '"]').typeahead({hint: true, highlight: true,}, {source: sourceNames, displayKey: 'name', autoselect: true});
}
}
@@ -161,5 +161,5 @@ function initCategoryAC() {
}
});
categories.initialize();
$('input[name="category"]').typeahead({hint: true, highlight: true,}, {source: categories, displayKey: 'name', autoSelect: false});
$('input[name="category"]').typeahead({hint: true, highlight: true,}, {source: categories, displayKey: 'name', autoselect: true});
}

6
public/v1/js/lib/typeahead/bloodhound.js Normal file → Executable file
View File

@@ -1,7 +1,7 @@
/*!
* typeahead.js 1.3.1
* typeahead.js 1.3.3
* https://github.com/corejavascript/typeahead.js
* Copyright 2013-2020 Twitter, Inc. and other contributors; Licensed MIT
* Copyright 2013-2024 Twitter, Inc. and other contributors; Licensed MIT
*/
@@ -159,7 +159,7 @@
noop: function() {}
};
}();
var VERSION = "1.3.1";
var VERSION = "1.3.3";
var tokenizers = function() {
"use strict";
return {

6
public/v1/js/lib/typeahead/bloodhound.min.js vendored Normal file → Executable file

File diff suppressed because one or more lines are too long

7
public/v1/js/lib/typeahead/typeahead.bundle.js Normal file → Executable file
View File

@@ -1,7 +1,7 @@
/*!
* typeahead.js 1.3.1
* typeahead.js 1.3.3
* https://github.com/corejavascript/typeahead.js
* Copyright 2013-2020 Twitter, Inc. and other contributors; Licensed MIT
* Copyright 2013-2024 Twitter, Inc. and other contributors; Licensed MIT
*/
@@ -159,7 +159,7 @@
noop: function() {}
};
}();
var VERSION = "1.3.1";
var VERSION = "1.3.3";
var tokenizers = function() {
"use strict";
return {
@@ -1446,6 +1446,7 @@
});
this.$input.attr({
"aria-owns": id + "_listbox",
"aria-controls": id + "_listbox",
role: "combobox",
"aria-autocomplete": "list",
"aria-expanded": false

6
public/v1/js/lib/typeahead/typeahead.bundle.min.js vendored Normal file → Executable file

File diff suppressed because one or more lines are too long

5
public/v1/js/lib/typeahead/typeahead.jquery.js Normal file → Executable file
View File

@@ -1,7 +1,7 @@
/*!
* typeahead.js 1.3.1
* typeahead.js 1.3.3
* https://github.com/corejavascript/typeahead.js
* Copyright 2013-2020 Twitter, Inc. and other contributors; Licensed MIT
* Copyright 2013-2024 Twitter, Inc. and other contributors; Licensed MIT
*/
@@ -499,6 +499,7 @@
});
this.$input.attr({
"aria-owns": id + "_listbox",
"aria-controls": id + "_listbox",
role: "combobox",
"aria-autocomplete": "list",
"aria-expanded": false

6
public/v1/js/lib/typeahead/typeahead.jquery.min.js vendored Normal file → Executable file

File diff suppressed because one or more lines are too long

View File

@@ -29,7 +29,7 @@
"bootstrap5-tags": "^1.7",
"chart.js": "^4.4.0",
"chartjs-adapter-date-fns": "^3.0.0",
"chartjs-chart-sankey": "^0.12.1",
"chartjs-chart-sankey": "^0.13.0",
"date-fns": "^4.0.0",
"i18next": "^23.15.2",
"i18next-chained-backend": "^4.6.2",

View File

@@ -1,12 +1,12 @@
// date ranges
var ranges = {};
{% for title, range in dateRangeConfig.ranges %}
ranges["{{ title }}"] = [moment("{{ range[0].format('Y-m-d') }}"), moment("{{ range[1].format('Y-m-d') }}")];
ranges["{{ title|escape('js') }}"] = [moment("{{ range[0].format('Y-m-d') }}"), moment("{{ range[1].format('Y-m-d') }}")];
{% endfor %}
// date range meta configuration
var dateRangeMeta = {
title: "{{ dateRangeTitle }}",
title: "{{ dateRangeTitle|escape('js') }}",
url: "{{ route('daterange') }}",
labels: {
apply: "{{ 'apply'|_ }}",

View File

@@ -47,9 +47,9 @@ Route::group(
],
static function (): void {
Route::get('accounts', ['uses' => 'AccountController@accounts', 'as' => 'accounts']);
Route::get('categories', ['uses' => 'CategoryController@categories', 'as' => 'categories']);
Route::get('tags', ['uses' => 'TagController@tags', 'as' => 'tags']);
Route::get('transaction-descriptions', ['uses' => 'TransactionController@transactionDescriptions', 'as' => 'transaction-descriptions']);
// Route::get('categories', ['uses' => 'CategoryController@categories', 'as' => 'categories']);
// Route::get('tags', ['uses' => 'TagController@tags', 'as' => 'tags']);
// Route::get('transaction-descriptions', ['uses' => 'TransactionController@transactionDescriptions', 'as' => 'transaction-descriptions']);
}
);
@@ -58,12 +58,12 @@ Route::group(
[
'namespace' => 'FireflyIII\Api\V2\Controllers\Chart',
'prefix' => 'v2/chart',
'as' => 'api.v1.chart.',
'as' => 'api.v2.chart.',
],
static function (): void {
Route::get('account/dashboard', ['uses' => 'AccountController@dashboard', 'as' => 'account.dashboard']);
Route::get('budget/dashboard', ['uses' => 'BudgetController@dashboard', 'as' => 'budget.dashboard']);
Route::get('category/dashboard', ['uses' => 'CategoryController@dashboard', 'as' => 'category.dashboard']);
// Route::get('account/dashboard', ['uses' => 'AccountController@dashboard', 'as' => 'account.dashboard']);
// Route::get('budget/dashboard', ['uses' => 'BudgetController@dashboard', 'as' => 'budget.dashboard']);
// Route::get('category/dashboard', ['uses' => 'CategoryController@dashboard', 'as' => 'category.dashboard']);
Route::get('balance/balance', ['uses' => 'BalanceController@balance', 'as' => 'balance.balance']);
}
);
@@ -77,7 +77,7 @@ Route::group(
'as' => 'api.v2.summary.',
],
static function (): void {
Route::get('basic', ['uses' => 'BasicController@basic', 'as' => 'basic']);
// Route::get('basic', ['uses' => 'BasicController@basic', 'as' => 'basic']);
}
);
// V2 API route for all kinds of Transaction lists.
@@ -91,11 +91,11 @@ Route::group(
],
static function (): void {
// basic list
Route::get('transactions', ['uses' => 'TransactionController@list', 'as' => 'transactions.list']);
// Route::get('transactions', ['uses' => 'TransactionController@list', 'as' => 'transactions.list']);
// list by parent or related object.
// note how the check is done on the user group, not the user itself.
Route::get('accounts/{userGroupAccount}/transactions', ['uses' => 'AccountController@list', 'as' => 'accounts.transactions']);
// Route::get('accounts/{userGroupAccount}/transactions', ['uses' => 'AccountController@list', 'as' => 'accounts.transactions']);
}
);
@@ -109,7 +109,7 @@ Route::group(
'as' => 'api.v2.net-worth.',
],
static function (): void {
Route::get('', ['uses' => 'NetWorthController@get', 'as' => 'index']);
// Route::get('', ['uses' => 'NetWorthController@get', 'as' => 'index']);
}
);
@@ -135,10 +135,10 @@ Route::group(
'as' => 'api.v2.subscriptions.',
],
static function (): void {
Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
Route::get('{userGroupBill}', ['uses' => 'ShowController@show', 'as' => 'show']);
Route::get('sum/paid', ['uses' => 'SumController@paid', 'as' => 'sum.paid']);
Route::get('sum/unpaid', ['uses' => 'SumController@unpaid', 'as' => 'sum.unpaid']);
// Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
// Route::get('{userGroupBill}', ['uses' => 'ShowController@show', 'as' => 'show']);
// Route::get('sum/paid', ['uses' => 'SumController@paid', 'as' => 'sum.paid']);
// Route::get('sum/unpaid', ['uses' => 'SumController@unpaid', 'as' => 'sum.unpaid']);
}
);
@@ -150,7 +150,7 @@ Route::group(
'as' => 'api.v2.piggy-banks.',
],
static function (): void {
Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
// Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
}
);
@@ -162,7 +162,7 @@ Route::group(
'as' => 'api.v2.currencies.',
],
static function (): void {
Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
// Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
}
);
@@ -174,9 +174,9 @@ Route::group(
'as' => 'api.v2.transactions.',
],
static function (): void {
Route::post('', ['uses' => 'StoreController@post', 'as' => 'store']);
Route::get('{userGroupTransaction}', ['uses' => 'ShowController@show', 'as' => 'show']);
Route::put('{userGroupTransaction}', ['uses' => 'UpdateController@update', 'as' => 'update']);
// Route::post('', ['uses' => 'StoreController@post', 'as' => 'store']);
// Route::get('{userGroupTransaction}', ['uses' => 'ShowController@show', 'as' => 'show']);
// Route::put('{userGroupTransaction}', ['uses' => 'UpdateController@update', 'as' => 'update']);
}
);
// infinite (transactions) list:
@@ -187,7 +187,7 @@ Route::group(
'as' => 'api.v2.infinite.transactions.',
],
static function (): void {
Route::get('', ['uses' => 'TransactionController@infiniteList', 'as' => 'list']);
// Route::get('', ['uses' => 'TransactionController@infiniteList', 'as' => 'list']);
}
);
@@ -199,11 +199,11 @@ Route::group(
'as' => 'api.v2.budgets',
],
static function (): void {
Route::get('', ['uses' => 'Budget\IndexController@index', 'as' => 'index']);
Route::get('{budget}', ['uses' => 'Budget\ShowController@show', 'as' => 'show']);
Route::get('{budget}/limits', ['uses' => 'BudgetLimit\IndexController@index', 'as' => 'budget-limits.index']);
Route::get('sum/budgeted', ['uses' => 'Budget\SumController@budgeted', 'as' => 'sum.budgeted']);
Route::get('sum/spent', ['uses' => 'Budget\SumController@spent', 'as' => 'sum.spent']);
// Route::get('', ['uses' => 'Budget\IndexController@index', 'as' => 'index']);
// Route::get('{budget}', ['uses' => 'Budget\ShowController@show', 'as' => 'show']);
// Route::get('{budget}/limits', ['uses' => 'BudgetLimit\IndexController@index', 'as' => 'budget-limits.index']);
// Route::get('sum/budgeted', ['uses' => 'Budget\SumController@budgeted', 'as' => 'sum.budgeted']);
// Route::get('sum/spent', ['uses' => 'Budget\SumController@spent', 'as' => 'sum.spent']);
// Route::get('{budget}/budgeted', ['uses' => 'Budget\ShowController@budgeted', 'as' => 'budget.budgeted']);
// Route::get('{budget}/spent', ['uses' => 'Budget\ShowController@spent', 'as' => 'budget.spent']);
}
@@ -217,7 +217,7 @@ Route::group(
'as' => 'api.v2.system.',
],
static function (): void {
Route::get('preferences/{preference}', ['uses' => 'PreferencesController@get', 'as' => 'preferences.get']);
// Route::get('preferences/{preference}', ['uses' => 'PreferencesController@get', 'as' => 'preferences.get']);
}
);
@@ -229,32 +229,32 @@ Route::group(
'as' => 'api.v2.user-groups.',
],
static function (): void {
Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']);
Route::get('{userGroup}', ['uses' => 'ShowController@show', 'as' => 'show']);
Route::put('{userGroup}', ['uses' => 'UpdateController@update', 'as' => 'update']);
Route::post('{userGroup}/use', ['uses' => 'UpdateController@useUserGroup', 'as' => 'use']);
Route::put('{userGroup}/update-membership', ['uses' => 'UpdateController@updateMembership', 'as' => 'updateMembership']);
Route::delete('{userGroup}', ['uses' => 'DestroyController@destroy', 'as' => 'destroy']);
// Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
// Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']);
// Route::get('{userGroup}', ['uses' => 'ShowController@show', 'as' => 'show']);
// Route::put('{userGroup}', ['uses' => 'UpdateController@update', 'as' => 'update']);
// Route::post('{userGroup}/use', ['uses' => 'UpdateController@useUserGroup', 'as' => 'use']);
// Route::put('{userGroup}/update-membership', ['uses' => 'UpdateController@updateMembership', 'as' => 'updateMembership']);
// Route::delete('{userGroup}', ['uses' => 'DestroyController@destroy', 'as' => 'destroy']);
}
);
// V2 JSON API ROUTES
JsonApiRoute::server('v2')->prefix('v2')
->resources(function (ResourceRegistrar $server): void {
// ACCOUNTS
$server->resource('accounts', AccountController::class)
->relationships(function (Relationships $relations): void {
$relations->hasOne('user')->readOnly();
})
;
// USERS
$server->resource('users', JsonApiController::class)->readOnly()->relationships(function (Relationships $relations): void {
$relations->hasMany('accounts')->readOnly();
});
})
;
// JsonApiRoute::server('v2')->prefix('v2')
// ->resources(function (ResourceRegistrar $server): void {
// // ACCOUNTS
// $server->resource('accounts', AccountController::class)
// ->relationships(function (Relationships $relations): void {
// $relations->hasOne('user')->readOnly();
// })
// ;
//
// // USERS
// $server->resource('users', JsonApiController::class)->readOnly()->relationships(function (Relationships $relations): void {
// $relations->hasMany('accounts')->readOnly();
// });
// })
// ;
/*
* ____ ____ __ .______ ______ __ __ .___________. _______ _______.