Merge branch 'release/v6.1.2'

This commit is contained in:
James Cole
2024-01-02 20:26:52 +01:00
971 changed files with 74278 additions and 72131 deletions

27
.ci/all.sh Executable file
View File

@@ -0,0 +1,27 @@
#!/usr/bin/env bash
#
# all.sh
# 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/>.
#
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
$SCRIPT_DIR/phpcs.sh
$SCRIPT_DIR/phpstan.sh
$SCRIPT_DIR/phpmd.sh

View File

@@ -57,7 +57,14 @@ return $config->setRules([
'statement_indentation' => true,
'type_declaration_spaces' => false,
'cast_spaces' => false,
'binary_operator_spaces' => false,
'binary_operator_spaces' => [
'default' => 'at_least_single_space',
'operators' => [
'=>' => 'align_single_space_by_scope',
'=' => 'align_single_space_minimal_by_scope',
'??=' => 'align_single_space_minimal_by_scope',
],
],
'void_return' => true,
])
->setFinder($finder);

View File

@@ -226,16 +226,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.42.0",
"version": "v3.45.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "632ef1be3447a9b890bef06147475facee535d0f"
"reference": "c0daa33cb2533cd73f48dde1c70c2afa3e7953b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/632ef1be3447a9b890bef06147475facee535d0f",
"reference": "632ef1be3447a9b890bef06147475facee535d0f",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/c0daa33cb2533cd73f48dde1c70c2afa3e7953b5",
"reference": "c0daa33cb2533cd73f48dde1c70c2afa3e7953b5",
"shasum": ""
},
"require": {
@@ -265,7 +265,7 @@
"php-cs-fixer/accessible-object": "^1.1",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.4",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.4",
"phpunit/phpunit": "^9.6",
"phpunit/phpunit": "^9.6 || ^10.5.5",
"symfony/yaml": "^5.4 || ^6.0 || ^7.0"
},
"suggest": {
@@ -304,7 +304,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.42.0"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.45.0"
},
"funding": [
{
@@ -312,7 +312,7 @@
"type": "github"
}
],
"time": "2023-12-24T14:38:51+00:00"
"time": "2023-12-30T02:07:07+00:00"
},
{
"name": "psr/container",
@@ -536,16 +536,16 @@
},
{
"name": "symfony/console",
"version": "v7.0.1",
"version": "v7.0.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "cdce5c684b2f920bb1343deecdfba356ffad83d5"
"reference": "f8587c4cdc5acad67af71c37db34ef03af91e59c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/cdce5c684b2f920bb1343deecdfba356ffad83d5",
"reference": "cdce5c684b2f920bb1343deecdfba356ffad83d5",
"url": "https://api.github.com/repos/symfony/console/zipball/f8587c4cdc5acad67af71c37db34ef03af91e59c",
"reference": "f8587c4cdc5acad67af71c37db34ef03af91e59c",
"shasum": ""
},
"require": {
@@ -609,7 +609,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.0.1"
"source": "https://github.com/symfony/console/tree/v7.0.2"
},
"funding": [
{
@@ -625,7 +625,7 @@
"type": "tidelift"
}
],
"time": "2023-12-01T15:10:06+00:00"
"time": "2023-12-10T16:54:46+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -696,16 +696,16 @@
},
{
"name": "symfony/event-dispatcher",
"version": "v7.0.0",
"version": "v7.0.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "c459b40ffe67c49af6fd392aac374c9edf8a027e"
"reference": "098b62ae81fdd6cbf941f355059f617db28f4f9a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c459b40ffe67c49af6fd392aac374c9edf8a027e",
"reference": "c459b40ffe67c49af6fd392aac374c9edf8a027e",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/098b62ae81fdd6cbf941f355059f617db28f4f9a",
"reference": "098b62ae81fdd6cbf941f355059f617db28f4f9a",
"shasum": ""
},
"require": {
@@ -756,7 +756,7 @@
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/event-dispatcher/tree/v7.0.0"
"source": "https://github.com/symfony/event-dispatcher/tree/v7.0.2"
},
"funding": [
{
@@ -772,7 +772,7 @@
"type": "tidelift"
}
],
"time": "2023-07-27T16:29:09+00:00"
"time": "2023-12-27T22:24:19+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
@@ -1538,16 +1538,16 @@
},
{
"name": "symfony/process",
"version": "v7.0.0",
"version": "v7.0.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "13bdb1670c7f510494e04fcb2bfa29af63db9c0d"
"reference": "acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/13bdb1670c7f510494e04fcb2bfa29af63db9c0d",
"reference": "13bdb1670c7f510494e04fcb2bfa29af63db9c0d",
"url": "https://api.github.com/repos/symfony/process/zipball/acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a",
"reference": "acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a",
"shasum": ""
},
"require": {
@@ -1579,7 +1579,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v7.0.0"
"source": "https://github.com/symfony/process/tree/v7.0.2"
},
"funding": [
{
@@ -1595,25 +1595,25 @@
"type": "tidelift"
}
],
"time": "2023-11-20T16:43:42+00:00"
"time": "2023-12-24T09:15:37+00:00"
},
{
"name": "symfony/service-contracts",
"version": "v3.4.0",
"version": "v3.4.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838"
"reference": "fe07cbc8d837f60caf7018068e350cc5163681a0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838",
"reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0",
"reference": "fe07cbc8d837f60caf7018068e350cc5163681a0",
"shasum": ""
},
"require": {
"php": ">=8.1",
"psr/container": "^2.0"
"psr/container": "^1.1|^2.0"
},
"conflict": {
"ext-psr": "<1.1|>=2"
@@ -1661,7 +1661,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/service-contracts/tree/v3.4.0"
"source": "https://github.com/symfony/service-contracts/tree/v3.4.1"
},
"funding": [
{
@@ -1677,7 +1677,7 @@
"type": "tidelift"
}
],
"time": "2023-07-30T20:28:31+00:00"
"time": "2023-12-26T14:02:43+00:00"
},
{
"name": "symfony/stopwatch",
@@ -1743,16 +1743,16 @@
},
{
"name": "symfony/string",
"version": "v7.0.0",
"version": "v7.0.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620"
"reference": "cc78f14f91f5e53b42044d0620961c48028ff9f5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/92bd2bfbba476d4a1838e5e12168bef2fd1e6620",
"reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620",
"url": "https://api.github.com/repos/symfony/string/zipball/cc78f14f91f5e53b42044d0620961c48028ff9f5",
"reference": "cc78f14f91f5e53b42044d0620961c48028ff9f5",
"shasum": ""
},
"require": {
@@ -1809,7 +1809,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v7.0.0"
"source": "https://github.com/symfony/string/tree/v7.0.2"
},
"funding": [
{
@@ -1825,7 +1825,7 @@
"type": "tidelift"
}
],
"time": "2023-11-29T08:40:23+00:00"
"time": "2023-12-10T16:54:46+00:00"
}
],
"packages-dev": [],

View File

@@ -470,16 +470,16 @@
},
{
"name": "symfony/dependency-injection",
"version": "v7.0.1",
"version": "v7.0.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/dependency-injection.git",
"reference": "f6667642954bce638733f254c39e5b5700b47ba4"
"reference": "bd25ef7c937b9da12510bdc4f1c66728f19620e3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f6667642954bce638733f254c39e5b5700b47ba4",
"reference": "f6667642954bce638733f254c39e5b5700b47ba4",
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/bd25ef7c937b9da12510bdc4f1c66728f19620e3",
"reference": "bd25ef7c937b9da12510bdc4f1c66728f19620e3",
"shasum": ""
},
"require": {
@@ -530,7 +530,7 @@
"description": "Allows you to standardize and centralize the way objects are constructed in your application",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/dependency-injection/tree/v7.0.1"
"source": "https://github.com/symfony/dependency-injection/tree/v7.0.2"
},
"funding": [
{
@@ -546,7 +546,7 @@
"type": "tidelift"
}
],
"time": "2023-12-01T15:10:06+00:00"
"time": "2023-12-28T19:18:20+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -845,21 +845,21 @@
},
{
"name": "symfony/service-contracts",
"version": "v3.4.0",
"version": "v3.4.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838"
"reference": "fe07cbc8d837f60caf7018068e350cc5163681a0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838",
"reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0",
"reference": "fe07cbc8d837f60caf7018068e350cc5163681a0",
"shasum": ""
},
"require": {
"php": ">=8.1",
"psr/container": "^2.0"
"psr/container": "^1.1|^2.0"
},
"conflict": {
"ext-psr": "<1.1|>=2"
@@ -907,7 +907,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/service-contracts/tree/v3.4.0"
"source": "https://github.com/symfony/service-contracts/tree/v3.4.1"
},
"funding": [
{
@@ -923,20 +923,20 @@
"type": "tidelift"
}
],
"time": "2023-07-30T20:28:31+00:00"
"time": "2023-12-26T14:02:43+00:00"
},
{
"name": "symfony/var-exporter",
"version": "v7.0.1",
"version": "v7.0.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-exporter.git",
"reference": "a3d7c877414fcd59ab7075ecdc3b8f9c00f7bcc3"
"reference": "345c62fefe92243c3a06fc0cc65f2ec1a47e0764"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/a3d7c877414fcd59ab7075ecdc3b8f9c00f7bcc3",
"reference": "a3d7c877414fcd59ab7075ecdc3b8f9c00f7bcc3",
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/345c62fefe92243c3a06fc0cc65f2ec1a47e0764",
"reference": "345c62fefe92243c3a06fc0cc65f2ec1a47e0764",
"shasum": ""
},
"require": {
@@ -981,7 +981,7 @@
"serialize"
],
"support": {
"source": "https://github.com/symfony/var-exporter/tree/v7.0.1"
"source": "https://github.com/symfony/var-exporter/tree/v7.0.2"
},
"funding": [
{
@@ -997,7 +997,7 @@
"type": "tidelift"
}
],
"time": "2023-11-30T11:38:21+00:00"
"time": "2023-12-27T08:42:13+00:00"
}
],
"aliases": [],

View File

@@ -8,7 +8,7 @@ body:
options:
- label: I've read the [support guidelines](https://github.com/firefly-iii/firefly-iii/blob/main/.github/support.md)
required: true
- label: My request is not listed as [a very good idea, but unfortunately...](https://docs.firefly-iii.org/firefly-iii/more-information/what-its-not/)
- label: My request is not listed as [a very good idea, but unfortunately...](https://docs.firefly-iii.org/explanation/more-information/what-its-not/)
required: true
- label: I've used [the search](https://github.com/firefly-iii/firefly-iii/issues?q=is%3Aissue) and this has not been requested before.
required: true

View File

@@ -1,3 +1,3 @@
# [Contributing guidelines](https://docs.firefly-iii.org/firefly-iii/support/#contributing-code)
# [Contributing guidelines](https://docs.firefly-iii.org/explanation/support/#contributing-code)
[Contributing guidelines](https://docs.firefly-iii.org/firefly-iii/support/#contributing-code)
[Contributing guidelines](https://docs.firefly-iii.org/explanation/support/#contributing-code)

2
.github/support.md vendored
View File

@@ -27,7 +27,7 @@ Only then [create a new issue](https://github.com/firefly-iii/firefly-iii/issues
- Issues can be converted into discussions if it's not a bug or feature request.
- Features that won't be implemented will be labelled "
wontfix". [This isn't personal](https://docs.firefly-iii.org/firefly-iii/about-firefly-iii/what-its-not/).
wontfix". [This isn't personal](https://docs.firefly-iii.org/explanation/more-information/what-its-not/).
- Issues can be closed if they're duplicates of other issues.
- Issues can be closed if the answer is in the FAQ.
- Issues will be closed automatically after 14 days.

View File

@@ -76,10 +76,9 @@ class AccountController extends Controller
$types = $data['types'];
$query = $data['query'];
$date = $data['date'] ?? today(config('app.timezone'));
$return = [];
$result = $this->repository->searchAccount((string) $query, $types, $this->parameters->get('limit'));
// TODO this code is duplicated in the V2 Autocomplete controller, which means this code is due to be deprecated.
$defaultCurrency = app('amount')->getDefaultCurrency();

View File

@@ -62,7 +62,6 @@ class TagController extends Controller
public function tags(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();
$result = $this->repository->searchTags($data['query'], $this->parameters->get('limit'));
$array = [];

View File

@@ -29,6 +29,8 @@ use FireflyIII\Models\Attachment;
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class DestroyController
@@ -64,6 +66,12 @@ class DestroyController extends Controller
*/
public function destroy(Attachment $attachment): JsonResponse
{
if(true === auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__));
throw new NotFoundHttpException();
}
$this->repository->destroy($attachment);
app('preferences')->mark();

View File

@@ -33,9 +33,11 @@ use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Response as LaravelResponse;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Log;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class ShowController
@@ -73,6 +75,11 @@ class ShowController extends Controller
*/
public function download(Attachment $attachment): LaravelResponse
{
if(true === auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__));
throw new NotFoundHttpException();
}
if (false === $attachment->uploaded) {
throw new FireflyException('200000: File has not been uploaded (yet).');
}
@@ -116,6 +123,12 @@ class ShowController extends Controller
*/
public function index(): JsonResponse
{
if(true === auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__));
throw new NotFoundHttpException();
}
$manager = $this->getManager();
// types to get, page size:
@@ -148,6 +161,11 @@ class ShowController extends Controller
*/
public function show(Attachment $attachment): JsonResponse
{
if(true === auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__));
throw new NotFoundHttpException();
}
$manager = $this->getManager();
/** @var AttachmentTransformer $transformer */

View File

@@ -34,7 +34,9 @@ use FireflyIII\Transformers\AttachmentTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use League\Fractal\Resource\Item;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class StoreController
@@ -72,6 +74,11 @@ class StoreController extends Controller
*/
public function store(StoreRequest $request): JsonResponse
{
if(true === auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__));
throw new NotFoundHttpException();
}
app('log')->debug(sprintf('Now in %s', __METHOD__));
$data = $request->getAll();
$attachment = $this->repository->store($data);
@@ -91,6 +98,12 @@ class StoreController extends Controller
*/
public function upload(Request $request, Attachment $attachment): JsonResponse
{
if(true === auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__));
throw new NotFoundHttpException();
}
/** @var AttachmentHelperInterface $helper */
$helper = app(AttachmentHelperInterface::class);
$body = $request->getContent();

