Compare commits

..

29 Commits

Author SHA1 Message Date
github-actions
ac8a43bb37 Auto commit for release 'develop' on 2024-07-29 2024-07-29 05:06:54 +02:00
James Cole
2df4b40a28 Add a command to sync up currency information 2024-07-28 15:13:49 +02:00
James Cole
e06736c254 Returns accounts consistently. 2024-07-28 12:23:45 +02:00
James Cole
ec367e94ce Expand account object api 2024-07-28 07:47:54 +02:00
James Cole
1515dea9fa add user group validation 2024-07-28 07:02:04 +02:00
James Cole
adedf9c17d Merge branch 'main' into develop 2024-07-27 15:43:37 +02:00
James Cole
0b52fb84f1 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2024-07-27 15:42:54 +02:00
James Cole
16e742ae73 Various API updates. 2024-07-27 15:42:43 +02:00
James Cole
1b4471dfae Merge pull request #9090 from firefly-iii/dependabot/composer/develop/symfony/http-client-7.1.3
Bump symfony/http-client from 7.1.2 to 7.1.3
2024-07-27 15:42:12 +02:00
James Cole
ae152ce0a4 Merge pull request #9092 from firefly-iii/dependabot/npm_and_yarn/npm_and_yarn-19f3e7cf70
Bump postcss from 8.4.39 to 8.4.40 in the npm_and_yarn group across 1 directory
2024-07-27 15:41:55 +02:00
James Cole
2aa023f140 Merge pull request #9091 from firefly-iii/dependabot/npm_and_yarn/develop/vue/compiler-sfc-3.4.34
Bump @vue/compiler-sfc from 3.4.33 to 3.4.34
2024-07-27 15:41:47 +02:00
dependabot[bot]
6e2e4c6f08 Bump symfony/http-client from 7.1.2 to 7.1.3
Bumps [symfony/http-client](https://github.com/symfony/http-client) from 7.1.2 to 7.1.3.
- [Release notes](https://github.com/symfony/http-client/releases)
- [Changelog](https://github.com/symfony/http-client/blob/7.1/CHANGELOG.md)
- [Commits](https://github.com/symfony/http-client/compare/v7.1.2...v7.1.3)

---
updated-dependencies:
- dependency-name: symfony/http-client
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-27 13:33:08 +00:00
James Cole
1c8c038735 Merge pull request #9089 from firefly-iii/dependabot/composer/develop/symfony/mailgun-mailer-7.1.3
Bump symfony/mailgun-mailer from 7.1.2 to 7.1.3
2024-07-27 15:32:43 +02:00
James Cole
4d339a6da8 Merge pull request #9088 from firefly-iii/dependabot/npm_and_yarn/develop/postcss-8.4.40
Bump postcss from 8.4.39 to 8.4.40
2024-07-27 15:31:49 +02:00
James Cole
b7edd4407a Merge pull request #9087 from firefly-iii/dependabot/composer/develop/laravel/framework-11.18.1
Bump laravel/framework from 11.17.0 to 11.18.1
2024-07-27 15:31:34 +02:00
dependabot[bot]
a679a1e94a Bump postcss in the npm_and_yarn group across 1 directory
Bumps the npm_and_yarn group with 1 update in the / directory: [postcss](https://github.com/postcss/postcss).


Updates `postcss` from 8.4.39 to 8.4.40
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.39...8.4.40)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-27 13:30:39 +00:00
James Cole
180451d32f Merge pull request #9086 from firefly-iii/dependabot/github_actions/github/command-1.2.1
Bump github/command from 1.2.0 to 1.2.1
2024-07-27 15:30:04 +02:00
dependabot[bot]
7396f22bca Bump @vue/compiler-sfc from 3.4.33 to 3.4.34
Bumps [@vue/compiler-sfc](https://github.com/vuejs/core/tree/HEAD/packages/compiler-sfc) from 3.4.33 to 3.4.34.
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/commits/v3.4.34/packages/compiler-sfc)

---
updated-dependencies:
- dependency-name: "@vue/compiler-sfc"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-27 13:28:28 +00:00
dependabot[bot]
058019aa84 Bump symfony/mailgun-mailer from 7.1.2 to 7.1.3
Bumps [symfony/mailgun-mailer](https://github.com/symfony/mailgun-mailer) from 7.1.2 to 7.1.3.
- [Release notes](https://github.com/symfony/mailgun-mailer/releases)
- [Changelog](https://github.com/symfony/mailgun-mailer/blob/7.1/CHANGELOG.md)
- [Commits](https://github.com/symfony/mailgun-mailer/compare/v7.1.2...v7.1.3)

---
updated-dependencies:
- dependency-name: symfony/mailgun-mailer
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-27 13:28:11 +00:00
dependabot[bot]
695f83d1d8 Bump postcss from 8.4.39 to 8.4.40
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.39 to 8.4.40.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.39...8.4.40)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-27 13:28:06 +00:00
dependabot[bot]
ac4dfb3baf Bump laravel/framework from 11.17.0 to 11.18.1
Bumps [laravel/framework](https://github.com/laravel/framework) from 11.17.0 to 11.18.1.
- [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.17.0...v11.18.1)

---
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-07-27 13:28:05 +00:00
dependabot[bot]
427001b223 Bump github/command from 1.2.0 to 1.2.1
Bumps [github/command](https://github.com/github/command) from 1.2.0 to 1.2.1.
- [Release notes](https://github.com/github/command/releases)
- [Commits](https://github.com/github/command/compare/v1.2.0...v1.2.1)

---
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-07-27 13:27:56 +00:00
James Cole
3117d8b30d Update dependabot.yml
Remove labels.

Signed-off-by: James Cole <james@firefly-iii.org>
2024-07-27 15:27:34 +02:00
James Cole
d19dd2a8b2 Clean up some code, clean routes. [skip ci] 2024-07-26 18:50:41 +02:00
James Cole
de3dcc3fc2 Add "not expected this period" message, fix https://github.com/firefly-iii/firefly-iii/issues/9084 2024-07-26 12:53:13 +02:00
James Cole
077f3e095b Various API changes for v2 2024-07-26 12:52:54 +02:00
James Cole
ad3b0bb320 Time to enable the expression engine. 2024-07-26 04:19:49 +02:00
James Cole
8538741341 Fix https://github.com/orgs/firefly-iii/discussions/9080 2024-07-26 03:57:35 +02:00
James Cole
a0aef5d579 Fix https://github.com/firefly-iii/firefly-iii/issues/9078 2024-07-24 14:57:51 +02:00
47 changed files with 1681 additions and 790 deletions

View File

@@ -72,30 +72,38 @@
},
{
"name": "composer/pcre",
"version": "3.1.4",
"version": "3.2.0",
"source": {
"type": "git",
"url": "https://github.com/composer/pcre.git",
"reference": "04229f163664973f68f38f6f73d917799168ef24"
"reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/pcre/zipball/04229f163664973f68f38f6f73d917799168ef24",
"reference": "04229f163664973f68f38f6f73d917799168ef24",
"url": "https://api.github.com/repos/composer/pcre/zipball/ea4ab6f9580a4fd221e0418f2c357cdd39102a90",
"reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0"
},
"conflict": {
"phpstan/phpstan": "<1.11.8"
},
"require-dev": {
"phpstan/phpstan": "^1.3",
"phpstan/phpstan": "^1.11.8",
"phpstan/phpstan-strict-rules": "^1.1",
"symfony/phpunit-bridge": "^5"
"phpunit/phpunit": "^8 || ^9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.x-dev"
},
"phpstan": {
"includes": [
"extension.neon"
]
}
},
"autoload": {
@@ -123,7 +131,7 @@
],
"support": {
"issues": "https://github.com/composer/pcre/issues",
"source": "https://github.com/composer/pcre/tree/3.1.4"
"source": "https://github.com/composer/pcre/tree/3.2.0"
},
"funding": [
{
@@ -139,7 +147,7 @@
"type": "tidelift"
}
],
"time": "2024-05-27T13:40:54+00:00"
"time": "2024-07-25T09:36:02+00:00"
},
{
"name": "composer/semver",
@@ -398,16 +406,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.59.3",
"version": "v3.60.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "30ba9ecc2b0e5205e578fe29973c15653d9bfd29"
"reference": "e595e4e070d17c5d42ed8c4206f630fcc5f401a4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/30ba9ecc2b0e5205e578fe29973c15653d9bfd29",
"reference": "30ba9ecc2b0e5205e578fe29973c15653d9bfd29",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/e595e4e070d17c5d42ed8c4206f630fcc5f401a4",
"reference": "e595e4e070d17c5d42ed8c4206f630fcc5f401a4",
"shasum": ""
},
"require": {
@@ -489,7 +497,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.59.3"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.60.0"
},
"funding": [
{
@@ -497,7 +505,7 @@
"type": "github"
}
],
"time": "2024-06-16T14:17:03+00:00"
"time": "2024-07-25T09:26:51+00:00"
},
{
"name": "psr/container",
@@ -1026,31 +1034,31 @@
},
{
"name": "react/socket",
"version": "v1.15.0",
"version": "v1.16.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/socket.git",
"reference": "216d3aec0b87f04a40ca04f481e6af01bdd1d038"
"reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/socket/zipball/216d3aec0b87f04a40ca04f481e6af01bdd1d038",
"reference": "216d3aec0b87f04a40ca04f481e6af01bdd1d038",
"url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1",
"reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1",
"shasum": ""
},
"require": {
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
"php": ">=5.3.0",
"react/dns": "^1.11",
"react/dns": "^1.13",
"react/event-loop": "^1.2",
"react/promise": "^3 || ^2.6 || ^1.2.1",
"react/stream": "^1.2"
"react/promise": "^3.2 || ^2.6 || ^1.2.1",
"react/stream": "^1.4"
},
"require-dev": {
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
"react/async": "^4 || ^3 || ^2",
"react/async": "^4.3 || ^3.3 || ^2",
"react/promise-stream": "^1.4",
"react/promise-timer": "^1.10"
"react/promise-timer": "^1.11"
},
"type": "library",
"autoload": {
@@ -1094,7 +1102,7 @@
],
"support": {
"issues": "https://github.com/reactphp/socket/issues",
"source": "https://github.com/reactphp/socket/tree/v1.15.0"
"source": "https://github.com/reactphp/socket/tree/v1.16.0"
},
"funding": [
{
@@ -1102,7 +1110,7 @@
"type": "open_collective"
}
],
"time": "2023-12-15T11:02:10+00:00"
"time": "2024-07-26T10:38:09+00:00"
},
{
"name": "react/stream",
@@ -1251,16 +1259,16 @@
},
{
"name": "symfony/console",
"version": "v7.1.2",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "0aa29ca177f432ab68533432db0de059f39c92ae"
"reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/0aa29ca177f432ab68533432db0de059f39c92ae",
"reference": "0aa29ca177f432ab68533432db0de059f39c92ae",
"url": "https://api.github.com/repos/symfony/console/zipball/cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9",
"reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9",
"shasum": ""
},
"require": {
@@ -1324,7 +1332,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.1.2"
"source": "https://github.com/symfony/console/tree/v7.1.3"
},
"funding": [
{
@@ -1340,7 +1348,7 @@
"type": "tidelift"
}
],
"time": "2024-06-28T10:03:55+00:00"
"time": "2024-07-26T12:41:01+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -1633,16 +1641,16 @@
},
{
"name": "symfony/finder",
"version": "v7.1.1",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "fbb0ba67688b780efbc886c1a0a0948dcf7205d6"
"reference": "717c6329886f32dc65e27461f80f2a465412fdca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/fbb0ba67688b780efbc886c1a0a0948dcf7205d6",
"reference": "fbb0ba67688b780efbc886c1a0a0948dcf7205d6",
"url": "https://api.github.com/repos/symfony/finder/zipball/717c6329886f32dc65e27461f80f2a465412fdca",
"reference": "717c6329886f32dc65e27461f80f2a465412fdca",
"shasum": ""
},
"require": {
@@ -1677,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.1"
"source": "https://github.com/symfony/finder/tree/v7.1.3"
},
"funding": [
{
@@ -1693,7 +1701,7 @@
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
"time": "2024-07-24T07:08:44+00:00"
},
{
"name": "symfony/options-resolver",
@@ -2238,16 +2246,16 @@
},
{
"name": "symfony/process",
"version": "v7.1.1",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "febf90124323a093c7ee06fdb30e765ca3c20028"
"reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/febf90124323a093c7ee06fdb30e765ca3c20028",
"reference": "febf90124323a093c7ee06fdb30e765ca3c20028",
"url": "https://api.github.com/repos/symfony/process/zipball/7f2f542c668ad6c313dc4a5e9c3321f733197eca",
"reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca",
"shasum": ""
},
"require": {
@@ -2279,7 +2287,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v7.1.1"
"source": "https://github.com/symfony/process/tree/v7.1.3"
},
"funding": [
{
@@ -2295,7 +2303,7 @@
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
"time": "2024-07-26T12:44:47+00:00"
},
{
"name": "symfony/service-contracts",
@@ -2444,16 +2452,16 @@
},
{
"name": "symfony/string",
"version": "v7.1.2",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "14221089ac66cf82e3cf3d1c1da65de305587ff8"
"reference": "ea272a882be7f20cad58d5d78c215001617b7f07"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/14221089ac66cf82e3cf3d1c1da65de305587ff8",
"reference": "14221089ac66cf82e3cf3d1c1da65de305587ff8",
"url": "https://api.github.com/repos/symfony/string/zipball/ea272a882be7f20cad58d5d78c215001617b7f07",
"reference": "ea272a882be7f20cad58d5d78c215001617b7f07",
"shasum": ""
},
"require": {
@@ -2511,7 +2519,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v7.1.2"
"source": "https://github.com/symfony/string/tree/v7.1.3"
},
"funding": [
{
@@ -2527,7 +2535,7 @@
"type": "tidelift"
}
],
"time": "2024-06-28T09:27:18+00:00"
"time": "2024-07-22T10:25:37+00:00"
}
],
"packages-dev": [],

View File

@@ -4,6 +4,7 @@ updates:
# Check for updates to GitHub Actions every week
- package-ecosystem: "github-actions"
directory: "/"
labels: []
schedule:
interval: "weekly"
@@ -11,6 +12,7 @@ updates:
- package-ecosystem: "composer"
directory: "/" # Location of package manifests
target-branch: develop
labels: []
versioning-strategy: increase
schedule:
interval: "weekly"
@@ -18,6 +20,7 @@ updates:
# yarn / JS updates
- package-ecosystem: "npm"
directory: "/"
labels: []
target-branch: develop
versioning-strategy: increase
schedule:

View File

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

View File

@@ -0,0 +1,110 @@
<?php
/*
* AccountController.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V2\Controllers\JsonApi;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\JsonApi\V2\Accounts\AccountCollectionQuery;
use FireflyIII\JsonApi\V2\Accounts\AccountSchema;
use FireflyIII\JsonApi\V2\Accounts\AccountSingleQuery;
use FireflyIII\Models\Account;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Core\Responses\DataResponse;
use LaravelJsonApi\Laravel\Http\Controllers\Actions;
/**
* Class AccountController
*
* This class handles api/v2 requests for accounts.
* Most stuff is default stuff.
*/
class AccountController extends Controller
{
use Actions\AttachRelationship;
use Actions\Destroy;
use Actions\DetachRelationship;
// use Actions\FetchMany;
// use Actions\FetchOne;
use Actions\FetchRelated;
use Actions\FetchRelationship;
use Actions\Store;
use Actions\Update;
use Actions\UpdateRelationship;
/**
* Fetch zero to many JSON API resources.
*
* @return Responsable|Response
*/
public function index(AccountSchema $schema, AccountCollectionQuery $request)
{
Log::debug(__METHOD__);
$models = $schema
->repository()
->queryAll()
->withRequest($request)
->get()
;
// do something custom...
return new DataResponse($models);
}
/**
* Fetch zero to one JSON API resource by id.
*
* @return Responsable|Response
*/
public function show(AccountSchema $schema, AccountSingleQuery $request, Account $account)
{
$model = $schema
->repository()
->queryOne($account)
->withRequest($request)
->first()
;
// do something custom...
return new DataResponse($model);
}
// public function readAccountBalances(AnonymousQuery $query, AccountBalanceSchema $schema, Account $account): Responsable
// {
// $schema = JsonApi::server()->schemas()->schemaFor('account-balances');
//
// $models = $schema
// ->repository()
// ->queryAll()
// ->withRequest($query)
// ->withAccount($account)
// ->get()
// ;
//
// return DataResponse::make($models);
// }
}

View File

@@ -44,55 +44,8 @@ class FixUnevenAmount extends Command
*/
public function handle(): int
{
$count = 0;
$journals = \DB::table('transactions')
->groupBy('transaction_journal_id')
->whereNull('deleted_at')
->get(['transaction_journal_id', \DB::raw('SUM(amount) AS the_sum')])
;
/** @var \stdClass $entry */
foreach ($journals as $entry) {
$sum = (string)$entry->the_sum;
if (!is_numeric($sum)
|| '' === $sum // @phpstan-ignore-line
|| str_contains($sum, 'e')
|| str_contains($sum, ',')) {
$message = sprintf(
'Journal #%d has an invalid sum ("%s"). No sure what to do.',
$entry->transaction_journal_id,
$entry->the_sum
);
$this->friendlyWarning($message);
app('log')->warning($message);
++$count;
continue;
}
$res = -1;
try {
$res = bccomp($sum, '0');
} catch (\ValueError $e) {
$this->friendlyError(sprintf('Could not bccomp("%s", "0").', $sum));
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
}
if (0 !== $res) {
$message = sprintf(
'Sum of journal #%d is %s instead of zero.',
$entry->transaction_journal_id,
$entry->the_sum
);
$this->friendlyWarning($message);
app('log')->warning($message);
$this->fixJournal($entry->transaction_journal_id);
++$count;
}
}
if (0 === $count) {
$this->friendlyPositive('Database amount integrity is OK');
}
$this->fixUnevenAmounts();
$this->matchCurrencies();
return 0;
}
@@ -149,4 +102,76 @@ class FixUnevenAmount extends Command
$message = sprintf('Corrected amount in transaction journal #%d', $param);
$this->friendlyInfo($message);
}
private function fixUnevenAmounts(): void
{
$count = 0;
$journals = \DB::table('transactions')
->groupBy('transaction_journal_id')
->whereNull('deleted_at')
->get(['transaction_journal_id', \DB::raw('SUM(amount) AS the_sum')])
;
/** @var \stdClass $entry */
foreach ($journals as $entry) {
$sum = (string) $entry->the_sum;
if (!is_numeric($sum)
|| '' === $sum // @phpstan-ignore-line
|| str_contains($sum, 'e')
|| str_contains($sum, ',')) {
$message = sprintf(
'Journal #%d has an invalid sum ("%s"). No sure what to do.',
$entry->transaction_journal_id,
$entry->the_sum
);
$this->friendlyWarning($message);
app('log')->warning($message);
++$count;
continue;
}
$res = -1;
try {
$res = bccomp($sum, '0');
} catch (\ValueError $e) {
$this->friendlyError(sprintf('Could not bccomp("%s", "0").', $sum));
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
}
if (0 !== $res) {
$message = sprintf(
'Sum of journal #%d is %s instead of zero.',
$entry->transaction_journal_id,
$entry->the_sum
);
$this->friendlyWarning($message);
app('log')->warning($message);
$this->fixJournal($entry->transaction_journal_id);
++$count;
}
}
if (0 === $count) {
$this->friendlyPositive('Database amount integrity is OK');
}
}
private function matchCurrencies(): void
{
$journals = TransactionJournal::leftJoin('transactions', 'transaction_journals.id', 'transactions.transaction_journal_id')
->where('transactions.transaction_currency_id', '!=', \DB::raw('transaction_journals.transaction_currency_id'))
->get(['transaction_journals.*'])
;
if (0 === $journals->count()) {
$this->friendlyPositive('Journal currency integrity is OK');
return;
}
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
Transaction::where('transaction_journal_id', $journal->id)->update(['transaction_currency_id' => $journal->transaction_currency_id]);
}
$this->friendlyPositive(sprintf('Fixed %d journal(s) with mismatched currencies.', $journals->count()));
}
}

View File

@@ -1,43 +0,0 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Api\V3\Controllers;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\JsonApi\V3\AccountBalances\AccountBalanceSchema;
use FireflyIII\Models\Account;
use Illuminate\Contracts\Support\Responsable;
use LaravelJsonApi\Core\Facades\JsonApi;
use LaravelJsonApi\Core\Responses\DataResponse;
use LaravelJsonApi\Laravel\Http\Controllers\Actions;
use LaravelJsonApi\Laravel\Http\Requests\AnonymousQuery;
class AccountController extends Controller
{
use Actions\AttachRelationship;
use Actions\Destroy;
use Actions\DetachRelationship;
use Actions\FetchMany;
use Actions\FetchOne;
use Actions\FetchRelated;
use Actions\FetchRelationship;
use Actions\Store;
use Actions\Update;
use Actions\UpdateRelationship;
public function readAccountBalances(AnonymousQuery $query, AccountBalanceSchema $schema, Account $account): Responsable
{
$schema = JsonApi::server()->schemas()->schemaFor('account-balances');
$models = $schema
->repository()
->queryAll()
->withRequest($query)
->withAccount($account)
->get()
;
return DataResponse::make($models);
}
}

View File

@@ -21,7 +21,7 @@
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\AccountBalances;
namespace FireflyIII\JsonApi\V2\AccountBalances;
use FireflyIII\Entities\AccountBalance;
use LaravelJsonApi\Contracts\Store\QueriesAll;

View File

@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\AccountBalances;
namespace FireflyIII\JsonApi\V2\AccountBalances;
use Illuminate\Http\Request;
use LaravelJsonApi\Core\Resources\JsonApiResource;

View File

@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\AccountBalances;
namespace FireflyIII\JsonApi\V2\AccountBalances;
use FireflyIII\Entities\AccountBalance;
use LaravelJsonApi\Core\Schema\Schema;

View File

@@ -21,7 +21,7 @@
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\AccountBalances\Capabilities;
namespace FireflyIII\JsonApi\V2\AccountBalances\Capabilities;
use FireflyIII\Entities\AccountBalance;
use FireflyIII\Models\Account;

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\Accounts;
use FireflyIII\Models\Account;
use FireflyIII\Rules\IsAllowedGroupAction;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Laravel\Http\Requests\ResourceQuery;
use LaravelJsonApi\Validation\Rule as JsonApiRule;
class AccountCollectionQuery extends ResourceQuery
{
/**
* Get the validation rules that apply to the request query parameters.
*/
public function rules(): array
{
Log::debug(__METHOD__);
return [
'fields' => [
'nullable',
'array',
JsonApiRule::fieldSets(),
],
'user_group_id' => [
'nullable',
'integer',
new IsAllowedGroupAction(Account::class, request()->method()),
],
'filter' => [
'nullable',
'array',
JsonApiRule::filter(),
],
'include' => [
'nullable',
'string',
JsonApiRule::includePaths(),
],
'page' => [
'nullable',
'array',
JsonApiRule::page(),
],
'sort' => [
'nullable',
'string',
JsonApiRule::sort(),
],
'withCount' => [
'nullable',
'string',
JsonApiRule::countable(),
],
];
}
}

View File

@@ -0,0 +1,98 @@
<?php
/*
* AccountRepository.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\Accounts;
use FireflyIII\Models\Account;
use FireflyIII\Support\JsonApi\Concerns\UsergroupAware;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Contracts\Store\QueriesAll;
use LaravelJsonApi\NonEloquent\AbstractRepository;
use LaravelJsonApi\NonEloquent\Capabilities\CrudRelations;
use LaravelJsonApi\NonEloquent\Concerns\HasCrudCapability;
use LaravelJsonApi\NonEloquent\Concerns\HasRelationsCapability;
/**
* Class AccountRepository
*
* The repository collects a single or many (account) objects from the database and returns them to the
* account resource. The account resource links all account properties to the JSON properties.
*
* For the queryAll thing, a separate query is constructed that does the actual querying of the database.
* This is necessary because the user can't just query all accounts (it would return other user's data)
* and because we also need to collect all kinds of metadata, like the currency and user info.
*/
class AccountRepository extends AbstractRepository implements QueriesAll
{
use HasCrudCapability;
use HasRelationsCapability;
use UsergroupAware;
/**
* SiteRepository constructor.
*/
public function __construct() {}
public function exists(string $resourceId): bool
{
Log::debug(__METHOD__);
return null !== Account::find((int) $resourceId);
}
public function find(string $resourceId): ?object
{
Log::debug(__METHOD__);
// throw new \RuntimeException('trace me');
$account = Account::find((int) $resourceId);
if (null === $account) {
return null;
}
// enrich the collected data
$enrichment = new AccountEnrichment();
return $enrichment->enrichSingle($account);
}
public function queryAll(): Capabilities\AccountQuery
{
Log::debug(__METHOD__);
return Capabilities\AccountQuery::make()
->withUserGroup($this->userGroup)
->withServer($this->server)
->withSchema($this->schema)
;
}
protected function crud(): Capabilities\CrudAccount
{
return Capabilities\CrudAccount::make();
}
protected function relations(): CrudRelations
{
return Capabilities\CrudAccountRelations::make();
}
}

View File

@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\Accounts;
use FireflyIII\Models\Account;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Core\Resources\JsonApiResource;
/**
* @property Account $resource
*/
class AccountResource extends JsonApiResource
{
/**
* Get the resource id.
*/
public function id(): string
{
Log::debug(__METHOD__);
return (string) $this->resource->id;
}
/**
* Get the resource's attributes.
*
* @param null|Request $request
*/
public function attributes($request): iterable
{
Log::debug(__METHOD__);
return [
'created_at' => $this->resource->created_at,
'updated_at' => $this->resource->updated_at,
'name' => $this->resource->name,
'active' => $this->resource->active,
'order' => $this->resource->order,
'type' => $this->resource->account_type_string,
'account_role' => $this->resource->account_role,
'account_number' => '' === $this->resource->account_number ? null : $this->resource->account_number,
// currency
'currency_id' => $this->resource->currency_id,
'currency_name' => $this->resource->currency_name,
'currency_code' => $this->resource->currency_code,
'currency_symbol' => $this->resource->currency_symbol,
'currency_decimal_places' => $this->resource->currency_decimal_places,
// liability things
'liability_direction' => $this->resource->liability_direction,
'interest' => $this->resource->interest,
'interest_period' => $this->resource->interest_period,
'current_debt' => $this->resource->current_debt,
'last_activity' => $this->resource->last_activity,
];
}
/**
* Get the resource's relationships.
*
* @param null|Request $request
*/
public function relationships($request): iterable
{
return [
$this->relation('user')->withData($this->resource->user),
];
}
}

View File

@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\Accounts;
namespace FireflyIII\JsonApi\V2\Accounts;
use FireflyIII\Models\Account;
use Illuminate\Http\Request;
@@ -10,8 +10,13 @@ use LaravelJsonApi\Core\Resources\JsonApiResource;
/**
* @property Account $resource
*
* This class collects the resources attributes, the account in this case.
* Generally speaking, each property here is directly related to a property on the account object itself.
* However, many properties are collected from other sources, like the user or the currency.
* As a result, the account repository is where it's at, which is where the collection takes place and is optimised.
*/
class AccountResource extends JsonApiResource
class AccountResourceOld extends JsonApiResource
{
/**
* Get the resource's attributes.
@@ -20,32 +25,23 @@ class AccountResource extends JsonApiResource
*/
public function attributes($request): iterable
{
// fields removed here have been migrated.
return [
'created_at' => $this->resource->created_at,
'updated_at' => $this->resource->updated_at,
'name' => $this->resource->name,
'iban' => '' === $this->resource->iban ? null : $this->resource->iban,
'active' => $this->resource->active,
'last_activity' => $this->resource->last_activity,
'type' => $this->resource->type,
'account_role' => $this->resource->account_role,
'created_at' => $this->resource->created_at,
'updated_at' => $this->resource->updated_at,
'name' => $this->resource->name,
// 'virtual_balance' => $this->resource->virtual_balance,
// 'native_balance' => $this->resource->native_balance,
// 'user' => $this->resource->user_array,
// 'balances' => []
//
// currency
// 'currency_id' => $this->resource->currency_id,
// 'currency_code' => $this->resource->currency_code,
// 'currency_symbol' => $this->resource->currency_symbol,
// 'currency_decimal_places' => $this->resource->currency_decimal_places,
// balance (in currency, on date)
// 'current_balance' => $this->resource->current_balance,
// 'current_balance' => app('steam')->bcround(app('steam')->balance($account, $date), $decimalPlaces),
// 'current_balance_date' => $date->toAtomString(),
// 'notes' => $this->repository->getNoteText($account),
// 'monthly_payment_date' => $monthlyPaymentDate,
// 'credit_card_type' => $creditCardType,
@@ -65,11 +61,6 @@ class AccountResource extends JsonApiResource
// 'order' => $order,
// 'currency_id' => (string) $currency->id,
// 'currency_code' => $currency->code,
// 'currency_symbol' => $currency->symbol,
// 'currency_decimal_places' => $currency->decimal_places,
//
// 'native_currency_id' => (string) $this->default->id,
// 'native_currency_code' => $this->default->code,
// 'native_currency_symbol' => $this->default->symbol,
@@ -86,15 +77,9 @@ class AccountResource extends JsonApiResource
// 'balance_difference_start' => $diffStart,
// 'balance_difference_end' => $diffEnd,
//
// // more meta
// 'last_activity' => array_key_exists($id, $this->lastActivity) ? $this->lastActivity[$id]->toAtomString() : null,
//
// // liability stuff
// 'liability_type' => $liabilityType,
// 'liability_direction' => $liabilityDirection,
// 'interest' => $interest,
// 'interest_period' => $interestPeriod,
// 'current_debt' => $currentDebt,
//
// // object group
// 'object_group_id' => null !== $objectGroupId ? (string) $objectGroupId : null,
@@ -123,7 +108,8 @@ class AccountResource extends JsonApiResource
{
return [
$this->relation('user')->withData($this->resource->user),
$this->relation('account_balances')->withData($this->resource->balances),
$this->relation('currency')->withData($this->resource->transactionCurrency),
// $this->relation('account_balances')->withData($this->resource->balances),
];
}
}

View File

@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\Accounts;
use FireflyIII\Models\Account;
use FireflyIII\Support\JsonApi\Concerns\UsergroupAware;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Core\Schema\Schema;
use LaravelJsonApi\Eloquent\Fields\Relations\HasOne;
use LaravelJsonApi\NonEloquent\Fields\Attribute;
use LaravelJsonApi\NonEloquent\Fields\ID;
use LaravelJsonApi\NonEloquent\Filters\Filter;
class AccountSchema extends Schema
{
use UsergroupAware;
/**
* The model the schema corresponds to.
*/
public static string $model = Account::class;
/**
* Get the resource fields.
*/
public function fields(): array
{
Log::debug(__METHOD__);
return [
ID::make(),
Attribute::make('created_at'),
Attribute::make('updated_at'),
// basic info and meta data
Attribute::make('name'),
Attribute::make('active'),
Attribute::make('order'),
Attribute::make('type'),
Attribute::make('account_role'),
Attribute::make('account_number'),
// currency
Attribute::make('currency_id'),
Attribute::make('currency_name'),
Attribute::make('currency_code'),
Attribute::make('currency_symbol'),
Attribute::make('currency_decimal_places'),
// liability things
Attribute::make('liability_direction'),
Attribute::make('interest'),
Attribute::make('interest_period'),
Attribute::make('current_debt'),
// dynamic data
Attribute::make('last_activity'),
HasOne::make('user')->readOnly(),
];
}
/**
* Get the resource filters.
*/
public function filters(): array
{
Log::debug(__METHOD__);
return [
Filter::make('id'),
];
}
public function repository(): AccountRepository
{
Log::debug(__METHOD__);
$this->setUserGroup($this->server->getUsergroup());
return AccountRepository::make()
->withServer($this->server)
->withSchema($this)
->withUserGroup($this->userGroup)
;
}
}

View File

@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\Accounts;
namespace FireflyIII\JsonApi\V2\Accounts;
use FireflyIII\Models\Account;
use LaravelJsonApi\Eloquent\Contracts\Paginator;
@@ -17,7 +17,14 @@ use LaravelJsonApi\Eloquent\Filters\WhereIdIn;
use LaravelJsonApi\Eloquent\Pagination\PagePagination;
use LaravelJsonApi\Eloquent\Schema;
class AccountSchema extends Schema
/**
* Class AccountSchema
*
* This is the schema of all fields that an account exposes to the world.
* Fields do not have to have a relation to the actual model.
* Fields mentioned here still need to be filled in by the AccountResource.
*/
class AccountSchemaOld extends Schema
{
/**
* The model the schema corresponds to.
@@ -34,18 +41,19 @@ class AccountSchema extends Schema
DateTime::make('created_at')->sortable()->readOnly(),
DateTime::make('updated_at')->sortable()->readOnly(),
Str::make('name')->sortable(),
Str::make('account_type'),
Str::make('virtual_balance'),
Str::make('iban'),
Boolean::make('active'),
Number::make('order'),
HasOne::make('user'),
HasMany::make('account_balances'),
// Str::make('account_type'),
// Str::make('virtual_balance'),
// Str::make('iban'),
// Boolean::make('active'),
// Number::make('order'),
HasOne::make('user')->readOnly(),
// HasMany::make('account_balances'),
];
}
/**
* Get the resource filters.
* Filters mentioned here can be used to filter the results.
* TODO write down exactly how this works.
*/
public function filters(): array
{

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\Accounts;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Laravel\Http\Requests\ResourceQuery;
use LaravelJsonApi\Validation\Rule as JsonApiRule;
class AccountSingleQuery extends ResourceQuery
{
/**
* Get the validation rules that apply to the request query parameters.
*/
public function rules(): array
{
Log::debug(__METHOD__);
return [
'fields' => [
'nullable',
'array',
JsonApiRule::fieldSets(),
],
'filter' => [
'nullable',
'array',
JsonApiRule::filter()->forget('id'),
],
'include' => [
'nullable',
'string',
JsonApiRule::includePaths(),
],
'page' => JsonApiRule::notSupported(),
'sort' => JsonApiRule::notSupported(),
'withCount' => [
'nullable',
'string',
JsonApiRule::countable(),
],
];
}
}

View File

@@ -21,7 +21,7 @@
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\Accounts\Capabilities;
namespace FireflyIII\JsonApi\V2\Accounts\Capabilities;
use FireflyIII\Support\JsonApi\Concerns\UsergroupAware;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
@@ -29,6 +29,7 @@ use FireflyIII\Support\JsonApi\ExpandsQuery;
use FireflyIII\Support\JsonApi\FiltersPagination;
use FireflyIII\Support\JsonApi\SortsCollection;
use FireflyIII\Support\JsonApi\ValidateSortParameters;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Contracts\Store\HasPagination;
use LaravelJsonApi\NonEloquent\Capabilities\QueryAll;
use LaravelJsonApi\NonEloquent\Concerns\PaginatesEnumerables;
@@ -43,31 +44,45 @@ class AccountQuery extends QueryAll implements HasPagination
use ValidateSortParameters;
#[\Override]
/**
* This method returns all accounts, given a bunch of filters and sort fields, together with pagination.
*/
public function get(): iterable
{
Log::debug(__METHOD__);
// collect filters
$filters = $this->queryParameters->filter();
// collect sort options
$sort = $this->queryParameters->sortFields();
// collect pagination based on the page
$pagination = $this->filtersPagination($this->queryParameters->page());
$needsAll = $this->validateParams('account', $sort);
// check if we need all accounts, regardless of pagination
// This is necessary when the user wants to sort on specific params.
$needsAll = $this->needsFullDataset('account', $sort);
// start the query
$query = $this->userGroup->accounts();
// add pagination to the query, limiting the results.
if (!$needsAll) {
$query = $this->addPagination($query, $pagination);
}
// add sort and filter parameters to the query.
$query = $this->addSortParams($query, $sort);
$query = $this->addFilterParams('account', $query, $filters);
// collect the result.
$collection = $query->get(['accounts.*']);
// enrich data
// enrich the collected data
$enrichment = new AccountEnrichment();
$collection = $enrichment->enrich($collection);
// add filters after the query
// TODO add filters after the query, if there are filters that cannot be applied to the database but only
// to the enriched results.
// add sort after the query
// sort the data after the query, and return it right away.
return $this->sortCollection($collection, $sort);
// var_dump($filters->value('name'));
// exit;
}
}

View File

@@ -1,6 +1,6 @@
<?php
/*
* AccountRepository.php
* CrudAccount.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
@@ -21,33 +21,22 @@
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\Accounts;
namespace FireflyIII\JsonApi\V2\Accounts\Capabilities;
use FireflyIII\Models\Account;
use FireflyIII\Support\JsonApi\Concerns\UsergroupAware;
use LaravelJsonApi\Contracts\Store\QueriesAll;
use LaravelJsonApi\NonEloquent\AbstractRepository;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use LaravelJsonApi\NonEloquent\Capabilities\CrudResource;
class AccountRepository extends AbstractRepository implements QueriesAll
class CrudAccount extends CrudResource
{
use UsergroupAware;
/**
* SiteRepository constructor.
* Read the supplied site.
*/
public function __construct() {}
public function find(string $resourceId): ?object
public function read(Account $account): ?Account
{
return Account::find((int) $resourceId);
}
// enrich the collected data
$enrichment = new AccountEnrichment();
public function queryAll(): Capabilities\AccountQuery
{
return Capabilities\AccountQuery::make()
->withUserGroup($this->userGroup)
->withServer($this->server)
->withSchema($this->schema)
;
return $enrichment->enrichSingle($account);
}
}

View File

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

53
app/JsonApi/V2/Server.php Normal file
View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2;
use FireflyIII\JsonApi\V2\Accounts\AccountSchema;
use FireflyIII\JsonApi\V2\Users\UserSchema;
use FireflyIII\Support\JsonApi\Concerns\UsergroupAware;
use FireflyIII\Support\JsonApi\Concerns\UserGroupDetectable;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Core\Server\Server as BaseServer;
/**
* Class Server
*
* This class serves as a generic class for the v2 API "server".
*/
class Server extends BaseServer
{
use UsergroupAware;
use UserGroupDetectable;
/**
* The base URI namespace for this server.
*/
protected string $baseUri = '/api/v2';
/**
* Bootstrap the server when it is handling an HTTP request.
*/
public function serving(): void
{
Log::debug(__METHOD__);
// at this point the user may not actually have access to this user group.
$res = $this->detectUserGroup();
$this->setUserGroup($res);
}
/**
* Get the server's list of schemas.
*/
protected function allSchemas(): array
{
Log::debug(__METHOD__);
return [
AccountSchema::class,
UserSchema::class,
// AccountBalanceSchema::class,
];
}
}

View File

@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\Users;
namespace FireflyIII\JsonApi\V2\Users;
use FireflyIII\Models\User;
use Illuminate\Http\Request;

View File

@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\Users;
namespace FireflyIII\JsonApi\V2\Users;
use FireflyIII\User;
use LaravelJsonApi\Eloquent\Contracts\Paginator;

View File

@@ -1,38 +0,0 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3;
use FireflyIII\JsonApi\V3\Accounts\AccountSchema;
use FireflyIII\JsonApi\V3\AccountBalances\AccountBalanceSchema;
use FireflyIII\JsonApi\V3\Users\UserSchema;
use LaravelJsonApi\Core\Server\Server as BaseServer;
class Server extends BaseServer
{
/**
* The base URI namespace for this server.
*/
protected string $baseUri = '/api/v3';
/**
* Bootstrap the server when it is handling an HTTP request.
*/
public function serving(): void
{
// no-op
}
/**
* Get the server's list of schemas.
*/
protected function allSchemas(): array
{
return [
AccountSchema::class,
UserSchema::class,
AccountBalanceSchema::class,
];
}
}

View File

@@ -28,6 +28,7 @@ use Eloquent;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
use FireflyIII\User;
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Collection;
@@ -50,6 +51,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @property null|Carbon $deleted_at
* @property int $user_id
* @property int $account_type_id
* @property string $account_type_string
* @property string $name
* @property string $virtual_balance
* @property null|string $iban
@@ -74,6 +76,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @property Collection|Transaction[] $transactions
* @property null|int $transactions_count
* @property User $user
* @property string $last_activity
*
* @method static EloquentBuilder|Account accountTypeIn($types)
* @method static EloquentBuilder|Account newModelQuery()
@@ -116,6 +119,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
*/
class Account extends Model
{
use Cachable;
use HasFactory;
use ReturnsIntegerIdTrait;
use ReturnsIntegerUserIdTrait;
@@ -144,7 +148,7 @@ class Account extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$accountId = (int)$value;
$accountId = (int) $value;
/** @var User $user */
$user = auth()->user();
@@ -246,7 +250,7 @@ class Account extends Model
public function setVirtualBalanceAttribute(mixed $value): void
{
$value = (string)$value;
$value = (string) $value;
if ('' === $value) {
$value = null;
}
@@ -266,7 +270,7 @@ class Account extends Model
protected function accountId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
get: static fn ($value) => (int) $value,
);
}
@@ -276,21 +280,21 @@ class Account extends Model
protected function accountTypeId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
get: static fn ($value) => (int) $value,
);
}
protected function iban(): Attribute
{
return Attribute::make(
get: static fn ($value) => null === $value ? null : trim(str_replace(' ', '', (string)$value)),
get: static fn ($value) => null === $value ? null : trim(str_replace(' ', '', (string) $value)),
);
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
get: static fn ($value) => (int) $value,
);
}
@@ -300,7 +304,7 @@ class Account extends Model
protected function virtualBalance(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string)$value,
get: static fn ($value) => (string) $value,
);
}
}

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Models;
use Carbon\Carbon;
use Eloquent;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Collection;
@@ -93,6 +94,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
*/
class Transaction extends Model
{
use Cachable;
use HasFactory;
use ReturnsIntegerIdTrait;
use SoftDeletes;

View File

@@ -554,6 +554,12 @@ class AccountRepository implements AccountRepositoryInterface
++$index;
}
}
// reset the rest to zero.
$all = [AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::CREDITCARD, AccountType::MORTGAGE];
$this->user->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->whereNotIn('account_types.type', $all)
->update(['order' => 0])
;
}
public function searchAccount(string $query, array $types, int $limit): Collection

