Compare commits

...

78 Commits
5.4.4 ... 5.4.6

Author SHA1 Message Date
James Cole
bcb734a459 Merge branch 'release/5.4.6' into main 2020-11-07 17:40:29 +01:00
James Cole
8e9e0f71bf Some last minute updates 2020-11-07 17:39:06 +01:00
James Cole
647c9c5eb0 Fix #4023 2020-11-07 17:22:11 +01:00
James Cole
4fa92ed5f7 Code for #4023 2020-11-07 17:19:23 +01:00
James Cole
febcbac1de Expand changelog. 2020-11-07 14:32:38 +01:00
James Cole
c9763c3d05 Fix #4006 2020-11-07 14:31:06 +01:00
James Cole
fab6d5df8d Update meta files ahead of new release. 2020-11-07 14:30:59 +01:00
James Cole
5d872bead7 Update category code for #4015 2020-11-07 14:25:43 +01:00
James Cole
60a3cc1f72 Remove migration. 2020-11-07 14:11:41 +01:00
James Cole
413df5a005 Expand tests 2020-11-07 14:05:53 +01:00
James Cole
5b35612be0 Expand v2 views 2020-11-07 14:05:42 +01:00
James Cole
54441df562 Update version 2020-11-07 14:05:28 +01:00
James Cole
87f4b59cfe Rebuild frontend. 2020-11-07 14:05:20 +01:00
James Cole
4d0839cf9c New empty views for new layout 2020-11-07 07:23:17 +01:00
James Cole
56388e46f2 Merge pull request #4015 from weimdall/develop
Forward REMOTE_USER (remote_user_guard auth mode)
2020-11-06 16:58:51 +00:00
James Cole
6b20b7ecdb Can edit category notes 2020-11-05 06:25:56 +01:00
James Cole
ee82547eed Basic expand and collapse. #4031 2020-11-05 06:16:22 +01:00
James Cole
ebd9b30f55 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2020-11-04 19:06:18 +01:00
James Cole
5b70f70aa7 update packages. 2020-11-04 19:05:35 +01:00
James Cole
80ef6fcb04 Send messages to owner, not to user when user is demo. 2020-11-03 20:33:46 +01:00
Julien Cassagne
700db14de2 Fix : Forward REMOTE_USER
In remote_user_guard auth mode, fireflyiii wasn't receiving REMOTE_USER
This issue occurs with docker image and standalone (when using apache)
2020-11-03 01:19:50 -05:00
James Cole
433397cb3d Update composer JSON 2020-11-02 19:56:34 +01:00
James Cole
40b952c4e8 Merge pull request #4022 from firefly-iii/dependabot/composer/develop/vimeo/psalm-4.1.0
Bump vimeo/psalm from 3.18.2 to 4.1.0
2020-11-02 08:25:29 +01:00
James Cole
b46b01e03d Merge pull request #4025 from firefly-iii/dependabot/npm_and_yarn/frontend/develop/popperjs/core-2.5.4
Bump @popperjs/core from 2.5.3 to 2.5.4 in /frontend
2020-11-02 08:24:49 +01:00
James Cole
70b8a10e94 Merge pull request #4026 from firefly-iii/dependabot/npm_and_yarn/frontend/develop/sass-1.28.0
Bump sass from 1.27.0 to 1.28.0 in /frontend
2020-11-02 08:24:27 +01:00
dependabot[bot]
b40eaf7585 Bump sass from 1.27.0 to 1.28.0 in /frontend
Bumps [sass](https://github.com/sass/dart-sass) from 1.27.0 to 1.28.0.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.27.0...1.28.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-11-02 06:03:39 +00:00
dependabot[bot]
aeca645a37 Bump @popperjs/core from 2.5.3 to 2.5.4 in /frontend
Bumps [@popperjs/core](https://github.com/popperjs/popper-core) from 2.5.3 to 2.5.4.
- [Release notes](https://github.com/popperjs/popper-core/releases)
- [Commits](https://github.com/popperjs/popper-core/compare/v2.5.3...v2.5.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-11-02 06:02:12 +00:00
dependabot[bot]
713dbf47fb Bump vimeo/psalm from 3.18.2 to 4.1.0
Bumps [vimeo/psalm](https://github.com/vimeo/psalm) from 3.18.2 to 4.1.0.
- [Release notes](https://github.com/vimeo/psalm/releases)
- [Commits](https://github.com/vimeo/psalm/compare/3.18.2...4.1.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-11-02 05:28:11 +00:00
James Cole
d6c7ccf62d Remodel seeds and factories. 2020-11-02 06:20:49 +01:00
James Cole
52385ae980 Expand docs for #4015 2020-11-01 07:01:59 +01:00
James Cole
53da61429a Fix help text, courtesy of @weimdall 2020-11-01 06:58:19 +01:00
James Cole
c8a87833c6 Various code cleanup. 2020-10-31 08:00:44 +01:00
James Cole
55c8b9012c Remove unused methods. 2020-10-31 06:54:33 +01:00
James Cole
0104d46206 Update jobs table. 2020-10-31 06:47:36 +01:00
James Cole
b9963ce0bf Remove unused rule. 2020-10-31 06:47:29 +01:00
James Cole
09247cc30b Upgrade to laravel 8. 2020-10-30 17:34:51 +01:00
James Cole
b03b0c630b Fix missing method. 2020-10-30 17:13:29 +01:00
James Cole
ebd076e0ed Remove excessive debug things. 2020-10-30 12:07:26 +01:00
James Cole
a0f34666a3 Fix #4011 2020-10-29 17:20:58 +01:00
James Cole
1ed71ea742 Fix #4011 2020-10-29 14:42:24 +01:00
James Cole
b75c221626 Fix #4005 2020-10-29 06:42:14 +01:00
James Cole
3cf340e44c Merge tag '5.4.5' into develop
5.4.5
2020-10-28 20:26:31 +01:00
James Cole
e6c0bbf082 Merge branch 'release/5.4.5' into main 2020-10-28 20:26:28 +01:00
James Cole
86ca234625 Update meta files for new release. 2020-10-28 20:25:51 +01:00
James Cole
7bd86fe2b6 Update translations. 2020-10-28 20:22:30 +01:00
James Cole
23356b3884 Cleanup JS 2020-10-28 20:16:21 +01:00
James Cole
ca3d836c83 Add notes to category #4002 2020-10-28 06:32:37 +01:00
James Cole
3aa835a985 Fix #4003 2020-10-28 06:14:12 +01:00
James Cole
7b9f2b6ce5 Fix #4000 2020-10-27 19:04:19 +01:00
James Cole
31a9b03c1a Refactor and cleanup. 2020-10-27 16:26:10 +01:00
James Cole
141436aebb Exclude from code coverage. 2020-10-27 06:53:47 +01:00
James Cole
8d84bffc2d Simplify methods. 2020-10-27 06:53:33 +01:00
James Cole
0fb81a6112 Append sonarqube properties file 2020-10-27 06:41:26 +01:00
James Cole
6563a79483 Clean up code. 2020-10-27 06:40:53 +01:00
James Cole
372c6ac667 Various issues fixed (SonarQube) 2020-10-26 19:15:57 +01:00
James Cole
e4923a3c69 Move JS files 2020-10-26 19:15:35 +01:00
James Cole
54f3e60ae4 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2020-10-26 17:05:23 +01:00
James Cole
df6f65e0aa Sonarqube properties file. 2020-10-26 16:54:14 +01:00
James Cole
e83fdc58ff Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2020-10-26 16:52:34 +01:00
James Cole
7720482930 Fix bread crumbs 2020-10-26 16:51:49 +01:00
James Cole
c07fae19f9 Remove unused variable. 2020-10-26 16:50:05 +01:00
James Cole
aa3e467a3e Add message so exception isn't empty. 2020-10-26 16:22:20 +01:00
James Cole
c837692d72 Merge pull request #3997 from firefly-iii/dependabot/npm_and_yarn/frontend/develop/axios-0.21.0
Bump axios from 0.20.0 to 0.21.0 in /frontend
2020-10-26 05:58:45 +00:00
dependabot[bot]
61c8b79e30 Bump axios from 0.20.0 to 0.21.0 in /frontend
Bumps [axios](https://github.com/axios/axios) from 0.20.0 to 0.21.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/master/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.20.0...v0.21.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-26 05:52:19 +00:00
James Cole
d0a8e6eb5b Merge pull request #3996 from firefly-iii/dependabot/composer/develop/phpstan/phpstan-0.12.52
Bump phpstan/phpstan from 0.12.51 to 0.12.52
2020-10-26 05:50:05 +00:00
James Cole
e61236836b Merge pull request #3995 from firefly-iii/dependabot/npm_and_yarn/develop/uiv-1.1.2
Bump uiv from 1.1.1 to 1.1.2
2020-10-26 05:47:16 +00:00
James Cole
a235b60bef Merge pull request #3994 from firefly-iii/dependabot/npm_and_yarn/develop/axios-0.21.0
Bump axios from 0.20.0 to 0.21.0
2020-10-26 05:47:06 +00:00
James Cole
a7d15ef287 Temp fix for tes 2020-10-26 06:45:24 +01:00
James Cole
045cec4421 Fix #3992 2020-10-26 06:43:09 +01:00
dependabot[bot]
465e49476a Bump phpstan/phpstan from 0.12.51 to 0.12.52
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 0.12.51 to 0.12.52.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Commits](https://github.com/phpstan/phpstan/compare/0.12.51...0.12.52)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-26 05:24:43 +00:00
dependabot[bot]
ed6a331faa Bump uiv from 1.1.1 to 1.1.2
Bumps [uiv](https://github.com/uiv-lib/uiv) from 1.1.1 to 1.1.2.
- [Release notes](https://github.com/uiv-lib/uiv/releases)
- [Commits](https://github.com/uiv-lib/uiv/compare/v1.1.1...v1.1.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-26 05:19:05 +00:00
dependabot[bot]
f578e2c9e7 Bump axios from 0.20.0 to 0.21.0
Bumps [axios](https://github.com/axios/axios) from 0.20.0 to 0.21.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/master/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.20.0...v0.21.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-26 05:18:49 +00:00
James Cole
e0526508cb Fix #3853 2020-10-26 06:18:09 +01:00
James Cole
9151f44022 Fix #3991 2020-10-26 06:11:55 +01:00
James Cole
276de8a470 fix #3993 2020-10-26 06:10:10 +01:00
James Cole
f6ce49b586 Escape input, fixes #3990 2020-10-25 06:36:33 +01:00
James Cole
cf3d9d26fa Less hysterical about JSON errors 2020-10-25 06:35:58 +01:00
James Cole
265385c833 Merge tag '5.4.4' into develop
5.4.4
2020-10-24 18:55:40 +02:00
286 changed files with 3766 additions and 3196 deletions

View File

@@ -179,6 +179,9 @@ AUTHENTICATION_GUARD=web
# Some systems use X-Auth headers. In that case, use HTTP_X_AUTH_USERNAME or HTTP_X_AUTH_EMAIL
# Depending on your system, REMOTE_USER may need to be changed to HTTP_REMOTE_USER
#
# If this header is 'unexpectedly empty', check out the documentation.
# https://docs.firefly-iii.org/advanced-installation/authentication
#
AUTHENTICATION_GUARD_HEADER=REMOTE_USER
#

View File

@@ -58,7 +58,6 @@ class BillController extends Controller
}
/**
* TODO add limit
* @param AutocompleteRequest $request
*
* @return JsonResponse

View File

@@ -58,7 +58,6 @@ class BudgetController extends Controller
}
/**
* TODO add limit
* @param AutocompleteRequest $request
*
* @return JsonResponse

View File

@@ -24,7 +24,8 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\BillRequest;
use FireflyIII\Api\V1\Requests\BillUpdateRequest;
use FireflyIII\Api\V1\Requests\BillStoreRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Bill;
@@ -49,8 +50,7 @@ class BillController extends Controller
{
use TransactionFilter;
/** @var BillRepositoryInterface The bill repository */
private $repository;
private BillRepositoryInterface $repository;
/**
@@ -205,12 +205,12 @@ class BillController extends Controller
/**
* Store a bill.
*
* @param BillRequest $request
* @param BillStoreRequest $request
*
* @return JsonResponse
* @throws FireflyException
*/
public function store(BillRequest $request): JsonResponse
public function store(BillStoreRequest $request): JsonResponse
{
$bill = $this->repository->store($request->getAll());
$manager = $this->getManager();
@@ -285,12 +285,12 @@ class BillController extends Controller
/**
* Update a bill.
*
* @param BillRequest $request
* @param BillUpdateRequest $request
* @param Bill $bill
*
* @return JsonResponse
*/
public function update(BillRequest $request, Bill $bill): JsonResponse
public function update(BillUpdateRequest $request, Bill $bill): JsonResponse
{
$data = $request->getAll();
$bill = $this->repository->update($bill, $data);

View File

@@ -23,7 +23,8 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\CategoryRequest;
use FireflyIII\Api\V1\Requests\CategoryStoreRequest;
use FireflyIII\Api\V1\Requests\CategoryUpdateRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Category;
@@ -46,9 +47,7 @@ use League\Fractal\Resource\Item;
class CategoryController extends Controller
{
use TransactionFilter;
/** @var CategoryRepositoryInterface The category repository */
private $repository;
private CategoryRepositoryInterface $repository;
/**
@@ -175,12 +174,12 @@ class CategoryController extends Controller
/**
* Store new category.
*
* @param CategoryRequest $request
* @param CategoryStoreRequest $request
*
* @return JsonResponse
* @throws FireflyException
*/
public function store(CategoryRequest $request): JsonResponse
public function store(CategoryStoreRequest $request): JsonResponse
{
$category = $this->repository->store($request->getAll());
$manager = $this->getManager();
@@ -253,12 +252,12 @@ class CategoryController extends Controller
/**
* Update the category.
*
* @param CategoryRequest $request
* @param CategoryUpdateRequest $request
* @param Category $category
*
* @return JsonResponse
*/
public function update(CategoryRequest $request, Category $category): JsonResponse
public function update(CategoryUpdateRequest $request, Category $category): JsonResponse
{
$data = $request->getAll();
$category = $this->repository->update($category, $data);

View File

@@ -38,15 +38,10 @@ use Illuminate\Http\JsonResponse;
*/
class CategoryController extends Controller
{
/** @var CategoryRepositoryInterface */
private $categoryRepository;
/** @var NoCategoryRepositoryInterface */
private $noCatRepository;
/** @var OperationsRepositoryInterface */
private $opsRepository;
private CategoryRepositoryInterface $categoryRepository;
private NoCategoryRepositoryInterface $noCatRepository;
private OperationsRepositoryInterface $opsRepository;
private array $categories;
/**
* AccountController constructor.
@@ -63,6 +58,7 @@ class CategoryController extends Controller
$this->categoryRepository = app(CategoryRepositoryInterface::class);
$this->opsRepository = app(OperationsRepositoryInterface::class);
$this->noCatRepository = app(NoCategoryRepositoryInterface::class);
$this->categories = [];
$this->categoryRepository->setUser($user);
$this->opsRepository->setUser($user);
$this->noCatRepository->setUser($user);
@@ -87,53 +83,75 @@ class CategoryController extends Controller
/** @var Carbon $end */
$end = $dates['end'];
$tempData = [];
$spentWith = $this->opsRepository->listExpenses($start, $end);
$spentWithout = $this->noCatRepository->listExpenses($start, $end);
$categories = [];
/** @var array $set */
foreach ([$spentWith, $spentWithout,] as $set) {
foreach ($set as $currency) {
foreach ($currency['categories'] as $category) {
$categories[] = $category['name'];
$outKey = sprintf('%d-e', $currency['currency_id']);
$tempData[$outKey] = $tempData[$outKey] ?? [
'currency_id' => $currency['currency_id'],
'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]),
'currency_code' => $currency['currency_code'],
'currency_symbol' => $currency['currency_symbol'],
'currency_decimal_places' => $currency['currency_decimal_places'],
'type' => 'bar', // line, area or bar
'yAxisID' => 0, // 0, 1, 2
'entries' => [],
];
$tempData = $this->processArray($tempData, $set);
}
foreach ($category['transaction_journals'] as $journal) {
// is it expense or income?
$currentKey = sprintf('%d-%s', $currency['currency_id'], 'e');
$name = $category['name'];
$tempData[$currentKey]['entries'][$name] = $tempData[$currentKey]['entries'][$name] ?? '0';
$tempData[$currentKey]['entries'][$name] = bcadd($tempData[$currentKey]['entries'][$name], $journal['amount']);
}
$chartData = $this->sortArray($tempData);
return response()->json($chartData);
}
/**
* @param array $tempData
* @param array $set
*
* @return array
*/
private function processArray(array $tempData, array $set): array
{
foreach ($set as $currency) {
foreach ($currency['categories'] as $category) {
$this->categories[] = $category['name'];
$outKey = sprintf('%d-e', $currency['currency_id']);
$tempData[$outKey] = $tempData[$outKey] ?? [
'currency_id' => $currency['currency_id'],
'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]),
'currency_code' => $currency['currency_code'],
'currency_symbol' => $currency['currency_symbol'],
'currency_decimal_places' => $currency['currency_decimal_places'],
'type' => 'bar', // line, area or bar
'yAxisID' => 0, // 0, 1, 2
'entries' => [],
];
foreach ($category['transaction_journals'] as $journal) {
// is it expense or income?
$currentKey = sprintf('%d-%s', $currency['currency_id'], 'e');
$name = $category['name'];
$tempData[$currentKey]['entries'][$name] = $tempData[$currentKey]['entries'][$name] ?? '0';
$tempData[$currentKey]['entries'][$name] = bcadd($tempData[$currentKey]['entries'][$name], $journal['amount']);
}
}
}
return $tempData;
}
/**
* @param array $tempData
*
* @return array
*/
private function sortArray(array $tempData): array
{
// re-sort every spent array and add 0 for missing entries.
foreach ($tempData as $index => $set) {
$oldSet = $set['entries'];
$newSet = [];
foreach ($categories as $category) {
foreach ($this->categories as $category) {
$value = $oldSet[$category] ?? '0';
$value = -1 === bccomp($value, '0') ? bcmul($value, '-1') : $value;
$newSet[$category] = $value;
}
$tempData[$index]['entries'] = $newSet;
}
$chartData = array_values($tempData);
return response()->json($chartData);
return array_values($tempData);
}
}

View File

@@ -1,7 +1,7 @@
<?php
/**
* BillRequest.php
* BillStoreRequest.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
@@ -25,31 +25,19 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Validator;
/**
* Class BillRequest
*
* TODO AFTER 4.8,0: split this into two request classes.
* Class BillStoreRequest
*
* @codeCoverageIgnore
*/
class BillRequest extends FormRequest
class BillStoreRequest extends FormRequest
{
use ConvertsDataTypes;
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
return auth()->check();
}
use ConvertsDataTypes, ChecksLogin;
/**
* Get all data from the request.
@@ -85,7 +73,7 @@ class BillRequest extends FormRequest
*/
public function rules(): array
{
$rules = [
return [
'name' => 'between:1,255|uniqueObjectForUser:bills,name',
'amount_min' => 'numeric|gt:0',
'amount_max' => 'numeric|gt:0',
@@ -97,17 +85,6 @@ class BillRequest extends FormRequest
'active' => [new IsBoolean],
'notes' => 'between:1,65536',
];
switch ($this->method()) {
default:
break;
case 'PUT':
case 'PATCH':
$bill = $this->route()->parameter('bill');
$rules['name'] .= ',' . $bill->id;
break;
}
return $rules;
}
/**

View File

@@ -0,0 +1,111 @@
<?php
/**
* BillUpdateRequest.php
* Copyright (c) 2019 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\V1\Requests;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Validator;
/**
* Class BillUpdateRequest
*
* @codeCoverageIgnore
*/
class BillUpdateRequest extends FormRequest
{
use ConvertsDataTypes, ChecksLogin;
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
{
$active = true;
if (null !== $this->get('active')) {
$active = $this->boolean('active');
}
return [
'name' => $this->string('name'),
'amount_min' => $this->string('amount_min'),
'amount_max' => $this->string('amount_max'),
'currency_id' => $this->integer('currency_id'),
'currency_code' => $this->string('currency_code'),
'date' => $this->date('date'),
'repeat_freq' => $this->string('repeat_freq'),
'skip' => $this->integer('skip'),
'active' => $active,
'order' => $this->integer('order'),
'notes' => $this->nlString('notes'),
];
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
$bill = $this->route()->parameter('bill');
return [
'name' => sprintf('between:1,255|uniqueObjectForUser:bills,name,%d', $bill->id),
'amount_min' => 'numeric|gt:0',
'amount_max' => 'numeric|gt:0',
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'date' => 'date',
'repeat_freq' => 'in:weekly,monthly,quarterly,half-year,yearly',
'skip' => 'between:0,31',
'active' => [new IsBoolean],
'notes' => 'between:1,65536',
];
}
/**
* Configure the validator instance.
*
* @param Validator $validator
*
* @return void
*/
public function withValidator(Validator $validator): void
{
$validator->after(
static function (Validator $validator) {
$data = $validator->getData();
$min = (float) ($data['amount_min'] ?? 0);
$max = (float) ($data['amount_max'] ?? 0);
if ($min > $max) {
$validator->errors()->add('amount_min', (string) trans('validation.amount_min_over_max'));
}
}
);
}
}

View File

@@ -1,6 +1,6 @@
<?php
/**
* CategoryRequest.php
* CategoryStoreRequest.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
@@ -23,7 +23,8 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Models\Category;
use FireflyIII\Rules\ZeroOrMore;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
@@ -31,22 +32,10 @@ use Illuminate\Foundation\Http\FormRequest;
* Class CategoryRequest
*
* @codeCoverageIgnore
* TODO AFTER 4.8,0: split this into two request classes.
*/
class CategoryRequest extends FormRequest
class CategoryStoreRequest extends FormRequest
{
use ConvertsDataTypes;
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
return auth()->check();
}
use ConvertsDataTypes, ChecksLogin;
/**
* Get all data from the request.
@@ -56,7 +45,8 @@ class CategoryRequest extends FormRequest
public function getAll(): array
{
return [
'name' => $this->string('name'),
'name' => $this->string('name'),
'notes' => $this->nlString('notes'),
];
}
@@ -67,20 +57,8 @@ class CategoryRequest extends FormRequest
*/
public function rules(): array
{
$rules = [
return [
'name' => 'required|between:1,100|uniqueObjectForUser:categories,name',
];
switch ($this->method()) {
default:
break;
case 'PUT':
case 'PATCH':
/** @var Category $category */
$category = $this->route()->parameter('category');
$rules['name'] = sprintf('required|between:1,100|uniqueObjectForUser:categories,name,%d', $category->id);
break;
}
return $rules;
}
}

View File

@@ -0,0 +1,71 @@
<?php
/**
* CategoryUpdateRequest.php
* Copyright (c) 2019 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\V1\Requests;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
/**
* Class CategoryUpdateRequest
*
* @codeCoverageIgnore
*/
class CategoryUpdateRequest extends FormRequest
{
use ConvertsDataTypes, ChecksLogin;
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
{
$notes = null;
$all = $this->all();
if (array_key_exists('notes', $all)) {
$notes = $this->nlString('notes');
}
return [
'name' => $this->string('name'),
'notes' => $notes,
];
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
$category = $this->route()->parameter('category');
return [
'name' => sprintf('required|between:1,100|uniqueObjectForUser:categories,name,%d', $category->id),
];
}
}

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Rules\IsAssetAccountId;
use FireflyIII\Rules\LessThanPiggyTarget;
use FireflyIII\Rules\ZeroOrMore;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
@@ -79,7 +78,7 @@ class PiggyBankRequest extends FormRequest
{
$rules = [
'name' => 'required|between:1,255|uniquePiggyBankForUser',
'current_amount' => ['numeric', new ZeroOrMore, 'lte:target_amount'],
'current_amount' => ['numeric', 'gte:0', 'lte:target_amount'],
'start_date' => 'date|nullable',
'target_date' => 'date|nullable|after:start_date',
'notes' => 'max:65000',
@@ -95,7 +94,7 @@ class PiggyBankRequest extends FormRequest
$rules['name'] = 'between:1,255|uniquePiggyBankForUser:' . $piggyBank->id;
$rules['account_id'] = ['belongsToUser:accounts', new IsAssetAccountId];
$rules['target_amount'] = 'numeric|gt:0';
$rules['current_amount'] = ['numeric', new ZeroOrMore, new LessThanPiggyTarget];
$rules['current_amount'] = ['numeric', 'gte:0', new LessThanPiggyTarget];
break;
}

View File

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

View File

@@ -82,10 +82,9 @@ class TransactionStoreRequest extends FormRequest
{
$return = [];
/**
* @var int $index
* @var array $transaction
*/
foreach ($this->get('transactions') as $index => $transaction) {
foreach ($this->get('transactions') as $transaction) {
$object = new NullArrayObject($transaction);
$return[] = [
'type' => $this->stringFromValue($object['type']),

View File

@@ -27,6 +27,7 @@ use DB;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command;
use Log;
use stdClass;
/**
@@ -55,6 +56,7 @@ class FixUnevenAmount extends Command
*/
public function handle(): int
{
Log::debug(sprintf('Now in %s', __METHOD__));
$start = microtime(true);
$count = 0;
// get invalid journals
@@ -64,8 +66,11 @@ class FixUnevenAmount extends Command
->get(['transaction_journal_id', DB::raw('SUM(amount) AS the_sum')]);
/** @var stdClass $entry */
foreach ($journals as $entry) {
if (0 !== bccomp((string) $entry->the_sum, '0')) {
$this->fixJournal((int) $entry->transaction_journal_id);
if (0 !== bccomp((string)$entry->the_sum, '0')) {
$message = sprintf('Sum of journal #%d is %s instead of zero.', $entry->transaction_journal_id, $entry->the_sum);
$this->warn($message);
Log::warning($message);
$this->fixJournal((int)$entry->transaction_journal_id);
$count++;
}
}
@@ -106,7 +111,7 @@ class FixUnevenAmount extends Command
return;
}
$amount = bcmul('-1', (string) $source->amount);
$amount = bcmul('-1', (string)$source->amount);
// fix amount of destination:
/** @var Transaction $destination */

View File

@@ -61,39 +61,46 @@ class CreateDatabase extends Command
return 0;
}
// try to set up a raw connection:
$pdo = false;
$exists = false;
$checked = false; // checked for existence of DB?
$dsn = sprintf('mysql:host=%s;port=%d;charset=utf8mb4', env('DB_HOST', 'localhost'), env('DB_PORT', '3306'));
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
// when it fails, display error
try {
$pdo = new PDO($dsn, env('DB_USERNAME'), env('DB_PASSWORD'), $options);
} catch (PDOException $e) {
$this->error(sprintf('Error when connecting to DB: %s', $e->getMessage()));
return 1;
}
// with PDO, try to list DB's (
$stmt = $pdo->query('SHOW DATABASES;');
$exists = false;
// slightly more complex but less error prone.
foreach ($stmt as $row) {
$name = $row['Database'] ?? false;
if ($name === env('DB_DATABASE')) {
$exists = true;
// only continue when no error.
if (false !== $pdo) {
// with PDO, try to list DB's (
$stmt = $pdo->query('SHOW DATABASES;');
$checked = true;
// slightly more complex but less error prone.
foreach ($stmt as $row) {
$name = $row['Database'] ?? false;
if ($name === env('DB_DATABASE')) {
$exists = true;
}
}
}
if (false === $exists) {
if (false === $exists && true === $checked) {
$this->error(sprintf('Database "%s" does not exist.', env('DB_DATABASE')));
// try to create it.
$pdo->exec(sprintf('CREATE DATABASE `%s` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;', env('DB_DATABASE')));
$this->info(sprintf('Created database "%s"', env('DB_DATABASE')));
return 0;
}
$this->info(sprintf('Database "%s" exists.', env('DB_DATABASE')));
if (true === $exists && true === $checked) {
$this->info(sprintf('Database "%s" exists.', env('DB_DATABASE')));
}
return 0;
}

View File

@@ -204,11 +204,11 @@ class DecryptDatabase extends Command
try {
$newValue = json_decode($value, true, 512, JSON_THROW_ON_ERROR) ?? $value;
} catch (JsonException $e) {
$message = sprintf('Could not JSON decode preference row #%d: %s', $id, $e->getMessage());
$message = sprintf('Could not JSON decode preference row #%d: %s. This does not have to be a problem.', $id, $e->getMessage());
$this->error($message);
Log::error($message);
Log::error($value);
Log::error($e->getTraceAsString());
Log::warning($message);
Log::warning($value);
Log::warning($e->getTraceAsString());
return;
}

View File

@@ -57,7 +57,7 @@ class ExportData extends Command
*
* @var string
*/
protected $signature = 'firefly-iii:export-data
protected $signature = 'firefly-iii:export-data
{--user=1 : The user ID that the export should run for.}
{--token= : The user\'s access token.}
{--start= : First transaction to export. Defaults to your very first transaction. Only applies to transaction export.}
@@ -74,20 +74,17 @@ class ExportData extends Command
{--export-bills : Create a file with all your bills and some meta data.}
{--export-piggies : Create a file with all your piggy banks and some meta data.}
{--force : Force overwriting of previous exports if found.}';
/** @var AccountRepositoryInterface */
private $accountRepository;
/** @var JournalRepositoryInterface */
private $journalRepository;
/** @var User */
private $user;
private AccountRepositoryInterface $accountRepository;
private JournalRepositoryInterface $journalRepository;
private User $user;
/**
* Execute the console command.
*
* @throws FireflyException
* @throws CannotInsertRecord
* @return int
* @throws CannotInsertRecord
* @throws FireflyException
*/
public function handle(): int
{
@@ -111,7 +108,6 @@ class ExportData extends Command
return 1;
}
// make export object and configure it.
/** @var ExportDataGenerator $exporter */
$exporter = app(ExportDataGenerator::class);
$exporter->setUser($this->user);
@@ -126,26 +122,24 @@ class ExportData extends Command
$exporter->setExportRules($options['export']['rules']);
$exporter->setExportBills($options['export']['bills']);
$exporter->setExportPiggies($options['export']['piggies']);
$data = $exporter->export();
if (0 === count($data)) {
if (empty($data)) {
$this->error('You must export *something*. Use --export-transactions or another option. See docs.firefly-iii.org');
}
$returnCode = 0;
if (!empty($data)) {
try {
$this->exportData($options, $data);
app('telemetry')->feature('system.command.executed', $this->signature);
} catch (FireflyException $e) {
$this->error(sprintf('Could not store data: %s', $e->getMessage()));
return 1;
app('telemetry')->feature('system.command.errored', $this->signature);
$returnCode = 1;
}
}
try {
$this->exportData($options, $data);
} catch (FireflyException $e) {
$this->error(sprintf('Could not store data: %s', $e->getMessage()));
app('telemetry')->feature('system.command.errored', $this->signature);
return 1;
}
app('telemetry')->feature('system.command.executed', $this->signature);
return 0;
return $returnCode;
}
/**
@@ -172,8 +166,8 @@ class ExportData extends Command
}
/**
* @throws FireflyException
* @return Collection
* @throws FireflyException
*/
private function getAccountsParameter(): Collection
{
@@ -181,7 +175,7 @@ class ExportData extends Command
$accounts = new Collection;
$accountList = $this->option('accounts');
$types = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE];
if (null !== $accountList && '' !== (string) $accountList) {
if (null !== $accountList && '' !== (string)$accountList) {
$accountIds = explode(',', $accountList);
$accounts = $this->accountRepository->getAccountsById($accountIds);
}
@@ -205,35 +199,30 @@ class ExportData extends Command
/**
* @param string $field
*
* @throws FireflyException
* @throws Exception
* @return Carbon
* @throws Exception
*/
private function getDateParameter(string $field): Carbon
{
$date = Carbon::now()->subYear();
$error = false;
if (null !== $this->option($field)) {
try {
$date = Carbon::createFromFormat('Y-m-d', $this->option($field));
} catch (InvalidArgumentException $e) {
Log::error($e->getMessage());
$this->error(sprintf('%s date "%s" must be formatted YYYY-MM-DD. Field will be ignored.', $field, $this->option('start')));
$error = true;
}
return $date;
}
if ('start' === $field) {
if (false === $error && 'start' === $field) {
$journal = $this->journalRepository->firstNull();
$date = null === $journal ? Carbon::now()->subYear() : $journal->date;
$date->startOfDay();
return $date;
}
if ('end' === $field) {
if (false === $error && 'end' === $field) {
$date = today(config('app.timezone'));
$date->endOfDay();
return $date;
}
// fallback
@@ -241,13 +230,13 @@ class ExportData extends Command
}
/**
* @return string
* @throws FireflyException
*
* @return string
*/
private function getExportDirectory(): string
{
$directory = (string) $this->option('export_directory');
$directory = (string)$this->option('export_directory');
if (null === $directory) {
$directory = './';
}
@@ -259,8 +248,8 @@ class ExportData extends Command
}
/**
* @throws FireflyException
* @return array
* @throws FireflyException
*/
private function parseOptions(): array
{

View File

@@ -29,9 +29,7 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalCLIRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Console\Command;
use Log;
@@ -52,32 +50,18 @@ class TransferCurrenciesCorrections extends Command
*
* @var string
*/
protected $signature = 'firefly-iii:transfer-currencies {--F|force : Force the execution of this command.}';
/** @var array */
private $accountCurrencies;
/** @var AccountRepositoryInterface */
private $accountRepos;
/** @var JournalCLIRepositoryInterface */
private $cliRepos;
/** @var int */
private $count;
/** @var CurrencyRepositoryInterface */
private $currencyRepos;
/** @var Account The destination account of the current journal. */
private $destinationAccount;
/** @var TransactionCurrency The currency preference of the destination account of the current journal. */
private $destinationCurrency;
/** @var Transaction The destination transaction of the current journal. */
private $destinationTransaction;
/** @var JournalRepositoryInterface */
private $journalRepos;
/** @var Account The source account of the current journal. */
private $sourceAccount;
/** @var TransactionCurrency The currency preference of the source account of the current journal. */
private $sourceCurrency;
/** @var Transaction The source transaction of the current journal. */
private $sourceTransaction;
protected $signature = 'firefly-iii:transfer-currencies {--F|force : Force the execution of this command.}';
private array $accountCurrencies;
private AccountRepositoryInterface $accountRepos;
private JournalCLIRepositoryInterface $cliRepos;
private int $count;
private ?Account $destinationAccount;
private ?TransactionCurrency $destinationCurrency;
private ?Transaction $destinationTransaction;
private ?Account $sourceAccount;
private ?TransactionCurrency $sourceCurrency;
private ?Transaction $sourceTransaction;
/**
* Execute the console command.
@@ -124,7 +108,7 @@ class TransferCurrenciesCorrections extends Command
if (null === $this->destinationTransaction->transaction_currency_id && null !== $this->destinationCurrency) {
$this->destinationTransaction
->transaction_currency_id
= (int) $this->destinationCurrency->id;
= (int)$this->destinationCurrency->id;
$message = sprintf(
'Transaction #%d has no currency setting, now set to %s.',
$this->destinationTransaction->id,
@@ -144,7 +128,7 @@ class TransferCurrenciesCorrections extends Command
private function fixDestNullForeignAmount(): void
{
if (null === $this->destinationTransaction->foreign_amount && null !== $this->sourceTransaction->foreign_amount) {
$this->destinationTransaction->foreign_amount = bcmul((string) $this->sourceTransaction->foreign_amount, '-1');
$this->destinationTransaction->foreign_amount = bcmul((string)$this->sourceTransaction->foreign_amount, '-1');
$this->destinationTransaction->save();
$this->count++;
Log::debug(
@@ -165,7 +149,7 @@ class TransferCurrenciesCorrections extends Command
{
if (null !== $this->destinationCurrency
&& null === $this->destinationTransaction->foreign_amount
&& (int) $this->destinationTransaction->transaction_currency_id !== (int) $this->destinationCurrency->id
&& (int)$this->destinationTransaction->transaction_currency_id !== (int)$this->destinationCurrency->id
) {
$message = sprintf(
'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.',
@@ -177,7 +161,7 @@ class TransferCurrenciesCorrections extends Command
Log::debug($message);
$this->line($message);
$this->count++;
$this->destinationTransaction->transaction_currency_id = (int) $this->destinationCurrency->id;
$this->destinationTransaction->transaction_currency_id = (int)$this->destinationCurrency->id;
$this->destinationTransaction->save();
}
}
@@ -189,7 +173,7 @@ class TransferCurrenciesCorrections extends Command
*/
private function fixInvalidForeignCurrency(): void
{
if ((int) $this->destinationCurrency->id === (int) $this->sourceCurrency->id) {
if ((int)$this->destinationCurrency->id === (int)$this->sourceCurrency->id) {
// update both transactions to match:
$this->sourceTransaction->foreign_amount = null;
$this->sourceTransaction->foreign_currency_id = null;
@@ -223,7 +207,7 @@ class TransferCurrenciesCorrections extends Command
*/
private function fixMismatchedForeignCurrency(): void
{
if ((int) $this->sourceCurrency->id !== (int) $this->destinationCurrency->id) {
if ((int)$this->sourceCurrency->id !== (int)$this->destinationCurrency->id) {
$this->sourceTransaction->transaction_currency_id = $this->sourceCurrency->id;
$this->sourceTransaction->foreign_currency_id = $this->destinationCurrency->id;
$this->destinationTransaction->transaction_currency_id = $this->sourceCurrency->id;
@@ -245,7 +229,7 @@ class TransferCurrenciesCorrections extends Command
if (null === $this->sourceTransaction->transaction_currency_id && null !== $this->sourceCurrency) {
$this->sourceTransaction
->transaction_currency_id
= (int) $this->sourceCurrency->id;
= (int)$this->sourceCurrency->id;
$message = sprintf(
'Transaction #%d has no currency setting, now set to %s.',
$this->sourceTransaction->id,
@@ -265,7 +249,7 @@ class TransferCurrenciesCorrections extends Command
private function fixSourceNullForeignAmount(): void
{
if (null === $this->sourceTransaction->foreign_amount && null !== $this->destinationTransaction->foreign_amount) {
$this->sourceTransaction->foreign_amount = bcmul((string) $this->destinationTransaction->foreign_amount, '-1');
$this->sourceTransaction->foreign_amount = bcmul((string)$this->destinationTransaction->foreign_amount, '-1');
$this->sourceTransaction->save();
$this->count++;
Log::debug(
@@ -286,7 +270,7 @@ class TransferCurrenciesCorrections extends Command
{
if (null !== $this->sourceCurrency
&& null === $this->sourceTransaction->foreign_amount
&& (int) $this->sourceTransaction->transaction_currency_id !== (int) $this->sourceCurrency->id
&& (int)$this->sourceTransaction->transaction_currency_id !== (int)$this->sourceCurrency->id
) {
$message = sprintf(
'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.',
@@ -298,7 +282,7 @@ class TransferCurrenciesCorrections extends Command
Log::debug($message);
$this->line($message);
$this->count++;
$this->sourceTransaction->transaction_currency_id = (int) $this->sourceCurrency->id;
$this->sourceTransaction->transaction_currency_id = (int)$this->sourceCurrency->id;
$this->sourceTransaction->save();
}
}
@@ -310,7 +294,7 @@ class TransferCurrenciesCorrections extends Command
*/
private function fixTransactionJournalCurrency(TransactionJournal $journal): void
{
if ((int) $journal->transaction_currency_id !== (int) $this->sourceCurrency->id) {
if ((int)$journal->transaction_currency_id !== (int)$this->sourceCurrency->id) {
$oldCurrencyCode = $journal->transactionCurrency->code ?? '(nothing)';
$journal->transaction_currency_id = $this->sourceCurrency->id;
$message = sprintf(
@@ -424,7 +408,7 @@ class TransferCurrenciesCorrections extends Command
{
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
if (null !== $configVar) {
return (bool) $configVar->data;
return (bool)$configVar->data;
}
return false; // @codeCoverageIgnore
@@ -502,8 +486,6 @@ class TransferCurrenciesCorrections extends Command
* For transfers, this is can be a destructive routine since we FORCE them into a currency setting whether they
* like it or not. Previous routines MUST have set the currency setting for both accounts for this to work.
*
* A transfer always has the
*
* Both source and destination must match the respective currency preference. So FF3 must verify ALL
* transactions.
*/
@@ -527,8 +509,6 @@ class TransferCurrenciesCorrections extends Command
{
$this->count = 0;
$this->accountRepos = app(AccountRepositoryInterface::class);
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
$this->journalRepos = app(JournalRepositoryInterface::class);
$this->cliRepos = app(JournalCLIRepositoryInterface::class);
$this->accountCurrencies = [];
$this->resetInformation();

View File

@@ -51,13 +51,13 @@ class CategoryFactory
* @param int|null $categoryId
* @param null|string $categoryName
*
* @throws FireflyException
* @return Category|null
* @throws FireflyException
*/
public function findOrCreate(?int $categoryId, ?string $categoryName): ?Category
{
$categoryId = (int) $categoryId;
$categoryName = (string) $categoryName;
$categoryId = (int)$categoryId;
$categoryName = (string)$categoryName;
Log::debug(sprintf('Going to find category with ID %d and name "%s"', $categoryId, $categoryName));

View File

@@ -63,7 +63,6 @@ class TransactionJournalFactory
private array $fields;
private PiggyBankEventFactory $piggyEventFactory;
private PiggyBankRepositoryInterface $piggyRepository;
private TransactionFactory $transactionFactory;
private TransactionTypeRepositoryInterface $typeRepository;
private User $user;

View File

@@ -35,14 +35,10 @@ use Throwable;
*/
class MonthReportGenerator implements ReportGeneratorInterface
{
/** @var Collection The accounts involved in the report. */
private $accounts;
/** @var Carbon The end date */
private $end;
/** @var Collection The expense accounts. */
private $expense;
/** @var Carbon The start date. */
private $start;
private Collection $accounts;
private Carbon $end;
private Collection $expense;
private Carbon $start;
/**
* Generate the report.

View File

@@ -155,7 +155,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
return [
'journals' => $journals,
'currency' => $currency,
'exists' => count($journals) > 0,
'exists' => !empty($journals),
'end' => $this->end->formatLocalized((string) trans('config.month_and_day', [], $locale)),
'endBalance' => app('steam')->balance($account, $this->end),
'dayBefore' => $date->formatLocalized((string) trans('config.month_and_day', [], $locale)),

View File

@@ -41,16 +41,11 @@ use Throwable;
*/
class MonthReportGenerator implements ReportGeneratorInterface
{
/** @var Collection The accounts in the report. */
private $accounts;
/** @var Collection The budgets in the report. */
private $budgets;
/** @var Carbon The end date. */
private $end;
/** @var array The expenses in the report. */
private $expenses;
/** @var Carbon The start date. */
private $start;
private Collection $accounts;
private Collection $budgets;
private Carbon $end;
private array $expenses;
private Carbon $start;
/**
* MonthReportGenerator constructor.

View File

@@ -52,6 +52,12 @@ class APIEventHandler
$user = $repository->findNull((int) $event->userId);
if (null !== $user) {
$email = $user->email;
// if user is demo user, send to owner:
if($user->hasRole('demo')) {
$email = config('firefly.site_owner');
}
$ipAddress = Request::ip();
// see if user has alternative email address:

View File

@@ -52,6 +52,11 @@ class AdminEventHandler
$email = $event->user->email;
$ipAddress = $event->ipAddress;
// if user is demo user, send to owner:
if($event->user->hasRole('demo')) {
$email = config('firefly.site_owner');
}
// see if user has alternative email address:
$pref = app('preferences')->getForUser($event->user, 'remote_guard_alt_email', null);
if (null !== $pref) {

View File

@@ -65,6 +65,10 @@ class AutomationHandler
$email = $pref->data;
}
// if user is demo user, send to owner:
if($user->hasRole('demo')) {
$email = config('firefly.site_owner');
}
try {
Log::debug('Trying to mail...');

View File

@@ -182,6 +182,11 @@ class UserEventHandler
$user = $event->user;
$email = $user->email;
$ipAddress = $event->ipAddress;
if($user->hasRole('demo')) {
return; // do not email demo user.
}
$list = app('preferences')->getForUser($user, 'login_ip_history', [])->data;
// see if user has alternative email address:

View File

@@ -224,7 +224,7 @@ class AttachmentHelper implements AttachmentHelperInterface
}
Log::debug('Done processing uploads.');
}
if (!is_array($files) || (is_array($files) && 0 === count($files))) {
if (!is_array($files) || empty($files)) {
Log::debug('Array of files is not an array. Probably nothing uploaded. Will not store attachments.');
}

View File

@@ -244,20 +244,6 @@ class GroupCollector implements GroupCollectorInterface
return $this;
}
/**
* Limit the result to a specific transaction group.
*
* @param TransactionGroup $transactionGroup
*
* @return GroupCollectorInterface
*/
public function setGroup(TransactionGroup $transactionGroup): GroupCollectorInterface
{
$this->query->where('transaction_groups.id', $transactionGroup->id);
return $this;
}
/**
* Limit the result to a set of specific journals.
*
@@ -267,7 +253,7 @@ class GroupCollector implements GroupCollectorInterface
*/
public function setJournalIds(array $journalIds): GroupCollectorInterface
{
if (count($journalIds) > 0) {
if (!empty($journalIds)) {
$this->query->whereIn('transaction_journals.id', $journalIds);
}

View File

@@ -238,15 +238,6 @@ interface GroupCollectorInterface
*/
public function setDestinationAccounts(Collection $accounts): GroupCollectorInterface;
/**
* Limit the result to a specific transaction group.
*
* @param TransactionGroup $transactionGroup
*
* @return GroupCollectorInterface
*/
public function setGroup(TransactionGroup $transactionGroup): GroupCollectorInterface;
/**
* Limit the result to a set of specific transaction journals.
*

View File

@@ -61,11 +61,10 @@ class HomeController extends Controller
$mainTitleIcon = 'fa-hand-spock-o';
$email = auth()->user()->email;
$pref = app('preferences')->get('remote_guard_alt_email', null);
if(null !== $pref) {
if(null !== $pref && is_string($pref->data)) {
$email = $pref->data;
}
Log::debug('Email is ', [$email]);
return view('admin.index', compact('title', 'mainTitleIcon','email'));
}

View File

@@ -116,7 +116,7 @@ class LoginController extends Controller
$this->incrementLoginAttempts($request);
Log::channel('audit')->info(sprintf('Login failed. Attempt for user "%s" failed.', $request->get('email')));
return $this->sendFailedLoginResponse($request);
$this->sendFailedLoginResponse($request);
}
/**
@@ -198,6 +198,9 @@ class LoginController extends Controller
if ('remote_user_guard' === $authGuard && '' !== $logoutUri) {
return redirect($logoutUri);
}
if ('remote_user_guard' === $authGuard && '' === $logoutUri) {
session()->flash('error',trans('firefly.cant_logout_guard'));
}
$this->guard()->logout();

View File

@@ -264,10 +264,9 @@ class IndexController extends Controller
return [];
}
/**
* @var int $objectGroupId
* @var array $array
*/
foreach ($sums as $objectGroupId => $array) {
foreach ($sums as $array) {
/**
* @var int $currencyId
* @var array $entry

View File

@@ -111,7 +111,7 @@ class AvailableBudgetController extends Controller
*/
public function createAlternative(Request $request, Carbon $start, Carbon $end)
{
$currencies = $this->currencyRepos->getEnabled();
$currencies = $this->currencyRepos->get();
$availableBudgets = $this->abRepository->get($start, $end);
// remove already budgeted currencies:

View File

@@ -86,7 +86,7 @@ class BudgetLimitController extends Controller
*/
public function create(Budget $budget, Carbon $start, Carbon $end)
{
$collection = $this->currencyRepos->getEnabled();
$collection = $this->currencyRepos->get();
$budgetLimits = $this->blRepository->getBudgetLimits($budget, $start, $end);
// remove already budgeted currencies:

View File

@@ -99,7 +99,7 @@ class IndexController extends Controller
$start = $start ?? session('start', Carbon::now()->startOfMonth());
$end = $end ?? app('navigation')->endOfPeriod($start, $range);
$defaultCurrency = app('amount')->getDefaultCurrency();
$currencies = $this->currencyRepository->getEnabled();
$currencies = $this->currencyRepository->get();
$budgeted = '0';
$spent = '0';

View File

@@ -41,11 +41,8 @@ use Illuminate\View\View;
class EditController extends Controller
{
/** @var CategoryRepositoryInterface The category repository */
private $repository;
/** @var AttachmentHelperInterface Helper for attachments. */
private $attachments;
private CategoryRepositoryInterface $repository;
private AttachmentHelperInterface $attachments;
/**
* CategoryController constructor.
@@ -58,9 +55,9 @@ class EditController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.categories'));
app('view')->share('title', (string)trans('firefly.categories'));
app('view')->share('mainTitleIcon', 'fa-bookmark');
$this->repository = app(CategoryRepositoryInterface::class);
$this->repository = app(CategoryRepositoryInterface::class);
$this->attachments = app(AttachmentHelperInterface::class);
return $next($request);
@@ -79,7 +76,7 @@ class EditController extends Controller
*/
public function edit(Request $request, Category $category)
{
$subTitle = (string) trans('firefly.edit_category', ['name' => $category->name]);
$subTitle = (string)trans('firefly.edit_category', ['name' => $category->name]);
// put previous url in session if not redirect from store (not "return_to_edit").
if (true !== session('categories.edit.fromUpdate')) {
@@ -87,7 +84,11 @@ class EditController extends Controller
}
$request->session()->forget('categories.edit.fromUpdate');
return view('categories.edit', compact('category', 'subTitle'));
$preFilled = [
'notes' => $request->old('notes') ?? $this->repository->getNoteText($category),
];
return view('categories.edit', compact('category', 'subTitle', 'preFilled'));
}
/**
@@ -103,7 +104,7 @@ class EditController extends Controller
$data = $request->getCategoryData();
$this->repository->update($category, $data);
$request->session()->flash('success', (string) trans('firefly.updated_category', ['name' => $category->name]));
$request->session()->flash('success', (string)trans('firefly.updated_category', ['name' => $category->name]));
app('preferences')->mark();
// store new attachment(s):
@@ -112,7 +113,7 @@ class EditController extends Controller
$this->attachments->saveAttachmentsForModel($category, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
session()->flash('info', (string)trans('firefly.no_att_demo_user'));
}
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
@@ -122,7 +123,7 @@ class EditController extends Controller
$redirect = redirect($this->getPreviousUri('categories.edit.uri'));
if (1 === (int) $request->get('return_to_edit')) {
if (1 === (int)$request->get('return_to_edit')) {
// @codeCoverageIgnoreStart
$request->session()->put('categories.edit.fromUpdate', true);

View File

@@ -50,16 +50,11 @@ class BudgetController extends Controller
{
use DateCalculation, AugumentData;
/** @var GeneratorInterface Chart generation methods. */
protected $generator;
/** @var OperationsRepositoryInterface */
protected $opsRepository;
/** @var BudgetRepositoryInterface The budget repository */
protected $repository;
/** @var BudgetLimitRepositoryInterface */
private $blRepository;
/** @var NoBudgetRepositoryInterface */
private $nbRepository;
protected GeneratorInterface $generator;
protected OperationsRepositoryInterface $opsRepository;
protected BudgetRepositoryInterface $repository;
private BudgetLimitRepositoryInterface $blRepository;
private NoBudgetRepositoryInterface $nbRepository;
/**
* BudgetController constructor.
@@ -185,12 +180,12 @@ class BudgetController extends Controller
while ($start <= $end) {
$spent = $this->opsRepository->spentInPeriod($budgetCollection, new Collection, $start, $start);
$amount = bcadd($amount, $spent);
$format = $start->formatLocalized((string) trans('config.month_and_day', [], $locale));
$format = $start->formatLocalized((string)trans('config.month_and_day', [], $locale));
$entries[$format] = $amount;
$start->addDay();
}
$data = $this->generator->singleSet((string) trans('firefly.left'), $entries);
$data = $this->generator->singleSet((string)trans('firefly.left'), $entries);
// add currency symbol from budget limit:
$data['datasets'][0]['currency_symbol'] = $budgetLimit->transactionCurrency->symbol;
$data['datasets'][0]['currency_code'] = $budgetLimit->transactionCurrency->code;
@@ -239,7 +234,7 @@ class BudgetController extends Controller
// group by asset account ID:
foreach ($journals as $journal) {
$key = sprintf('%d-%d', (int) $journal['source_account_id'], $journal['currency_id']);
$key = sprintf('%d-%d', (int)$journal['source_account_id'], $journal['currency_id']);
$result[$key] = $result[$key] ?? [
'amount' => '0',
'currency_symbol' => $journal['currency_symbol'],
@@ -252,7 +247,7 @@ class BudgetController extends Controller
$names = $this->getAccountNames(array_keys($result));
foreach ($result as $combinedId => $info) {
$parts = explode('-', $combinedId);
$assetId = (int) $parts[0];
$assetId = (int)$parts[0];
$title = sprintf('%s (%s)', $names[$assetId] ?? '(empty)', $info['currency_name']);
$chartData[$title]
= [
@@ -319,7 +314,7 @@ class BudgetController extends Controller
$names = $this->getCategoryNames(array_keys($result));
foreach ($result as $combinedId => $info) {
$parts = explode('-', $combinedId);
$categoryId = (int) $parts[0];
$categoryId = (int)$parts[0];
$title = sprintf('%s (%s)', $names[$categoryId] ?? '(empty)', $info['currency_name']);
$chartData[$title] = [
'amount' => $info['amount'],
@@ -385,7 +380,7 @@ class BudgetController extends Controller
$names = $this->getAccountNames(array_keys($result));
foreach ($result as $combinedId => $info) {
$parts = explode('-', $combinedId);
$opposingId = (int) $parts[0];
$opposingId = (int)$parts[0];
$name = $names[$opposingId] ?? 'no name';
$title = sprintf('%s (%s)', $name, $info['currency_name']);
$chartData[$title] = [
@@ -422,12 +417,12 @@ class BudgetController extends Controller
return response()->json($cache->get()); // @codeCoverageIgnore
}
$generator = app(FrontpageChartGenerator::class);
$generator->setUser(auth()->user());
$generator->setStart($start);
$generator->setEnd($end);
$chartGenerator = app(FrontpageChartGenerator::class);
$chartGenerator->setUser(auth()->user());
$chartGenerator->setStart($start);
$chartGenerator->setEnd($end);
$chartData = $generator->generate();
$chartData = $chartGenerator->generate();
$data = $this->generator->multiSet($chartData);
$cache->store($data);
@@ -463,14 +458,14 @@ class BudgetController extends Controller
$preferredRange = app('navigation')->preferredRangeFormat($start, $end);
$chartData = [
[
'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency->name]),
'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $currency->name]),
'type' => 'bar',
'entries' => [],
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,
],
[
'label' => (string) trans('firefly.box_budgeted_in_currency', ['currency' => $currency->name]),
'label' => (string)trans('firefly.box_budgeted_in_currency', ['currency' => $currency->name]),
'type' => 'bar',
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,
@@ -549,7 +544,7 @@ class BudgetController extends Controller
$currentStart = app('navigation')->addPeriod($currentStart, $preferredRange, 0);
}
$data = $this->generator->singleSet((string) trans('firefly.spent'), $chartData);
$data = $this->generator->singleSet((string)trans('firefly.spent'), $chartData);
$cache->store($data);
return response()->json($data);

View File

@@ -187,7 +187,7 @@ class ExpenseReportController extends Controller
$newSet[$key] = $chartData[$key]; // @codeCoverageIgnore
}
}
if (0 === count($newSet)) {
if (empty($newSet)) {
$newSet = $chartData; // @codeCoverageIgnore
}
$data = $this->generator->multiSet($newSet);

View File

@@ -62,6 +62,13 @@ abstract class Controller extends BaseController
app('view')->share('DEMO_PASSWORD', config('firefly.demo_password'));
app('view')->share('FF_VERSION', config('firefly.version'));
// share custom auth guard info.
$authGuard = config('firefly.authentication_guard');
$logoutUri = config('firefly.custom_logout_uri');
app('view')->share('authGuard', $authGuard);
app('view')->share('logoutUri', $logoutUri);
// upload size
$maxFileSize = app('steam')->phpBytes(ini_get('upload_max_filesize'));
$maxPostSize = app('steam')->phpBytes(ini_get('post_max_size'));

View File

@@ -193,7 +193,7 @@ class BoxController extends Controller
$incomes[$currencyId] = app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false);
$expenses[$currencyId] = app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false);
}
if (0 === count($sums)) {
if (empty($sums)) {
$currency = app('amount')->getDefaultCurrency();
$sums[$currency->id] = app('amount')->formatAnything($currency, '0', false);
$incomes[$currency->id] = app('amount')->formatAnything($currency, '0', false);
@@ -257,7 +257,7 @@ class BoxController extends Controller
$return = [];
foreach ($netWorthSet as $index => $data) {
foreach ($netWorthSet as $data) {
/** @var TransactionCurrency $currency */
$currency = $data['currency'];
$return[$currency->id] = app('amount')->formatAnything($currency, $data['balance'], false);

View File

@@ -64,7 +64,7 @@ class FrontpageController extends Controller
}
}
$html = '';
if (count($info) > 0) {
if (!empty($info)) {
try {
$html = view('json.piggy-banks', compact('info'))->render();
// @codeCoverageIgnoreStart

View File

@@ -67,8 +67,6 @@ class EditController extends Controller
{
$subTitle = (string) trans('firefly.edit_object_group', ['title' => $objectGroup->title]);
$subTitleIcon = 'fa-pencil';
$targetDate = null;
$startDate = null;
if (true !== session('object-groups.edit.fromUpdate')) {
$this->rememberPreviousUri('object-groups.edit.uri');

View File

@@ -165,7 +165,6 @@ class BudgetController extends Controller
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, $budgets);
$result = [];
foreach ($spent as $currency) {
$currencyId = $currency['currency_id'];
foreach ($currency['budgets'] as $budget) {
foreach ($budget['transaction_journals'] as $journal) {
$destinationId = $journal['destination_account_id'];
@@ -328,7 +327,6 @@ class BudgetController extends Controller
foreach ($expenses as $currency) {
foreach ($currency['budgets'] as $budget) {
$count = 0;
$total = '0';
foreach ($budget['transaction_journals'] as $journal) {
$count++;
$key = sprintf('%d-%d', $budget['id'], $currency['currency_id']);
@@ -377,7 +375,6 @@ class BudgetController extends Controller
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, $budgets);
$result = [];
foreach ($spent as $currency) {
$currencyId = $currency['currency_id'];
foreach ($currency['budgets'] as $budget) {
foreach ($budget['transaction_journals'] as $journal) {
$result[] = [

View File

@@ -40,10 +40,7 @@ use Throwable;
*/
class TagController extends Controller
{
/** @var OperationsRepositoryInterface */
private $opsRepository;
private OperationsRepositoryInterface $opsRepository;
/**
* ExpenseReportController constructor.
@@ -282,7 +279,6 @@ class TagController extends Controller
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, $tags);
$result = [];
foreach ($spent as $currency) {
$currencyId = $currency['currency_id'];
foreach ($currency['tags'] as $tag) {
foreach ($tag['transaction_journals'] as $journal) {
$destinationId = $journal['destination_account_id'];
@@ -335,7 +331,6 @@ class TagController extends Controller
$spent = $this->opsRepository->listIncome($start, $end, $accounts, $tags);
$result = [];
foreach ($spent as $currency) {
$currencyId = $currency['currency_id'];
foreach ($currency['tags'] as $tag) {
foreach ($tag['transaction_journals'] as $journal) {
$sourceId = $journal['source_account_id'];
@@ -496,7 +491,6 @@ class TagController extends Controller
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, $tags);
$result = [];
foreach ($spent as $currency) {
$currencyId = $currency['currency_id'];
foreach ($currency['tags'] as $tag) {
foreach ($tag['transaction_journals'] as $journal) {
$result[] = [
@@ -546,7 +540,6 @@ class TagController extends Controller
$spent = $this->opsRepository->listIncome($start, $end, $accounts, $tags);
$result = [];
foreach ($spent as $currency) {
$currencyId = $currency['currency_id'];
foreach ($currency['tags'] as $tag) {
foreach ($tag['transaction_journals'] as $journal) {
$result[] = [

View File

@@ -346,7 +346,6 @@ class ReportController extends Controller
$budgets = implode(',', $request->getBudgetList()->pluck('id')->toArray());
$tags = implode(',', $request->getTagList()->pluck('id')->toArray());
$double = implode(',', $request->getDoubleList()->pluck('id')->toArray());
$uri = route('reports.index');
if (0 === $request->getAccountList()->count()) {
Log::debug('Account count is zero');

View File

@@ -242,17 +242,15 @@ class ConvertController extends Controller
private function getAssetAccounts(): array
{
// make repositories
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$accountList = $accountRepository->getActiveAccountsByType([AccountType::ASSET]);
$accountList = $this->accountRepository->getActiveAccountsByType([AccountType::ASSET]);
$defaultCurrency = app('amount')->getDefaultCurrency();
$grouped = [];
// group accounts:
/** @var Account $account */
foreach ($accountList as $account) {
$balance = app('steam')->balance($account, today());
$currency = $accountRepository->getAccountCurrency($account) ?? $defaultCurrency;
$role = (string)$accountRepository->getMetaValue($account, 'account_role');
$currency = $this->accountRepository->getAccountCurrency($account) ?? $defaultCurrency;
$role = (string)$this->accountRepository->getMetaValue($account, 'account_role');
if ('' === $role) {
$role = 'no_account_type'; // @codeCoverageIgnore
}
@@ -271,16 +269,14 @@ class ConvertController extends Controller
private function getLiabilities(): array
{
// make repositories
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$accountList = $accountRepository->getActiveAccountsByType([AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]);
$accountList = $this->accountRepository->getActiveAccountsByType([AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]);
$defaultCurrency = app('amount')->getDefaultCurrency();
$grouped = [];
// group accounts:
/** @var Account $account */
foreach ($accountList as $account) {
$balance = app('steam')->balance($account, today());
$currency = $accountRepository->getAccountCurrency($account) ?? $defaultCurrency;
$currency = $this->accountRepository->getAccountCurrency($account) ?? $defaultCurrency;
$role = 'l_' . $account->accountType->type;
$key = (string)trans('firefly.opt_group_' . $role);
$grouped[$key][$account->id] = $account->name . ' (' . app('amount')->formatAnything($currency, $balance, false) . ')';
@@ -295,16 +291,14 @@ class ConvertController extends Controller
private function getValidDepositSources(): array
{
// make repositories
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$liabilityTypes = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN];
$accountList = $accountRepository
$accountList = $this->accountRepository
->getActiveAccountsByType([AccountType::REVENUE, AccountType::CASH, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]);
$grouped = [];
// group accounts:
/** @var Account $account */
foreach ($accountList as $account) {
$role = (string)$accountRepository->getMetaValue($account, 'account_role');
$role = (string)$this->accountRepository->getMetaValue($account, 'account_role');
$name = $account->name;
if ('' === $role) {
$role = 'no_account_type'; // @codeCoverageIgnore
@@ -337,17 +331,15 @@ class ConvertController extends Controller
private function getValidWithdrawalDests(): array
{
// make repositories
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$liabilityTypes = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN];
$accountList = $accountRepository->getActiveAccountsByType(
$accountList = $this->accountRepository->getActiveAccountsByType(
[AccountType::EXPENSE, AccountType::CASH, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]
);
$grouped = [];
// group accounts:
/** @var Account $account */
foreach ($accountList as $account) {
$role = (string)$accountRepository->getMetaValue($account, 'account_role');
$role = (string)$this->accountRepository->getMetaValue($account, 'account_role');
$name = $account->name;
if ('' === $role) {
$role = 'no_account_type'; // @codeCoverageIgnore

View File

@@ -171,7 +171,7 @@ class ShowController extends Controller
{
$accounts = [];
foreach ($group['transactions'] as $index => $transaction) {
foreach ($group['transactions'] as $transaction) {
$accounts['source'][] = [
'type' => $transaction['source_type'],
'id' => $transaction['source_id'],

View File

@@ -49,17 +49,12 @@ class StartFireflySession extends StartSession
$isJsonPage = strpos($uri, '/json');
// also stop remembering "delete" URL's.
if (false === $isScriptPage && false === $isDeletePage
&& false === $isLoginPage
&& false === $isJsonPage
&& 'GET' === $request->method()
&& !$request->ajax()) {
$session->setPreviousUrl($uri);
Log::debug(sprintf('Will set previous URL to %s', $uri));
return;
}
Log::debug(sprintf('Will NOT set previous URL to %s', $uri));
}
}

View File

@@ -47,7 +47,7 @@ class BudgetFormStoreRequest extends FormRequest
'name' => $this->string('name'),
'active' => $this->boolean('active'),
'auto_budget_type' => $this->integer('auto_budget_type'),
'transaction_currency_id' => $this->integer('transaction_currency_id'),
'transaction_currency_id' => $this->integer('auto_budget_currency_id'),
'auto_budget_amount' => $this->string('auto_budget_amount'),
'auto_budget_period' => $this->string('auto_budget_period'),
];

View File

@@ -42,7 +42,8 @@ class CategoryFormRequest extends FormRequest
public function getCategoryData(): array
{
return [
'name' => $this->string('name'),
'name' => $this->string('name'),
'notes' => $this->nlString('notes'),
];
}

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
/**
@@ -32,7 +33,7 @@ use Illuminate\Foundation\Http\FormRequest;
*/
class EmailFormRequest extends FormRequest
{
use ChecksLogin;
use ChecksLogin, ConvertsDataTypes;
/**
* Rules for this request.

View File

@@ -354,17 +354,7 @@ class CreateRecurringTransactions implements ShouldQueue
$includeWeekend = clone $this->date;
$includeWeekend->addDays(2);
$occurrences = $this->repository->getOccurrencesInRange($repetition, $recurrence->first_date, $includeWeekend);
/*
Log::debug(
sprintf(
'Calculated %d occurrences between %s and %s',
count($occurrences),
$recurrence->first_date->format('Y-m-d'),
$includeWeekend->format('Y-m-d')
),
$this->debugArray($occurrences)
);
*/
unset($includeWeekend);
$result = $this->handleOccurrences($recurrence, $repetition, $occurrences);

View File

@@ -70,12 +70,6 @@ class ReportNewJournalsMail extends Mailable
*/
public function build(): self
{
$subject = 1 === $this->groups->count()
? 'Firefly III has created a new transaction'
: sprintf(
'Firefly III has created new %d transactions',
$this->groups->count()
);
$this->transform();
return $this->view('emails.report-new-journals-html')->text('emails.report-new-journals-text')

View File

@@ -26,6 +26,7 @@ use Carbon\Carbon;
use Eloquent;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
@@ -97,7 +98,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
*/
class Account extends Model
{
use SoftDeletes;
use SoftDeletes, HasFactory;
/**
* The attributes that should be casted to native types.

View File

@@ -70,7 +70,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @property-read int|null $attachments_count
* @property-read int|null $transaction_journals_count
* @property-read int|null $transactions_count
* @property bool $encrypted
*/
class Category extends Model
{
@@ -135,6 +134,15 @@ class Category extends Model
return $this->morphMany(Attachment::class, 'attachable');
}
/**
* @codeCoverageIgnore
* Get all of the category's notes.
*/
public function notes(): MorphMany
{
return $this->morphMany(Note::class, 'noteable');
}
/**
* @codeCoverageIgnore
* @return BelongsToMany

View File

@@ -26,6 +26,7 @@ use Carbon\Carbon;
use Eloquent;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
@@ -132,13 +133,10 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @mixin Eloquent
* @property-read int|null $budgets_count
* @property-read int|null $categories_count
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property bool $reconciled
*/
class Transaction extends Model
{
use SoftDeletes;
use SoftDeletes, HasFactory;
/**
* The attributes that should be casted to native types.
*
@@ -185,7 +183,6 @@ class Transaction extends Model
return false;
}
/**
* Get the account this object belongs to.
*

View File

@@ -28,6 +28,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
@@ -124,7 +125,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
*/
class TransactionJournal extends Model
{
use SoftDeletes;
use SoftDeletes, HasFactory;
/**
* The attributes that should be casted to native types.
@@ -248,32 +249,6 @@ class TransactionJournal extends Model
return $this->hasMany(TransactionJournalLink::class, 'destination_id');
}
/**
* @codeCoverageIgnore
* @return bool
*/
public function isDeposit(): bool
{
if (null !== $this->transaction_type_type) {
return TransactionType::DEPOSIT === $this->transaction_type_type;
}
return $this->transactionType->isDeposit();
}
/**
* @codeCoverageIgnore
* @return bool
*/
public function isOpeningBalance(): bool
{
if (null !== $this->transaction_type_type) {
return TransactionType::OPENING_BALANCE === $this->transaction_type_type;
}
return $this->transactionType->isOpeningBalance();
}
/**
* @codeCoverageIgnore
* @return bool
@@ -287,19 +262,6 @@ class TransactionJournal extends Model
return $this->transactionType->isTransfer();
}
/**
* @codeCoverageIgnore
* @return bool
*/
public function isWithdrawal(): bool
{
if (null !== $this->transaction_type_type) {
return TransactionType::WITHDRAWAL === $this->transaction_type_type;
}
return $this->transactionType->isWithdrawal();
}
/**
* @codeCoverageIgnore
* Get all of the notes.
@@ -355,7 +317,7 @@ class TransactionJournal extends Model
if (!self::isJoined($query, 'transaction_types')) {
$query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
}
if (count($types) > 0) {
if (!empty($types)) {
$query->whereIn('transaction_types.type', $types);
}
}

View File

@@ -115,33 +115,6 @@ class AccountRepository implements AccountRepositoryInterface
}
/**
* @param string $number
* @param array $types
*
* @return Account|null
*/
public function findByAccountNumber(string $number, array $types): ?Account
{
$query = $this->user->accounts()
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
->where('account_meta.name', 'account_number')
->where('account_meta.data', json_encode($number));
if (count($types) > 0) {
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$query->whereIn('account_types.type', $types);
}
/** @var Collection $accounts */
$accounts = $query->get(['accounts.*']);
if ($accounts->count() > 0) {
return $accounts->first();
}
return null;
}
/**
* @param string $iban
* @param array $types
@@ -152,7 +125,7 @@ class AccountRepository implements AccountRepositoryInterface
{
$query = $this->user->accounts()->where('iban', '!=', '')->whereNotNull('iban');
if (count($types) > 0) {
if (!empty($types)) {
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$query->whereIn('account_types.type', $types);
}
@@ -180,7 +153,7 @@ class AccountRepository implements AccountRepositoryInterface
{
$query = $this->user->accounts();
if (count($types) > 0) {
if (!empty($types)) {
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$query->whereIn('account_types.type', $types);
}
@@ -228,16 +201,6 @@ class AccountRepository implements AccountRepositoryInterface
return null;
}
/**
* @param Account $account
*
* @return string
*/
public function getAccountType(Account $account): string
{
return $account->accountType->type;
}
/**
* Return account type or null if not found.
*
@@ -260,7 +223,7 @@ class AccountRepository implements AccountRepositoryInterface
/** @var Collection $result */
$query = $this->user->accounts();
if (count($accountIds) > 0) {
if (!empty($accountIds)) {
$query->whereIn('accounts.id', $accountIds);
}
$query->orderBy('accounts.order', 'ASC');
@@ -279,7 +242,7 @@ class AccountRepository implements AccountRepositoryInterface
{
/** @var Collection $result */
$query = $this->user->accounts();
if (count($types) > 0) {
if (!empty($types)) {
$query->accountTypeIn($types);
}
$query->orderBy('accounts.order', 'ASC');
@@ -303,7 +266,7 @@ class AccountRepository implements AccountRepositoryInterface
$query->where('name', 'account_role');
}, 'attachments']
);
if (count($types) > 0) {
if (!empty($types)) {
$query->accountTypeIn($types);
}
$query->where('active', 1);
@@ -572,7 +535,7 @@ class AccountRepository implements AccountRepositoryInterface
}
}
if (count($types) > 0) {
if (!empty($types)) {
$dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$dbQuery->whereIn('account_types.type', $types);
}
@@ -630,7 +593,7 @@ class AccountRepository implements AccountRepositoryInterface
$query->where('name', 'account_role');
}]
);
if (count($types) > 0) {
if (!empty($types)) {
$query->accountTypeIn($types);
}
$query->where('active', 0);
@@ -728,7 +691,7 @@ class AccountRepository implements AccountRepositoryInterface
});
}
}
if (count($types) > 0) {
if (!empty($types)) {
$dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$dbQuery->whereIn('account_types.type', $types);
}

View File

@@ -80,7 +80,7 @@ interface AccountRepositoryInterface
/**
* Moved here from account CRUD.
*
* @param Account $account
* @param Account $account
* @param Account|null $moveTo
*
* @return bool
@@ -96,19 +96,9 @@ interface AccountRepositoryInterface
*/
public function expandWithDoubles(Collection $accounts): Collection;
/**
* Find by account number. Is used.
*
* @param string $number
* @param array $types
*
* @return Account|null
*/
public function findByAccountNumber(string $number, array $types): ?Account;
/**
* @param string $iban
* @param array $types
* @param array $types
*
* @return Account|null
*/
@@ -116,7 +106,7 @@ interface AccountRepositoryInterface
/**
* @param string $name
* @param array $types
* @param array $types
*
* @return Account|null
*/
@@ -136,13 +126,6 @@ interface AccountRepositoryInterface
*/
public function getAccountCurrency(Account $account): ?TransactionCurrency;
/**
* @param Account $account
*
* @return string
*/
public function getAccountType(Account $account): string;
/**
* Return account type or null if not found.
*
@@ -189,7 +172,7 @@ interface AccountRepositoryInterface
* Return meta value for account. Null if not found.
*
* @param Account $account
* @param string $field
* @param string $field
*
* @return null|string
*/
@@ -282,7 +265,7 @@ interface AccountRepositoryInterface
/**
* @param string $query
* @param array $types
* @param array $types
* @param int $limit
*
* @return Collection
@@ -291,7 +274,7 @@ interface AccountRepositoryInterface
/**
* @param string $query
* @param array $types
* @param array $types
* @param int $limit
*
* @return Collection
@@ -312,7 +295,7 @@ interface AccountRepositoryInterface
/**
* @param Account $account
* @param array $data
* @param array $data
*
* @return Account
*/

View File

@@ -157,11 +157,11 @@ class BudgetRepository implements BudgetRepositoryInterface
*/
public function firstUseDate(Budget $budget): ?Carbon
{
$oldest = null;
$journal = $budget->transactionJournals()->orderBy('date', 'ASC')->first();
if (null !== $journal) {
return $journal->date;
}
return null;
}
@@ -209,7 +209,7 @@ class BudgetRepository implements BudgetRepositoryInterface
/**
* @param string $query
* @param int $limit
* @param int $limit
*
* @return Collection
*/
@@ -221,7 +221,7 @@ class BudgetRepository implements BudgetRepositoryInterface
$search->where('name', 'LIKE', sprintf('%%%s%%', $query));
}
$search->orderBy('order', 'ASC')
->orderBy('name', 'ASC')->where('active', 1);
->orderBy('name', 'ASC')->where('active', 1);
return $search->take($limit)->get();
}
@@ -278,15 +278,15 @@ class BudgetRepository implements BudgetRepositoryInterface
if ('rollover' === $type) {
$type = AutoBudget::AUTO_BUDGET_ROLLOVER;
}
$repos = app(CurrencyRepositoryInterface::class);
$currencyId = (int)($data['transaction_currency_id'] ?? 0);
$repos = app(CurrencyRepositoryInterface::class);
$currencyId = (int)($data['transaction_currency_id'] ?? 0);
$currencyCode = (string)($data['transaction_currency_code'] ?? '');
$currency = $repos->findNull($currencyId);
if(null === $currency) {
if (null === $currency) {
$currency = $repos->findByCodeNull($currencyCode);
}
if(null === $currency) {
if (null === $currency) {
$currency = app('amount')->getDefaultCurrencyByUser($this->user);
}
@@ -307,11 +307,11 @@ class BudgetRepository implements BudgetRepositoryInterface
$limitRepos->setUser($this->user);
$limitRepos->store(
[
'budget_id' => $newBudget->id,
'transaction_currency_id' => $autoBudget->transaction_currency_id,
'start_date' => $start,
'end_date' => $end,
'amount' => $autoBudget->amount,
'budget_id' => $newBudget->id,
'currency_id' => $autoBudget->transaction_currency_id,
'start_date' => $start,
'end_date' => $end,
'amount' => $autoBudget->amount,
]
);
@@ -349,15 +349,15 @@ class BudgetRepository implements BudgetRepositoryInterface
$autoBudget->budget()->associate($budget);
}
$repos = app(CurrencyRepositoryInterface::class);
$currencyId = (int)($data['transaction_currency_id'] ?? 0);
$repos = app(CurrencyRepositoryInterface::class);
$currencyId = (int)($data['transaction_currency_id'] ?? 0);
$currencyCode = (string)($data['transaction_currency_code'] ?? '');
$currency = $repos->findNull($currencyId);
if(null === $currency) {
if (null === $currency) {
$currency = $repos->findByCodeNull($currencyCode);
}
if(null === $currency) {
if (null === $currency) {
$currency = app('amount')->getDefaultCurrencyByUser($this->user);
}

View File

@@ -160,6 +160,9 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* TODO this method does not include foreign amount transactions. It only sums up "amount".
* TODO this probably also applies to the other "sumExpenses" methods.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection|null $accounts
@@ -196,6 +199,7 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface
'currency_decimal_places' => $journal['currency_decimal_places'],
];
$array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->negative($journal['amount']));
}
return $array;

View File

@@ -28,6 +28,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\CategoryFactory;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\Category;
use FireflyIII\Models\Note;
use FireflyIII\Models\RecurrenceTransactionMeta;
use FireflyIII\Models\RuleAction;
use FireflyIII\Services\Internal\Destroy\CategoryDestroyService;
@@ -202,7 +203,7 @@ class CategoryRepository implements CategoryRepositoryInterface
/**
* @param string $query
* @param int $limit
* @param int $limit
*
* @return Collection
*/
@@ -241,10 +242,28 @@ class CategoryRepository implements CategoryRepositoryInterface
if (null === $category) {
throw new FireflyException(sprintf('400003: Could not store new category with name "%s"', $data['name']));
}
if (array_key_exists('notes', $data) && '' === $data['notes']) {
$this->removeNotes($category);
}
if (array_key_exists('notes', $data) && '' !== $data['notes']) {
$this->updateNotes($category, $data['notes']);
}
return $category;
}
/**
* @param Category $category
*/
public function removeNotes(Category $category): void
{
$category->notes()->delete();
}
/**
* @param Category $category
* @param array $data
@@ -383,4 +402,31 @@ class CategoryRepository implements CategoryRepositoryInterface
}
);
}
/**
* @inheritDoc
*/
public function updateNotes(Category $category, string $notes): void
{
$dbNote = $category->notes()->first();
if (null === $dbNote) {
$dbNote = new Note;
$dbNote->noteable()->associate($category);
}
$dbNote->text = trim($notes);
$dbNote->save();
}
/**
* @inheritDoc
*/
public function getNoteText(Category $category): ?string
{
$dbNote = $category->notes()->first();
if (null === $dbNote) {
return null;
}
return $dbNote->text;
}
}

View File

@@ -33,6 +33,27 @@ use Illuminate\Support\Collection;
*/
interface CategoryRepositoryInterface
{
/**
* Remove notes.
*
* @param Category $category
*/
public function removeNotes(Category $category): void;
/**
* @param Category $category
* @param string $notes
*/
public function updateNotes(Category $category, string $notes): void;
/**
* @param Category $category
*
* @return string|null
*/
public function getNoteText(Category $category): ?string;
/**
* Delete all categories.
*/

View File

@@ -401,14 +401,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface
return $preferred;
}
/**
* @return Collection
*/
public function getEnabled(): Collection
{
return TransactionCurrency::where('enabled', true)->orderBy('code', 'ASC')->get();
}
/**
* Get currency exchange rate.
*

View File

@@ -203,11 +203,6 @@ interface CurrencyRepositoryInterface
*/
public function getCurrencyByPreference(Preference $preference): TransactionCurrency;
/**
* @return Collection
*/
public function getEnabled(): Collection;
/**
* Get currency exchange rate.
*

View File

@@ -215,7 +215,6 @@ class JournalRepository implements JournalRepositoryInterface
*/
public function getLinkNoteText(TransactionJournalLink $link): string
{
$notes = null;
/** @var Note $note */
$note = $link->notes()->first();
if (null !== $note) {

View File

@@ -60,7 +60,6 @@ trait CreatesObjectGroups
*/
protected function findOrCreateObjectGroup(string $title): ?ObjectGroup
{
$group = null;
$maxOrder = $this->getObjectGroupMaxOrder();
if (!$this->hasObjectGroup($title)) {
return ObjectGroup::create(

View File

@@ -328,8 +328,7 @@ trait ModifiesPiggyBanks
*
* @return PiggyBank
*/
public function update(PiggyBank $piggyBank, array $data): PiggyBank
{
private function updateProperties(PiggyBank $piggyBank, array $data): PiggyBank {
if (array_key_exists('name', $data) && '' !== $data['name']) {
$piggyBank->name = $data['name'];
}
@@ -344,7 +343,18 @@ trait ModifiesPiggyBanks
}
$piggyBank->startdate = $data['startdate'] ?? $piggyBank->startdate;
$piggyBank->save();
return $piggyBank;
}
/**
* @param PiggyBank $piggyBank
* @param array $data
*
* @return PiggyBank
*/
public function update(PiggyBank $piggyBank, array $data): PiggyBank
{
$piggyBank = $this->updateProperties($piggyBank, $data);
$this->updateNote($piggyBank, $data['notes'] ?? '');
// update the order of the piggy bank:

View File

@@ -327,7 +327,7 @@ class RecurringRepository implements RecurringRepositoryInterface
foreach ($journalMeta as $journalId) {
$search[] = (int)$journalId;
}
if (0 === count($search)) {
if (empty($search)) {
return new Collection;
}

View File

@@ -476,7 +476,6 @@ class TagRepository implements TagRepositoryInterface
*/
public function update(Tag $tag, array $data): Tag
{
$oldTag = $data['tag'];
$tag->tag = $data['tag'];
$tag->date = $data['date'];
$tag->description = $data['description'];

View File

@@ -275,7 +275,7 @@ trait RecurringTransactionTrait
*/
protected function updateTags(RecurrenceTransaction $transaction, array $tags): void
{
if (count($tags) > 0) {
if (!empty($tags)) {
/** @var RecurrenceMeta $entry */
$entry = $transaction->recurrenceTransactionMeta()->where('name', 'tags')->first();
if (null === $entry) {
@@ -284,7 +284,7 @@ trait RecurringTransactionTrait
$entry->value = json_encode($tags);
$entry->save();
}
if (0 === count($tags)) {
if (empty($tags)) {
// delete if present
$transaction->recurrenceTransactionMeta()->where('name', 'tags')->delete();
}

View File

@@ -47,7 +47,7 @@ class BillUpdateService
protected User $user;
/**
* @param Bill $bill
* @param Bill $bill
* @param array $data
*
* @return Bill
@@ -69,43 +69,19 @@ class BillUpdateService
$currency->enabled = true;
$currency->save();
// old values
$oldData = [
'name' => $bill->name,
'amount_min' => $bill->amount_min,
'amount_max' => $bill->amount_max,
'transaction_currency_name' => $bill->transactionCurrency->name,
];
// new values
$data['transaction_currency_name'] = $currency->name;
if (isset($data['name']) && '' !== (string)$data['name']) {
$bill->name = $data['name'];
}
if (isset($data['amount_min']) && '' !== (string)$data['amount_min']) {
$bill->amount_min = $data['amount_min'];
}
if (isset($data['amount_max']) && '' !== (string)$data['amount_max']) {
$bill->amount_max = $data['amount_max'];
}
if (isset($data['date']) && '' !== (string)$data['date']) {
$bill->date = $data['date'];
}
if (isset($data['repeat_freq']) && '' !== (string)$data['repeat_freq']) {
$bill->repeat_freq = $data['repeat_freq'];
}
if (isset($data['skip']) && '' !== (string)$data['skip']) {
$bill->skip = $data['skip'];
}
if (isset($data['active']) && is_bool($data['active'])) {
$bill->active = $data['active'];
}
$bill->transaction_currency_id = $currency->id;
$bill->match = 'EMPTY';
$bill->automatch = true;
$bill = $this->updateBillProperties($bill, $data);
$bill->transaction_currency_id = $currency->id;
$bill->save();
// old values
$oldData = [
'name' => $bill->name,
'amount_min' => $bill->amount_min,
'amount_max' => $bill->amount_max,
'transaction_currency_name' => $bill->transactionCurrency->name,
];
// update note:
if (isset($data['notes'])) {
@@ -132,6 +108,7 @@ class BillUpdateService
$bill->objectGroups()->sync([$objectGroup->id]);
$bill->save();
}
return $bill;
}
// remove if name is empty. Should be overruled by ID.
@@ -148,9 +125,10 @@ class BillUpdateService
$bill->objectGroups()->sync([$objectGroup->id]);
$bill->save();
}
return $bill;
}
if(0 === $objectGroupId) {
if (0 === $objectGroupId) {
$bill->objectGroups()->sync([]);
$bill->save();
}
@@ -159,7 +137,7 @@ class BillUpdateService
}
/**
* @param Bill $bill
* @param Bill $bill
* @param array $oldData
* @param array $newData
*/
@@ -177,9 +155,9 @@ class BillUpdateService
}
Log::debug(sprintf('Found %d rules', $rules->count()));
$fields = [
'name' => 'description_contains',
'amount_min' => 'amount_more',
'amount_max' => 'amount_less',
'name' => 'description_contains',
'amount_min' => 'amount_more',
'amount_max' => 'amount_less',
'transaction_currency_name' => 'currency_is'];
foreach ($fields as $field => $ruleTriggerKey) {
if ($oldData[$field] === $newData[$field]) {
@@ -193,9 +171,9 @@ class BillUpdateService
/**
* @param Collection $rules
* @param string $key
* @param string $oldValue
* @param string $newValue
* @param string $key
* @param string $oldValue
* @param string $newValue
*/
private function updateRules(Collection $rules, string $key, string $oldValue, string $newValue): void
{
@@ -219,7 +197,7 @@ class BillUpdateService
/**
* @param Rule $rule
* @param Rule $rule
* @param string $key
*
* @return RuleTrigger|null
@@ -231,25 +209,64 @@ class BillUpdateService
/**
* @param Bill $bill
* @param int $oldOrder
* @param int $newOrder
* @param int $oldOrder
* @param int $newOrder
*/
private function updateOrder(Bill $bill, int $oldOrder, int $newOrder): void
{
if ($newOrder > $oldOrder) {
$this->user->bills()->where('order', '<=', $newOrder)->where('order', '>', $oldOrder)
->where('bills.id', '!=', $bill->id)
->update(['order' => DB::raw('bills.order-1')]);
->where('bills.id', '!=', $bill->id)
->update(['order' => DB::raw('bills.order-1')]);
$bill->order = $newOrder;
$bill->save();
}
if ($newOrder < $oldOrder) {
$this->user->bills()->where('order', '>=', $newOrder)->where('order', '<', $oldOrder)
->where('bills.id', '!=', $bill->id)
->update(['order' => DB::raw('bills.order+1')]);
->where('bills.id', '!=', $bill->id)
->update(['order' => DB::raw('bills.order+1')]);
$bill->order = $newOrder;
$bill->save();
}
}
/**
* @param Bill $bill
* @param array $data
*
* @return Bill
*/
private function updateBillProperties(Bill $bill, array $data): Bill
{
if (isset($data['name']) && '' !== (string)$data['name']) {
$bill->name = $data['name'];
}
if (isset($data['amount_min']) && '' !== (string)$data['amount_min']) {
$bill->amount_min = $data['amount_min'];
}
if (isset($data['amount_max']) && '' !== (string)$data['amount_max']) {
$bill->amount_max = $data['amount_max'];
}
if (isset($data['date']) && '' !== (string)$data['date']) {
$bill->date = $data['date'];
}
if (isset($data['repeat_freq']) && '' !== (string)$data['repeat_freq']) {
$bill->repeat_freq = $data['repeat_freq'];
}
if (isset($data['skip']) && '' !== (string)$data['skip']) {
$bill->skip = $data['skip'];
}
if (isset($data['active']) && is_bool($data['active'])) {
$bill->active = $data['active'];
}
$bill->match = 'EMPTY';
$bill->automatch = true;
$bill->save();
return $bill;
}
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Services\Internal\Update;
use FireflyIII\Models\Category;
use FireflyIII\Models\Note;
use FireflyIII\Models\RecurrenceTransactionMeta;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\RuleTrigger;
@@ -68,6 +69,7 @@ class CategoryUpdateService
$this->updateRuleTriggers($oldName, $data['name']);
$this->updateRuleActions($oldName, $data['name']);
$this->updateRecurrences($oldName, $data['name']);
$this->updateNotes($category, $data);
return $category;
}
@@ -137,4 +139,40 @@ class CategoryUpdateService
->update(['rt_meta.value' => $newName]);
}
/**
* @param Category $category
* @param array $data
*
* @throws \Exception
*/
private function updateNotes(Category $category, array $data): void
{
$note = array_key_exists('notes', $data) ? $data['notes'] : null;
if (null === $note) {
return;
}
if ('' === $note) {
$dbNote = $category->notes()->first();
if (null !== $dbNote) {
try {
$dbNote->delete();
} catch (Exception $e) {
Log::debug($e->getMessage());
}
}
return;
}
$dbNote = $category->notes()->first();
if (null === $dbNote) {
$dbNote = new Note;
$dbNote->noteable()->associate($category);
}
$dbNote->text = trim($note);
$dbNote->save();
return;
}
}

View File

@@ -76,10 +76,6 @@ class JournalUpdateService
private $transactionGroup;
/** @var TransactionJournal The journal to update. */
private $transactionJournal;
/** @var Account If new account info is submitted, this array will hold the valid destination. */
private $validDestination;
/** @var Account If new account info is submitted, this array will hold the valid source. */
private $validSource;
/**
* JournalUpdateService constructor.

View File

@@ -41,8 +41,7 @@ class RecurrenceUpdateService
{
use TransactionTypeTrait, RecurringTransactionTrait;
/** @var User */
private $user;
private User $user;
/**
* Updates a recurrence.
@@ -99,7 +98,7 @@ class RecurrenceUpdateService
$this->createRepetitions($recurrence, $data['repetitions'] ?? []);
}
// update all transactions (and associated meta-data);
// update all transactions (and associated meta-data)
if (null !== $data['transactions']) {
$this->deleteTransactions($recurrence);
$this->createTransactions($recurrence, $data['transactions'] ?? []);

View File

@@ -350,6 +350,7 @@ class Amount
try {
$value = Crypt::decrypt($value); // verified
} catch (DecryptException $e) {
// ignore decryption error.
}
return $value;

View File

@@ -67,9 +67,9 @@ class RemoteUserGuard implements Guard
return;
}
// Get the user identifier from $_SERVER
// Get the user identifier from $_SERVER or apache filtered headers
$header = config('auth.guard_header', 'REMOTE_USER');
$userID = request()->server($header) ?? null;
$userID = request()->server($header) ?? apache_request_headers()[$header] ?? null;
if (null === $userID) {
Log::error(sprintf('No user in header "%s".', $header));
throw new FireflyException('The guard header was unexpectedly empty. See the logs.');
@@ -80,7 +80,7 @@ class RemoteUserGuard implements Guard
// store email address if present in header and not already set.
$header = config('auth.guard_email');
$emailAddress = request()->server($header) ?? null;
$emailAddress = (string) (request()->server($header) ?? null);
$preference = app('preferences')->getForUser($retrievedUser, 'remote_guard_alt_email', null);
if (null !== $emailAddress && null === $preference && $emailAddress !== $userID) {

View File

@@ -31,6 +31,7 @@ use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\User;
use Illuminate\Support\Collection;
/**
* Class FrontpageChartGenerator
*/
@@ -64,9 +65,9 @@ class FrontpageChartGenerator
{
$budgets = $this->budgetRepository->getActiveBudgets();
$data = [
['label' => (string) trans('firefly.spent_in_budget'), 'entries' => [], 'type' => 'bar'],
['label' => (string) trans('firefly.left_to_spend'), 'entries' => [], 'type' => 'bar'],
['label' => (string) trans('firefly.overspent'), 'entries' => [], 'type' => 'bar'],
['label' => (string)trans('firefly.spent_in_budget'), 'entries' => [], 'type' => 'bar'],
['label' => (string)trans('firefly.left_to_spend'), 'entries' => [], 'type' => 'bar'],
['label' => (string)trans('firefly.overspent'), 'entries' => [], 'type' => 'bar'],
];
// loop al budgets:
@@ -91,7 +92,7 @@ class FrontpageChartGenerator
$this->opsRepository->setUser($user);
$locale = app('steam')->getLocale();
$this->monthAndDayFormat = (string) trans('config.month_and_day', [], $locale);
$this->monthAndDayFormat = (string)trans('config.month_and_day', [], $locale);
}
/**
@@ -117,6 +118,7 @@ class FrontpageChartGenerator
*
* @param array $data
* @param Budget $budget
*
* @return array
*/
private function processBudget(array $data, Budget $budget): array
@@ -133,6 +135,7 @@ class FrontpageChartGenerator
if (0 !== $limits->count()) {
return $this->budgetLimits($data, $budget, $limits);
}
return $data;
}
@@ -142,6 +145,7 @@ class FrontpageChartGenerator
*
* @param array $data
* @param Budget $budget
*
* @return array
*/
private function noBudgetLimits(array $data, Budget $budget): array
@@ -154,6 +158,7 @@ class FrontpageChartGenerator
$data[1]['entries'][$title] = 0; // left to spend
$data[2]['entries'][$title] = 0; // overspent
}
return $data;
}
@@ -163,6 +168,7 @@ class FrontpageChartGenerator
* @param array $data
* @param Budget $budget
* @param Collection $limits
*
* @return array
*/
private function budgetLimits(array $data, Budget $budget, Collection $limits): array
@@ -171,6 +177,7 @@ class FrontpageChartGenerator
foreach ($limits as $limit) {
$data = $this->processLimit($data, $budget, $limit);
}
return $data;
}
@@ -180,6 +187,7 @@ class FrontpageChartGenerator
* @param array $data
* @param Budget $budget
* @param BudgetLimit $limit
*
* @return array
*/
private function processLimit(array $data, Budget $budget, BudgetLimit $limit): array
@@ -187,8 +195,12 @@ class FrontpageChartGenerator
$spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection([$budget]), $limit->transactionCurrency);
/** @var array $entry */
foreach ($spent as $entry) {
$data = $this->processRow($data, $budget, $limit, $entry);
// only spent the entry where the entry's currency matches the budget limit's currency
if ($entry['currency_id'] === (int)$limit->transaction_currency_id) {
$data = $this->processRow($data, $budget, $limit, $entry);
}
}
return $data;
}
@@ -202,6 +214,7 @@ class FrontpageChartGenerator
* @param Budget $budget
* @param BudgetLimit $limit
* @param array $entry
*
* @return array
*/
private function processRow(array $data, Budget $budget, BudgetLimit $limit, array $entry): array

View File

@@ -57,8 +57,6 @@ class WholePeriodChartGenerator
public function generate(Category $category, Carbon $start, Carbon $end): array
{
$collection = new Collection([$category]);
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
/** @var OperationsRepositoryInterface $opsRepository */
$opsRepository = app(OperationsRepositoryInterface::class);

View File

@@ -717,7 +717,7 @@ class ExportDataGenerator
*/
private function mergeTags(array $tags): string
{
if (0 === count($tags)) {
if (empty($tags)) {
return '';
}
$smol = [];

View File

@@ -72,41 +72,6 @@ class AccountForm
return $grouped;
}
/**
* Shows a <select> with all active asset accounts.
*
* @param string $name
* @param mixed $value
* @param array $options
*
* @return string
*/
public function activeAssetAccountList(string $name, $value = null, array $options = null): string
{
$types = [AccountType::ASSET, AccountType::DEFAULT];
$grouped = $this->getAccountsGrouped($types);
return $this->select($name, $grouped, $value, $options);
}
/**
* Return a list that includes liabilities.
*
* @param string $name
* @param mixed $value
* @param array $options
*
* @return string
*/
public function activeLongAccountList(string $name, $value = null, array $options = null): string
{
$types = [AccountType::ASSET, AccountType::DEFAULT, AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN,];
$grouped = $this->getAccountsGrouped($types);
return $this->select($name, $grouped, $value, $options);
}
/**
* Grouped dropdown list of all accounts that are valid as the destination of a withdrawal.
*

View File

@@ -129,7 +129,7 @@ trait RequestInformation
$triggers = [];
$data = $request->get('triggers');
if (is_array($data)) {
foreach ($data as $index => $triggerInfo) {
foreach ($data as $triggerInfo) {
$triggers[] = [
'type' => $triggerInfo['type'] ?? '',
'value' => $triggerInfo['value'] ?? '',

View File

@@ -153,11 +153,8 @@ class Preferences
{
$fullName = sprintf('preference%s%s', $user->id, $name);
if (Cache::has($fullName)) {
Log::debug(sprintf('Retrieved preference "%s" from cache ("%s").', $name, $fullName));
return Cache::get($fullName);
}
Log::debug(sprintf('Retrieved preference "%s" FRESH.', $name));
$preference = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data', 'updated_at', 'created_at']);
if (null !== $preference && null === $preference->data) {
try {
@@ -192,7 +189,6 @@ class Preferences
public function getFreshForUser(User $user, string $name, $default = null): ?Preference
{
$fullName = sprintf('preference%s%s', $user->id, $name);
Log::debug(sprintf('Retrieved preference "%s" FRESH.', $name));
$preference = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data', 'updated_at', 'created_at']);
if (null !== $preference && null === $preference->data) {
try {
@@ -272,7 +268,6 @@ class Preferences
public function forget(User $user, string $name): void
{
$key = sprintf('preference%s%s', $user->id, $name);
Log::debug(sprintf('Going to forget key "%s"', $key));
Cache::forget($key);
Cache::put($key, '', 5);
}
@@ -307,7 +302,6 @@ class Preferences
if (null !== $pref) {
$pref->data = $value;
$pref->save();
Log::debug(sprintf('Saved new value under existing preference object. "%s"', $fullName));
Cache::forever($fullName, $pref);
return $pref;
@@ -319,7 +313,6 @@ class Preferences
$pref->user()->associate($user);
$pref->save();
Log::debug(sprintf('Saved new value under new preference object. "%s"', $fullName));
Cache::forever($fullName, $pref);
return $pref;

View File

@@ -154,9 +154,9 @@ class BudgetReportGenerator
*/
private function generalBudgetReport(): void
{
$budgets = $this->repository->getBudgets();
$budgetList = $this->repository->getBudgets();
/** @var Budget $budget */
foreach ($budgets as $budget) {
foreach ($budgetList as $budget) {
$this->processBudget($budget);
}
}
@@ -192,14 +192,14 @@ class BudgetReportGenerator
*/
private function processLimit(Budget $budget, BudgetLimit $limit): void
{
$budgetId = (int)$budget->id;
$limitId = (int)$limit->id;
$currency = $limit->transactionCurrency ?? $this->currency;
$currencyId = (int)$currency->id;
$expenses = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, $this->accounts, new Collection([$budget]));
$spent = $expenses[$currencyId]['sum'] ?? '0';
$left = -1 === bccomp(bcadd($limit->amount, $spent), '0') ? '0' : bcadd($limit->amount, $spent);
$overspent = 1 === bccomp(bcmul($spent, '-1'), $limit->amount) ? bcadd($spent, $limit->amount) : '0';
$budgetId = (int)$budget->id;
$limitId = (int)$limit->id;
$limitCurrency = $limit->transactionCurrency ?? $this->currency;
$currencyId = (int)$limitCurrency->id;
$expenses = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, $this->accounts, new Collection([$budget]));
$spent = $expenses[$currencyId]['sum'] ?? '0';
$left = -1 === bccomp(bcadd($limit->amount, $spent), '0') ? '0' : bcadd($limit->amount, $spent);
$overspent = 1 === bccomp(bcmul($spent, '-1'), $limit->amount) ? bcadd($spent, $limit->amount) : '0';
$this->report['budgets'][$budgetId]['budget_limits'][$limitId] = $this->report['budgets'][$budgetId]['budget_limits'][$limitId] ?? [
'budget_limit_id' => $limitId,
@@ -212,10 +212,10 @@ class BudgetReportGenerator
'left' => $left,
'overspent' => $overspent,
'currency_id' => $currencyId,
'currency_code' => $currency->code,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'currency_code' => $limitCurrency->code,
'currency_name' => $limitCurrency->name,
'currency_symbol' => $limitCurrency->symbol,
'currency_decimal_places' => $limitCurrency->decimal_places,
];
// make sum information:
@@ -226,10 +226,10 @@ class BudgetReportGenerator
'left' => '0',
'overspent' => '0',
'currency_id' => $currencyId,
'currency_code' => $currency->code,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'currency_code' => $limitCurrency->code,
'currency_name' => $limitCurrency->name,
'currency_symbol' => $limitCurrency->symbol,
'currency_decimal_places' => $limitCurrency->decimal_places,
];
$this->report['sums'][$currencyId]['budgeted'] = bcadd($this->report['sums'][$currencyId]['budgeted'], $limit->amount);
$this->report['sums'][$currencyId]['spent'] = bcadd($this->report['sums'][$currencyId]['spent'], $spent);

View File

@@ -54,6 +54,7 @@ class Steam
$cache->addProperty($account->id);
$cache->addProperty('balance');
$cache->addProperty($date);
$cache->addProperty($currency ? $currency->id : 0);
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
@@ -186,7 +187,7 @@ class Steam
$end->addDay();
$balances = [];
$formatted = $start->format('Y-m-d');
$startBalance = $this->balance($account, $start);
$startBalance = $this->balance($account, $start, $currency);
/** @var AccountRepositoryInterface $repository */
@@ -196,7 +197,7 @@ class Steam
$repository->setUser($account->user);
$currency = $repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUser($account->user);
}
$currencyId = $currency->id;
$currencyId = (int)$currency->id;
$start->addDay();

View File

@@ -1,127 +0,0 @@
<?php
declare(strict_types=1);
/*
* Breadcrumbs.php
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Support\Twig;
use FireflyIII\Exceptions\FireflyException;
use Route;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
/**
* Class Breadcrumbs
*/
class Breadcrumbs extends AbstractExtension
{
/**
* {@inheritdoc}
*/
public function getFunctions(): array
{
return [
$this->renderBreadcrumb(),
];
}
/**
* @return TwigFunction
*/
private function renderBreadcrumb(): TwigFunction
{
return new TwigFunction(
'ff3bc',
static function (?array $args): string {
$name = Route::getCurrentRoute()->getName() ?? '';
// loop for actual breadcrumb:
$arr = config(sprintf('bc.%s', $name));
if (null === $arr) {
throw new FireflyException(sprintf('No breadcrumbs for route "%s".', $name));
}
$breadcrumbs = $this->getBreadcrumbs($arr);
return $this->getHtml($breadcrumbs);
},
['is_safe' => ['html']]
);
}
/**
* @param array $arr
*
* @return array
* @throws FireflyException
*/
private function getBreadcrumbs(array $arr)
{
$breadcrumbs = [];
$hasParent = true;
$loop = 0;
while (true === $hasParent && $loop < 30) {
$breadcrumbs[] = $arr;
if (null === $arr['parent']) {
$hasParent = false;
}
if (null !== $arr['parent']) {
$arr = config(sprintf('bc.%s', $arr['parent']));
if (null === $arr) {
throw new FireflyException(sprintf('No (2) breadcrumbs for route "%s".', $name));
}
}
$loop++; // safety catch
}
// reverse order
return array_reverse($breadcrumbs);
}
/**
* @param array $breadcrumbs
*
* @return string
*/
private function getHtml(array $breadcrumbs): string
{
// get HTML
$html = '<ol class="breadcrumb float-sm-right">';
foreach ($breadcrumbs as $index => $breadcrumb) {
$class = 'breadcrumb-item';
if ($index === count($breadcrumbs) - 1) {
// active!
$class = 'breadcrumb-item active';
}
$route = '#';
if (null !== $breadcrumb['static_route']) {
$route = route($breadcrumb['static_route']);
}
if (null !== $breadcrumb['dynamic_route']) {
$route = route($breadcrumb['dynamic_route'], $args[$index - 1] ?? []);
}
$html .= sprintf('<li class="%1$s"><a href="%2$s" title="%3$s">%3$s</a></li>', $class, $route, trans($breadcrumb['title']));
}
return $html . '</ol>';
}
}

View File

@@ -25,6 +25,7 @@ namespace FireflyIII\Transformers;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Category\OperationsRepositoryInterface;
use Illuminate\Support\Collection;
@@ -33,8 +34,8 @@ use Illuminate\Support\Collection;
*/
class CategoryTransformer extends AbstractTransformer
{
/** @var OperationsRepositoryInterface */
private $opsRepository;
private OperationsRepositoryInterface $opsRepository;
private CategoryRepositoryInterface $repository;
/**
* CategoryTransformer constructor.
@@ -44,6 +45,7 @@ class CategoryTransformer extends AbstractTransformer
public function __construct()
{
$this->opsRepository = app(OperationsRepositoryInterface::class);
$this->repository = app(CategoryRepositoryInterface::class);
}
/**
@@ -56,6 +58,7 @@ class CategoryTransformer extends AbstractTransformer
public function transform(Category $category): array
{
$this->opsRepository->setUser($category->user);
$this->repository->setUser($category->user);
$spent = [];
$earned = [];
@@ -65,11 +68,14 @@ class CategoryTransformer extends AbstractTransformer
$earned = $this->beautify($this->opsRepository->sumIncome($start, $end, null, new Collection([$category])));
$spent = $this->beautify($this->opsRepository->sumExpenses($start, $end, null, new Collection([$category])));
}
$notes = $this->repository->getNoteText($category);
return [
'id' => (int)$category->id,
'created_at' => $category->created_at->toAtomString(),
'updated_at' => $category->updated_at->toAtomString(),
'name' => $category->name,
'notes' => $notes,
'spent' => $spent,
'earned' => $earned,
'links' => [
@@ -90,7 +96,7 @@ class CategoryTransformer extends AbstractTransformer
{
$return = [];
foreach ($array as $data) {
$data['sum'] = number_format((float) $data['sum'], (int) $data['currency_decimal_places'], '.', '');
$data['sum'] = number_format((float)$data['sum'], (int)$data['currency_decimal_places'], '.', '');
$return[] = $data;
}

View File

@@ -569,6 +569,6 @@ class TransactionGroupTransformer extends AbstractTransformer
return null;
}
return $object['interest_date']->toAtomString();
return $object[$key]->toAtomString();
}
}

View File

@@ -360,16 +360,8 @@ class FireflyValidator extends Validator
return false;
}
}
return true;
// and finally a "will match everything check":
$classes = array_keys(config('firefly.search.operators'));
/** @var TriggerInterface $class */
$class = $classes[$triggerType] ?? false;
if (false === $class) {
return false;
}
return !$class::willMatchEverything($value);
}
/**

View File

@@ -2,6 +2,49 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 5.4.6 (API 1.4.0) - 2020-10-07
### Added
- [Issue 4031](https://github.com/firefly-iii/firefly-iii/issues/4031) Rule groups can be collapsed.
- [Issue 4002](https://github.com/firefly-iii/firefly-iii/issues/4002) Category now support notes, although they're not displayed anywhere yet.
### Changed
- Upgrade to Laravel 8
### Deprecated
- Initial release.
### Removed
- Initial release.
### Fixed
- [Issue 4001](https://github.com/firefly-iii/firefly-iii/issues/4001) [issue 4005](https://github.com/firefly-iii/firefly-iii/issues/4005) [issue 4011](https://github.com/firefly-iii/firefly-iii/issues/4011) Special characters are double escaped.
- [Issue 4006](https://github.com/firefly-iii/firefly-iii/issues/4006) Unclear error message fixed.
- [Issue 4015](https://github.com/firefly-iii/firefly-iii/issues/4015) Better handling of headers in Apache.
- [Issue 4023](https://github.com/firefly-iii/firefly-iii/issues/4023) Fix issue with logout and admin view.
- Missing help text can now be translated.
- Demo sites send messages to me, not "demo@firefly".
### Security
- Initial release.
### API
- Initial release
## 5.4.5 (API 1.4.0) - 2020-10-28
### Fixed
- [Issue 3853](https://github.com/firefly-iii/firefly-iii/issues/3853) Could not create rules with IBAN values.
- [Issue 3991](https://github.com/firefly-iii/firefly-iii/issues/3991) Hardcoded array key broke editing.
- [Issue 3992](https://github.com/firefly-iii/firefly-iii/issues/3992) Amount problems in account chart for multi-currency charts.
- [Issue 4000](https://github.com/firefly-iii/firefly-iii/issues/4000) Budget chart did not handle multiple currencies well.
- [Issue 4003](https://github.com/firefly-iii/firefly-iii/issues/4003) Was unable to create new auto budget limits in foreign currency.
### Security
- [Issue 3990](https://github.com/firefly-iii/firefly-iii/issues/3990) Unescaped content could break the auto-complete.
## 5.4.4 (API 1.4.0) - 2020-10-24
### Changed

View File

@@ -84,14 +84,15 @@
"ext-xml": "*",
"adldap2/adldap2-laravel": "6.*",
"bacon/bacon-qr-code": "2.*",
"davejamesmiller/laravel-breadcrumbs": "5.*",
"diglactic/laravel-breadcrumbs": "^6.0",
"doctrine/dbal": "2.*",
"fideloper/proxy": "4.*",
"gdbots/query-parser": "^2.0",
"guzzlehttp/guzzle": "^7.2",
"jc5/google2fa-laravel": "2.0.5",
"laravel/framework": "^7.0",
"laravel/passport": "9.*",
"laravel/ui": "^2.0",
"laravel/framework": "^8.0",
"laravel/passport": "10.*",
"laravel/ui": "^3.0",
"laravelcollective/html": "6.*",
"league/commonmark": "1.*",
"league/csv": "^9.6",
@@ -107,30 +108,22 @@
"barryvdh/laravel-ide-helper": "2.*",
"ergebnis/phpstan-rules": "^0.15.0",
"filp/whoops": "2.*",
"fzaninotto/faker": "1.*",
"fakerphp/faker": "1.*",
"johnkary/phpunit-speedtrap": "^3.1",
"mockery/mockery": "1.*",
"nunomaduro/larastan": "^0.6.2",
"phpstan/phpstan": "^0.12.34",
"phpstan/phpstan-deprecation-rules": "^0.12.5",
"phpunit/phpunit": "^9.2",
"psalm/plugin-laravel": "^1.1",
"roave/security-advisories": "dev-master",
"thecodingmachine/phpstan-strict-rules": "^0.12.0",
"vimeo/psalm": "^3.10"
"vimeo/psalm": "^4.1"
},
"suggest": {
},
"repositories": [
],
"autoload": {
"classmap": [
"database/seeds",
"database/factories"
],
"psr-4": {
"FireflyIII\\": "app/"
"FireflyIII\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {

2358
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,30 +0,0 @@
<?php
return [
'index' => [
'parent' => null,
'title' => 'breadcrumbs.home',
'static_route' => 'index',
'dynamic_route' => null,
],
'home' => [
'parent' => null,
'title' => 'breadcrumbs.home',
'static_route' => 'index',
'dynamic_route' => null,
],
'accounts' => [
'index' => [
'parent' => 'index',
'title' => 'breadcrumbs.accounts',
'static_route' => null,
'dynamic_route' => 'accounts.index',
],
'show' => [
'parent' => 'accounts.index',
'title' => 'breadcrumbs.accounts_show',
'static_route' => null,
'dynamic_route' => 'accounts.show',
],
],
];

View File

@@ -94,8 +94,7 @@ return [
'telemetry' => true,
],
//'encryption' => null === env('USE_ENCRYPTION') || true === env('USE_ENCRYPTION'),
'version' => '5.4.4',
'version' => '5.4.6',
'api_version' => '1.4.0',
'db_version' => 15,
'maxUploadSize' => 1073741824, // 1 GB

View File

@@ -25,7 +25,6 @@ use TwigBridge\Extension\Laravel\Url;
use TwigBridge\Extension\Loader\Facades;
use TwigBridge\Extension\Loader\Filters;
use TwigBridge\Extension\Loader\Functions;
use FireflyIII\Support\Twig\Breadcrumbs;
/**
* Configuration options for Twig.
@@ -138,7 +137,6 @@ return [
Rule::class,
TransactionGroupTwig::class,
Translation::class,
Breadcrumbs::class,
],
@@ -194,8 +192,7 @@ return [
],
'AccountForm' => [
'is_safe' => [
'activeAssetAccountList', 'activeLongAccountList', 'activeWithdrawalDestinations', 'activeDepositDestinations',
'assetAccountCheckList', 'assetAccountList', 'longAccountList',
'activeWithdrawalDestinations', 'activeDepositDestinations', 'assetAccountCheckList', 'assetAccountList', 'longAccountList',
],
],
'CurrencyForm' => [

View File

@@ -1,38 +0,0 @@
<?php
declare(strict_types=1);
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
$factory->define(Account::class, function (Faker $faker) {
return [
'user_id' => 1,
'account_type_id' => 1,
'name' => $faker->words(3, true),
'virtual_balance' => '0',
'active' => 1,
'encrypted' => 0,
'order' => 1,
];
});
$factory->state(Account::class, AccountType::ASSET, function ($faker) {
return [
'account_type_id' => 3,
];
});
$factory->state(Account::class, AccountType::INITIAL_BALANCE, function ($faker) {
return [
'account_type_id' => 6,
];
});
$factory->state(Account::class, AccountType::EXPENSE, function ($faker) {
return [
'account_type_id' => 4,
];
});

View File

@@ -0,0 +1,101 @@
<?php
/*
* AccountFactory.php
* Copyright (c) 2020 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 Database\Factories\FireflyIII\Models;
use FireflyIII\Models\Account;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* Class AccountFactory
*/
class AccountFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Account::class;
/**
* @inheritDoc
*/
public function definition()
{
return [
'user_id' => 1,
'account_type_id' => 1,
'name' => $this->faker->words(3, true),
'virtual_balance' => '0',
'active' => 1,
'encrypted' => 0,
'order' => 1,
];
}
/**
* @return AccountFactory
*/
public function asset()
{
return $this->state(
function () {
return [
'account_type_id' => 3,
];
}
);
}
/**
* @return AccountFactory
*/
public function initialBalance()
{
return $this->state(
function () {
return [
'account_type_id' => 6,
];
}
);
}
/**
* @return AccountFactory
*/
public function expense()
{
return $this->state(
function () {
return [
'account_type_id' => 4,
];
}
);
}
}

View File

@@ -1,8 +1,7 @@
<?php
/**
* ZeroOrMore.php
* Copyright (c) 2019 james@firefly-iii.org
/*
* TransactionFactory.php
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -22,44 +21,35 @@
declare(strict_types=1);
namespace FireflyIII\Rules;
namespace Database\Factories\FireflyIII\Models;
use Illuminate\Contracts\Validation\Rule;
use FireflyIII\Models\Transaction;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
*
* Class ZeroOrMore
* @codeCoverageIgnore
* Class TransactionFactory
*/
class ZeroOrMore implements Rule
class TransactionFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Transaction::class;
/**
* Get the validation error message.
* Define the model's default state.
*
* @return string
* @return array
*/
public function message(): string
public function definition()
{
return trans('validation.zero_or_more');
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
*
* @return bool
*/
public function passes($attribute, $value): bool
{
$value = (string)$value;
if ('' === $value) {
return true;
}
$res = bccomp('0', $value);
return $res <= 0;
return [
'transaction_journal_id' => 0,
'account_id' => 0,
'amount' => 5,
];
}
}

View File

@@ -0,0 +1,123 @@
<?php
/*
* TransactionJournalFactory.php
* Copyright (c) 2020 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 Database\Factories\FireflyIII\Models;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* Class TransactionJournalFactory
*/
class TransactionJournalFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = TransactionJournal::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'user_id' => 1,
'transaction_type_id' => 1,
'description' => $this->faker->words(3, true),
'tag_count' => 0,
'date' => $this->faker->date('Y-m-d'),
];
}
/**
* @return \Illuminate\Database\Eloquent\Factories\Factory
*/
public function openingBalance()
{
return $this
->state(fn () => ['transaction_type_id' => 4])
->afterCreating(
function (TransactionJournal $journal) {
// fix factory
$obAccount = Account::factory(Account::class)->initialBalance()->create();
$assetAccount = Account::factory(Account::class)->asset()->create();
Transaction::factory()->create(
[
'account_id' => $obAccount->id,
'transaction_journal_id' => $journal->id,
'amount' => '5',
]
);
Transaction::factory()->create(
[
'account_id' => $assetAccount->id,
'transaction_journal_id' => $journal->id,
'amount' => '-5',
]
);
}
);
}
/**
* @return \Illuminate\Database\Eloquent\Factories\Factory
*/
public function brokenOpeningBalance()
{
return $this->state(
function () {
return [
'transaction_type_id' => 4,
];
}
)->afterCreating(
function (TransactionJournal $journal) {
$ob1 = Account::factory(Account::class)->initialBalance()->create();
$ob2 = Account::factory(Account::class)->initialBalance()->create();
Transaction::factory()->create(
[
'account_id' => $ob1->id,
'transaction_journal_id' => $journal->id,
'amount' => '5',
]
);
Transaction::factory()->create(
[
'account_id' => $ob2->id,
'transaction_journal_id' => $journal->id,
'amount' => '5',
]
);
}
);
}
}

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