View File

@@ -31,7 +31,9 @@ use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
use FireflyIII\Transformers\AttachmentTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use League\Fractal\Resource\Item;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class UpdateController
@@ -67,6 +69,11 @@ class UpdateController extends Controller
*/
public function update(UpdateRequest $request, Attachment $attachment): JsonResponse
{
if(true === auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__));
throw new NotFoundHttpException();
}
$data = $request->getAll();
$this->repository->update($attachment, $data);
$manager = $this->getManager();

View File

@@ -32,9 +32,11 @@ use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
use FireflyIII\Transformers\WebhookAttemptTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Log;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class AttemptController
@@ -68,6 +70,12 @@ class AttemptController extends Controller
if ($message->webhook_id !== $webhook->id) {
throw new FireflyException('200040: Webhook and webhook message are no match');
}
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info(sprintf('User lists webhook attempts of webhook #%d and message #%d, but webhooks are DISABLED.', $webhook->id, $message->id));
throw new NotFoundHttpException('Webhooks are not enabled.');
}
Log::channel('audit')->info(sprintf('User lists webhook attempts of webhook #%d and message #%d.', $webhook->id, $message->id));
$manager = $this->getManager();
$pageSize = $this->parameters->get('limit');
@@ -106,6 +114,14 @@ class AttemptController extends Controller
throw new FireflyException('200041: Webhook message and webhook attempt are no match');
}
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info(sprintf('User views single webhook attempt #%d of webhook #%d and message #%d, but webhooks are DISABLED', $attempt->id, $webhook->id, $message->id));
throw new NotFoundHttpException('Webhooks are not enabled.');
}
Log::channel('audit')->info(sprintf('User views single webhook attempt #%d of webhook #%d and message #%d.', $attempt->id, $webhook->id, $message->id));
$manager = $this->getManager();
/** @var WebhookAttemptTransformer $transformer */

View File

@@ -31,6 +31,8 @@ use FireflyIII\Models\WebhookAttempt;
use FireflyIII\Models\WebhookMessage;
use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class DestroyController
@@ -60,6 +62,13 @@ class DestroyController extends Controller
*/
public function destroy(Webhook $webhook): JsonResponse
{
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info(sprintf('User tries to destroy webhook #%d. but webhooks are DISABLED.', $webhook->id));
throw new NotFoundHttpException('Webhooks are not enabled.');
}
Log::channel('audit')->info(sprintf('User destroys webhook #%d.', $webhook->id));
$this->repository->destroy($webhook);
app('preferences')->mark();
@@ -83,6 +92,14 @@ class DestroyController extends Controller
throw new FireflyException('200041: Webhook message and webhook attempt are no match');
}
if (false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info(sprintf('User tries to destroy webhook #%d, message #%d, attempt #%d, but webhooks are DISABLED.', $webhook->id, $message->id, $attempt->id));
throw new NotFoundHttpException('Webhooks are not enabled.');
}
Log::channel('audit')->info(sprintf('User destroys webhook #%d, message #%d, attempt #%d.', $webhook->id, $message->id, $attempt->id));
$this->repository->destroyAttempt($attempt);
app('preferences')->mark();
@@ -102,6 +119,14 @@ class DestroyController extends Controller
if ($message->webhook_id !== $webhook->id) {
throw new FireflyException('200040: Webhook and webhook message are no match');
}
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info(sprintf('User tries to destroy webhook #%d, message #%d, but webhooks are DISABLED.', $webhook->id, $message->id));
throw new NotFoundHttpException('Webhooks are not enabled.');
}
Log::channel('audit')->info(sprintf('User destroys webhook #%d, message #%d.', $webhook->id, $message->id));
$this->repository->destroyMessage($message);
app('preferences')->mark();

View File

@@ -31,9 +31,11 @@ use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
use FireflyIII\Transformers\WebhookMessageTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Log;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class MessageController
@@ -64,6 +66,12 @@ class MessageController extends Controller
*/
public function index(Webhook $webhook): JsonResponse
{
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info(sprintf('User tries to view messages of webhook #%d, but webhooks are DISABLED.', $webhook->id));
throw new NotFoundHttpException('Webhooks are not enabled.');
}
Log::channel('audit')->info(sprintf('User views messages of webhook #%d.', $webhook->id));
$manager = $this->getManager();
$pageSize = $this->parameters->get('limit');
$collection = $this->repository->getMessages($webhook);
@@ -98,6 +106,13 @@ class MessageController extends Controller
if ($message->webhook_id !== $webhook->id) {
throw new FireflyException('200040: Webhook and webhook message are no match');
}
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info(sprintf('User tries to view message #%d of webhook #%d, but webhooks are DISABLED.', $message->id, $webhook->id));
throw new NotFoundHttpException('Webhooks are not enabled.');
}
Log::channel('audit')->info(sprintf('User views message #%d of webhook #%d.', $message->id, $webhook->id));
$manager = $this->getManager();

View File

@@ -34,9 +34,11 @@ use FireflyIII\Transformers\WebhookTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class ShowController
@@ -69,6 +71,13 @@ class ShowController extends Controller
*/
public function index(): JsonResponse
{
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info('User tries to view all webhooks, but webhooks are DISABLED.');
throw new NotFoundHttpException('Webhooks are not enabled.');
}
Log::channel('audit')->info('User views all webhooks.');
$manager = $this->getManager();
$collection = $this->repository->all();
$pageSize = $this->parameters->get('limit');
@@ -97,6 +106,13 @@ class ShowController extends Controller
*/
public function show(Webhook $webhook): JsonResponse
{
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info(sprintf('User tries to view webhook #%d, but webhooks are DISABLED.', $webhook->id));
throw new NotFoundHttpException('Webhooks are not enabled.');
}
Log::channel('audit')->info(sprintf('User views webhook #%d.', $webhook->id));
$manager = $this->getManager();
/** @var WebhookTransformer $transformer */
@@ -115,7 +131,14 @@ class ShowController extends Controller
*/
public function triggerTransaction(Webhook $webhook, TransactionGroup $group): JsonResponse
{
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info(sprintf('User tries to trigger webhook #%d on transaction group #%d, but webhooks are DISABLED.', $webhook->id, $group->id));
throw new NotFoundHttpException('Webhooks are not enabled.');
}
app('log')->debug(sprintf('Now in triggerTransaction(%d, %d)', $webhook->id, $group->id));
Log::channel('audit')->info(sprintf('User triggers webhook #%d on transaction group #%d.', $webhook->id, $group->id));
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);

View File

@@ -28,7 +28,9 @@ use FireflyIII\Api\V1\Requests\Models\Webhook\CreateRequest;
use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
use FireflyIII\Transformers\WebhookTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use League\Fractal\Resource\Item;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class StoreController
@@ -58,9 +60,17 @@ class StoreController extends Controller
public function store(CreateRequest $request): JsonResponse
{
$data = $request->getData();
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info('User tries to store new webhook, but webhooks are DISABLED.', $data);
throw new NotFoundHttpException('Webhooks are not enabled.');
}
$webhook = $this->repository->store($data);
$manager = $this->getManager();
Log::channel('audit')->info('User stores new webhook', $data);
/** @var WebhookTransformer $transformer */
$transformer = app(WebhookTransformer::class);
$transformer->setParameters($this->parameters);

View File