View File

@@ -241,6 +241,12 @@ class AccountRepository implements AccountRepositoryInterface
++$index;
}
}
// reset the rest to zero.
$all = [AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::CREDITCARD, AccountType::MORTGAGE];
$this->user->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->whereNotIn('account_types.type', $all)
->update(['order' => 0])
;
}
public function getAccountsByType(array $types, ?array $sort = [], ?array $filters = []): Collection

View File

@@ -0,0 +1,130 @@
<?php
/*
* IsAllowedGroupAction.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Rules;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface;
use FireflyIII\User;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Support\Facades\Log;
class IsAllowedGroupAction implements ValidationRule
{
private string $className;
private string $methodName;
private array $acceptedRoles;
private UserGroupRepositoryInterface $repository;
public function __construct(string $className, string $methodName)
{
$this->className = $className;
$this->methodName = $methodName;
// you need these roles to do anything with any endpoint.
$this->acceptedRoles = [UserRoleEnum::OWNER, UserRoleEnum::FULL];
$this->repository = app(UserGroupRepositoryInterface::class);
}
/**
* @throws AuthorizationException
*/
#[\Override]
public function validate(string $attribute, mixed $value, \Closure $fail): void
{
if ('GET' === $this->methodName) {
// need at least "read only rights".
$this->acceptedRoles[] = UserRoleEnum::READ_ONLY;
}
if ('GET' !== $this->methodName) {
// either post, put or delete or something else.. you need more access rights.
switch ($this->className) {
default:
throw new AuthorizationException(sprintf('Cannot handle class "%s"', $this->className));
case Account::class:
$this->acceptedRoles[] = UserRoleEnum::MANAGE_TRANSACTIONS;
break;
}
}
$this->validateUserGroup((int)$value, $fail);
}
private function validateUserGroup(int $userGroupId, \Closure $fail): void
{
Log::debug(sprintf('validateUserGroup: %s', static::class));
if (!auth()->check()) {
Log::debug('validateUserGroup: user is not logged in, return NULL.');
$fail('validation.no_auth_user_group')->translate();
return;
}
/** @var User $user */
$user = auth()->user();
if (0 !== $userGroupId) {
Log::debug(sprintf('validateUserGroup: user group submitted, search for memberships in group #%d.', $userGroupId));
}
if (0 === $userGroupId) {
$userGroupId = $user->user_group_id;
Log::debug(sprintf('validateUserGroup: no user group submitted, use default group #%d.', $userGroupId));
}
$this->repository->setUser($user);
$memberships = $this->repository->getMembershipsFromGroupId($userGroupId);
if (0 === $memberships->count()) {
Log::debug(sprintf('validateUserGroup: user has no access to group #%d.', $userGroupId));
$fail('validation.no_access_user_group')->translate();
return;
}
// need to get the group from the membership:
$userGroup = $this->repository->getById($userGroupId);
if (null === $userGroup) {
Log::debug(sprintf('validateUserGroup: group #%d does not exist.', $userGroupId));
$fail('validation.belongs_user_or_user_group')->translate();
return;
}
Log::debug(sprintf('validateUserGroup: validate access of user to group #%d ("%s").', $userGroupId, $userGroup->title));
Log::debug(sprintf('validateUserGroup: have %d roles to check.', count($this->acceptedRoles)), $this->acceptedRoles);
/** @var UserRoleEnum $role */
foreach ($this->acceptedRoles as $role) {
if ($user->hasRoleInGroupOrOwner($userGroup, $role)) {
Log::debug(sprintf('validateUserGroup: User has role "%s" in group #%d, return.', $role->value, $userGroupId));
return;
}
Log::debug(sprintf('validateUserGroup: User does NOT have role "%s" in group #%d, continue searching.', $role->value, $userGroupId));
}
Log::debug('validateUserGroup: User does NOT have enough rights to access endpoint.');
$fail('validation.belongs_user_or_user_group')->translate();
}
}