@@ -28,6 +28,8 @@ use FireflyIII\Jobs\SendWebhookMessage;
use FireflyIII\Models\Webhook;
use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class SubmitController
@@ -55,6 +57,13 @@ class SubmitController extends Controller
*/
public function submit(Webhook $webhook): JsonResponse
{
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info(sprintf('User tries to submit webhook #%d, but webhooks are DISABLED.', $webhook->id));
throw new NotFoundHttpException('Webhooks are not enabled.');
}
Log::channel('audit')->info(sprintf('User submits webhook #%d', $webhook->id));
// count messages that can be sent.
$messages = $this->repository->getReadyMessages($webhook);
if (0 === $messages->count()) {

View File

@@ -29,7 +29,9 @@ use FireflyIII\Models\Webhook;
use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
use FireflyIII\Transformers\WebhookTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use League\Fractal\Resource\Item;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class UpdateController
@@ -58,9 +60,17 @@ class UpdateController extends Controller
public function update(Webhook $webhook, UpdateRequest $request): JsonResponse
{
$data = $request->getData();
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info(sprintf('User tries to update webhook #%d, but webhooks are DISABLED.', $webhook->id), $data);
throw new NotFoundHttpException('Webhooks are not enabled.');
}
$webhook = $this->repository->update($webhook, $data);
$manager = $this->getManager();
Log::channel('audit')->info(sprintf('User updates webhook #%d', $webhook->id), $data);
/** @var WebhookTransformer $transformer */
$transformer = app(WebhookTransformer::class);
$transformer->setParameters($this->parameters);

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Data;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
@@ -43,9 +44,15 @@ class DateRequest extends FormRequest
*/
public function getAll(): array
{
$start = $this->getCarbonDate('start');
$end = $this->getCarbonDate('end');
if($start->diffInYears($end) > 5) {
throw new FireflyException('Date range out of range.');
}
return [
'start' => $this->getCarbonDate('start'),
'end' => $this->getCarbonDate('end'),
'start' => $start,
'end' => $end,
];
}

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Requests\Models\Account;
use FireflyIII\Models\Location;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Rules\UniqueAccountNumber;
use FireflyIII\Rules\UniqueIban;
use FireflyIII\Support\Request\AppendsLocationData;
@@ -113,7 +114,7 @@ class StoreRequest extends FormRequest
'credit_card_type' => sprintf('nullable|in:%s|required_if:account_role,ccAsset', $ccPaymentTypes),
'monthly_payment_date' => 'nullable|date|required_if:account_role,ccAsset|required_if:credit_card_type,monthlyFull',
'liability_type' => 'nullable|required_if:type,liability|required_if:type,liabilities|in:loan,debt,mortgage',
'liability_amount' => 'required_with:liability_start_date|min:0|numeric|max:1000000000',
'liability_amount' => ['required_with:liability_start_date', new IsValidPositiveAmount()],
'liability_start_date' => 'required_with:liability_amount|date',
'liability_direction' => 'nullable|required_if:type,liability|required_if:type,liabilities|in:credit,debit',
'interest' => 'between:0,100|numeric',

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\AvailableBudget;
use Carbon\Carbon;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
@@ -62,7 +63,7 @@ class Request extends FormRequest
return [
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'amount' => 'numeric|gt:0',
'amount' => ['nullable', new IsValidPositiveAmount()],
'start' => 'date',
'end' => 'date',
];

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Bill;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
@@ -72,8 +73,8 @@ class StoreRequest extends FormRequest
{
return [
'name' => 'between:1,255|uniqueObjectForUser:bills,name',
'amount_min' => 'numeric|gt:0|required',
'amount_max' => 'numeric|gt:0|required',
'amount_min' => ['required', new IsValidPositiveAmount()],
'amount_max' => ['required', new IsValidPositiveAmount()],
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'date' => 'date|required',

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Requests\Models\Bill;
use FireflyIII\Models\Bill;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
@@ -75,8 +76,8 @@ class UpdateRequest extends FormRequest
return [
'name' => sprintf('between:1,255|uniqueObjectForUser:bills,name,%d', $bill->id),
'amount_min' => 'numeric|gt:0',
'amount_max' => 'numeric|gt:0',
'amount_min' => ['nullable', new IsValidPositiveAmount()],
'amount_max' => ['nullable', new IsValidPositiveAmount()],
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'date' => 'date',

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Budget;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\Validation\AutoBudget\ValidatesAutoBudgetRequest;
@@ -74,7 +75,7 @@ class StoreRequest extends FormRequest
'notes' => 'nullable|between:1,65536',
// auto budget info
'auto_budget_type' => 'in:reset,rollover,adjusted,none',
'auto_budget_amount' => 'numeric|min:0|max:1000000000|required_if:auto_budget_type,reset|required_if:auto_budget_type,rollover|required_if:auto_budget_type,adjusted',
'auto_budget_amount' => ['required_if:auto_budget_type,reset', 'required_if:auto_budget_type,rollover', 'required_if:auto_budget_type,adjusted', new IsValidPositiveAmount()],
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly|required_if:auto_budget_type,reset|required_if:auto_budget_type,rollover|required_if:auto_budget_type,adjusted',
];
}

View File

@@ -25,6 +25,7 @@ namespace FireflyIII\Api\V1\Requests\Models\Budget;
use FireflyIII\Models\Budget;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\Validation\AutoBudget\ValidatesAutoBudgetRequest;
@@ -86,7 +87,7 @@ class UpdateRequest extends FormRequest
'auto_budget_type' => 'in:reset,rollover,adjusted,none',
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
'auto_budget_currency_code' => 'exists:transaction_currencies,code',
'auto_budget_amount' => 'min:0|max:1000000000',
'auto_budget_amount' => ['nullable', new IsValidPositiveAmount()],
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
];
}

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
@@ -57,7 +58,7 @@ class StoreRequest extends FormRequest
return [
'start' => 'required|before:end|date',
'end' => 'required|after:start|date',
'amount' => 'required|gt:0',
'amount' => ['required', new IsValidPositiveAmount()],
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
];

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit;
use Carbon\Carbon;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
@@ -61,7 +62,7 @@ class UpdateRequest extends FormRequest
return [
'start' => 'date',
'end' => 'date',
'amount' => 'gt:0',
'amount' => ['nullable', new IsValidPositiveAmount()],
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
];

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\PiggyBank;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
@@ -64,11 +65,11 @@ class StoreRequest extends FormRequest
{
return [
'name' => 'required|between:1,255|uniquePiggyBankForUser',
'current_amount' => ['numeric', 'gte:0', 'lte:target_amount'],
'current_amount' => ['nullable', new IsValidPositiveAmount()],
'account_id' => 'required|numeric|belongsToUser:accounts,id',
'object_group_id' => 'numeric|belongsToUser:object_groups,id',
'object_group_title' => 'between:1,255',
'target_amount' => ['numeric', 'gte:0', 'lte:target_amount', 'required'],
'target_amount' => ['required', new IsValidPositiveAmount()],
'start_date' => 'date|nullable',
'target_date' => 'date|nullable|after:start_date',
'notes' => 'max:65000',

View File

@@ -25,6 +25,7 @@ namespace FireflyIII\Api\V1\Requests\Models\PiggyBank;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Rules\IsAssetAccountId;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Rules\LessThanPiggyTarget;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
@@ -69,8 +70,8 @@ class UpdateRequest extends FormRequest
return [
'name' => 'between:1,255|uniquePiggyBankForUser:'.$piggyBank->id,
'current_amount' => ['numeric', 'gte:0', new LessThanPiggyTarget()],
'target_amount' => 'numeric|gte:0',
'current_amount' => ['nullable', new LessThanPiggyTarget(), new IsValidPositiveAmount()],
'target_amount' => ['nullable', new IsValidPositiveAmount()],
'start_date' => 'date|nullable',
'target_date' => 'date|nullable|after:start_date',
'notes' => 'max:65000',

View File

@@ -25,6 +25,7 @@ namespace FireflyIII\Api\V1\Requests\Models\Recurrence;
use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\Support\Request\GetRecurrenceData;
@@ -92,8 +93,8 @@ class StoreRequest extends FormRequest
'repetitions.*.weekend' => 'numeric|min:1|max:4',
'transactions.*.description' => 'required|between:1,255',
'transactions.*.amount' => 'required|numeric|gt:0',
'transactions.*.foreign_amount' => 'nullable|numeric|gt:0',
'transactions.*.amount' => ['required', new IsValidPositiveAmount()],
'transactions.*.foreign_amount' => ['nullable', new IsValidPositiveAmount()],
'transactions.*.currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
'transactions.*.currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code',
'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
@@ -110,7 +111,7 @@ class StoreRequest extends FormRequest
'transactions.*.category_name' => 'between:1,255|nullable',
'transactions.*.piggy_bank_id' => ['nullable', 'numeric', 'mustExist:piggy_banks,id', new BelongsUser()],
'transactions.*.piggy_bank_name' => ['between:1,255', 'nullable', new BelongsUser()],
'transactions.*.tags' => 'nullable|between:1,64000',
'transactions.*.tags' => 'nullable|between:1,255',
];
}

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Requests\Models\Recurrence;
use FireflyIII\Models\Recurrence;
use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\Support\Request\GetRecurrenceData;
@@ -99,8 +100,8 @@ class UpdateRequest extends FormRequest
'repetitions.*.weekend' => 'nullable|numeric|min:1|max:4',
'transactions.*.description' => 'between:1,255',
'transactions.*.amount' => 'numeric|gt:0',
'transactions.*.foreign_amount' => 'nullable|numeric|gt:0',
'transactions.*.amount' => [new IsValidPositiveAmount()],
'transactions.*.foreign_amount' => ['nullable', new IsValidPositiveAmount()],
'transactions.*.currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
'transactions.*.currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code',
'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
@@ -117,7 +118,7 @@ class UpdateRequest extends FormRequest
'transactions.*.category_name' => 'between:1,255|nullable',
'transactions.*.piggy_bank_id' => ['nullable', 'numeric', 'mustExist:piggy_banks,id', new BelongsUser()],
'transactions.*.piggy_bank_name' => ['between:1,255', 'nullable', new BelongsUser()],
'transactions.*.tags' => 'nullable|between:1,64000',
'transactions.*.tags' => 'nullable|between:1,255',
];
}

View File

@@ -27,6 +27,7 @@ namespace FireflyIII\Api\V1\Requests\Models\Transaction;
use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsDateOrTime;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\NullArrayObject;
use FireflyIII\Support\Request\AppendsLocationData;
use FireflyIII\Support\Request\ChecksLogin;
@@ -92,8 +93,8 @@ class StoreRequest extends FormRequest
'transactions.*.foreign_currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
// amount
'transactions.*.amount' => 'required|numeric|gt:0',
'transactions.*.foreign_amount' => 'numeric',
'transactions.*.amount' => ['required', new IsValidPositiveAmount()],
'transactions.*.foreign_amount' => ['nullable', new IsValidPositiveAmount()],
// description
'transactions.*.description' => 'nullable|between:1,1000',
@@ -126,6 +127,7 @@ class StoreRequest extends FormRequest
'transactions.*.reconciled' => [new IsBoolean()],
'transactions.*.notes' => 'min:1|max:50000|nullable',
'transactions.*.tags' => 'between:0,255',
'transactions.*.tags.*' => 'between:0,255',
// meta info fields
'transactions.*.internal_reference' => 'min:1|max:255|nullable',

View File

@@ -29,6 +29,7 @@ use FireflyIII\Models\TransactionGroup;
use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsDateOrTime;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\Validation\GroupValidation;
@@ -115,8 +116,8 @@ class UpdateRequest extends FormRequest
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code',
// amount
'transactions.*.amount' => 'numeric|gt:0|max:100000000000',
'transactions.*.foreign_amount' => 'nullable|numeric|gte:0',
'transactions.*.amount' => ['nullable', new IsValidPositiveAmount()],
'transactions.*.foreign_amount' => ['nullable', new IsValidPositiveAmount()],
// description
'transactions.*.description' => 'nullable|between:1,1000',
@@ -141,6 +142,7 @@ class UpdateRequest extends FormRequest
'transactions.*.reconciled' => [new IsBoolean()],
'transactions.*.notes' => 'min:1|max:50000|nullable',
'transactions.*.tags' => 'between:0,255|nullable',
'transactions.*.tags.*' => 'between:0,255',
// meta info fields
'transactions.*.internal_reference' => 'min:1|max:255|nullable',

View File

@@ -0,0 +1,83 @@
<?php
/*
* CategoryController.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\Autocomplete;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Autocomplete\AutocompleteRequest;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\UserGroups\Category\CategoryRepositoryInterface;
use Illuminate\Http\JsonResponse;
/**
* Class CategoryController
*/
class CategoryController extends Controller
{
private CategoryRepositoryInterface $repository;
/**
* AccountController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(CategoryRepositoryInterface::class);
$userGroup = $this->validateUserGroup($request);
if (null !== $userGroup) {
$this->repository->setUserGroup($userGroup);
}
return $next($request);
}
);
}
/**
* Documentation for this endpoint:
* TODO list of checks
* 1. use dates from ParameterBag
* 2. Request validates dates
* 3. Request includes user_group_id
* 4. Endpoint is documented.
* 5. Collector uses user_group_id
*/
public function categories(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();
$result = $this->repository->searchCategory($data['query'], $this->parameters->get('limit'));
$filtered = $result->map(
static function (Category $item) {
return [
'id' => (string) $item->id,
'name' => $item->name,
];
}
);
return response()->json($filtered);
}
}

View File

@@ -0,0 +1,85 @@
<?php
/*
* CategoryController.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\Autocomplete;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Autocomplete\AutocompleteRequest;
use FireflyIII\Models\Tag;
use FireflyIII\Repositories\UserGroups\Tag\TagRepositoryInterface;
use Illuminate\Http\JsonResponse;
/**
* Class TagController
*/
class TagController extends Controller
{
private TagRepositoryInterface $repository;
/**
* AccountController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(TagRepositoryInterface::class);
$userGroup = $this->validateUserGroup($request);
if (null !== $userGroup) {
$this->repository->setUserGroup($userGroup);
}
return $next($request);
}
);
}
/**
* Documentation for this endpoint:
* TODO list of checks
* 1. use dates from ParameterBag
* 2. Request validates dates
* 3. Request includes user_group_id
* 4. Endpoint is documented.
* 5. Collector uses user_group_id
*/
public function tags(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();
$result = $this->repository->searchTag($data['query'], $data['limit']);
$filtered = $result->map(
static function (Tag $item) {
return [
'id' => (string) $item->id,
'name' => $item->tag,
'value' => (string) $item->id,
'label' => $item->tag,
];
}
);
return response()->json($filtered);
}
}

View File

@@ -134,10 +134,10 @@ class AccountController extends Controller
'currency_decimal_places' => $currency->decimal_places,
// the default currency of the user (could be the same!)
'native_id' => (string)$default->id,
'native_code' => $default->code,
'native_symbol' => $default->symbol,
'native_decimal_places' => $default->decimal_places,
'native_currency_id' => (string)$default->id,
'native_currency_code' => $default->code,
'native_currency_symbol' => $default->symbol,
'native_currency_decimal_places' => $default->decimal_places,
'start' => $start->toAtomString(),
'end' => $end->toAtomString(),
'period' => '1D',

View File

@@ -39,6 +39,7 @@ use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* Class BudgetController
@@ -131,10 +132,10 @@ class BudgetController extends Controller
'currency_code' => $row['currency_code'],
'currency_name' => $row['currency_name'],
'currency_decimal_places' => $row['currency_decimal_places'],
'native_id' => (string)$row['native_id'],
'native_code' => $row['native_code'],
'native_name' => $row['native_name'],
'native_decimal_places' => $row['native_decimal_places'],
'native_currency_id' => (string) $row['native_currency_id'],
'native_currency_code' => $row['native_currency_code'],
'native_currency_name' => $row['native_currency_name'],
'native_currency_decimal_places' => $row['native_currency_decimal_places'],
'period' => null,
'start' => $row['start'],
'end' => $row['end'],
@@ -169,7 +170,8 @@ class BudgetController extends Controller
}
/**
* Shared between the "noBudgetLimits" function and "processLimit". Will take a single set of expenses and return its info.
* Shared between the "noBudgetLimits" function and "processLimit". Will take a single set of expenses and return
* its info.
*
* @param array<int, array<int, string>> $array
*
@@ -177,6 +179,7 @@ class BudgetController extends Controller
*/
private function processExpenses(int $budgetId, array $array, Carbon $start, Carbon $end): array
{
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$converter = new ExchangeRateConverter();
$return = [];
@@ -195,11 +198,11 @@ class BudgetController extends Controller
'currency_name' => $block['currency_name'],
'currency_symbol' => $block['currency_symbol'],
'currency_decimal_places' => (int) $block['currency_decimal_places'],
'native_id' => (string)$this->currency->id,
'native_code' => $this->currency->code,
'native_name' => $this->currency->name,
'native_symbol' => $this->currency->symbol,
'native_decimal_places' => $this->currency->decimal_places,
'native_currency_id' => (string) $this->currency->id,
'native_currency_code' => $this->currency->code,
'native_currency_name' => $this->currency->name,
'native_currency_symbol' => $this->currency->symbol,
'native_currency_decimal_places' => $this->currency->decimal_places,
'start' => $start->toAtomString(),
'end' => $end->toAtomString(),
'spent' => '0',
@@ -258,6 +261,7 @@ class BudgetController extends Controller
*/
private function processLimit(Budget $budget, BudgetLimit $limit): array
{
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$end = clone $limit->end_date;
$end->endOfDay();
$spent = $this->opsRepository->listExpenses($limit->start_date, $end, null, new Collection([$budget]));

View File

@@ -37,6 +37,7 @@ use FireflyIII\Support\Http\Api\CleansChartData;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
/**
* Class BudgetController
@@ -76,6 +77,8 @@ class CategoryController extends Controller
*/
public function dashboard(DateRequest $request): JsonResponse
{
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
/** @var Carbon $start */
$start = $this->parameters->get('start');
@@ -115,11 +118,11 @@ class CategoryController extends Controller
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'native_id' => (string)$default->id,
'native_code' => $default->code,
'native_name' => $default->name,
'native_symbol' => $default->symbol,
'native_decimal_places' => $default->decimal_places,
'native_currency_id' => (string)$default->id,
'native_currency_code' => $default->code,
'native_currency_name' => $default->name,
'native_currency_symbol' => $default->symbol,
'native_currency_decimal_places' => $default->decimal_places,
'period' => null,
'start' => $start->toAtomString(),
'end' => $end->toAtomString(),

View File

@@ -28,6 +28,7 @@ use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Generic\DateRequest;
use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Transformers\V2\BudgetTransformer;
use Illuminate\Http\JsonResponse;
/**
@@ -50,6 +51,20 @@ class ShowController extends Controller
);
}
/**
* Show a budget.
*/
public function show(Budget $budget): JsonResponse
{
$transformer = new BudgetTransformer();
$transformer->setParameters($this->parameters);
return response()
->api($this->jsonApiObject('budgets', $budget, $transformer))
->header('Content-Type', self::CONTENT_TYPE)
;
}
/**
* 2023-10-29 removed the cerSum reference, not sure where this is used atm
* so removed from api.php. Also applies to "spent" method.

View File

@@ -0,0 +1,65 @@
<?php
/*
* IndexController.php
* Copyright (c) 2023 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\Model\BudgetLimit;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Transformers\V2\BudgetLimitTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
class IndexController extends Controller
{
private BudgetLimitRepositoryInterface $repository;
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(BudgetLimitRepositoryInterface::class);
return $next($request);
}
);
}
/**
* This endpoint is documented at:
* TODO add URL
*/
public function index(Budget $budget): JsonResponse
{
$pageSize = $this->parameters->get('limit');
$collection = $this->repository->getBudgetLimits($budget);
$total = $collection->count();
$collection->slice($pageSize * $this->parameters->get('page'), $pageSize);
$paginator = new LengthAwarePaginator($collection, $total, $pageSize, $this->parameters->get('page'));
$transformer = new BudgetLimitTransformer();
return response()->api($this->jsonApiList('budget-limits', $paginator, $transformer))->header('Content-Type', self::CONTENT_TYPE);
}
}

View File

@@ -45,6 +45,7 @@ use FireflyIII\Support\Http\Api\SummaryBalanceGrouped;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
/**
* Class BasicController
@@ -205,10 +206,10 @@ class BasicController extends Controller
$return[] = [
'key' => 'bills-paid-in-native',
'value' => $nativeAmount,
'currency_id' => (string)$info['native_id'],
'currency_code' => $info['native_code'],
'currency_symbol' => $info['native_symbol'],
'currency_decimal_places' => $info['native_decimal_places'],
'currency_id' => (string)$info['native_currency_id'],
'currency_code' => $info['native_currency_code'],
'currency_symbol' => $info['native_currency_symbol'],
'currency_decimal_places' => $info['native_currency_decimal_places'],
];
}
@@ -229,10 +230,10 @@ class BasicController extends Controller
$return[] = [
'key' => 'bills-unpaid-in-native',
'value' => $nativeAmount,
'currency_id' => (string)$info['native_id'],
'currency_code' => $info['native_code'],
'currency_symbol' => $info['native_symbol'],
'currency_decimal_places' => $info['native_decimal_places'],
'currency_id' => (string)$info['native_currency_id'],
'currency_code' => $info['native_currency_code'],
'currency_symbol' => $info['native_currency_symbol'],
'currency_decimal_places' => $info['native_currency_decimal_places'],
];
}
@@ -244,6 +245,7 @@ class BasicController extends Controller
*/
private function getLeftToSpendInfo(Carbon $start, Carbon $end): array
{
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
app('log')->debug('Now in getLeftToSpendInfo');
$return = [];
$today = today(config('app.timezone'));

View File

@@ -107,8 +107,8 @@ class StoreRequest extends FormRequest
'transactions.*.foreign_currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
// amount
'transactions.*.amount' => 'required|numeric|gt:0',
'transactions.*.foreign_amount' => 'numeric',
'transactions.*.amount' => 'required|numeric|gt:0|max:1000000000',
'transactions.*.foreign_amount' => 'numeric|gt:0|max:1000000000',
// description
'transactions.*.description' => 'nullable|between:1,1000',

View File

@@ -32,6 +32,7 @@ use FireflyIII\Support\Cronjobs\BillWarningCronjob;
use FireflyIII\Support\Cronjobs\ExchangeRatesCronjob;
use FireflyIII\Support\Cronjobs\RecurringCronjob;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
/**
* Class Cron
@@ -103,6 +104,7 @@ class Cron extends Command
private function exchangeRatesCronJob(bool $force, ?Carbon $date): void
{
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$exchangeRates = new ExchangeRatesCronjob();
$exchangeRates->setForce($force);
// set date in cron job:

View File

@@ -25,9 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Upgrade;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\Recurrence;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\TransactionType;
use Illuminate\Console\Command;
/**
@@ -53,8 +50,7 @@ class MigrateRecurrenceType extends Command
return 0;
}
$this->migrateTypes();
$this->friendlyWarning('This command has been disabled.');
$this->markAsExecuted();
return 0;
@@ -67,38 +63,6 @@ class MigrateRecurrenceType extends Command
return (bool)$configVar?->data;
}
private function migrateTypes(): void
{
$set = Recurrence::get();
/** @var Recurrence $recurrence */
foreach ($set as $recurrence) {
if (TransactionType::INVALID !== $recurrence->transactionType->type) {
$this->migrateRecurrence($recurrence);
}
}
}
private function migrateRecurrence(Recurrence $recurrence): void
{
$originalType = $recurrence->transaction_type_id;
$newType = $this->getInvalidType();
$recurrence->transaction_type_id = $newType->id;
$recurrence->save();
/** @var RecurrenceTransaction $transaction */
foreach ($recurrence->recurrenceTransactions as $transaction) {
$transaction->transaction_type_id = $originalType;
$transaction->save();
}
$this->friendlyInfo(sprintf('Updated recurrence #%d to new transaction type model.', $recurrence->id));
}
private function getInvalidType(): TransactionType
{
return TransactionType::whereType(TransactionType::INVALID)->firstOrCreate(['type' => TransactionType::INVALID]);
}
private function markAsExecuted(): void
{
app('fireflyconfig')->set(self::CONFIG_NAME, true);

View File

@@ -355,6 +355,9 @@ class TransactionJournalFactory
/** @var null|TransactionJournalMeta $result */
$result = TransactionJournalMeta::withTrashed()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
->whereNotNull('transaction_journals.id')
->where('transaction_journals.user_id', $this->user->id)
->where('data', json_encode($hash, JSON_THROW_ON_ERROR))
->with(['transactionJournal', 'transactionJournal.transactionGroup'])
->first()

View File

@@ -670,7 +670,58 @@ trait MetaCollection
}
/**
* Limit results to a specific set of tags.
* Limit results to a SPECIFIC set of tags.
*/
public function setAllTags(Collection $tags): GroupCollectorInterface
{
Log::debug(sprintf('Now in setAllTags(%d tag(s))', $tags->count()));
$this->withTagInformation();
$this->query->whereNotNull('tag_transaction_journal.tag_id');
// this method adds a "postFilter" to the collector.
$list = $tags->pluck('tag')->toArray();
$list = array_map('strtolower', $list);
$filter = static function (array $object) use ($list): bool|array {
$return = $object;
unset($return['transactions']);
$return['transactions'] = [];
Log::debug(sprintf('Now in setAllTags(%s) filter', implode(', ', $list)));
$expectedTagCount = count($list);
$foundTagCount = 0;
foreach ($object['transactions'] as $transaction) {
$transactionTagCount = count($transaction['tags']);
app('log')->debug(sprintf('Transaction #%d has %d tag(s)', $transaction['transaction_journal_id'], $transactionTagCount));
if ($transactionTagCount < $expectedTagCount) {
app('log')->debug(sprintf('Transaction has %d tag(s), we expect %d tag(s), return false.', $transactionTagCount, $expectedTagCount));
return false;
}
foreach ($transaction['tags'] as $tag) {
Log::debug(sprintf('"%s" versus', strtolower($tag['name'])), $list);
if (in_array(strtolower($tag['name']), $list, true)) {
app('log')->debug(sprintf('Transaction has tag "%s" so count++.', $tag['name']));
++$foundTagCount;
$return['transactions'][] = $transaction;
}
}
}
Log::debug(sprintf('Found %d tags, need at least %d.', $foundTagCount, $expectedTagCount));
// found at least the expected tags.
$result = $foundTagCount >= $expectedTagCount;
if (true === $result) {
return $return;
}
return false;
};
$this->postFilters[] = $filter;
return $this;
}
/**
* Limit results to any of the tags in the list.
*/
public function setTags(Collection $tags): GroupCollectorInterface
{
@@ -683,28 +734,19 @@ trait MetaCollection
$list = array_map('strtolower', $list);
$filter = static function (array $object) use ($list): bool {
Log::debug(sprintf('Now in setTags(%s) filter', implode(', ', $list)));
$expectedTagCount = count($list);
$foundTagCount = 0;
foreach ($object['transactions'] as $transaction) {
$transactionTagCount = count($transaction['tags']);
app('log')->debug(sprintf('Transaction has %d tag(s)', $transactionTagCount));
if ($transactionTagCount < $expectedTagCount) {
app('log')->debug(sprintf('Transaction has %d tag(s), we expect %d tag(s), return false.', $transactionTagCount, $expectedTagCount));
return false;
}
foreach ($transaction['tags'] as $tag) {
Log::debug(sprintf('"%s" versus', strtolower($tag['name'])), $list);
if (in_array(strtolower($tag['name']), $list, true)) {
app('log')->debug(sprintf('Transaction has tag "%s" so count++.', $tag['name']));
++$foundTagCount;
}
}
}
Log::debug(sprintf('Found %d tags, need at least %d.', $foundTagCount, $expectedTagCount));
app('log')->debug(sprintf('Transaction has tag "%s" so return true.', $tag['name']));
// found at least the expected tags.
return $foundTagCount >= $expectedTagCount;
return true;
}
}
}
app('log')->debug('Transaction has no tags from the list, so return false.');
return false;
};
$this->postFilters[] = $filter;

View File

@@ -43,6 +43,7 @@ use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* Class GroupCollector
@@ -560,6 +561,7 @@ class GroupCollector implements GroupCollectorInterface
if (0 !== count($journalIds)) {
// make all integers.
$integerIDs = array_map('intval', $journalIds);
Log::debug(sprintf('GroupCollector: setJournalIds: %s', implode(', ', $integerIDs)));
$this->query->whereIn('transaction_journals.id', $integerIDs);
}
@@ -948,8 +950,15 @@ class GroupCollector implements GroupCollectorInterface
// skip other filters, continue to next item.
continue;
}
// if the result is a bool, use the unedited results.
if(true === $result) {
$nextCollection->push($item);
}
// if the result is an array, the filter has changed what's being returned.
if(is_array($result)) {
$nextCollection->push($result);
}
}
$currentCollection = $nextCollection;
app('log')->debug(sprintf('GroupCollector: postFilterCollection has %d transaction(s) left.', count($currentCollection)));
}