View File

@@ -0,0 +1,62 @@
<?php
/*
* UserGroupDetectable.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Concerns;
use FireflyIII\Models\UserGroup;
use FireflyIII\User;
trait UserGroupDetectable
{
/**
* Return the user group or NULL if none is set.
* Will throw exception if invalid.
* TODO Duplicate from API v2 code.
*/
public function detectUserGroup(): ?UserGroup
{
/** @var User $user */
$user = auth()->user();
app('log')->debug('Now in detectUserGroup()');
/** @var null|UserGroup $userGroup */
$userGroup = request()->route()?->parameter('userGroup');
if (null === $userGroup) {
app('log')->debug('Request class has no userGroup parameter, but perhaps there is a parameter.');
$userGroupId = (int)request()->get('user_group_id');
if (0 === $userGroupId) {
app('log')->debug(sprintf('Request class has no user_group_id parameter, grab default from user (group #%d).', $user->user_group_id));
$userGroupId = (int)$user->user_group_id;
}
$userGroup = UserGroup::find($userGroupId);
if (null === $userGroup) {
app('log')->error(sprintf('Request class has user_group_id (#%d), but group does not exist.', $userGroupId));
return null;
}
app('log')->debug('Request class has valid user_group_id.');
}
return $userGroup;
}
}