View File

@@ -559,10 +559,15 @@ interface GroupCollectorInterface
public function setTag(Tag $tag): self;
/**
* Limit results to a specific set of tags.
* Limit results to any of the specified tags.
*/
public function setTags(Collection $tags): self;
/**
* Limit results to a SPECIFIC set of tags.
*/
public function setAllTags(Collection $tags): self;
/**
* Limit the search to one specific transaction group.
*/

View File

@@ -36,6 +36,7 @@ use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\User;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* This class can handle both request with and without a user group and will return the appropriate repository when
@@ -72,7 +73,7 @@ class NetWorth implements NetWorthInterface
return $cache->get();
}
app('log')->debug(sprintf('Now in byAccounts("%s", "%s")', $ids, $date->format('Y-m-d')));
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$default = app('amount')->getDefaultCurrency();
$converter = new ExchangeRateConverter();
@@ -86,11 +87,11 @@ class NetWorth implements NetWorthInterface
'currency_name' => $default->name,
'currency_symbol' => $default->symbol,
'currency_decimal_places' => $default->decimal_places,
'native_id' => $default->id,
'native_code' => $default->code,
'native_name' => $default->name,
'native_symbol' => $default->symbol,
'native_decimal_places' => $default->decimal_places,
'native_currency_id' => $default->id,
'native_currency_code' => $default->code,
'native_currency_name' => $default->name,
'native_currency_symbol' => $default->symbol,
'native_currency_decimal_places' => $default->decimal_places,
],
];
$balances = app('steam')->balancesByAccountsConverted($accounts, $date);
@@ -102,7 +103,7 @@ class NetWorth implements NetWorthInterface
if (null === $currency) {
$currency = app('amount')->getDefaultCurrency();
}
$currencyId = $currency->id;
$currencyCode = $currency->code;
$balance = '0';
$nativeBalance = '0';
if (array_key_exists($account->id, $balances)) {
@@ -117,23 +118,23 @@ class NetWorth implements NetWorthInterface
$nativeVirtualBalance = $converter->convert($default, $currency, $account->created_at, $virtualBalance);
$nativeBalance = bcsub($nativeBalance, $nativeVirtualBalance);
}
$netWorth[$currencyId] ??= [
$netWorth[$currencyCode] ??= [
'balance' => '0',
'native_balance' => '0',
'currency_id' => $currencyId,
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'native_id' => $default->id,
'native_code' => $default->code,
'native_name' => $default->name,
'native_symbol' => $default->symbol,
'native_decimal_places' => $default->decimal_places,
'native_currency_id' => (string) $default->id,
'native_currency_code' => $default->code,
'native_currency_name' => $default->name,
'native_currency_symbol' => $default->symbol,
'native_currency_decimal_places' => $default->decimal_places,
];
$netWorth[$currencyId]['balance'] = bcadd($balance, $netWorth[$currencyId]['balance']);
$netWorth[$currencyId]['native_balance'] = bcadd($nativeBalance, $netWorth[$currencyId]['native_balance']);
$netWorth[$currencyCode]['balance'] = bcadd($balance, $netWorth[$currencyCode]['balance']);
$netWorth[$currencyCode]['native_balance'] = bcadd($nativeBalance, $netWorth[$currencyCode]['native_balance']);
$netWorth['native']['balance'] = bcadd($nativeBalance, $netWorth['native']['balance']);
$netWorth['native']['native_balance'] = bcadd($nativeBalance, $netWorth['native']['native_balance']);
}

View File

@@ -109,6 +109,11 @@ class CreateController extends Controller
'include_net_worth' => $hasOldInput ? (bool) $request->old('include_net_worth') : true,
]
);
// issue #8321
$showNetWorth = true;
if ('liabilities' !== $objectType && 'asset' !== $objectType) {
$showNetWorth = false;
}
// put previous url in session if not redirect from store (not "create another").
if (true !== session('accounts.create.fromStore')) {
@@ -119,7 +124,7 @@ class CreateController extends Controller
return view(
'accounts.create',
compact('subTitleIcon', 'liabilityDirections', 'locations', 'objectType', 'interestPeriods', 'subTitle', 'roles', 'liabilityTypes')
compact('subTitleIcon', 'liabilityDirections', 'showNetWorth', 'locations', 'objectType', 'interestPeriods', 'subTitle', 'roles', 'liabilityTypes')
);
}
@@ -156,6 +161,7 @@ class CreateController extends Controller
$this->attachments->saveAttachmentsForModel($account, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string) trans('firefly.no_att_demo_user'));
}

View File

@@ -33,6 +33,7 @@ use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
/**
@@ -127,6 +128,12 @@ class EditController extends Controller
$includeNetWorth = $repository->getMetaValue($account, 'include_net_worth');
$includeNetWorth = null === $includeNetWorth ? true : '1' === $includeNetWorth;
// issue #8321
$showNetWorth = true;
if ('liabilities' !== $objectType && 'asset' !== $objectType) {
$showNetWorth = false;
}
// code to handle active-checkboxes
$hasOldInput = null !== $request->old('_token');
$virtualBalance = null === $account->virtual_balance ? '0' : $account->virtual_balance;
@@ -154,7 +161,7 @@ class EditController extends Controller
$request->session()->flash('preFilled', $preFilled);
return view('accounts.edit', compact('account', 'currency', 'subTitle', 'subTitleIcon', 'locations', 'liabilityDirections', 'objectType', 'roles', 'preFilled', 'liabilityTypes', 'interestPeriods'));
return view('accounts.edit', compact('account', 'currency', 'showNetWorth', 'subTitle', 'subTitleIcon', 'locations', 'liabilityDirections', 'objectType', 'roles', 'preFilled', 'liabilityTypes', 'interestPeriods'));
}
/**
@@ -170,7 +177,7 @@ class EditController extends Controller
$data = $request->getAccountData();
$this->repository->update($account, $data);
Log::channel('audit')->info(sprintf('Updated account #%d.', $account->id), $data);
$request->session()->flash('success', (string) trans('firefly.updated_account', ['name' => $account->name]));
// store new attachment(s):
@@ -180,6 +187,7 @@ class EditController extends Controller
$this->attachments->saveAttachmentsForModel($account, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string) trans('firefly.no_att_demo_user'));
}

View File

@@ -33,6 +33,7 @@ use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
/**
* Class CreateController
@@ -104,6 +105,8 @@ class CreateController extends Controller
return redirect(route('bills.create'))->withInput();
}
Log::channel('audit')->info('Stored new bill.', $billData);
$request->session()->flash('success', (string)trans('firefly.stored_new_bill', ['name' => $bill->name]));
app('preferences')->mark();
@@ -113,6 +116,7 @@ class CreateController extends Controller
$this->attachments->saveAttachmentsForModel($bill, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
}

View File

@@ -33,6 +33,7 @@ use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
/**
* Class EditController
@@ -116,6 +117,8 @@ class EditController extends Controller
$billData = $request->getBillData();
$bill = $this->repository->update($bill, $billData);
Log::channel('audit')->info(sprintf('Updated bill #%d.', $bill->id), $billData);
$request->session()->flash('success', (string) trans('firefly.updated_bill', ['name' => $bill->name]));
app('preferences')->mark();
@@ -125,6 +128,7 @@ class EditController extends Controller
$this->attachments->saveAttachmentsForModel($bill, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string) trans('firefly.no_att_demo_user'));
}

View File

@@ -32,6 +32,7 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
/**
@@ -117,6 +118,8 @@ class CreateController extends Controller
$request->session()->flash('success', (string)trans('firefly.stored_new_budget', ['name' => $budget->name]));
app('preferences')->mark();
Log::channel('audit')->info('Stored new budget.', $data);
// store attachment(s):
/** @var null|array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
@@ -124,6 +127,7 @@ class CreateController extends Controller
$this->attachments->saveAttachmentsForModel($budget, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
}

View File

@@ -32,6 +32,7 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
/**
@@ -125,6 +126,8 @@ class EditController extends Controller
$this->repository->cleanupBudgets();
app('preferences')->mark();
Log::channel('audit')->info(sprintf('Updated budget #%d.', $budget->id), $data);
$redirect = redirect($this->getPreviousUrl('budgets.edit.url'));
// store new attachment(s):
@@ -134,6 +137,7 @@ class EditController extends Controller
$this->attachments->saveAttachmentsForModel($budget, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
}

View File

@@ -32,6 +32,7 @@ use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
/**
@@ -99,6 +100,7 @@ class CreateController extends Controller
$this->attachments->saveAttachmentsForModel($category, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
}

View File

@@ -32,6 +32,7 @@ use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
/**
@@ -103,6 +104,7 @@ class EditController extends Controller
$this->attachments->saveAttachmentsForModel($category, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
}

View File

@@ -60,6 +60,7 @@ class RecurrenceController extends Controller
* Shows all events for a repetition. Used in calendar.
*
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @throws FireflyException
*/
@@ -74,7 +75,11 @@ class RecurrenceController extends Controller
$endsAt = (string) $request->get('ends');
$repetitionType = explode(',', $request->get('type'))[0];
$repetitions = (int) $request->get('reps');
$weekend = (int) $request->get('weekend');
$repetitionMoment = '';
$skip = (int) $request->get('skip');
$skip = $skip < 1 || $skip > 31 ? 1 : $skip;
$weekend = $weekend < 1 || $weekend > 4 ? 1 : $weekend;
if (false === $start || false === $end || false === $firstDate || false === $endDate) {
return response()->json();
@@ -102,8 +107,8 @@ class RecurrenceController extends Controller
$repetition = new RecurrenceRepetition();
$repetition->repetition_type = $repetitionType;
$repetition->repetition_moment = $repetitionMoment;
$repetition->repetition_skip = (int)$request->get('skip');
$repetition->weekend = (int)$request->get('weekend');
$repetition->repetition_skip = $skip;
$repetition->weekend = $weekend;
$actualEnd = clone $end;
if ('until_date' === $endsAt) {

View File

@@ -32,6 +32,7 @@ use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
/**
@@ -106,6 +107,7 @@ class CreateController extends Controller
$this->attachments->saveAttachmentsForModel($piggyBank, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
}

View File

@@ -33,6 +33,7 @@ use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
/**
@@ -126,6 +127,7 @@ class EditController extends Controller
$this->attachments->saveAttachmentsForModel($piggyBank, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
}

View File

@@ -37,6 +37,7 @@ use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
/**
@@ -241,6 +242,7 @@ class CreateController extends Controller
$this->attachments->saveAttachmentsForModel($recurrence, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
}

View File

@@ -37,6 +37,7 @@ use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\ParameterBag;
@@ -179,6 +180,7 @@ class EditController extends Controller
$this->attachments->saveAttachmentsForModel($recurrence, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
}

View File

@@ -57,6 +57,8 @@ class TagController extends Controller
/**
* @return Factory|View
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function accountPerTag(Collection $accounts, Collection $tags, Carbon $start, Carbon $end)
{
@@ -81,6 +83,7 @@ class TagController extends Controller
/** @var array $tag */
foreach ($currency['tags'] as $tag) {
$tagId = $tag['id'];
foreach ($tag['transaction_journals'] as $journal) {
$sourceAccountId = $journal['source_account_id'];
$report[$sourceAccountId]['currencies'][$currencyId] ??= [
@@ -91,18 +94,18 @@ class TagController extends Controller
'tags' => [],
];
$report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']]
$report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tagId]
??= [
'spent' => '0',
'earned' => '0',
'sum' => '0',
];
$report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']]['spent'] = bcadd(
$report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']]['spent'],
$report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tagId]['spent'] = bcadd(
$report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tagId]['spent'],
$journal['amount']
);
$report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']]['sum'] = bcadd(
$report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']]['sum'],
$report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tagId]['sum'] = bcadd(
$report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tagId]['sum'],
$journal['amount']
);
}
@@ -114,6 +117,7 @@ class TagController extends Controller
/** @var array $tag */
foreach ($currency['tags'] as $tag) {
$tagId = $tag['id'];
foreach ($tag['transaction_journals'] as $journal) {
$destinationId = $journal['destination_account_id'];
$report[$destinationId]['currencies'][$currencyId]
@@ -124,18 +128,18 @@ class TagController extends Controller
'currency_decimal_places' => $currency['currency_decimal_places'],
'tags' => [],
];
$report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']]
$report[$destinationId]['currencies'][$currencyId]['tags'][$tagId]
??= [
'spent' => '0',
'earned' => '0',
'sum' => '0',
];
$report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']]['earned'] = bcadd(
$report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']]['earned'],
$report[$destinationId]['currencies'][$currencyId]['tags'][$tagId]['earned'] = bcadd(
$report[$destinationId]['currencies'][$currencyId]['tags'][$tagId]['earned'],
$journal['amount']
);
$report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']]['sum'] = bcadd(
$report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']]['sum'],
$report[$destinationId]['currencies'][$currencyId]['tags'][$tagId]['sum'] = bcadd(
$report[$destinationId]['currencies'][$currencyId]['tags'][$tagId]['sum'],
$journal['amount']
);
}
@@ -383,27 +387,23 @@ class TagController extends Controller
/** @var array $tag */
foreach ($currency['tags'] as $tag) {
$tagId = $tag['id'];
if(!array_key_exists($tagId, $report)) {
continue;
}
foreach ($tag['transaction_journals'] as $journal) {
// add currency info to report array:
$report[$tagId]['currencies'][$currencyId] ??= [
'spent' => '0',
'earned' => '0',
$tagId => $tagId,
'sum' => '0',
'currency_id' => $currency['currency_id'],
'currency_symbol' => $currency['currency_symbol'],
'currency_name' => $currency['currency_name'],
'currency_decimal_places' => $currency['currency_decimal_places'],
];
$report[$tagId]['currencies'][$currencyId]['spent'] = bcadd(
$report[$tagId]['currencies'][$currencyId]['spent'],
$journal['amount']
);
$report[$tagId]['currencies'][$currencyId]['sum'] = bcadd(
$report[$tagId]['currencies'][$currencyId]['sum'],
$journal['amount']
);
$report[$tagId]['currencies'][$currencyId]['spent'] = bcadd($report[$tagId]['currencies'][$currencyId]['spent'], $journal['amount']);
$report[$tagId]['currencies'][$currencyId]['sum'] = bcadd($report[$tagId]['currencies'][$currencyId]['sum'], $journal['amount']);
$sums[$currencyId]['spent_sum'] = bcadd($sums[$currencyId]['spent_sum'], $journal['amount']);
$sums[$currencyId]['total_sum'] = bcadd($sums[$currencyId]['total_sum'], $journal['amount']);
}
@@ -425,27 +425,23 @@ class TagController extends Controller
/** @var array $tag */
foreach ($currency['tags'] as $tag) {
$tagId = $tag['id'];
if(!array_key_exists($tagId, $report)) {
continue;
}
foreach ($tag['transaction_journals'] as $journal) {
// add currency info to report array:
$report[$tagId]['currencies'][$currencyId] ??= [
'spent' => '0',
'earned' => '0',
'sum' => '0',
$tagId => $tagId,
'currency_id' => $currency['currency_id'],
'currency_symbol' => $currency['currency_symbol'],
'currency_name' => $currency['currency_name'],
'currency_decimal_places' => $currency['currency_decimal_places'],
];
$report[$tagId]['currencies'][$currencyId]['earned'] = bcadd(
$report[$tagId]['currencies'][$currencyId]['earned'],
$journal['amount']
);
$report[$tagId]['currencies'][$currencyId]['sum'] = bcadd(
$report[$tagId]['currencies'][$currencyId]['sum'],
$journal['amount']
);
$report[$tagId]['currencies'][$currencyId]['earned'] = bcadd($report[$tagId]['currencies'][$currencyId]['earned'], $journal['amount']);
$report[$tagId]['currencies'][$currencyId]['sum'] = bcadd($report[$tagId]['currencies'][$currencyId]['sum'], $journal['amount']);
$sums[$currencyId]['earned_sum'] = bcadd($sums[$currencyId]['earned_sum'], $journal['amount']);
$sums[$currencyId]['total_sum'] = bcadd($sums[$currencyId]['total_sum'], $journal['amount']);
}

View File

@@ -34,6 +34,7 @@ use FireflyIII\Support\Http\Controllers\PeriodOverview;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
/**
@@ -304,6 +305,7 @@ class TagController extends Controller
$this->attachmentsHelper->saveAttachmentsForModel($result, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
}
@@ -338,6 +340,7 @@ class TagController extends Controller
$this->attachmentsHelper->saveAttachmentsForModel($tag, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__));
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
}

View File

@@ -26,7 +26,9 @@ namespace FireflyIII\Http\Controllers\Webhooks;
use FireflyIII\Http\Controllers\Controller;
use Illuminate\Contracts\View\Factory;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class CreateController
@@ -57,6 +59,12 @@ class CreateController extends Controller
*/
public function index()
{
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info('User visits webhook create page, but webhooks are DISABLED.');
throw new NotFoundHttpException('Webhooks are not enabled.');
}
Log::channel('audit')->info('User visits webhook create page.');
$previousUrl = $this->rememberPreviousUrl('webhooks.create.url');
return view('webhooks.create', compact('previousUrl'));

View File

@@ -28,6 +28,8 @@ use FireflyIII\Models\Webhook;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class DeleteController
@@ -61,6 +63,12 @@ class DeleteController extends Controller
*/
public function index(Webhook $webhook)
{
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info('User visits webhook delete page, but webhooks are DISABLED.');
throw new NotFoundHttpException('Webhooks are not enabled.');
}
Log::channel('audit')->info('User visits webhook delete page.');
$subTitle = (string)trans('firefly.delete_webhook', ['title' => $webhook->title]);
$this->rememberPreviousUrl('webhooks.delete.url');

View File

@@ -28,6 +28,8 @@ use FireflyIII\Models\Webhook;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class EditController
@@ -60,8 +62,14 @@ class EditController extends Controller
*/
public function index(Webhook $webhook)
{
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info('User visits webhook edit page, but webhooks are DISABLED.');
throw new NotFoundHttpException('Webhooks are not enabled.');
}
Log::channel('audit')->info('User visits webhook edit page.');
$subTitle = (string)trans('firefly.edit_webhook', ['title' => $webhook->title]);
$this->rememberPreviousUrl('webhooks.delete.url');
$this->rememberPreviousUrl('webhooks.edit.url');
return view('webhooks.edit', compact('webhook', 'subTitle'));
}

View File

@@ -26,7 +26,9 @@ namespace FireflyIII\Http\Controllers\Webhooks;
use FireflyIII\Http\Controllers\Controller;
use Illuminate\Contracts\View\Factory;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class IndexController
@@ -53,6 +55,13 @@ class IndexController extends Controller
*/
public function index()
{
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info('User visits webhook index page, but webhooks are DISABLED.');
throw new NotFoundHttpException('Webhooks are not enabled.');
}
Log::channel('audit')->info('User visits webhook index page.');
return view('webhooks.index');
}
}

View File

@@ -28,6 +28,8 @@ use FireflyIII\Models\Webhook;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class ShowController
@@ -60,6 +62,12 @@ class ShowController extends Controller
*/
public function index(Webhook $webhook)
{
if(false === config('firefly.allow_webhooks')) {
Log::channel('audit')->info(sprintf('User visits webhook #%d page, but webhooks are DISABLED.', $webhook->id));
throw new NotFoundHttpException('Webhooks are not enabled.');
}
Log::channel('audit')->info(sprintf('User visits webhook #%d page.', $webhook->id));
$subTitle = (string)trans('firefly.show_webhook', ['title' => $webhook->title]);
return view('webhooks.show', compact('webhook', 'subTitle'));

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Http\Requests;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\Account;
use FireflyIII\Models\Location;
use FireflyIII\Rules\IsValidAmount;
use FireflyIII\Rules\UniqueIban;
use FireflyIII\Support\Request\AppendsLocationData;
use FireflyIII\Support\Request\ChecksLogin;
@@ -101,11 +102,11 @@ class AccountFormRequest extends FormRequest
$ccPaymentTypes = implode(',', array_keys(config('firefly.ccTypes')));
$rules = [
'name' => 'required|max:1024|min:1|uniqueAccountForUser',
'opening_balance' => 'numeric|nullable|max:1000000000',
'opening_balance' => ['nullable', new IsValidAmount()],
'opening_balance_date' => 'date|required_with:opening_balance|nullable',
'iban' => ['iban', 'nullable', new UniqueIban(null, $this->convertString('objectType'))],
'BIC' => 'bic|nullable',
'virtual_balance' => 'numeric|nullable|max:1000000000',
'virtual_balance' => ['nullable', new IsValidAmount()],
'currency_id' => 'exists:transaction_currencies,id',
'account_number' => 'between:1,255|uniqueAccountNumberForUser|nullable',
'account_role' => 'in:'.$accountRoles,
@@ -115,6 +116,7 @@ class AccountFormRequest extends FormRequest
'amount_currency_id_virtual_balance' => 'exists:transaction_currencies,id',
'what' => 'in:'.$types,
'interest_period' => 'in:daily,monthly,yearly',
'notes' => 'between:1,65536|nullable',
];
$rules = Location::requestRules($rules);

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
@@ -64,10 +65,11 @@ class BillStoreRequest extends FormRequest
{
return [
'name' => 'required|between:1,255|uniqueObjectForUser:bills,name',
'amount_min' => 'required|numeric|gt:0|max:1000000000',
'amount_max' => 'required|numeric|gt:0|max:1000000000',
'amount_min' => ['required', new IsValidPositiveAmount()],
'amount_max' => ['required', new IsValidPositiveAmount()],
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
'date' => 'required|date',
'notes' => 'between:1,65536|nullable',
'bill_end_date' => 'nullable|date',
'extension_date' => 'nullable|date',
'repeat_freq' => sprintf('required|in:%s', implode(',', config('firefly.bill_periods'))),

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Models\Bill;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
@@ -68,8 +69,8 @@ class BillUpdateRequest extends FormRequest
return [
'name' => sprintf('required|between:1,255|uniqueObjectForUser:bills,name,%d', $bill->id),
'amount_min' => 'required|numeric|gt:0|max:1000000000',
'amount_max' => 'required|numeric|gt:0|max:1000000000',
'amount_min' => ['required', new IsValidPositiveAmount()],
'amount_max' => ['required', new IsValidPositiveAmount()],
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
'date' => 'required|date',
'bill_end_date' => 'nullable|date',

View File

@@ -23,10 +23,12 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\Validation\AutoBudget\ValidatesAutoBudgetRequest;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Validator;
/**
@@ -63,8 +65,9 @@ class BudgetFormStoreRequest extends FormRequest
'active' => 'numeric|between:0,1',
'auto_budget_type' => 'numeric|integer|gte:0|lte:3',
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
'auto_budget_amount' => 'min:0|max:1000000000|required_if:auto_budget_type,1|required_if:auto_budget_type,2',
'auto_budget_amount' => ['required_if:auto_budget_type,1', 'required_if:auto_budget_type,2', new IsValidPositiveAmount()],
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
'notes' => 'between:1,65536|nullable',
];
}
@@ -73,6 +76,10 @@ class BudgetFormStoreRequest extends FormRequest
*/
public function withValidator(Validator $validator): void
{
if($validator->fails()) {
Log::channel('audit')->error('Validation errors for budget', $validator->errors()->toArray());
}
$validator->after(
function (Validator $validator): void {
// validate all account info

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Models\Budget;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\Validation\AutoBudget\ValidatesAutoBudgetRequest;
@@ -73,8 +74,9 @@ class BudgetFormUpdateRequest extends FormRequest
'active' => 'numeric|between:0,1',
'auto_budget_type' => 'numeric|integer|gte:0|lte:31',
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
'auto_budget_amount' => 'min:0|max:1000000000|required_if:auto_budget_type,1|required_if:auto_budget_type,2|numeric',
'auto_budget_amount' => ['required_if:auto_budget_type,1', 'required_if:auto_budget_type,2|numeric', new IsValidPositiveAmount()],
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
'notes' => 'between:1,65536|nullable',
];
}

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use Illuminate\Foundation\Http\FormRequest;
@@ -40,7 +41,7 @@ class BudgetIncomeRequest extends FormRequest
{
// fixed
return [
'amount' => 'numeric|required|min:0|max:1000000000',
'amount' => ['required', new IsValidPositiveAmount()],
'start' => 'required|date|before:end',
'end' => 'required|date|after:start',
];

View File

@@ -64,6 +64,7 @@ class CategoryFormRequest extends FormRequest
// fixed
return [
'name' => $nameRule,
'notes' => 'between:1,65536|nullable',
];
}
}

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Rules\IsValidAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
@@ -43,9 +44,9 @@ class NewUserFormRequest extends FormRequest
// fixed
return [
'bank_name' => 'required|between:1,200',
'bank_balance' => 'required|numeric|max:1000000000',
'savings_balance' => 'numeric|max:1000000000',
'credit_card_limit' => 'numeric|max:1000000000',
'bank_balance' => ['required', new IsValidAmount()],
'savings_balance' => ['nullable', new IsValidAmount()],
'credit_card_limit' => ['nullable', new IsValidAmount()],
'amount_currency_id_bank_balance' => 'exists:transaction_currencies,id',
'amount_currency_id_savings_balance' => 'exists:transaction_currencies,id',
'amount_currency_id_credit_card_limit' => 'exists:transaction_currencies,id',

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
@@ -59,11 +60,12 @@ class PiggyBankStoreRequest extends FormRequest
return [
'name' => 'required|between:1,255|uniquePiggyBankForUser',
'account_id' => 'required|belongsToUser:accounts',
'targetamount' => 'nullable|numeric|max:1000000000',
'targetamount' => ['nullable', new IsValidPositiveAmount()],
'startdate' => 'date',
'targetdate' => 'date|nullable',
'order' => 'integer|min:1',
'object_group' => 'min:0|max:255',
'notes' => 'between:1,65536|nullable',
];
}
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
@@ -63,11 +64,12 @@ class PiggyBankUpdateRequest extends FormRequest
return [
'name' => sprintf('required|between:1,255|uniquePiggyBankForUser:%d', $piggy->id),
'account_id' => 'required|belongsToUser:accounts',
'targetamount' => 'nullable|numeric|max:1000000000',
'targetamount' => ['nullable', new IsValidPositiveAmount()],
'startdate' => 'date',
'targetdate' => 'date|nullable',
'order' => 'integer|max:65536|min:1',
'object_group' => 'min:0|max:255',
'notes' => 'between:1,65536|nullable',
];
}
}

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Rules\IsValidAmount;
use FireflyIII\Rules\ValidJournals;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
@@ -67,9 +68,9 @@ class ReconciliationStoreRequest extends FormRequest
return [
'start' => 'required|date',
'end' => 'required|date',
'startBalance' => 'numeric|max:1000000000',
'endBalance' => 'numeric|max:1000000000',
'difference' => 'required|numeric|max:1000000000',
'startBalance' => ['nullable', new IsValidAmount()],
'endBalance' => ['nullable', new IsValidAmount()],
'difference' => ['required', new IsValidAmount()],
'journals' => [new ValidJournals()],
'reconcile' => 'required|in:create,nothing',
];

View File

@@ -27,6 +27,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\CategoryFactory;
use FireflyIII\Models\Recurrence;
use FireflyIII\Models\TransactionType;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Rules\ValidRecurrenceRepetitionType;
use FireflyIII\Rules\ValidRecurrenceRepetitionValue;
use FireflyIII\Support\Request\ChecksLogin;
@@ -161,7 +162,7 @@ class RecurrenceFormRequest extends FormRequest
'first_date' => 'required|date|after:'.$today->format('Y-m-d'),
'repetition_type' => ['required', new ValidRecurrenceRepetitionValue(), new ValidRecurrenceRepetitionType(), 'between:1,20'],
'skip' => 'required|numeric|integer|gte:0|lte:31',
'notes' => 'between:1,65536|nullable',
// optional for recurrence:
'recurring_description' => 'between:0,65000',
'active' => 'numeric|between:0,1',
@@ -171,7 +172,7 @@ class RecurrenceFormRequest extends FormRequest
'transaction_description' => 'required|between:1,255',
'transaction_type' => 'required|in:withdrawal,deposit,transfer',
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
'amount' => 'numeric|required|gt:0|max:1000000000',
'amount' => ['required', new IsValidPositiveAmount()],
// mandatory account info:
'source_id' => 'numeric|belongsToUser:accounts,id|nullable',
'source_name' => 'between:1,255|nullable',
@@ -179,7 +180,7 @@ class RecurrenceFormRequest extends FormRequest
'destination_name' => 'between:1,255|nullable',
// foreign amount data:
'foreign_amount' => 'nullable|gt:0|max:1000000000',
'foreign_amount' => ['nullable', new IsValidPositiveAmount()],
// optional fields:
'budget_id' => 'mustExist:budgets,id|belongsToUser:budgets,id|nullable',

View File

@@ -29,6 +29,8 @@ use FireflyIII\Repositories\Category\NoCategoryRepository;
use FireflyIII\Repositories\Category\NoCategoryRepositoryInterface;
use FireflyIII\Repositories\Category\OperationsRepository;
use FireflyIII\Repositories\Category\OperationsRepositoryInterface;
use FireflyIII\Repositories\UserGroups\Category\CategoryRepository as UserGroupCategoryRepository;
use FireflyIII\Repositories\UserGroups\Category\CategoryRepositoryInterface as UserGroupCategoryRepositoryInterface;
use Illuminate\Foundation\Application;
use Illuminate\Support\ServiceProvider;
@@ -61,6 +63,20 @@ class CategoryServiceProvider extends ServiceProvider
}
);
// phpstan does not understand reference to 'auth'.
$this->app->bind(
UserGroupCategoryRepositoryInterface::class,
static function (Application $app) {
/** @var UserGroupCategoryRepository $repository */
$repository = app(UserGroupCategoryRepository::class);
if ($app->auth->check()) { // @phpstan-ignore-line
$repository->setUser(auth()->user());
}
return $repository;
}
);
$this->app->bind(
OperationsRepositoryInterface::class,
static function (Application $app) {

View File

@@ -27,6 +27,8 @@ use FireflyIII\Repositories\Tag\OperationsRepository;
use FireflyIII\Repositories\Tag\OperationsRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepository;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Repositories\UserGroups\Tag\TagRepository as UserGroupTagRepository;
use FireflyIII\Repositories\UserGroups\Tag\TagRepositoryInterface as UserGroupTagRepositoryInterface;
use Illuminate\Foundation\Application;
use Illuminate\Support\ServiceProvider;
@@ -59,6 +61,20 @@ class TagServiceProvider extends ServiceProvider
}
);
$this->app->bind(
UserGroupTagRepositoryInterface::class,
static function (Application $app) {
/** @var UserGroupTagRepository $repository */
$repository = app(UserGroupTagRepository::class);
if ($app->auth->check()) { // @phpstan-ignore-line (phpstan does not understand the reference to auth)
$repository->setUser(auth()->user());
}
return $repository;
}
);
$this->app->bind(
OperationsRepositoryInterface::class,
static function (Application $app) {

View File

@@ -39,6 +39,8 @@ use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
use FireflyIII\Services\Internal\Destroy\BudgetDestroyService;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\User;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\QueryException;
@@ -86,6 +88,8 @@ class BudgetRepository implements BudgetRepositoryInterface
$limitRepository = app(BudgetLimitRepository::class);
$limitRepository->setUser($this->user);
$budgets = $this->getActiveBudgets();
$defaultCurrency = app('amount')->getDefaultCurrency();
$converter = new ExchangeRateConverter();
/** @var Budget $budget */
foreach ($budgets as $budget) {
@@ -96,24 +100,34 @@ class BudgetRepository implements BudgetRepositoryInterface
foreach ($limits as $limit) {
app('log')->debug(sprintf('Budget limit #%d', $limit->id));
$currency = $limit->transactionCurrency;
$return[$currency->id] ??= [
'id' => (string) $currency->id,
'name' => $currency->name,
'symbol' => $currency->symbol,
'code' => $currency->code,
'decimal_places' => $currency->decimal_places,
$rate = $converter->getCurrencyRate($currency, $defaultCurrency, $end);
$currencyCode = $currency->code;
$return[$currencyCode] ??= [
'currency_id' => (string) $currency->id,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,
'currency_decimal_places' => $currency->decimal_places,
'native_currency_id' => (string) $defaultCurrency->id,
'native_currency_name' => $defaultCurrency->name,
'native_currency_symbol' => $defaultCurrency->symbol,
'native_currency_code' => $defaultCurrency->code,
'native_currency_decimal_places' => $defaultCurrency->decimal_places,
'sum' => '0',
'native_sum' => '0',
];
// same period
if ($limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end)) {
$return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $limit->amount);
$return[$currencyCode]['sum'] = bcadd($return[$currencyCode]['sum'], $limit->amount);
$return[$currencyCode]['native_sum'] = bcmul($rate, $return[$currencyCode]['sum']);
app('log')->debug(sprintf('Add full amount [1]: %s', $limit->amount));
continue;
}
// limit is inside of date range
if ($start->lte($limit->start_date) && $end->gte($limit->end_date)) {
$return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $limit->amount);
$return[$currencyCode]['sum'] = bcadd($return[$currencyCode]['sum'], $limit->amount);
$return[$currencyCode]['native_sum'] = bcmul($rate, $return[$currencyCode]['sum']);
app('log')->debug(sprintf('Add full amount [2]: %s', $limit->amount));
continue;
@@ -121,7 +135,8 @@ class BudgetRepository implements BudgetRepositoryInterface
$total = $limit->start_date->diffInDays($limit->end_date) + 1; // include the day itself.
$days = $this->daysInOverlap($limit, $start, $end);
$amount = bcmul(bcdiv($limit->amount, (string) $total), (string) $days);
$return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $amount);
$return[$currencyCode]['sum'] = bcadd($return[$currencyCode]['sum'], $amount);
$return[$currencyCode]['native_sum'] = bcmul($rate, $return[$currencyCode]['sum']);
app('log')->debug(
sprintf(
'Amount per day: %s (%s over %d days). Total amount for %d days: %s',

View File

@@ -36,8 +36,7 @@ use Illuminate\Support\Collection;
*/
class OperationsRepository implements OperationsRepositoryInterface
{
/** @var User */
private $user;
private User $user;
/**
* This method returns a list of all the withdrawal transaction journals (as arrays) set in that period
@@ -49,16 +48,19 @@ class OperationsRepository implements OperationsRepositoryInterface
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]);
$tagIds = [];
if (null !== $accounts && $accounts->count() > 0) {
$collector->setAccounts($accounts);
}
if (null !== $tags && $tags->count() > 0) {
$collector->setTags($tags);
$tagIds = $tags->pluck('id')->toArray();
}
if (null === $tags || 0 === $tags->count()) {
$collector->setTags($this->getTags());
$tagIds = $this->getTags()->pluck('id')->toArray();
}
$collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation()->withTagInformation();
$collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation();
$journals = $collector->getExtractedJournals();
$array = [];
$listedJournals = [];
@@ -78,7 +80,11 @@ class OperationsRepository implements OperationsRepositoryInterface
$tagId = (int)$tag['id'];
$tagName = (string)$tag['name'];
$journalId = (int)$journal['transaction_journal_id'];
if(!in_array($tagId, $tagIds, true)) {
continue;
}
// TODO not sure what this check does.
if (in_array($journalId, $listedJournals, true)) {
continue;
}
@@ -124,14 +130,17 @@ class OperationsRepository implements OperationsRepositoryInterface
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]);
$tagIds = [];
if (null !== $accounts && $accounts->count() > 0) {
$collector->setAccounts($accounts);
}
if (null !== $tags && $tags->count() > 0) {
$collector->setTags($tags);
$tagIds = $tags->pluck('id')->toArray();
}
if (null === $tags || 0 === $tags->count()) {
$collector->setTags($this->getTags());
$tagIds = $this->getTags()->pluck('id')->toArray();
}
$collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation()->withTagInformation();
$journals = $collector->getExtractedJournals();
@@ -155,6 +164,10 @@ class OperationsRepository implements OperationsRepositoryInterface
$tagName = (string)$tag['name'];
$journalId = (int)$journal['transaction_journal_id'];
if(!in_array($tagId, $tagIds, true)) {
continue;
}
if (in_array($journalId, $listedJournals, true)) {
continue;
}
@@ -205,6 +218,7 @@ class OperationsRepository implements OperationsRepositoryInterface
private function getTags(): Collection
{
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
return $repository->get();

View File

@@ -246,6 +246,17 @@ class TagRepository implements TagRepositoryInterface
/** @var array $journal */
foreach ($journals as $journal) {
$found = false;
/** @var array $localTag */
foreach ($journal['tags'] as $localTag) {
if ($localTag['id'] === $tag->id) {
$found = true;
}
}
if (false === $found) {
continue;
}
$currencyId = (int) $journal['currency_id'];
$sums[$currencyId] ??= [
'currency_id' => $currencyId,

View File

@@ -32,6 +32,7 @@ use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* Class BillRepository
@@ -66,6 +67,7 @@ class BillRepository implements BillRepositoryInterface
public function sumPaidInRange(Carbon $start, Carbon $end): array
{
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$bills = $this->getActiveBills();
$default = app('amount')->getDefaultCurrency();
$return = [];
@@ -84,11 +86,11 @@ class BillRepository implements BillRepositoryInterface
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,
'currency_decimal_places' => $currency->decimal_places,
'native_id' => (string)$default->id,
'native_name' => $default->name,
'native_symbol' => $default->symbol,
'native_code' => $default->code,
'native_decimal_places' => $default->decimal_places,
'native_currency_id' => (string)$default->id,
'native_currency_name' => $default->name,
'native_currency_symbol' => $default->symbol,
'native_currency_code' => $default->code,
'native_currency_decimal_places' => $default->decimal_places,
'sum' => '0',
'native_sum' => '0',
];
@@ -134,6 +136,7 @@ class BillRepository implements BillRepositoryInterface
public function sumUnpaidInRange(Carbon $start, Carbon $end): array
{
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$bills = $this->getActiveBills();
$return = [];
$default = app('amount')->getDefaultCurrency();
@@ -156,11 +159,11 @@ class BillRepository implements BillRepositoryInterface
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,
'currency_decimal_places' => $currency->decimal_places,
'native_id' => (string)$default->id,
'native_name' => $default->name,
'native_symbol' => $default->symbol,
'native_code' => $default->code,
'native_decimal_places' => $default->decimal_places,
'native_currency_id' => (string)$default->id,
'native_currency_name' => $default->name,
'native_currency_symbol' => $default->symbol,
'native_currency_code' => $default->code,
'native_currency_decimal_places' => $default->decimal_places,
'sum' => '0',
'native_sum' => '0',
];

View File

@@ -28,6 +28,7 @@ use Carbon\Carbon;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Support\Facades\Log;
/**
* Class AvailableBudgetRepository
@@ -38,6 +39,7 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
public function getAvailableBudgetWithCurrency(Carbon $start, Carbon $end): array
{
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$return = [];
$converter = new ExchangeRateConverter();
$default = app('amount')->getDefaultCurrency();
@@ -55,11 +57,11 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
'currency_symbol' => $availableBudget->transactionCurrency->symbol,
'currency_name' => $availableBudget->transactionCurrency->name,
'currency_decimal_places' => $availableBudget->transactionCurrency->decimal_places,
'native_id' => $default->id,
'native_code' => $default->code,
'native_symbol' => $default->symbol,
'native_name' => $default->name,
'native_decimal_places' => $default->decimal_places,
'native_currency_id' => $default->id,
'native_currency_code' => $default->code,
'native_currency_symbol' => $default->symbol,
'native_currency_name' => $default->name,
'native_currency_decimal_places' => $default->decimal_places,
'amount' => '0',
'native_amount' => '0',
];

View File

@@ -0,0 +1,42 @@
<?php
/*
* CategoryRepository.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\Repositories\UserGroups\Category;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Support\Collection;
class CategoryRepository implements CategoryRepositoryInterface
{
use UserGroupTrait;
public function searchCategory(string $query, int $limit): Collection
{
$search = $this->userGroup->categories();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%%%s%%', $query));
}
return $search->take($limit)->get();
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* CategoryRepositoryInterface.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\Repositories\UserGroups\Category;
use Illuminate\Support\Collection;
interface CategoryRepositoryInterface
{
/**
* Search for a category using wild cards. Uses the database, so case sensitive.
*/
public function searchCategory(string $query, int $limit): Collection;
}

View File

@@ -0,0 +1,45 @@
<?php
/*
* TagRepository.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\Repositories\UserGroups\Tag;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Support\Collection;
/**
* Class TagRepository
*/
class TagRepository implements TagRepositoryInterface
{
use UserGroupTrait;
public function searchTag(string $query, int $limit): Collection
{
$search = $this->user->tags();
if ('' !== $query) {
$search->where('tag', 'LIKE', sprintf('%%%s%%', $query));
}
return $search->take($limit)->get(['tags.*']);
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* TagRepositoryInterface.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\Repositories\UserGroups\Tag;
use Illuminate\Support\Collection;
interface TagRepositoryInterface
{
/**
* Find one or more tags based on the query.
*/
public function searchTag(string $query, int $limit): Collection;
}

View File

@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Rules;
use FireflyIII\Support\Validation\ValidatesAmountsTrait;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Log;
class IsValidAmount implements ValidationRule
{
use ValidatesAmountsTrait;
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function validate(string $attribute, mixed $value, \Closure $fail): void
{
$value = (string)$value;
// must not be empty:
if($this->emptyString($value)) {
$fail('validation.filled')->translate();
Log::info(sprintf('IsValidAmount: "%s" cannot be empty.', $value));
return;
}
// must be a number:
if(!$this->isValidNumber($value)) {
$fail('validation.numeric')->translate();
Log::info(sprintf('IsValidAmount: "%s" is not a number.', $value));
return;
}
// must not be scientific notation:
if($this->scientificNumber($value)) {
$fail('validation.scientific_notation')->translate();
Log::info(sprintf('IsValidAmount: "%s" cannot be in the scientific notation.', $value));
return;
}
// must be more than minus a lots:
if($this->lessThanLots($value)) {
$amount = bcmul('-1', self::BIG_AMOUNT);
$fail('validation.gte.numeric')->translate(['value' => $amount]);
Log::info(sprintf('IsValidAmount: "%s" must be more than %s.', $value, $amount));
return;
}
// must be less than 100 million and 1709:
if($this->moreThanLots($value)) {
Log::info(sprintf('IsValidPositiveAmount: "%s" must be more than %s.', $value, self::BIG_AMOUNT));
$fail('validation.lte.numeric')->translate(['value' => self::BIG_AMOUNT]);
}
Log::info(sprintf('IsValidAmount: "%s" is a valid positive amount.', $value));
}
}

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Rules;
use FireflyIII\Support\Validation\ValidatesAmountsTrait;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Log;
class IsValidPositiveAmount implements ValidationRule
{
use ValidatesAmountsTrait;
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function validate(string $attribute, mixed $value, \Closure $fail): void
{
$value = (string)$value;
// must not be empty:
if($this->emptyString($value)) {
$fail('validation.filled')->translate();
Log::info(sprintf('IsValidPositiveAmount: "%s" cannot be empty.', $value));
return;
}
// must be a number:
if(!$this->isValidNumber($value)) {
$fail('validation.numeric')->translate();
Log::info(sprintf('IsValidPositiveAmount: "%s" is not a number.', $value));
return;
}
// must not be scientific notation:
if($this->scientificNumber($value)) {
$fail('validation.scientific_notation')->translate();
Log::info(sprintf('IsValidPositiveAmount: "%s" cannot be in the scientific notation.', $value));
return;
}
// must be more than zero:
if($this->lessOrEqualToZero($value)) {
$fail('validation.more_than_zero')->translate();
Log::info(sprintf('IsValidPositiveAmount: "%s" must be more than zero.', $value));
return;
}
// must be less than 100 million and 1709:
if($this->moreThanLots($value)) {
Log::info(sprintf('IsValidPositiveAmount: "%s" must be less than %s.', $value, self::BIG_AMOUNT));
$fail('validation.lte.numeric')->translate(['value' => self::BIG_AMOUNT]);
}
Log::info(sprintf('IsValidPositiveAmount: "%s" is a valid positive amount.', $value));
}
}

View File

@@ -29,6 +29,7 @@ use FireflyIII\Models\WebhookAttempt;
use FireflyIII\Models\WebhookMessage;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;
/**
@@ -45,7 +46,7 @@ class StandardWebhookSender implements WebhookSenderInterface
}
/**
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws GuzzleException
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/

View File

@@ -27,6 +27,7 @@ use FireflyIII\Models\Tag;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Routing\Route;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
@@ -63,9 +64,13 @@ class TagList implements BinderInterface
$collection = $allTags->filter(
static function (Tag $tag) use ($list) {
if (in_array(strtolower($tag->tag), $list, true)) {
Log::debug(sprintf('TagList: (string) found tag #%d ("%s") in list.', $tag->id, $tag->tag));
return true;
}
if (in_array((string)$tag->id, $list, true)) {
Log::debug(sprintf('TagList: (id) found tag #%d ("%s") in list.', $tag->id, $tag->tag));
return true;
}

View File

@@ -28,6 +28,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* Class AccountBalanceGrouped
@@ -61,10 +62,10 @@ class AccountBalanceGrouped
'currency_symbol' => $currency['currency_symbol'],
'currency_code' => $currency['currency_code'],
'currency_decimal_places' => $currency['currency_decimal_places'],
'native_id' => (string)$currency['native_id'],
'native_symbol' => $currency['native_symbol'],
'native_code' => $currency['native_code'],
'native_decimal_places' => $currency['native_decimal_places'],
'native_currency_id' => (string) $currency['native_currency_id'],
'native_currency_symbol' => $currency['native_currency_symbol'],
'native_currency_code' => $currency['native_currency_code'],
'native_currency_decimal_places' => $currency['native_currency_decimal_places'],
'start' => $this->start->toAtomString(),
'end' => $this->end->toAtomString(),
'period' => $this->preferredRange,
@@ -77,10 +78,10 @@ class AccountBalanceGrouped
'currency_symbol' => $currency['currency_symbol'],
'currency_code' => $currency['currency_code'],
'currency_decimal_places' => $currency['currency_decimal_places'],
'native_id' => (string)$currency['native_id'],
'native_symbol' => $currency['native_symbol'],
'native_code' => $currency['native_code'],
'native_decimal_places' => $currency['native_decimal_places'],
'native_currency_id' => (string) $currency['native_currency_id'],
'native_currency_symbol' => $currency['native_currency_symbol'],
'native_currency_code' => $currency['native_currency_code'],
'native_currency_decimal_places' => $currency['native_currency_decimal_places'],
'start' => $this->start->toAtomString(),
'end' => $this->end->toAtomString(),
'period' => $this->preferredRange,
@@ -97,8 +98,8 @@ class AccountBalanceGrouped
$expense['entries'][$label] = app('steam')->bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']);
// converted entries
$income['native_entries'][$label] = app('steam')->bcround($currency[$key]['native_earned'] ?? '0', $currency['native_decimal_places']);
$expense['native_entries'][$label] = app('steam')->bcround($currency[$key]['native_spent'] ?? '0', $currency['native_decimal_places']);
$income['native_entries'][$label] = app('steam')->bcround($currency[$key]['native_earned'] ?? '0', $currency['native_currency_decimal_places']);
$expense['native_entries'][$label] = app('steam')->bcround($currency[$key]['native_spent'] ?? '0', $currency['native_currency_decimal_places']);
// next loop
$currentStart = app('navigation')->addPeriod($currentStart, $this->preferredRange, 0);
@@ -117,6 +118,7 @@ class AccountBalanceGrouped
*/
public function groupByCurrencyAndPeriod(): void
{
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$converter = new ExchangeRateConverter();
// loop. group by currency and by period.
@@ -136,10 +138,10 @@ class AccountBalanceGrouped
'currency_name' => $journal['currency_name'],
'currency_decimal_places' => $journal['currency_decimal_places'],
// native currency info (could be the same)
'native_id' => (string)$this->default->id,
'native_code' => $this->default->code,
'native_symbol' => $this->default->symbol,
'native_decimal_places' => $this->default->decimal_places,
'native_currency_id' => (string) $this->default->id,
'native_currency_code' => $this->default->code,
'native_currency_symbol' => $this->default->symbol,
'native_currency_decimal_places' => $this->default->decimal_places,
];
// set the array (in monetary info) with spent/earned in this $period, if it does not exist.
@@ -212,11 +214,11 @@ class AccountBalanceGrouped
'currency_code' => $default->code,
'currency_name' => $default->name,
'currency_decimal_places' => $default->decimal_places,
'native_id' => (string)$defaultCurrencyId,
'native_symbol' => $default->symbol,
'native_code' => $default->code,
'native_name' => $default->name,
'native_decimal_places' => $default->decimal_places,
'native_currency_id' => (string) $defaultCurrencyId,
'native_currency_symbol' => $default->symbol,
'native_currency_code' => $default->code,
'native_currency_name' => $default->name,
'native_currency_decimal_places' => $default->decimal_places,
];
}

View File

@@ -50,8 +50,8 @@ trait CleansChartData
if (array_key_exists('currency_id', $array)) {
$array['currency_id'] = (string)$array['currency_id'];
}
if (array_key_exists('native_id', $array)) {
$array['native_id'] = (string)$array['native_id'];
if (array_key_exists('native_currency_id', $array)) {
$array['native_currency_id'] = (string)$array['native_currency_id'];
}
if (!array_key_exists('start', $array)) {
throw new FireflyException(sprintf('Data-set "%s" is missing the "start"-variable.', $index));

Some files were not shown because too many files have changed in this diff Show More