View File

@@ -29,35 +29,56 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* Class AccountEnrichment
*
* This class "enriches" accounts and adds data from other tables and models to each account model.
*/
class AccountEnrichment implements EnrichmentInterface
{
private Collection $collection;
private array $currencies;
private AccountRepositoryInterface $repository;
private CurrencyRepositoryInterface $currencyRepository;
public function __construct()
{
$this->repository = app(AccountRepositoryInterface::class);
$this->currencyRepository = app(CurrencyRepositoryInterface::class);
}
#[\Override]
/**
* Do the actual enrichment.
*/
public function enrich(Collection $collection): Collection
{
Log::debug(sprintf('Now doing account enrichment for %d account(s)', $collection->count()));
// prep local fields
$this->collection = $collection;
$this->currencies = [];
// do everything here:
$this->getLastActivity();
// $this->getMetaBalances();
$this->collectAccountTypes();
$this->collectMetaData();
// $this->getMetaBalances();
$this->collection->transform(function (Account $account) {
$account->user_array = ['id' => 1, 'bla bla' => 'bla'];
$account->balances = collect([
['balance_id' => 1, 'balance' => 5],
['balance_id' => 2, 'balance' => 5],
['balance_id' => 3, 'balance' => 5],
]);
return $account;
});
// $this->collection->transform(function (Account $account) {
// $account->user_array = ['id' => 1, 'bla bla' => 'bla'];
// $account->balances = collect([
// ['balance_id' => 1, 'balance' => 5],
// ['balance_id' => 2, 'balance' => 5],
// ['balance_id' => 3, 'balance' => 5],
// ]);
//
// return $account;
// });
return $this->collection;
}
@@ -67,9 +88,7 @@ class AccountEnrichment implements EnrichmentInterface
*/
private function getLastActivity(): void
{
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$lastActivity = $accountRepository->getLastActivity($this->collection);
$lastActivity = $this->repository->getLastActivity($this->collection);
foreach ($lastActivity as $row) {
$this->collection->where('id', $row['account_id'])->first()->last_activity = Carbon::parse($row['date_max'], config('app.timezone'));
}
@@ -98,17 +117,15 @@ class AccountEnrichment implements EnrichmentInterface
*/
private function collectAccountTypes(): void
{
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$accountTypes = $accountRepository->getAccountTypes($this->collection);
$types = [];
$accountTypes = $this->repository->getAccountTypes($this->collection);
$types = [];
/** @var AccountType $row */
foreach ($accountTypes as $row) {
$types[$row->id] = $row->type;
}
$this->collection->transform(function (Account $account) use ($types) {
$account->type = $types[$account->id];
$account->account_type_string = $types[$account->id];
return $account;
});
@@ -116,17 +133,11 @@ class AccountEnrichment implements EnrichmentInterface
private function collectMetaData(): void
{
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$metaFields = $this->repository->getMetaValues($this->collection, ['currency_id', 'account_role', 'account_number', 'liability_direction', 'interest', 'interest_period', 'current_debt']);
$currencyIds = $metaFields->where('name', 'currency_id')->pluck('data')->toArray();
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$metaFields = $accountRepository->getMetaValues($this->collection, ['currency_id', 'account_role', 'account_number', 'liability_direction', 'interest', 'interest_period', 'current_debt']);
$currencyIds = $metaFields->where('name', 'currency_id')->pluck('data')->toArray();
$currencies = [];
foreach ($repository->getByIds($currencyIds) as $currency) {
$currencies = [];
foreach ($this->currencyRepository->getByIds($currencyIds) as $currency) {
$id = $currency->id;
$currencies[$id] = $currency;
}
@@ -137,6 +148,7 @@ class AccountEnrichment implements EnrichmentInterface
$account->{$entry->name} = $entry->data;
if ('currency_id' === $entry->name) {
$id = (int) $entry->data;
$account->currency_name = $currencies[$id]?->name;
$account->currency_code = $currencies[$id]?->code;
$account->currency_symbol = $currencies[$id]?->symbol;
$account->currency_decimal_places = $currencies[$id]?->decimal_places;
@@ -146,4 +158,14 @@ class AccountEnrichment implements EnrichmentInterface
return $account;
});
}
#[\Override]
public function enrichSingle(Model $model): Model
{
Log::debug(__METHOD__);
$collection = new Collection([$model]);
$collection = $this->enrich($collection);
return $collection->first();
}
}

View File

@@ -23,9 +23,12 @@ declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
interface EnrichmentInterface
{
public function enrich(Collection $collection): Collection;
public function enrichSingle(Model $model): Model;
}

View File

@@ -27,7 +27,7 @@ use LaravelJsonApi\Core\Query\SortFields;
trait ValidateSortParameters
{
public function validateParams(string $class, ?SortFields $params): bool
public function needsFullDataset(string $class, ?SortFields $params): bool
{
if (null === $params) {
return false;

View File

@@ -121,6 +121,10 @@ class AccountBalanceCalculator
$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);

View File

@@ -73,6 +73,7 @@ trait ChecksLogin
/**
* Return the user group or NULL if none is set.
* Will throw exception if invalid.
* TODO duplicated in JSONAPI code.
*/
public function getUserGroup(): ?UserGroup
{

View File

@@ -836,6 +836,7 @@ class Steam
if (!str_contains($value, 'E')) {
return $value;
}
Log::debug(sprintf('Floatalizing %s', $value));
$number = substr($value, 0, (int)strpos($value, 'E'));
if (str_contains($number, '.')) {

View File

@@ -84,6 +84,7 @@
"bacon/bacon-qr-code": "2.*",
"diglactic/laravel-breadcrumbs": "^9",
"gdbots/query-parser": "^3.0",
"genealabs/laravel-model-caching": "^11.0",
"guzzlehttp/guzzle": "^7.8",
"jc5/google2fa-laravel": "^2.0",
"jc5/recovery": "^2",
@@ -107,8 +108,8 @@
"spatie/laravel-ignition": "^2",
"spatie/period": "^2.4",
"symfony/expression-language": "^7.0",
"symfony/http-client": "^7.0",
"symfony/mailgun-mailer": "^7.0"
"symfony/http-client": "^7.1",
"symfony/mailgun-mailer": "^7.1"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.9",

412
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a3648ab093343dd83bf7e728034ab46c",
"content-hash": "49c6cd315f6cba880b9fa088236e8088",
"packages": [
{
"name": "bacon/bacon-qr-code",
@@ -1045,6 +1045,130 @@
},
"time": "2021-12-05T19:44:35+00:00"
},
{
"name": "genealabs/laravel-model-caching",
"version": "11.0.1",
"source": {
"type": "git",
"url": "https://github.com/mikebronner/laravel-model-caching.git",
"reference": "2a38f0f1ed3554cf2da272d66c4d08a7885f196b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mikebronner/laravel-model-caching/zipball/2a38f0f1ed3554cf2da272d66c4d08a7885f196b",
"reference": "2a38f0f1ed3554cf2da272d66c4d08a7885f196b",
"shasum": ""
},
"require": {
"genealabs/laravel-pivot-events": "^10.0|^11.0",
"illuminate/cache": "^10.0|^11.0",
"illuminate/config": "^10.0|^11.0",
"illuminate/console": "^10.0|^11.0",
"illuminate/container": "^10.0|^11.0",
"illuminate/database": "^10.0|^11.0",
"illuminate/http": "^10.0|^11.0",
"illuminate/support": "^10.0|^11.0",
"php": ">=8.1"
},
"require-dev": {
"doctrine/dbal": "^3.3",
"fakerphp/faker": "^1.11",
"laravel/legacy-factories": "^1.3",
"laravel/nova": "^4.0",
"orchestra/testbench": "^8.0|^9.0",
"orchestra/testbench-browser-kit": "^8.0",
"php-coveralls/php-coveralls": "^2.2",
"phpunit/phpunit": "^10.0",
"slevomat/coding-standard": "^7.0|^8.14",
"squizlabs/php_codesniffer": "^3.6",
"symfony/thanks": "^1.2"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"GeneaLabs\\LaravelModelCaching\\Providers\\Service"
]
}
},
"autoload": {
"psr-4": {
"GeneaLabs\\LaravelModelCaching\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mike Bronner",
"email": "hello@genealabs.com"
}
],
"description": "Automatic caching for Eloquent models.",
"support": {
"issues": "https://github.com/mikebronner/laravel-model-caching/issues",
"source": "https://github.com/mikebronner/laravel-model-caching/tree/11.0.1"
},
"time": "2024-03-14T23:34:57+00:00"
},
{
"name": "genealabs/laravel-pivot-events",
"version": "11.0.0",
"source": {
"type": "git",
"url": "https://github.com/mikebronner/laravel-pivot-events.git",
"reference": "16e974d80160774641f4323f5ffb757b79f300d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mikebronner/laravel-pivot-events/zipball/16e974d80160774641f4323f5ffb757b79f300d3",
"reference": "16e974d80160774641f4323f5ffb757b79f300d3",
"shasum": ""
},
"require": {
"illuminate/database": "^8.0|^9.0|^10.0|^11.0",
"illuminate/support": "^8.0|^9.0|^10.0|^11.0"
},
"require-dev": {
"orchestra/testbench": "^9.0",
"phpunit/phpunit": "^10.5",
"symfony/thanks": "^1.0"
},
"type": "library",
"autoload": {
"psr-4": {
"GeneaLabs\\LaravelPivotEvents\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mike Bronner",
"email": "hello@genealabs.com",
"homepage": "https://genealabs.com",
"role": "Developer"
}
],
"description": "This package introduces new eloquent events for sync(), attach(), detach() or updateExistingPivot() methods on BelongsToMany relation.",
"homepage": "https://github.com/mikebronner/laravel-pivot-events",
"keywords": [
"eloquent events",
"eloquent extra events",
"laravel BelongsToMany events",
"laravel pivot events",
"laravel sync events"
],
"support": {
"issues": "https://github.com/mikebronner/laravel-pivot-events/issues",
"source": "https://github.com/mikebronner/laravel-pivot-events"
},
"time": "2024-03-14T23:24:54+00:00"
},
{
"name": "graham-campbell/result-type",
"version": "v1.1.3",
@@ -1109,16 +1233,16 @@
},
{
"name": "guzzlehttp/guzzle",
"version": "7.9.1",
"version": "7.9.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "a629e5b69db96eb4939c1b34114130077dd4c6fc"
"reference": "d281ed313b989f213357e3be1a179f02196ac99b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/a629e5b69db96eb4939c1b34114130077dd4c6fc",
"reference": "a629e5b69db96eb4939c1b34114130077dd4c6fc",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b",
"reference": "d281ed313b989f213357e3be1a179f02196ac99b",
"shasum": ""
},
"require": {
@@ -1215,7 +1339,7 @@
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/7.9.1"
"source": "https://github.com/guzzle/guzzle/tree/7.9.2"
},
"funding": [
{
@@ -1231,7 +1355,7 @@
"type": "tidelift"
}
],
"time": "2024-07-19T16:19:57+00:00"
"time": "2024-07-24T11:22:20+00:00"
},
{
"name": "guzzlehttp/promises",
@@ -2286,16 +2410,16 @@
},
{
"name": "laravel/framework",
"version": "v11.16.0",
"version": "v11.18.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "bd4808aaf103ccb5cb4b00bcee46140c070c0ec4"
"reference": "b19ba518c56852567e99fbae9321bc436c2cc5a8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/bd4808aaf103ccb5cb4b00bcee46140c070c0ec4",
"reference": "bd4808aaf103ccb5cb4b00bcee46140c070c0ec4",
"url": "https://api.github.com/repos/laravel/framework/zipball/b19ba518c56852567e99fbae9321bc436c2cc5a8",
"reference": "b19ba518c56852567e99fbae9321bc436c2cc5a8",
"shasum": ""
},
"require": {
@@ -2488,20 +2612,20 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2024-07-16T14:33:07+00:00"
"time": "2024-07-26T10:39:29+00:00"
},
{
"name": "laravel/passport",
"version": "v12.2.0",
"version": "v12.2.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/passport.git",
"reference": "b24c6462835a16163141fbe588533d16603212b7"
"reference": "795bbb406c8f10167df6062032de803bd7d686f2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/passport/zipball/b24c6462835a16163141fbe588533d16603212b7",
"reference": "b24c6462835a16163141fbe588533d16603212b7",
"url": "https://api.github.com/repos/laravel/passport/zipball/795bbb406c8f10167df6062032de803bd7d686f2",
"reference": "795bbb406c8f10167df6062032de803bd7d686f2",
"shasum": ""
},
"require": {
@@ -2564,7 +2688,7 @@
"issues": "https://github.com/laravel/passport/issues",
"source": "https://github.com/laravel/passport"
},
"time": "2024-04-17T17:56:14+00:00"
"time": "2024-07-10T19:25:36+00:00"
},
{
"name": "laravel/prompts",
@@ -2750,16 +2874,16 @@
},
{
"name": "laravel/slack-notification-channel",
"version": "v3.2.0",
"version": "v3.3.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/slack-notification-channel.git",
"reference": "fc8d1873e3db63a480bc57aebb4bf5ec05332d91"
"reference": "8cd988fad1a08ed88dfd852f140477376c60217f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/slack-notification-channel/zipball/fc8d1873e3db63a480bc57aebb4bf5ec05332d91",
"reference": "fc8d1873e3db63a480bc57aebb4bf5ec05332d91",
"url": "https://api.github.com/repos/laravel/slack-notification-channel/zipball/8cd988fad1a08ed88dfd852f140477376c60217f",
"reference": "8cd988fad1a08ed88dfd852f140477376c60217f",
"shasum": ""
},
"require": {
@@ -2809,9 +2933,9 @@
],
"support": {
"issues": "https://github.com/laravel/slack-notification-channel/issues",
"source": "https://github.com/laravel/slack-notification-channel/tree/v3.2.0"
"source": "https://github.com/laravel/slack-notification-channel/tree/v3.3.0"
},
"time": "2024-01-15T20:07:45+00:00"
"time": "2024-07-10T19:39:44+00:00"
},
{
"name": "laravel/ui",
@@ -3015,16 +3139,16 @@
},
{
"name": "league/commonmark",
"version": "2.4.2",
"version": "2.5.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
"reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf"
"reference": "ac815920de0eff6de947eac0a6a94e5ed0fb147c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/91c24291965bd6d7c46c46a12ba7492f83b1cadf",
"reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/ac815920de0eff6de947eac0a6a94e5ed0fb147c",
"reference": "ac815920de0eff6de947eac0a6a94e5ed0fb147c",
"shasum": ""
},
"require": {
@@ -3037,8 +3161,8 @@
},
"require-dev": {
"cebe/markdown": "^1.0",
"commonmark/cmark": "0.30.3",
"commonmark/commonmark.js": "0.30.0",
"commonmark/cmark": "0.31.0",
"commonmark/commonmark.js": "0.31.0",
"composer/package-versions-deprecated": "^1.8",
"embed/embed": "^4.4",
"erusev/parsedown": "^1.0",
@@ -3060,7 +3184,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "2.5-dev"
"dev-main": "2.6-dev"
}
},
"autoload": {
@@ -3117,7 +3241,7 @@
"type": "tidelift"
}
],
"time": "2024-02-02T11:59:32+00:00"
"time": "2024-07-24T12:52:09+00:00"
},
{
"name": "league/config",
@@ -5785,16 +5909,16 @@
},
{
"name": "spatie/backtrace",
"version": "1.6.1",
"version": "1.6.2",
"source": {
"type": "git",
"url": "https://github.com/spatie/backtrace.git",
"reference": "8373b9d51638292e3bfd736a9c19a654111b4a23"
"reference": "1a9a145b044677ae3424693f7b06479fc8c137a9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/backtrace/zipball/8373b9d51638292e3bfd736a9c19a654111b4a23",
"reference": "8373b9d51638292e3bfd736a9c19a654111b4a23",
"url": "https://api.github.com/repos/spatie/backtrace/zipball/1a9a145b044677ae3424693f7b06479fc8c137a9",
"reference": "1a9a145b044677ae3424693f7b06479fc8c137a9",
"shasum": ""
},
"require": {
@@ -5832,7 +5956,7 @@
"spatie"
],
"support": {
"source": "https://github.com/spatie/backtrace/tree/1.6.1"
"source": "https://github.com/spatie/backtrace/tree/1.6.2"
},
"funding": [
{
@@ -5844,20 +5968,20 @@
"type": "other"
}
],
"time": "2024-04-24T13:22:11+00:00"
"time": "2024-07-22T08:21:24+00:00"
},
{
"name": "spatie/error-solutions",
"version": "1.0.5",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/spatie/error-solutions.git",
"reference": "4bb6c734dc992b2db3e26df1ef021c75d2218b13"
"reference": "ae7393122eda72eed7cc4f176d1e96ea444f2d67"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/error-solutions/zipball/4bb6c734dc992b2db3e26df1ef021c75d2218b13",
"reference": "4bb6c734dc992b2db3e26df1ef021c75d2218b13",
"url": "https://api.github.com/repos/spatie/error-solutions/zipball/ae7393122eda72eed7cc4f176d1e96ea444f2d67",
"reference": "ae7393122eda72eed7cc4f176d1e96ea444f2d67",
"shasum": ""
},
"require": {
@@ -5910,7 +6034,7 @@
],
"support": {
"issues": "https://github.com/spatie/error-solutions/issues",
"source": "https://github.com/spatie/error-solutions/tree/1.0.5"
"source": "https://github.com/spatie/error-solutions/tree/1.1.1"
},
"funding": [
{
@@ -5918,7 +6042,7 @@
"type": "github"
}
],
"time": "2024-07-09T12:13:32+00:00"
"time": "2024-07-25T11:06:04+00:00"
},
{
"name": "spatie/flare-client-php",
@@ -6297,16 +6421,16 @@
},
{
"name": "symfony/cache",
"version": "v7.1.2",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache.git",
"reference": "e933e1d947ffb88efcdd34a2bd51561cab7deaae"
"reference": "8ac37acee794372f9732fe8a61a8221f6762148e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/cache/zipball/e933e1d947ffb88efcdd34a2bd51561cab7deaae",
"reference": "e933e1d947ffb88efcdd34a2bd51561cab7deaae",
"url": "https://api.github.com/repos/symfony/cache/zipball/8ac37acee794372f9732fe8a61a8221f6762148e",
"reference": "8ac37acee794372f9732fe8a61a8221f6762148e",
"shasum": ""
},
"require": {
@@ -6374,7 +6498,7 @@
"psr6"
],
"support": {
"source": "https://github.com/symfony/cache/tree/v7.1.2"
"source": "https://github.com/symfony/cache/tree/v7.1.3"
},
"funding": [
{
@@ -6390,7 +6514,7 @@
"type": "tidelift"
}
],
"time": "2024-06-11T13:32:38+00:00"
"time": "2024-07-17T06:10:24+00:00"
},
{
"name": "symfony/cache-contracts",
@@ -6544,16 +6668,16 @@
},
{
"name": "symfony/console",
"version": "v7.1.2",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "0aa29ca177f432ab68533432db0de059f39c92ae"
"reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/0aa29ca177f432ab68533432db0de059f39c92ae",
"reference": "0aa29ca177f432ab68533432db0de059f39c92ae",
"url": "https://api.github.com/repos/symfony/console/zipball/cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9",
"reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9",
"shasum": ""
},
"require": {
@@ -6617,7 +6741,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.1.2"
"source": "https://github.com/symfony/console/tree/v7.1.3"
},
"funding": [
{
@@ -6633,7 +6757,7 @@
"type": "tidelift"
}
],
"time": "2024-06-28T10:03:55+00:00"
"time": "2024-07-26T12:41:01+00:00"
},
{
"name": "symfony/css-selector",
@@ -6769,16 +6893,16 @@
},
{
"name": "symfony/error-handler",
"version": "v7.1.2",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/error-handler.git",
"reference": "2412d3dddb5c9ea51a39cfbff1c565fc9844ca32"
"reference": "432bb369952795c61ca1def65e078c4a80dad13c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/error-handler/zipball/2412d3dddb5c9ea51a39cfbff1c565fc9844ca32",
"reference": "2412d3dddb5c9ea51a39cfbff1c565fc9844ca32",
"url": "https://api.github.com/repos/symfony/error-handler/zipball/432bb369952795c61ca1def65e078c4a80dad13c",
"reference": "432bb369952795c61ca1def65e078c4a80dad13c",
"shasum": ""
},
"require": {
@@ -6824,7 +6948,7 @@
"description": "Provides tools to manage errors and ease debugging PHP code",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/error-handler/tree/v7.1.2"
"source": "https://github.com/symfony/error-handler/tree/v7.1.3"
},
"funding": [
{
@@ -6840,7 +6964,7 @@
"type": "tidelift"
}
],
"time": "2024-06-25T19:55:06+00:00"
"time": "2024-07-26T13:02:51+00:00"
},
{
"name": "symfony/event-dispatcher",
@@ -7064,16 +7188,16 @@
},
{
"name": "symfony/finder",
"version": "v7.1.1",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "fbb0ba67688b780efbc886c1a0a0948dcf7205d6"
"reference": "717c6329886f32dc65e27461f80f2a465412fdca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/fbb0ba67688b780efbc886c1a0a0948dcf7205d6",
"reference": "fbb0ba67688b780efbc886c1a0a0948dcf7205d6",
"url": "https://api.github.com/repos/symfony/finder/zipball/717c6329886f32dc65e27461f80f2a465412fdca",
"reference": "717c6329886f32dc65e27461f80f2a465412fdca",
"shasum": ""
},
"require": {
@@ -7108,7 +7232,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.1"
"source": "https://github.com/symfony/finder/tree/v7.1.3"
},
"funding": [
{
@@ -7124,20 +7248,20 @@
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
"time": "2024-07-24T07:08:44+00:00"
},
{
"name": "symfony/http-client",
"version": "v7.1.2",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
"reference": "90ace27d17ccc9afc6f7ec0081e8529fb0e29425"
"reference": "b79858aa7a051ea791b0d50269a234a0b50cb231"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client/zipball/90ace27d17ccc9afc6f7ec0081e8529fb0e29425",
"reference": "90ace27d17ccc9afc6f7ec0081e8529fb0e29425",
"url": "https://api.github.com/repos/symfony/http-client/zipball/b79858aa7a051ea791b0d50269a234a0b50cb231",
"reference": "b79858aa7a051ea791b0d50269a234a0b50cb231",
"shasum": ""
},
"require": {
@@ -7202,7 +7326,7 @@
"http"
],
"support": {
"source": "https://github.com/symfony/http-client/tree/v7.1.2"
"source": "https://github.com/symfony/http-client/tree/v7.1.3"
},
"funding": [
{
@@ -7218,7 +7342,7 @@
"type": "tidelift"
}
],
"time": "2024-06-28T08:00:31+00:00"
"time": "2024-07-17T06:10:24+00:00"
},
{
"name": "symfony/http-client-contracts",
@@ -7300,16 +7424,16 @@
},
{
"name": "symfony/http-foundation",
"version": "v7.1.1",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "74d171d5b6a1d9e4bfee09a41937c17a7536acfa"
"reference": "f602d5c17d1fa02f8019ace2687d9d136b7f4a1a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/74d171d5b6a1d9e4bfee09a41937c17a7536acfa",
"reference": "74d171d5b6a1d9e4bfee09a41937c17a7536acfa",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/f602d5c17d1fa02f8019ace2687d9d136b7f4a1a",
"reference": "f602d5c17d1fa02f8019ace2687d9d136b7f4a1a",
"shasum": ""
},
"require": {
@@ -7357,7 +7481,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v7.1.1"
"source": "https://github.com/symfony/http-foundation/tree/v7.1.3"
},
"funding": [
{
@@ -7373,20 +7497,20 @@
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
"time": "2024-07-26T12:41:01+00:00"
},
{
"name": "symfony/http-kernel",
"version": "v7.1.2",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "ae3fa717db4d41a55d14c2bd92399e37cf5bc0f6"
"reference": "db9702f3a04cc471ec8c70e881825db26ac5f186"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/ae3fa717db4d41a55d14c2bd92399e37cf5bc0f6",
"reference": "ae3fa717db4d41a55d14c2bd92399e37cf5bc0f6",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/db9702f3a04cc471ec8c70e881825db26ac5f186",
"reference": "db9702f3a04cc471ec8c70e881825db26ac5f186",
"shasum": ""
},
"require": {
@@ -7471,7 +7595,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-kernel/tree/v7.1.2"
"source": "https://github.com/symfony/http-kernel/tree/v7.1.3"
},
"funding": [
{
@@ -7487,7 +7611,7 @@
"type": "tidelift"
}
],
"time": "2024-06-28T13:13:31+00:00"
"time": "2024-07-26T14:58:15+00:00"
},
{
"name": "symfony/mailer",
@@ -7571,16 +7695,16 @@
},
{
"name": "symfony/mailgun-mailer",
"version": "v7.1.2",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/mailgun-mailer.git",
"reference": "d5dcf1cbbc29cf43c979b9ed8a7ff509c903c64f"
"reference": "dac02fe68e9306849703025511c56f10701696a8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mailgun-mailer/zipball/d5dcf1cbbc29cf43c979b9ed8a7ff509c903c64f",
"reference": "d5dcf1cbbc29cf43c979b9ed8a7ff509c903c64f",
"url": "https://api.github.com/repos/symfony/mailgun-mailer/zipball/dac02fe68e9306849703025511c56f10701696a8",
"reference": "dac02fe68e9306849703025511c56f10701696a8",
"shasum": ""
},
"require": {
@@ -7620,7 +7744,7 @@
"description": "Symfony Mailgun Mailer Bridge",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/mailgun-mailer/tree/v7.1.2"
"source": "https://github.com/symfony/mailgun-mailer/tree/v7.1.3"
},
"funding": [
{
@@ -7636,7 +7760,7 @@
"type": "tidelift"
}
],
"time": "2024-06-28T08:00:31+00:00"
"time": "2024-07-04T11:20:59+00:00"
},
{
"name": "symfony/mime",
@@ -8434,16 +8558,16 @@
},
{
"name": "symfony/process",
"version": "v7.1.1",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "febf90124323a093c7ee06fdb30e765ca3c20028"
"reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/febf90124323a093c7ee06fdb30e765ca3c20028",
"reference": "febf90124323a093c7ee06fdb30e765ca3c20028",
"url": "https://api.github.com/repos/symfony/process/zipball/7f2f542c668ad6c313dc4a5e9c3321f733197eca",
"reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca",
"shasum": ""
},
"require": {
@@ -8475,7 +8599,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v7.1.1"
"source": "https://github.com/symfony/process/tree/v7.1.3"
},
"funding": [
{
@@ -8491,20 +8615,20 @@
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
"time": "2024-07-26T12:44:47+00:00"
},
{
"name": "symfony/psr-http-message-bridge",
"version": "v7.1.1",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/psr-http-message-bridge.git",
"reference": "9a5dbb606da711f5d40a7596ad577856f9402140"
"reference": "1365d10f5476f74a27cf9c2d1eee70c069019db0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/9a5dbb606da711f5d40a7596ad577856f9402140",
"reference": "9a5dbb606da711f5d40a7596ad577856f9402140",
"url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/1365d10f5476f74a27cf9c2d1eee70c069019db0",
"reference": "1365d10f5476f74a27cf9c2d1eee70c069019db0",
"shasum": ""
},
"require": {
@@ -8558,7 +8682,7 @@
"psr-7"
],
"support": {
"source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.1.1"
"source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.1.3"
},
"funding": [
{
@@ -8574,20 +8698,20 @@
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
"time": "2024-07-17T06:10:24+00:00"
},
{
"name": "symfony/routing",
"version": "v7.1.1",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
"reference": "60c31bab5c45af7f13091b87deb708830f3c96c0"
"reference": "8a908a3f22d5a1b5d297578c2ceb41b02fa916d0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/routing/zipball/60c31bab5c45af7f13091b87deb708830f3c96c0",
"reference": "60c31bab5c45af7f13091b87deb708830f3c96c0",
"url": "https://api.github.com/repos/symfony/routing/zipball/8a908a3f22d5a1b5d297578c2ceb41b02fa916d0",
"reference": "8a908a3f22d5a1b5d297578c2ceb41b02fa916d0",
"shasum": ""
},
"require": {
@@ -8639,7 +8763,7 @@
"url"
],
"support": {
"source": "https://github.com/symfony/routing/tree/v7.1.1"
"source": "https://github.com/symfony/routing/tree/v7.1.3"
},
"funding": [
{
@@ -8655,7 +8779,7 @@
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
"time": "2024-07-17T06:10:24+00:00"
},
{
"name": "symfony/service-contracts",
@@ -8742,16 +8866,16 @@
},
{
"name": "symfony/string",
"version": "v7.1.2",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "14221089ac66cf82e3cf3d1c1da65de305587ff8"
"reference": "ea272a882be7f20cad58d5d78c215001617b7f07"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/14221089ac66cf82e3cf3d1c1da65de305587ff8",
"reference": "14221089ac66cf82e3cf3d1c1da65de305587ff8",
"url": "https://api.github.com/repos/symfony/string/zipball/ea272a882be7f20cad58d5d78c215001617b7f07",
"reference": "ea272a882be7f20cad58d5d78c215001617b7f07",
"shasum": ""
},
"require": {
@@ -8809,7 +8933,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v7.1.2"
"source": "https://github.com/symfony/string/tree/v7.1.3"
},
"funding": [
{
@@ -8825,20 +8949,20 @@
"type": "tidelift"
}
],
"time": "2024-06-28T09:27:18+00:00"
"time": "2024-07-22T10:25:37+00:00"
},
{
"name": "symfony/translation",
"version": "v7.1.1",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
"reference": "cf5ae136e124fc7681b34ce9fac9d5b9ae8ceee3"
"reference": "8d5e50c813ba2859a6dfc99a0765c550507934a1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/cf5ae136e124fc7681b34ce9fac9d5b9ae8ceee3",
"reference": "cf5ae136e124fc7681b34ce9fac9d5b9ae8ceee3",
"url": "https://api.github.com/repos/symfony/translation/zipball/8d5e50c813ba2859a6dfc99a0765c550507934a1",
"reference": "8d5e50c813ba2859a6dfc99a0765c550507934a1",
"shasum": ""
},
"require": {
@@ -8903,7 +9027,7 @@
"description": "Provides tools to internationalize your application",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/translation/tree/v7.1.1"
"source": "https://github.com/symfony/translation/tree/v7.1.3"
},
"funding": [
{
@@ -8919,7 +9043,7 @@
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
"time": "2024-07-26T12:41:01+00:00"
},
{
"name": "symfony/translation-contracts",
@@ -9075,16 +9199,16 @@
},
{
"name": "symfony/var-dumper",
"version": "v7.1.2",
"version": "v7.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "5857c57c6b4b86524c08cf4f4bc95327270a816d"
"reference": "86af4617cca75a6e28598f49ae0690f3b9d4591f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/5857c57c6b4b86524c08cf4f4bc95327270a816d",
"reference": "5857c57c6b4b86524c08cf4f4bc95327270a816d",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/86af4617cca75a6e28598f49ae0690f3b9d4591f",
"reference": "86af4617cca75a6e28598f49ae0690f3b9d4591f",
"shasum": ""
},
"require": {
@@ -9138,7 +9262,7 @@
"dump"
],
"support": {
"source": "https://github.com/symfony/var-dumper/tree/v7.1.2"
"source": "https://github.com/symfony/var-dumper/tree/v7.1.3"
},
"funding": [
{
@@ -9154,7 +9278,7 @@
"type": "tidelift"
}
],
"time": "2024-06-28T08:00:31+00:00"
"time": "2024-07-26T12:41:01+00:00"
},
{
"name": "symfony/var-exporter",
@@ -9945,30 +10069,38 @@
},
{
"name": "composer/pcre",
"version": "3.1.4",
"version": "3.2.0",
"source": {
"type": "git",
"url": "https://github.com/composer/pcre.git",
"reference": "04229f163664973f68f38f6f73d917799168ef24"
"reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/pcre/zipball/04229f163664973f68f38f6f73d917799168ef24",
"reference": "04229f163664973f68f38f6f73d917799168ef24",
"url": "https://api.github.com/repos/composer/pcre/zipball/ea4ab6f9580a4fd221e0418f2c357cdd39102a90",
"reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0"
},
"conflict": {
"phpstan/phpstan": "<1.11.8"
},
"require-dev": {
"phpstan/phpstan": "^1.3",
"phpstan/phpstan": "^1.11.8",
"phpstan/phpstan-strict-rules": "^1.1",
"symfony/phpunit-bridge": "^5"
"phpunit/phpunit": "^8 || ^9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.x-dev"
},
"phpstan": {
"includes": [
"extension.neon"
]
}
},
"autoload": {
@@ -9996,7 +10128,7 @@
],
"support": {
"issues": "https://github.com/composer/pcre/issues",
"source": "https://github.com/composer/pcre/tree/3.1.4"
"source": "https://github.com/composer/pcre/tree/3.2.0"
},
"funding": [
{
@@ -10012,7 +10144,7 @@
"type": "tidelift"
}
],
"time": "2024-05-27T13:40:54+00:00"
"time": "2024-07-25T09:36:02+00:00"
},
{
"name": "doctrine/deprecations",
@@ -11093,16 +11225,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.11.7",
"version": "1.11.8",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "52d2bbfdcae7f895915629e4694e9497d0f8e28d"
"reference": "6adbd118e6c0515dd2f36b06cde1d6da40f1b8ec"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/52d2bbfdcae7f895915629e4694e9497d0f8e28d",
"reference": "52d2bbfdcae7f895915629e4694e9497d0f8e28d",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/6adbd118e6c0515dd2f36b06cde1d6da40f1b8ec",
"reference": "6adbd118e6c0515dd2f36b06cde1d6da40f1b8ec",
"shasum": ""
},
"require": {
@@ -11147,7 +11279,7 @@
"type": "github"
}
],
"time": "2024-07-06T11:17:41+00:00"
"time": "2024-07-24T07:01:22+00:00"
},
{
"name": "phpstan/phpstan-deprecation-rules",

View File

@@ -225,7 +225,7 @@ return [
'show_copy' => false, // Show copy button next to the query,
'slow_threshold' => false, // Only track queries that last longer than this time in ms
'memory_usage' => false, // Show queries memory usage
'soft_limit' => 100, // After the soft limit, no parameters/backtrace are captured
'soft_limit' => 250, // After the soft limit, no parameters/backtrace are captured
'hard_limit' => 500, // After the hard limit, queries are ignored
],
'mail' => [

View File

@@ -114,10 +114,10 @@ return [
'telemetry' => false,
'webhooks' => true,
'handle_debts' => true,
'expression_engine' => false,
'expression_engine' => true,
// see cer.php for exchange rates feature flag.
],
'version' => 'develop/2024-07-22',
'version' => 'develop/2024-07-29',
'api_version' => '2.1.0',
'db_version' => 24,
@@ -503,13 +503,13 @@ return [
'remove_tag' => RemoveTag::class,
'remove_all_tags' => RemoveAllTags::class,
'set_description' => SetDescription::class,
'append_description' => AppendDescription::class,
'prepend_description' => PrependDescription::class,
// 'append_description' => AppendDescription::class,
// 'prepend_description' => PrependDescription::class,
'set_source_account' => SetSourceAccount::class,
'set_destination_account' => SetDestinationAccount::class,
'set_notes' => SetNotes::class,
'append_notes' => AppendNotes::class,
'prepend_notes' => PrependNotes::class,
// 'append_notes' => AppendNotes::class,
// 'prepend_notes' => PrependNotes::class,
'clear_notes' => ClearNotes::class,
'link_to_bill' => LinkToBill::class,
'convert_withdrawal' => ConvertToWithdrawal::class,
@@ -518,10 +518,10 @@ return [
'switch_accounts' => SwitchAccounts::class,
'update_piggy' => UpdatePiggybank::class,
'delete_transaction' => DeleteTransaction::class,
'append_descr_to_notes' => AppendDescriptionToNotes::class,
'append_notes_to_descr' => AppendNotesToDescription::class,
'move_descr_to_notes' => MoveDescriptionToNotes::class,
'move_notes_to_descr' => MoveNotesToDescription::class,
// 'append_descr_to_notes' => AppendDescriptionToNotes::class,
// 'append_notes_to_descr' => AppendNotesToDescription::class,
// 'move_descr_to_notes' => MoveDescriptionToNotes::class,
// 'move_notes_to_descr' => MoveNotesToDescription::class,
'set_source_to_cash' => SetSourceToCashAccount::class,
'set_destination_to_cash' => SetDestinationToCashAccount::class,
'set_amount' => SetAmount::class,

View File

@@ -2,7 +2,7 @@
declare(strict_types=1);
use FireflyIII\JsonApi\V3\Server;
use FireflyIII\JsonApi\V2\Server;
return [
/*
@@ -30,6 +30,6 @@ return [
| class name of the server class.
*/
'servers' => [
'v3' => Server::class,
'v2' => Server::class,
],
];

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
return [
'cache-prefix' => '',
'enabled' => env('MODEL_CACHE_ENABLED', true),
'use-database-keying' => env('MODEL_CACHE_USE_DATABASE_KEYING', true),
'store' => env('MODEL_CACHE_STORE'),
];

590
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,7 @@
},
"devDependencies": {
"@johmun/vue-tags-input": "^2",
"@vue/compiler-sfc": "^3.3.4",
"@vue/compiler-sfc": "^3.4.34",
"axios": "^1.3",
"bootstrap-sass": "^3",
"cross-env": "^7.0",

View File

@@ -268,6 +268,7 @@ return [
'auto_budget_period_mandatory' => 'The auto budget period is a mandatory field.',
// no access to administration:
'no_auth_user_group' => 'You have to be logged in to access this administration.',
'no_access_user_group' => 'You do not have the correct access rights for this administration.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];

View File

@@ -98,6 +98,7 @@
<!-- {{ trans('firefly.bill_expected_date', {date: entry.next_expected_match_diff }) }} -->
</td>
<td class="expected_in_period hidden-sm hidden-xs">
{% for date in entry.pay_dates %}
{{ formatDate(date, monthAndDayFormat) }}<br>
{% endfor %}
@@ -111,6 +112,7 @@
#}
{% if entry.paid_dates|length > 0 and entry.active %}
<td class="paid_in_period text-success">
{% for currentPaid in entry.paid_dates %}
<a href="{{ route('transactions.show',currentPaid.transaction_group_id) }}">
{{ formatDate(currentPaid.date, monthAndDayFormat) }}
@@ -120,7 +122,9 @@
</td>
<td class="expected_in_period hidden-sm hidden-xs">
{% if entry.next_expected_match %}
{{ formatDate(entry.next_expected_match, monthAndDayFormat) }}
{{ formatDate(entry.next_expected_match, monthAndDayFormat) }}
{% else %}
<span class="text-muted">{{ entry.next_expected_match_diff }}</span>
{% endif %}
</td>
{% endif %}

View File

@@ -22,14 +22,23 @@
declare(strict_types=1);
use FireflyIII\Http\Controllers\Api\V3\Controllers\AccountController;
use FireflyIII\Api\V2\Controllers\JsonApi\AccountController;
use LaravelJsonApi\Laravel\Facades\JsonApiRoute;
use LaravelJsonApi\Laravel\Http\Controllers\JsonApiController;
use LaravelJsonApi\Laravel\Routing\ActionRegistrar;
use LaravelJsonApi\Laravel\Routing\Relationships;
use LaravelJsonApi\Laravel\Routing\ResourceRegistrar;
// V2 auto complete controller(s)
/*
*
* ____ ____ ___ .______ ______ __ __ .___________. _______ _______.
* \ \ / / |__ \ | _ \ / __ \ | | | | | || ____| / |
* \ \/ / ) | | |_) | | | | | | | | | `---| |----`| |__ | (----`
* \ / / / | / | | | | | | | | | | | __| \ \
* \ / / /_ | |\ \----.| `--' | | `--' | | | | |____.----) |
* \__/ |____| | _| `._____| \______/ \______/ |__| |_______|_______/
*/
// AUTOCOMPLETE ROUTES
Route::group(
[
'namespace' => 'FireflyIII\Api\V2\Controllers\Autocomplete',
@@ -37,7 +46,6 @@ Route::group(
'as' => 'api.v2.autocomplete.',
],
static function (): void {
// Auto complete routes
Route::get('accounts', ['uses' => 'AccountController@accounts', 'as' => 'accounts']);
Route::get('categories', ['uses' => 'CategoryController@categories', 'as' => 'categories']);
Route::get('tags', ['uses' => 'TagController@tags', 'as' => 'tags']);
@@ -45,7 +53,7 @@ Route::group(
}
);
// V2 API routes for charts
// CHART ROUTES
Route::group(
[
'namespace' => 'FireflyIII\Api\V2\Controllers\Chart',
@@ -54,28 +62,12 @@ Route::group(
],
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('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']);
}
);
// JsonApiRoute::server('v3')
// ->prefix('v3')
// ->resources(function (ResourceRegistrar $server) {
// $server->resource('accounts', AccountController::class)->readOnly()->relationships(function (Relationships $relations) {
// $relations->hasOne('user')->readOnly();
// //$relations->hasMany('account_balances')->readOnly();
// })
// ->actions(function (ActionRegistrar $actions) {
// $actions->withId()->get('account-balances', 'readAccountBalances'); // non-eloquent pseudo relation
// });
// $server->resource('users', JsonApiController::class)->readOnly()->relationships(function (Relationships $relations) {
// $relations->hasMany('accounts')->readOnly();
// });
// $server->resource('account-balances', JsonApiController::class);
// });
// V2 API route for Summary boxes
// BASIC
Route::group(
@@ -121,19 +113,19 @@ Route::group(
}
);
// V2 API route for accounts.
Route::group(
[
'namespace' => 'FireflyIII\Api\V2\Controllers\Model\Account',
'prefix' => 'v2/accounts',
'as' => 'api.v2.accounts.',
],
static function (): void {
Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
Route::get('{account}', ['uses' => 'ShowController@show', 'as' => 'show']);
Route::put('{account}', ['uses' => 'UpdateController@update', 'as' => 'update']);
}
);
// // V2 API route for accounts.
// Route::group(
// [
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Model\Account',
// 'prefix' => 'v2/accounts',
// 'as' => 'api.v2.accounts.',
// ],
// static function (): void {
// Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
// Route::get('{account}', ['uses' => 'ShowController@show', 'as' => 'show']);
// Route::put('{account}', ['uses' => 'UpdateController@update', 'as' => 'update']);
// }
// );
// V2 API route for subscriptions.
Route::group(
@@ -247,7 +239,30 @@ Route::group(
}
);
// down here is v1
// V2 JSON API ROUTES
JsonApiRoute::server('v2')->prefix('v2')
->resources(function (ResourceRegistrar $server): void {
// ACCOUNTS
$server->resource('accounts', AccountController::class)->readOnly()->relationships(function (Relationships $relations): void {
$relations->hasOne('user')->readOnly();
});
// $server->resource('accounts', AccountController::class)->readOnly();
// USERS
$server->resource('users', JsonApiController::class)->readOnly()->relationships(function (Relationships $relations): void {
$relations->hasMany('accounts')->readOnly();
});
})
;
/*
* ____ ____ __ .______ ______ __ __ .___________. _______ _______.
* \ \ / / /_ | | _ \ / __ \ | | | | | || ____| / |
* \ \/ / | | | |_) | | | | | | | | | `---| |----`| |__ | (----`
* \ / | | | / | | | | | | | | | | | __| \ \
* \ / | | | |\ \----.| `--' | | `--' | | | | |____.----) |
* \__/ |_| | _| `._____| \______/ \______/ |__| |_______|_______/
*/
// Autocomplete controllers
Route::group(
@@ -278,7 +293,7 @@ Route::group(
);
// CHART ROUTES.
// Accounts
// Chart accounts
Route::group(
[
'namespace' => 'FireflyIII\Api\V1\Controllers\Chart',