mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-19 02:52:44 +00:00
Compare commits
38 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4663fb2f12 | ||
|
e844ab592d | ||
|
3dcb35710b | ||
|
8d87abde64 | ||
|
2b78485a61 | ||
|
9d057b853f | ||
|
bd269eaadf | ||
|
7096c65f50 | ||
|
1462b0de69 | ||
|
86a1f170c4 | ||
|
40389fb6d5 | ||
|
36021d84cf | ||
|
1278f92355 | ||
|
573f9adb49 | ||
|
431c99c27b | ||
|
77cc558931 | ||
|
42043de34f | ||
|
f2b2c2109f | ||
|
7fe29ad983 | ||
|
07d9bcfb9d | ||
|
fdd235e4cb | ||
|
2646acadb8 | ||
|
5ef646b810 | ||
|
e8bdb5ef38 | ||
|
7a851c2cc6 | ||
|
624784e54e | ||
|
20eb2ebe58 | ||
|
25f8acb417 | ||
|
f75e6430b1 | ||
|
e72a483c49 | ||
|
68934858ce | ||
|
c08b5177d9 | ||
|
fe8635f1ce | ||
|
e96d28b981 | ||
|
784cc3d52d | ||
|
2ab3fb3a71 | ||
|
ff765d4687 | ||
|
ed36604050 |
12
.ci/php-cs-fixer/composer.lock
generated
12
.ci/php-cs-fixer/composer.lock
generated
@@ -379,16 +379,16 @@
|
||||
},
|
||||
{
|
||||
"name": "friendsofphp/php-cs-fixer",
|
||||
"version": "v3.17.0",
|
||||
"version": "v3.18.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
|
||||
"reference": "3f0ed862f22386c55a767461ef5083bddceeed79"
|
||||
"reference": "b123395c9fa3a70801f816f13606c0f3a7ada8df"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/3f0ed862f22386c55a767461ef5083bddceeed79",
|
||||
"reference": "3f0ed862f22386c55a767461ef5083bddceeed79",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/b123395c9fa3a70801f816f13606c0f3a7ada8df",
|
||||
"reference": "b123395c9fa3a70801f816f13606c0f3a7ada8df",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -463,7 +463,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
|
||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.17.0"
|
||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.18.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -471,7 +471,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-05-22T19:59:32+00:00"
|
||||
"time": "2023-06-18T22:25:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/cache",
|
||||
|
@@ -30,12 +30,9 @@ SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
|
||||
# clean up php code
|
||||
cd $SCRIPT_DIR/php-cs-fixer
|
||||
composer update
|
||||
composer update --quiet
|
||||
rm -f .php-cs-fixer.cache
|
||||
echo 'Removed cache...'
|
||||
echo 'Running...'
|
||||
PHP_CS_FIXER_IGNORE_ENV=true ./vendor/bin/php-cs-fixer fix --config $SCRIPT_DIR/php-cs-fixer/.php-cs-fixer.php --allow-risky=yes
|
||||
echo 'Done!'
|
||||
cd $SCRIPT_DIR/..
|
||||
|
||||
exit 0
|
||||
|
37
.ci/phpmd.sh
Normal file
37
.ci/phpmd.sh
Normal file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#
|
||||
# phpmd.sh
|
||||
# Copyright (c) 2023 james@firefly-iii.org
|
||||
#
|
||||
# This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
|
||||
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
|
||||
cd $SCRIPT_DIR/phpmd
|
||||
composer update --quiet
|
||||
./vendor/bin/phpmd \
|
||||
$SCRIPT_DIR/../app text phpmd.xml \
|
||||
--exclude $SCRIPT_DIR/../app/resources/** \
|
||||
--exclude $SCRIPT_DIR/../app/frontend/** \
|
||||
--exclude $SCRIPT_DIR/../app/public/** \
|
||||
--exclude $SCRIPT_DIR/../app/vendor/** \
|
||||
|
||||
cd $SCRIPT_DIR/..
|
||||
|
||||
exit 0
|
1
.ci/phpmd/.gitignore
vendored
Normal file
1
.ci/phpmd/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
vendor
|
5
.ci/phpmd/composer.json
Normal file
5
.ci/phpmd/composer.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"require-dev": {
|
||||
"phpmd/phpmd": "^2.13"
|
||||
}
|
||||
}
|
1012
.ci/phpmd/composer.lock
generated
Normal file
1012
.ci/phpmd/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
52
.ci/phpmd/phpmd.xml
Normal file
52
.ci/phpmd/phpmd.xml
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ruleset name="pcsg-generated-ruleset"
|
||||
xmlns="http://pmd.sf.net/ruleset/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
|
||||
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
|
||||
<description>Bla bla</description>
|
||||
|
||||
<!--
|
||||
Commando vanuit firefly directory:
|
||||
phpmd database,app,tests html /gdrive-all/development/phpmd/phpmd.xml > public/report.html
|
||||
-->
|
||||
|
||||
<!-- Import the entire controversial code rule set -->
|
||||
<rule ref="rulesets/controversial.xml">
|
||||
<exclude name="CamelCasePropertyName" />
|
||||
</rule>
|
||||
|
||||
<!-- clean code -->
|
||||
<rule ref="rulesets/codesize.xml" />
|
||||
<rule ref="rulesets/design.xml" />
|
||||
<rule ref="rulesets/naming.xml" />
|
||||
<rule ref="rulesets/unusedcode.xml" />
|
||||
|
||||
<rule ref="rulesets/codesize.xml/CyclomaticComplexity">
|
||||
<properties>
|
||||
<property name="reportLevel" value="5"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="rulesets/codesize.xml/NPathComplexity">
|
||||
<properties>
|
||||
<property name="minimum" value="128"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="rulesets/codesize.xml/ExcessiveMethodLength">
|
||||
<properties>
|
||||
<property name="minimum" value="40"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="rulesets/codesize.xml/ExcessiveParameterList">
|
||||
<properties>
|
||||
<property name="minimum" value="5"/>
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
<!-- include clean code manually -->
|
||||
<rule ref="rulesets/cleancode.xml/BooleanArgumentFlag" />
|
||||
<rule ref="rulesets/cleancode.xml/ElseExpression" />
|
||||
|
||||
<!-- no this one -->
|
||||
<!--<rule ref="rulesets/cleancode.xml/StaticAccess" />-->
|
||||
</ruleset>
|
@@ -59,3 +59,5 @@ fi
|
||||
|
||||
# restore .env file
|
||||
mv $SCRIPT_DIR/../.env.backup $SCRIPT_DIR/../.env
|
||||
|
||||
cd $SCRIPT_DIR/..
|
||||
|
@@ -16,6 +16,7 @@ ja_JP
|
||||
ko_KR
|
||||
nb_NO
|
||||
nl_NL
|
||||
nn_NO
|
||||
pl_PL
|
||||
pt_BR
|
||||
pt_PT
|
||||
|
@@ -143,6 +143,7 @@ MAIL_FROM=changeme@example.com
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_SENDMAIL_COMMAND=
|
||||
|
||||
# Other mail drivers:
|
||||
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE
|
||||
|
29
.github/code_of_conduct.md
vendored
29
.github/code_of_conduct.md
vendored
@@ -2,7 +2,10 @@
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making
|
||||
participation in our project and our community a harassment-free experience for everyone, regardless of age, body size,
|
||||
disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race,
|
||||
religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
@@ -24,23 +27,35 @@ Examples of unacceptable behavior by participants include:
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take
|
||||
appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits,
|
||||
issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any
|
||||
contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the
|
||||
project or its community. Examples of representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed representative at an online or offline
|
||||
event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at james@firefly-iii.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at
|
||||
james@firefly-iii.org. The project team will review and investigate all complaints, and will respond in a way that it
|
||||
deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the
|
||||
reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent
|
||||
repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available
|
||||
at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
|
10
.github/its_you_not_me.md
vendored
10
.github/its_you_not_me.md
vendored
@@ -2,9 +2,11 @@
|
||||
|
||||
Sometimes bugs reported to Firefly III are configuration and system problems on the user's side.
|
||||
|
||||
If you run into any of the following problems, there's a good chance it's not a Firefly III issue, but a configuration issue.
|
||||
If you run into any of the following problems, there's a good chance it's not a Firefly III issue, but a configuration
|
||||
issue.
|
||||
|
||||
- ⚠️ Firefly III can't connect to the database when starting or the password is wrong, even though you're sure it's correct.
|
||||
- ⚠️ Firefly III can't connect to the database when starting or the password is wrong, even though you're sure it's
|
||||
correct.
|
||||
- ⚠️ Errors about a missing `APP_KEY` or other encryption/hash problems
|
||||
- ⚠️ You can't login due to `419` errors (page expired)
|
||||
- ⚠️ Any `500` error when starting Firefly III
|
||||
@@ -13,4 +15,6 @@ If you run into any of the following problems, there's a good chance it's not a
|
||||
- ⚠️ Firefly III does not work behind your reverse proxy
|
||||
- ⚠️ You can't connect to the Data Importer due to 404's or authentication issues.
|
||||
|
||||
If you run into an issue like this, please start a [discussion](https://github.com/firefly-iii/firefly-iii/discussions) or chat on [Gitter.im](https://gitter.im/firefly-iii/firefly-iii). There's a good chance it's not a bug but something we can fix rather quickly :+1:
|
||||
If you run into an issue like this, please start a [discussion](https://github.com/firefly-iii/firefly-iii/discussions)
|
||||
or chat on [Gitter.im](https://gitter.im/firefly-iii/firefly-iii). There's a good chance it's not a bug but something we
|
||||
can fix rather quickly :+1:
|
||||
|
6
.github/support.md
vendored
6
.github/support.md
vendored
@@ -9,7 +9,8 @@ First of all: thank you for reporting a bug instead of ditching the tool altoget
|
||||
1. Open bugs will have open issues, so search for one first.
|
||||
2. If your feature request is already there, vote on it with :+1: or :-1: reactions.
|
||||
3. Do NOT hijack old issues with the bug you found, open your own issue.
|
||||
4. If relevant, take the time and see if the [demo site](https://demo.firefly-iii.org/) is also suffering from your issue.
|
||||
4. If relevant, take the time and see if the [demo site](https://demo.firefly-iii.org/) is also suffering from your
|
||||
issue.
|
||||
5. If relevant, read the [documentation](https://docs.firefly-iii.org/).
|
||||
|
||||
Please follow these guidelines when opening new issues:
|
||||
@@ -25,7 +26,8 @@ Only then [create a new issue](https://github.com/firefly-iii/firefly-iii/issues
|
||||
## Issue closure and abandonment policy
|
||||
|
||||
- Issues can be converted into discussions if it's not a bug or feature request.
|
||||
- Features that won't be implemented will be labelled "wontfix". [This isn't personal](https://docs.firefly-iii.org/firefly-iii/about-firefly-iii/what-its-not/).
|
||||
- Features that won't be implemented will be labelled "
|
||||
wontfix". [This isn't personal](https://docs.firefly-iii.org/firefly-iii/about-firefly-iii/what-its-not/).
|
||||
- Issues can be closed if they're duplicates of other issues.
|
||||
- Issues can be closed if the answer is in the FAQ.
|
||||
- Issues will be closed automatically after 14 days.
|
||||
|
3
.github/workflows/closed-issues.yml
vendored
3
.github/workflows/closed-issues.yml
vendored
@@ -7,8 +7,7 @@ jobs:
|
||||
auto_comment:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
uses: aws-actions/closed-issue-message@v1
|
||||
- uses: aws-actions/closed-issue-message@v1
|
||||
with:
|
||||
message: |
|
||||
Hi there! This is an automatic reply. `Share and enjoy`
|
||||
|
@@ -74,42 +74,6 @@ abstract class Controller extends BaseController
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to help build URL's.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final protected function buildParams(): string
|
||||
{
|
||||
$return = '?';
|
||||
$params = [];
|
||||
foreach ($this->parameters as $key => $value) {
|
||||
if ('page' === $key) {
|
||||
continue;
|
||||
}
|
||||
if ($value instanceof Carbon) {
|
||||
$params[$key] = $value->format('Y-m-d');
|
||||
continue;
|
||||
}
|
||||
$params[$key] = $value;
|
||||
}
|
||||
|
||||
return $return.http_build_query($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Manager
|
||||
*/
|
||||
final protected function getManager(): Manager
|
||||
{
|
||||
// create some objects:
|
||||
$manager = new Manager();
|
||||
$baseUrl = request()->getSchemeAndHttpHost().'/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
return $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to grab all parameters from the URL.
|
||||
*
|
||||
@@ -211,4 +175,40 @@ abstract class Controller extends BaseController
|
||||
|
||||
return $bag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to help build URL's.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final protected function buildParams(): string
|
||||
{
|
||||
$return = '?';
|
||||
$params = [];
|
||||
foreach ($this->parameters as $key => $value) {
|
||||
if ('page' === $key) {
|
||||
continue;
|
||||
}
|
||||
if ($value instanceof Carbon) {
|
||||
$params[$key] = $value->format('Y-m-d');
|
||||
continue;
|
||||
}
|
||||
$params[$key] = $value;
|
||||
}
|
||||
|
||||
return $return . http_build_query($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Manager
|
||||
*/
|
||||
final protected function getManager(): Manager
|
||||
{
|
||||
// create some objects:
|
||||
$manager = new Manager();
|
||||
$baseUrl = request()->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
return $manager;
|
||||
}
|
||||
}
|
||||
|
@@ -201,6 +201,91 @@ class DestroyController extends Controller
|
||||
return response()->json([], 204);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyBudgets(): void
|
||||
{
|
||||
/** @var AvailableBudgetRepositoryInterface $abRepository */
|
||||
$abRepository = app(AvailableBudgetRepositoryInterface::class);
|
||||
$abRepository->destroyAll();
|
||||
|
||||
/** @var BudgetLimitRepositoryInterface $blRepository */
|
||||
$blRepository = app(BudgetLimitRepositoryInterface::class);
|
||||
$blRepository->destroyAll();
|
||||
|
||||
/** @var BudgetRepositoryInterface $budgetRepository */
|
||||
$budgetRepository = app(BudgetRepositoryInterface::class);
|
||||
$budgetRepository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyBills(): void
|
||||
{
|
||||
/** @var BillRepositoryInterface $repository */
|
||||
$repository = app(BillRepositoryInterface::class);
|
||||
$repository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyPiggyBanks(): void
|
||||
{
|
||||
/** @var PiggyBankRepositoryInterface $repository */
|
||||
$repository = app(PiggyBankRepositoryInterface::class);
|
||||
$repository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyRules(): void
|
||||
{
|
||||
/** @var RuleGroupRepositoryInterface $repository */
|
||||
$repository = app(RuleGroupRepositoryInterface::class);
|
||||
$repository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyRecurringTransactions(): void
|
||||
{
|
||||
/** @var RecurringRepositoryInterface $repository */
|
||||
$repository = app(RecurringRepositoryInterface::class);
|
||||
$repository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyCategories(): void
|
||||
{
|
||||
/** @var CategoryRepositoryInterface $categoryRepos */
|
||||
$categoryRepos = app(CategoryRepositoryInterface::class);
|
||||
$categoryRepos->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyTags(): void
|
||||
{
|
||||
/** @var TagRepositoryInterface $tagRepository */
|
||||
$tagRepository = app(TagRepositoryInterface::class);
|
||||
$tagRepository->destroyAll();
|
||||
}
|
||||
|
||||
private function destroyObjectGroups(): void
|
||||
{
|
||||
/** @var ObjectGroupRepositoryInterface $repository */
|
||||
$repository = app(ObjectGroupRepositoryInterface::class);
|
||||
$repository->deleteAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $types
|
||||
*/
|
||||
@@ -226,91 +311,6 @@ class DestroyController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyBills(): void
|
||||
{
|
||||
/** @var BillRepositoryInterface $repository */
|
||||
$repository = app(BillRepositoryInterface::class);
|
||||
$repository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyBudgets(): void
|
||||
{
|
||||
/** @var AvailableBudgetRepositoryInterface $abRepository */
|
||||
$abRepository = app(AvailableBudgetRepositoryInterface::class);
|
||||
$abRepository->destroyAll();
|
||||
|
||||
/** @var BudgetLimitRepositoryInterface $blRepository */
|
||||
$blRepository = app(BudgetLimitRepositoryInterface::class);
|
||||
$blRepository->destroyAll();
|
||||
|
||||
/** @var BudgetRepositoryInterface $budgetRepository */
|
||||
$budgetRepository = app(BudgetRepositoryInterface::class);
|
||||
$budgetRepository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyCategories(): void
|
||||
{
|
||||
/** @var CategoryRepositoryInterface $categoryRepos */
|
||||
$categoryRepos = app(CategoryRepositoryInterface::class);
|
||||
$categoryRepos->destroyAll();
|
||||
}
|
||||
|
||||
private function destroyObjectGroups(): void
|
||||
{
|
||||
/** @var ObjectGroupRepositoryInterface $repository */
|
||||
$repository = app(ObjectGroupRepositoryInterface::class);
|
||||
$repository->deleteAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyPiggyBanks(): void
|
||||
{
|
||||
/** @var PiggyBankRepositoryInterface $repository */
|
||||
$repository = app(PiggyBankRepositoryInterface::class);
|
||||
$repository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyRecurringTransactions(): void
|
||||
{
|
||||
/** @var RecurringRepositoryInterface $repository */
|
||||
$repository = app(RecurringRepositoryInterface::class);
|
||||
$repository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyRules(): void
|
||||
{
|
||||
/** @var RuleGroupRepositoryInterface $repository */
|
||||
$repository = app(RuleGroupRepositoryInterface::class);
|
||||
$repository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function destroyTags(): void
|
||||
{
|
||||
/** @var TagRepositoryInterface $tagRepository */
|
||||
$tagRepository = app(TagRepositoryInterface::class);
|
||||
$tagRepository->destroyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $types
|
||||
*/
|
||||
|
@@ -69,6 +69,34 @@ class ExportController extends Controller
|
||||
return $this->returnExport('accounts');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return LaravelResponse
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function returnExport(string $key): LaravelResponse
|
||||
{
|
||||
$date = date('Y-m-d-H-i-s');
|
||||
$fileName = sprintf('%s-export-%s.csv', $date, $key);
|
||||
$data = $this->exporter->export();
|
||||
|
||||
/** @var LaravelResponse $response */
|
||||
$response = response($data[$key]);
|
||||
$response
|
||||
->header('Content-Description', 'File Transfer')
|
||||
->header('Content-Type', 'application/octet-stream')
|
||||
->header('Content-Disposition', 'attachment; filename=' . $fileName)
|
||||
->header('Content-Transfer-Encoding', 'binary')
|
||||
->header('Connection', 'Keep-Alive')
|
||||
->header('Expires', '0')
|
||||
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
|
||||
->header('Pragma', 'public')
|
||||
->header('Content-Length', (string)strlen($data[$key]));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* This endpoint is documented at:
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportBills
|
||||
@@ -200,32 +228,4 @@ class ExportController extends Controller
|
||||
|
||||
return $this->returnExport('transactions');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return LaravelResponse
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function returnExport(string $key): LaravelResponse
|
||||
{
|
||||
$date = date('Y-m-d-H-i-s');
|
||||
$fileName = sprintf('%s-export-%s.csv', $date, $key);
|
||||
$data = $this->exporter->export();
|
||||
|
||||
/** @var LaravelResponse $response */
|
||||
$response = response($data[$key]);
|
||||
$response
|
||||
->header('Content-Description', 'File Transfer')
|
||||
->header('Content-Type', 'application/octet-stream')
|
||||
->header('Content-Disposition', 'attachment; filename='.$fileName)
|
||||
->header('Content-Transfer-Encoding', 'binary')
|
||||
->header('Connection', 'Keep-Alive')
|
||||
->header('Expires', '0')
|
||||
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
|
||||
->header('Pragma', 'public')
|
||||
->header('Content-Length', (string)strlen($data[$key]));
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
@@ -96,6 +96,7 @@ class TriggerController extends Controller
|
||||
$ruleEngine->addOperator(['type' => 'account_id', 'value' => implode(',', $parameters['accounts'])]);
|
||||
}
|
||||
|
||||
|
||||
// file the rule(s)
|
||||
$transactions = $ruleEngine->find();
|
||||
$count = $transactions->count();
|
||||
@@ -149,6 +150,7 @@ class TriggerController extends Controller
|
||||
$ruleEngine->addOperator(['type' => 'account_id', 'value' => implode(',', $parameters['accounts'])]);
|
||||
}
|
||||
|
||||
|
||||
// fire the rule(s)
|
||||
$ruleEngine->fire();
|
||||
|
||||
|
@@ -97,6 +97,21 @@ class ShowController extends Controller
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* This endpoint is documented at:
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/getTransactionByJournal
|
||||
*
|
||||
* Show a single transaction, by transaction journal.
|
||||
*
|
||||
* @param TransactionJournal $transactionJournal
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function showJournal(TransactionJournal $transactionJournal): JsonResponse
|
||||
{
|
||||
return $this->show($transactionJournal->transactionGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* This endpoint is documented at:
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/getTransaction
|
||||
@@ -133,19 +148,4 @@ class ShowController extends Controller
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* This endpoint is documented at:
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/getTransactionByJournal
|
||||
*
|
||||
* Show a single transaction, by transaction journal.
|
||||
*
|
||||
* @param TransactionJournal $transactionJournal
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function showJournal(TransactionJournal $transactionJournal): JsonResponse
|
||||
{
|
||||
return $this->show($transactionJournal->transactionGroup);
|
||||
}
|
||||
}
|
||||
|
@@ -97,35 +97,6 @@ class UpdateController extends Controller
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* This endpoint is documented at:
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/enableCurrency
|
||||
*
|
||||
* Enable a currency.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function enable(TransactionCurrency $currency): JsonResponse
|
||||
{
|
||||
$this->repository->enable($currency);
|
||||
$manager = $this->getManager();
|
||||
|
||||
$defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user());
|
||||
$this->parameters->set('defaultCurrency', $defaultCurrency);
|
||||
|
||||
/** @var CurrencyTransformer $transformer */
|
||||
$transformer = app(CurrencyTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new Item($currency, $transformer, 'currencies');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* This endpoint is documented at:
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/defaultCurrency
|
||||
@@ -157,6 +128,35 @@ class UpdateController extends Controller
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* This endpoint is documented at:
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/enableCurrency
|
||||
*
|
||||
* Enable a currency.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function enable(TransactionCurrency $currency): JsonResponse
|
||||
{
|
||||
$this->repository->enable($currency);
|
||||
$manager = $this->getManager();
|
||||
|
||||
$defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user());
|
||||
$this->parameters->set('defaultCurrency', $defaultCurrency);
|
||||
|
||||
/** @var CurrencyTransformer $transformer */
|
||||
$transformer = app(CurrencyTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new Item($currency, $transformer, 'currencies');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* This endpoint is documented at:
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/updateCurrency
|
||||
|
@@ -33,7 +33,6 @@ use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use JsonException;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection as FractalCollection;
|
||||
|
||||
|
@@ -121,30 +121,6 @@ class BasicController extends Controller
|
||||
return response()->json($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if date is outside session range.
|
||||
*
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference
|
||||
{
|
||||
$result = false;
|
||||
if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
|
||||
$result = true;
|
||||
}
|
||||
// start and end in the past? use $end
|
||||
if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) {
|
||||
$result = true;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
@@ -422,4 +398,28 @@ class BasicController extends Controller
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if date is outside session range.
|
||||
*
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference
|
||||
{
|
||||
$result = false;
|
||||
if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
|
||||
$result = true;
|
||||
}
|
||||
// start and end in the past? use $end
|
||||
if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) {
|
||||
$result = true;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@@ -94,6 +94,42 @@ class ConfigurationController extends Controller
|
||||
return response()->json($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all config values.
|
||||
*
|
||||
* @return array
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function getDynamicConfiguration(): array
|
||||
{
|
||||
$isDemoSite = app('fireflyconfig')->get('is_demo_site');
|
||||
$updateCheck = app('fireflyconfig')->get('permission_update_check');
|
||||
$lastCheck = app('fireflyconfig')->get('last_update_check');
|
||||
$singleUser = app('fireflyconfig')->get('single_user_mode');
|
||||
|
||||
return [
|
||||
'is_demo_site' => $isDemoSite?->data,
|
||||
'permission_update_check' => null === $updateCheck ? null : (int)$updateCheck->data,
|
||||
'last_update_check' => null === $lastCheck ? null : (int)$lastCheck->data,
|
||||
'single_user_mode' => $singleUser?->data,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getStaticConfiguration(): array
|
||||
{
|
||||
$list = EitherConfigKey::$static;
|
||||
$return = [];
|
||||
foreach ($list as $key) {
|
||||
$return[$key] = config($key);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* This endpoint is documented at:
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/configuration/getSingleConfiguration
|
||||
@@ -164,40 +200,4 @@ class ConfigurationController extends Controller
|
||||
|
||||
return response()->json(['data' => $data])->header('Content-Type', self::CONTENT_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all config values.
|
||||
*
|
||||
* @return array
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function getDynamicConfiguration(): array
|
||||
{
|
||||
$isDemoSite = app('fireflyconfig')->get('is_demo_site');
|
||||
$updateCheck = app('fireflyconfig')->get('permission_update_check');
|
||||
$lastCheck = app('fireflyconfig')->get('last_update_check');
|
||||
$singleUser = app('fireflyconfig')->get('single_user_mode');
|
||||
|
||||
return [
|
||||
'is_demo_site' => $isDemoSite?->data,
|
||||
'permission_update_check' => null === $updateCheck ? null : (int)$updateCheck->data,
|
||||
'last_update_check' => null === $lastCheck ? null : (int)$lastCheck->data,
|
||||
'single_user_mode' => $singleUser?->data,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getStaticConfiguration(): array
|
||||
{
|
||||
$list = EitherConfigKey::$static;
|
||||
$return = [];
|
||||
foreach ($list as $key) {
|
||||
$return[$key] = config($key);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
@@ -82,6 +82,28 @@ class GenericRequest extends FormRequest
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function parseAccounts(): void
|
||||
{
|
||||
if (0 !== $this->accounts->count()) {
|
||||
return;
|
||||
}
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$array = $this->get('accounts');
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $accountId) {
|
||||
$accountId = (int)$accountId;
|
||||
$account = $repository->find($accountId);
|
||||
if (null !== $account) {
|
||||
$this->accounts->push($account);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
@@ -92,6 +114,28 @@ class GenericRequest extends FormRequest
|
||||
return $this->bills;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function parseBills(): void
|
||||
{
|
||||
if (0 !== $this->bills->count()) {
|
||||
return;
|
||||
}
|
||||
$repository = app(BillRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$array = $this->get('bills');
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $billId) {
|
||||
$billId = (int)$billId;
|
||||
$bill = $repository->find($billId);
|
||||
if (null !== $bill) {
|
||||
$this->bills->push($bill);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
@@ -102,6 +146,28 @@ class GenericRequest extends FormRequest
|
||||
return $this->budgets;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function parseBudgets(): void
|
||||
{
|
||||
if (0 !== $this->budgets->count()) {
|
||||
return;
|
||||
}
|
||||
$repository = app(BudgetRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$array = $this->get('budgets');
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $budgetId) {
|
||||
$budgetId = (int)$budgetId;
|
||||
$budget = $repository->find($budgetId);
|
||||
if (null !== $budget) {
|
||||
$this->budgets->push($budget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
@@ -112,6 +178,28 @@ class GenericRequest extends FormRequest
|
||||
return $this->categories;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function parseCategories(): void
|
||||
{
|
||||
if (0 !== $this->categories->count()) {
|
||||
return;
|
||||
}
|
||||
$repository = app(CategoryRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$array = $this->get('categories');
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $categoryId) {
|
||||
$categoryId = (int)$categoryId;
|
||||
$category = $repository->find($categoryId);
|
||||
if (null !== $category) {
|
||||
$this->categories->push($category);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Carbon
|
||||
*/
|
||||
@@ -180,114 +268,6 @@ class GenericRequest extends FormRequest
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
// this is cheating, but it works to initialize the collections.
|
||||
$this->accounts = new Collection();
|
||||
$this->budgets = new Collection();
|
||||
$this->categories = new Collection();
|
||||
$this->bills = new Collection();
|
||||
$this->tags = new Collection();
|
||||
|
||||
return [
|
||||
'start' => 'required|date',
|
||||
'end' => 'required|date|after_or_equal:start',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function parseAccounts(): void
|
||||
{
|
||||
if (0 !== $this->accounts->count()) {
|
||||
return;
|
||||
}
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$array = $this->get('accounts');
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $accountId) {
|
||||
$accountId = (int)$accountId;
|
||||
$account = $repository->find($accountId);
|
||||
if (null !== $account) {
|
||||
$this->accounts->push($account);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function parseBills(): void
|
||||
{
|
||||
if (0 !== $this->bills->count()) {
|
||||
return;
|
||||
}
|
||||
$repository = app(BillRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$array = $this->get('bills');
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $billId) {
|
||||
$billId = (int)$billId;
|
||||
$bill = $repository->find($billId);
|
||||
if (null !== $bill) {
|
||||
$this->bills->push($bill);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function parseBudgets(): void
|
||||
{
|
||||
if (0 !== $this->budgets->count()) {
|
||||
return;
|
||||
}
|
||||
$repository = app(BudgetRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$array = $this->get('budgets');
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $budgetId) {
|
||||
$budgetId = (int)$budgetId;
|
||||
$budget = $repository->find($budgetId);
|
||||
if (null !== $budget) {
|
||||
$this->budgets->push($budget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function parseCategories(): void
|
||||
{
|
||||
if (0 !== $this->categories->count()) {
|
||||
return;
|
||||
}
|
||||
$repository = app(CategoryRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$array = $this->get('categories');
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $categoryId) {
|
||||
$categoryId = (int)$categoryId;
|
||||
$category = $repository->find($categoryId);
|
||||
if (null !== $category) {
|
||||
$this->categories->push($category);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -309,4 +289,24 @@ class GenericRequest extends FormRequest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
// this is cheating, but it works to initialize the collections.
|
||||
$this->accounts = new Collection();
|
||||
$this->budgets = new Collection();
|
||||
$this->categories = new Collection();
|
||||
$this->bills = new Collection();
|
||||
$this->tags = new Collection();
|
||||
|
||||
return [
|
||||
'start' => 'required|date',
|
||||
'end' => 'required|date|after_or_equal:start',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -73,6 +73,65 @@ class StoreRequest extends FormRequest
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transaction data as it is found in the submitted data. It's a complex method according to code
|
||||
* standards but it just has a lot of ??-statements because of the fields that may or may not exist.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getTransactionData(): array
|
||||
{
|
||||
$return = [];
|
||||
// transaction data:
|
||||
/** @var array|null $transactions */
|
||||
$transactions = $this->get('transactions');
|
||||
if (null === $transactions) {
|
||||
return [];
|
||||
}
|
||||
/** @var array $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$return[] = $this->getSingleTransactionData($transaction);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the repetition data as it is found in the submitted data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getRepetitionData(): array
|
||||
{
|
||||
$return = [];
|
||||
// repetition data:
|
||||
/** @var array|null $repetitions */
|
||||
$repetitions = $this->get('repetitions');
|
||||
if (null === $repetitions) {
|
||||
return [];
|
||||
}
|
||||
/** @var array $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$current = [];
|
||||
if (array_key_exists('type', $repetition)) {
|
||||
$current['type'] = $repetition['type'];
|
||||
}
|
||||
if (array_key_exists('moment', $repetition)) {
|
||||
$current['moment'] = $repetition['moment'];
|
||||
}
|
||||
if (array_key_exists('skip', $repetition)) {
|
||||
$current['skip'] = (int)$repetition['skip'];
|
||||
}
|
||||
if (array_key_exists('weekend', $repetition)) {
|
||||
$current['weekend'] = (int)$repetition['weekend'];
|
||||
}
|
||||
|
||||
$return[] = $current;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*
|
||||
@@ -139,63 +198,4 @@ class StoreRequest extends FormRequest
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the repetition data as it is found in the submitted data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getRepetitionData(): array
|
||||
{
|
||||
$return = [];
|
||||
// repetition data:
|
||||
/** @var array|null $repetitions */
|
||||
$repetitions = $this->get('repetitions');
|
||||
if (null === $repetitions) {
|
||||
return [];
|
||||
}
|
||||
/** @var array $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$current = [];
|
||||
if (array_key_exists('type', $repetition)) {
|
||||
$current['type'] = $repetition['type'];
|
||||
}
|
||||
if (array_key_exists('moment', $repetition)) {
|
||||
$current['moment'] = $repetition['moment'];
|
||||
}
|
||||
if (array_key_exists('skip', $repetition)) {
|
||||
$current['skip'] = (int)$repetition['skip'];
|
||||
}
|
||||
if (array_key_exists('weekend', $repetition)) {
|
||||
$current['weekend'] = (int)$repetition['weekend'];
|
||||
}
|
||||
|
||||
$return[] = $current;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transaction data as it is found in the submitted data. It's a complex method according to code
|
||||
* standards but it just has a lot of ??-statements because of the fields that may or may not exist.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getTransactionData(): array
|
||||
{
|
||||
$return = [];
|
||||
// transaction data:
|
||||
/** @var array|null $transactions */
|
||||
$transactions = $this->get('transactions');
|
||||
if (null === $transactions) {
|
||||
return [];
|
||||
}
|
||||
/** @var array $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$return[] = $this->getSingleTransactionData($transaction);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
@@ -80,6 +80,70 @@ class UpdateRequest extends FormRequest
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the repetition data as it is found in the submitted data.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private function getRepetitionData(): ?array
|
||||
{
|
||||
$return = [];
|
||||
// repetition data:
|
||||
/** @var array|null $repetitions */
|
||||
$repetitions = $this->get('repetitions');
|
||||
if (null === $repetitions) {
|
||||
return null;
|
||||
}
|
||||
/** @var array $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$current = [];
|
||||
if (array_key_exists('type', $repetition)) {
|
||||
$current['type'] = $repetition['type'];
|
||||
}
|
||||
|
||||
if (array_key_exists('moment', $repetition)) {
|
||||
$current['moment'] = (string)$repetition['moment'];
|
||||
}
|
||||
|
||||
if (array_key_exists('skip', $repetition)) {
|
||||
$current['skip'] = (int)$repetition['skip'];
|
||||
}
|
||||
|
||||
if (array_key_exists('weekend', $repetition)) {
|
||||
$current['weekend'] = (int)$repetition['weekend'];
|
||||
}
|
||||
$return[] = $current;
|
||||
}
|
||||
if (0 === count($return)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transaction data as it is found in the submitted data. It's a complex method according to code
|
||||
* standards but it just has a lot of ??-statements because of the fields that may or may not exist.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private function getTransactionData(): ?array
|
||||
{
|
||||
$return = [];
|
||||
// transaction data:
|
||||
/** @var array|null $transactions */
|
||||
$transactions = $this->get('transactions');
|
||||
if (null === $transactions) {
|
||||
return null;
|
||||
}
|
||||
/** @var array $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$return[] = $this->getSingleTransactionData($transaction);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*
|
||||
@@ -154,68 +218,4 @@ class UpdateRequest extends FormRequest
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the repetition data as it is found in the submitted data.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private function getRepetitionData(): ?array
|
||||
{
|
||||
$return = [];
|
||||
// repetition data:
|
||||
/** @var array|null $repetitions */
|
||||
$repetitions = $this->get('repetitions');
|
||||
if (null === $repetitions) {
|
||||
return null;
|
||||
}
|
||||
/** @var array $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$current = [];
|
||||
if (array_key_exists('type', $repetition)) {
|
||||
$current['type'] = $repetition['type'];
|
||||
}
|
||||
|
||||
if (array_key_exists('moment', $repetition)) {
|
||||
$current['moment'] = (string)$repetition['moment'];
|
||||
}
|
||||
|
||||
if (array_key_exists('skip', $repetition)) {
|
||||
$current['skip'] = (int)$repetition['skip'];
|
||||
}
|
||||
|
||||
if (array_key_exists('weekend', $repetition)) {
|
||||
$current['weekend'] = (int)$repetition['weekend'];
|
||||
}
|
||||
$return[] = $current;
|
||||
}
|
||||
if (0 === count($return)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transaction data as it is found in the submitted data. It's a complex method according to code
|
||||
* standards but it just has a lot of ??-statements because of the fields that may or may not exist.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private function getTransactionData(): ?array
|
||||
{
|
||||
$return = [];
|
||||
// transaction data:
|
||||
/** @var array|null $transactions */
|
||||
$transactions = $this->get('transactions');
|
||||
if (null === $transactions) {
|
||||
return null;
|
||||
}
|
||||
/** @var array $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$return[] = $this->getSingleTransactionData($transaction);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -67,6 +67,48 @@ class StoreRequest extends FormRequest
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getRuleTriggers(): array
|
||||
{
|
||||
$triggers = $this->get('triggers');
|
||||
$return = [];
|
||||
if (is_array($triggers)) {
|
||||
foreach ($triggers as $trigger) {
|
||||
$return[] = [
|
||||
'type' => $trigger['type'],
|
||||
'value' => $trigger['value'],
|
||||
'active' => $this->convertBoolean((string)($trigger['active'] ?? 'true')),
|
||||
'stop_processing' => $this->convertBoolean((string)($trigger['stop_processing'] ?? 'false')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getRuleActions(): array
|
||||
{
|
||||
$actions = $this->get('actions');
|
||||
$return = [];
|
||||
if (is_array($actions)) {
|
||||
foreach ($actions as $action) {
|
||||
$return[] = [
|
||||
'type' => $action['type'],
|
||||
'value' => $action['value'],
|
||||
'active' => $this->convertBoolean((string)($action['active'] ?? 'true')),
|
||||
'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*
|
||||
@@ -120,6 +162,21 @@ class StoreRequest extends FormRequest
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when there are no triggers in the array of data.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
protected function atLeastOneTrigger(Validator $validator): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
$triggers = $data['triggers'] ?? [];
|
||||
// need at least one trigger
|
||||
if (!is_countable($triggers) || 0 === count($triggers)) {
|
||||
$validator->errors()->add('title', (string)trans('validation.at_least_one_trigger'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when there are no repetitions in the array of data.
|
||||
*
|
||||
@@ -135,35 +192,6 @@ class StoreRequest extends FormRequest
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when there are no ACTIVE actions in the array of data.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
protected function atLeastOneActiveAction(Validator $validator): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
$actions = $data['actions'] ?? [];
|
||||
// need at least one trigger
|
||||
if (!is_countable($actions) || 0 === count($actions)) {
|
||||
return;
|
||||
}
|
||||
$allInactive = true;
|
||||
$inactiveIndex = 0;
|
||||
foreach ($actions as $index => $action) {
|
||||
$active = array_key_exists('active', $action) ? $action['active'] : true; // assume true
|
||||
if (true === $active) {
|
||||
$allInactive = false;
|
||||
}
|
||||
if (false === $active) {
|
||||
$inactiveIndex = $index;
|
||||
}
|
||||
}
|
||||
if (true === $allInactive) {
|
||||
$validator->errors()->add(sprintf('actions.%d.active', $inactiveIndex), (string)trans('validation.at_least_one_active_action'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when there are no ACTIVE triggers in the array of data.
|
||||
*
|
||||
@@ -194,59 +222,31 @@ class StoreRequest extends FormRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when there are no triggers in the array of data.
|
||||
* Adds an error to the validator when there are no ACTIVE actions in the array of data.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
protected function atLeastOneTrigger(Validator $validator): void
|
||||
protected function atLeastOneActiveAction(Validator $validator): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
$triggers = $data['triggers'] ?? [];
|
||||
$actions = $data['actions'] ?? [];
|
||||
// need at least one trigger
|
||||
if (!is_countable($triggers) || 0 === count($triggers)) {
|
||||
$validator->errors()->add('title', (string)trans('validation.at_least_one_trigger'));
|
||||
if (!is_countable($actions) || 0 === count($actions)) {
|
||||
return;
|
||||
}
|
||||
$allInactive = true;
|
||||
$inactiveIndex = 0;
|
||||
foreach ($actions as $index => $action) {
|
||||
$active = array_key_exists('active', $action) ? $action['active'] : true; // assume true
|
||||
if (true === $active) {
|
||||
$allInactive = false;
|
||||
}
|
||||
if (false === $active) {
|
||||
$inactiveIndex = $index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getRuleActions(): array
|
||||
{
|
||||
$actions = $this->get('actions');
|
||||
$return = [];
|
||||
if (is_array($actions)) {
|
||||
foreach ($actions as $action) {
|
||||
$return[] = [
|
||||
'type' => $action['type'],
|
||||
'value' => $action['value'],
|
||||
'active' => $this->convertBoolean((string)($action['active'] ?? 'true')),
|
||||
'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')),
|
||||
];
|
||||
if (true === $allInactive) {
|
||||
$validator->errors()->add(sprintf('actions.%d.active', $inactiveIndex), (string)trans('validation.at_least_one_active_action'));
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getRuleTriggers(): array
|
||||
{
|
||||
$triggers = $this->get('triggers');
|
||||
$return = [];
|
||||
if (is_array($triggers)) {
|
||||
foreach ($triggers as $trigger) {
|
||||
$return[] = [
|
||||
'type' => $trigger['type'],
|
||||
'value' => $trigger['value'],
|
||||
'active' => $this->convertBoolean((string)($trigger['active'] ?? 'true')),
|
||||
'stop_processing' => $this->convertBoolean((string)($trigger['stop_processing'] ?? 'false')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
@@ -52,24 +52,11 @@ class TestRequest extends FormRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @return int
|
||||
*/
|
||||
public function rules(): array
|
||||
private function getPage(): int
|
||||
{
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after_or_equal:start',
|
||||
'accounts' => '',
|
||||
'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getAccounts(): array
|
||||
{
|
||||
return $this->get('accounts');
|
||||
return 0 === (int)$this->query('page') ? 1 : (int)$this->query('page');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,10 +70,23 @@ class TestRequest extends FormRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @return array
|
||||
*/
|
||||
private function getPage(): int
|
||||
private function getAccounts(): array
|
||||
{
|
||||
return 0 === (int)$this->query('page') ? 1 : (int)$this->query('page');
|
||||
return $this->get('accounts');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after_or_equal:start',
|
||||
'accounts' => '',
|
||||
'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -50,16 +50,13 @@ class TriggerRequest extends FormRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @param string $field
|
||||
*
|
||||
* @return Carbon|null
|
||||
*/
|
||||
public function rules(): array
|
||||
private function getDate(string $field): ?Carbon
|
||||
{
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after_or_equal:start',
|
||||
'accounts' => '',
|
||||
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
|
||||
];
|
||||
return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($this->query($field), 0, 10));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,12 +68,15 @@ class TriggerRequest extends FormRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
*
|
||||
* @return Carbon|null
|
||||
* @return array
|
||||
*/
|
||||
private function getDate(string $field): ?Carbon
|
||||
public function rules(): array
|
||||
{
|
||||
return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($this->query($field), 0, 10));
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after_or_equal:start',
|
||||
'accounts' => '',
|
||||
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -73,6 +73,56 @@ class UpdateRequest extends FormRequest
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
*/
|
||||
private function getRuleTriggers(): ?array
|
||||
{
|
||||
if (!$this->has('triggers')) {
|
||||
return null;
|
||||
}
|
||||
$triggers = $this->get('triggers');
|
||||
$return = [];
|
||||
if (is_array($triggers)) {
|
||||
foreach ($triggers as $trigger) {
|
||||
$active = array_key_exists('active', $trigger) ? $trigger['active'] : true;
|
||||
$stopProcessing = array_key_exists('stop_processing', $trigger) ? $trigger['stop_processing'] : false;
|
||||
$return[] = [
|
||||
'type' => $trigger['type'],
|
||||
'value' => $trigger['value'],
|
||||
'active' => $active,
|
||||
'stop_processing' => $stopProcessing,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
*/
|
||||
private function getRuleActions(): ?array
|
||||
{
|
||||
if (!$this->has('actions')) {
|
||||
return null;
|
||||
}
|
||||
$actions = $this->get('actions');
|
||||
$return = [];
|
||||
if (is_array($actions)) {
|
||||
foreach ($actions as $action) {
|
||||
$return[] = [
|
||||
'type' => $action['type'],
|
||||
'value' => $action['value'],
|
||||
'active' => $this->convertBoolean((string)($action['active'] ?? 'false')),
|
||||
'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*
|
||||
@@ -130,21 +180,6 @@ class UpdateRequest extends FormRequest
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when there are no repetitions in the array of data.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
protected function atLeastOneAction(Validator $validator): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
$actions = $data['actions'] ?? null;
|
||||
// need at least one action
|
||||
if (is_array($actions) && 0 === count($actions)) {
|
||||
$validator->errors()->add('title', (string)trans('validation.at_least_one_action'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when there are no repetitions in the array of data.
|
||||
*
|
||||
@@ -160,36 +195,6 @@ class UpdateRequest extends FormRequest
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when there are no repetitions in the array of data.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
protected function atLeastOneValidAction(Validator $validator): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
$actions = $data['actions'] ?? [];
|
||||
$allInactive = true;
|
||||
$inactiveIndex = 0;
|
||||
// need at least one action
|
||||
if (is_array($actions) && 0 === count($actions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($actions as $index => $action) {
|
||||
$active = array_key_exists('active', $action) ? $action['active'] : true; // assume true
|
||||
if (true === $active) {
|
||||
$allInactive = false;
|
||||
}
|
||||
if (false === $active) {
|
||||
$inactiveIndex = $index;
|
||||
}
|
||||
}
|
||||
if (true === $allInactive) {
|
||||
$validator->errors()->add(sprintf('actions.%d.active', $inactiveIndex), (string)trans('validation.at_least_one_active_action'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when there are no repetitions in the array of data.
|
||||
*
|
||||
@@ -220,52 +225,47 @@ class UpdateRequest extends FormRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
* Adds an error to the validator when there are no repetitions in the array of data.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
private function getRuleActions(): ?array
|
||||
protected function atLeastOneAction(Validator $validator): void
|
||||
{
|
||||
if (!$this->has('actions')) {
|
||||
return null;
|
||||
$data = $validator->getData();
|
||||
$actions = $data['actions'] ?? null;
|
||||
// need at least one action
|
||||
if (is_array($actions) && 0 === count($actions)) {
|
||||
$validator->errors()->add('title', (string)trans('validation.at_least_one_action'));
|
||||
}
|
||||
$actions = $this->get('actions');
|
||||
$return = [];
|
||||
if (is_array($actions)) {
|
||||
foreach ($actions as $action) {
|
||||
$return[] = [
|
||||
'type' => $action['type'],
|
||||
'value' => $action['value'],
|
||||
'active' => $this->convertBoolean((string)($action['active'] ?? 'false')),
|
||||
'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
* Adds an error to the validator when there are no repetitions in the array of data.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
private function getRuleTriggers(): ?array
|
||||
protected function atLeastOneValidAction(Validator $validator): void
|
||||
{
|
||||
if (!$this->has('triggers')) {
|
||||
return null;
|
||||
}
|
||||
$triggers = $this->get('triggers');
|
||||
$return = [];
|
||||
if (is_array($triggers)) {
|
||||
foreach ($triggers as $trigger) {
|
||||
$active = array_key_exists('active', $trigger) ? $trigger['active'] : true;
|
||||
$stopProcessing = array_key_exists('stop_processing', $trigger) ? $trigger['stop_processing'] : false;
|
||||
$return[] = [
|
||||
'type' => $trigger['type'],
|
||||
'value' => $trigger['value'],
|
||||
'active' => $active,
|
||||
'stop_processing' => $stopProcessing,
|
||||
];
|
||||
}
|
||||
$data = $validator->getData();
|
||||
$actions = $data['actions'] ?? [];
|
||||
$allInactive = true;
|
||||
$inactiveIndex = 0;
|
||||
// need at least one action
|
||||
if (is_array($actions) && 0 === count($actions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return $return;
|
||||
foreach ($actions as $index => $action) {
|
||||
$active = array_key_exists('active', $action) ? $action['active'] : true; // assume true
|
||||
if (true === $active) {
|
||||
$allInactive = false;
|
||||
}
|
||||
if (false === $active) {
|
||||
$inactiveIndex = $index;
|
||||
}
|
||||
}
|
||||
if (true === $allInactive) {
|
||||
$validator->errors()->add(sprintf('actions.%d.active', $inactiveIndex), (string)trans('validation.at_least_one_active_action'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -50,16 +50,13 @@ class TestRequest extends FormRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @param string $field
|
||||
*
|
||||
* @return Carbon|null
|
||||
*/
|
||||
public function rules(): array
|
||||
private function getDate(string $field): ?Carbon
|
||||
{
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after_or_equal:start',
|
||||
'accounts' => '',
|
||||
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
|
||||
];
|
||||
return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,12 +68,15 @@ class TestRequest extends FormRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
*
|
||||
* @return Carbon|null
|
||||
* @return array
|
||||
*/
|
||||
private function getDate(string $field): ?Carbon
|
||||
public function rules(): array
|
||||
{
|
||||
return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field));
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after_or_equal:start',
|
||||
'accounts' => '',
|
||||
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -50,14 +50,13 @@ class TriggerRequest extends FormRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @param string $field
|
||||
*
|
||||
* @return Carbon|null
|
||||
*/
|
||||
public function rules(): array
|
||||
private function getDate(string $field): ?Carbon
|
||||
{
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after_or_equal:start',
|
||||
];
|
||||
return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,12 +68,13 @@ class TriggerRequest extends FormRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
*
|
||||
* @return Carbon|null
|
||||
* @return array
|
||||
*/
|
||||
private function getDate(string $field): ?Carbon
|
||||
public function rules(): array
|
||||
{
|
||||
return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field));
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after_or_equal:start',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -69,6 +69,103 @@ class StoreRequest extends FormRequest
|
||||
// TODO include location and ability to process it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getTransactionData(): array
|
||||
{
|
||||
$return = [];
|
||||
/**
|
||||
* @var array $transaction
|
||||
*/
|
||||
foreach ($this->get('transactions') as $transaction) {
|
||||
$object = new NullArrayObject($transaction);
|
||||
$return[] = [
|
||||
'type' => $this->clearString($object['type'], false),
|
||||
'date' => $this->dateFromValue($object['date']),
|
||||
'order' => $this->integerFromValue((string)$object['order']),
|
||||
|
||||
'currency_id' => $this->integerFromValue((string)$object['currency_id']),
|
||||
'currency_code' => $this->clearString((string)$object['currency_code'], false),
|
||||
|
||||
// foreign currency info:
|
||||
'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']),
|
||||
'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code'], false),
|
||||
|
||||
// amount and foreign amount. Cannot be 0.
|
||||
'amount' => $this->clearString((string)$object['amount'], false),
|
||||
'foreign_amount' => $this->clearString((string)$object['foreign_amount'], false),
|
||||
|
||||
// description.
|
||||
'description' => $this->clearString($object['description'], false),
|
||||
|
||||
// source of transaction. If everything is null, assume cash account.
|
||||
'source_id' => $this->integerFromValue((string)$object['source_id']),
|
||||
'source_name' => $this->clearString((string)$object['source_name'], false),
|
||||
'source_iban' => $this->clearString((string)$object['source_iban'], false),
|
||||
'source_number' => $this->clearString((string)$object['source_number'], false),
|
||||
'source_bic' => $this->clearString((string)$object['source_bic'], false),
|
||||
|
||||
// destination of transaction. If everything is null, assume cash account.
|
||||
'destination_id' => $this->integerFromValue((string)$object['destination_id']),
|
||||
'destination_name' => $this->clearString((string)$object['destination_name'], false),
|
||||
'destination_iban' => $this->clearString((string)$object['destination_iban'], false),
|
||||
'destination_number' => $this->clearString((string)$object['destination_number'], false),
|
||||
'destination_bic' => $this->clearString((string)$object['destination_bic'], false),
|
||||
|
||||
// budget info
|
||||
'budget_id' => $this->integerFromValue((string)$object['budget_id']),
|
||||
'budget_name' => $this->clearString((string)$object['budget_name'], false),
|
||||
|
||||
// category info
|
||||
'category_id' => $this->integerFromValue((string)$object['category_id']),
|
||||
'category_name' => $this->clearString((string)$object['category_name'], false),
|
||||
|
||||
// journal bill reference. Optional. Will only work for withdrawals
|
||||
'bill_id' => $this->integerFromValue((string)$object['bill_id']),
|
||||
'bill_name' => $this->clearString((string)$object['bill_name'], false),
|
||||
|
||||
// piggy bank reference. Optional. Will only work for transfers
|
||||
'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']),
|
||||
'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name'], false),
|
||||
|
||||
// some other interesting properties
|
||||
'reconciled' => $this->convertBoolean((string)$object['reconciled']),
|
||||
'notes' => $this->clearString((string)$object['notes']),
|
||||
'tags' => $this->arrayFromValue($object['tags']),
|
||||
|
||||
// all custom fields:
|
||||
'internal_reference' => $this->clearString((string)$object['internal_reference'], false),
|
||||
'external_id' => $this->clearString((string)$object['external_id'], false),
|
||||
'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')),
|
||||
'recurrence_id' => $this->integerFromValue($object['recurrence_id']),
|
||||
'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id'], false),
|
||||
'external_url' => $this->clearString((string)$object['external_url'], false),
|
||||
|
||||
'sepa_cc' => $this->clearString((string)$object['sepa_cc'], false),
|
||||
'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op'], false),
|
||||
'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id'], false),
|
||||
'sepa_db' => $this->clearString((string)$object['sepa_db'], false),
|
||||
'sepa_country' => $this->clearString((string)$object['sepa_country'], false),
|
||||
'sepa_ep' => $this->clearString((string)$object['sepa_ep'], false),
|
||||
'sepa_ci' => $this->clearString((string)$object['sepa_ci'], false),
|
||||
'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id'], false),
|
||||
// custom date fields. Must be Carbon objects. Presence is optional.
|
||||
'interest_date' => $this->dateFromValue($object['interest_date']),
|
||||
'book_date' => $this->dateFromValue($object['book_date']),
|
||||
'process_date' => $this->dateFromValue($object['process_date']),
|
||||
'due_date' => $this->dateFromValue($object['due_date']),
|
||||
'payment_date' => $this->dateFromValue($object['payment_date']),
|
||||
'invoice_date' => $this->dateFromValue($object['invoice_date']),
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*
|
||||
@@ -197,101 +294,4 @@ class StoreRequest extends FormRequest
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getTransactionData(): array
|
||||
{
|
||||
$return = [];
|
||||
/**
|
||||
* @var array $transaction
|
||||
*/
|
||||
foreach ($this->get('transactions') as $transaction) {
|
||||
$object = new NullArrayObject($transaction);
|
||||
$return[] = [
|
||||
'type' => $this->clearString($object['type'], false),
|
||||
'date' => $this->dateFromValue($object['date']),
|
||||
'order' => $this->integerFromValue((string)$object['order']),
|
||||
|
||||
'currency_id' => $this->integerFromValue((string)$object['currency_id']),
|
||||
'currency_code' => $this->clearString((string)$object['currency_code'], false),
|
||||
|
||||
// foreign currency info:
|
||||
'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']),
|
||||
'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code'], false),
|
||||
|
||||
// amount and foreign amount. Cannot be 0.
|
||||
'amount' => $this->clearString((string)$object['amount'], false),
|
||||
'foreign_amount' => $this->clearString((string)$object['foreign_amount'], false),
|
||||
|
||||
// description.
|
||||
'description' => $this->clearString($object['description'], false),
|
||||
|
||||
// source of transaction. If everything is null, assume cash account.
|
||||
'source_id' => $this->integerFromValue((string)$object['source_id']),
|
||||
'source_name' => $this->clearString((string)$object['source_name'], false),
|
||||
'source_iban' => $this->clearString((string)$object['source_iban'], false),
|
||||
'source_number' => $this->clearString((string)$object['source_number'], false),
|
||||
'source_bic' => $this->clearString((string)$object['source_bic'], false),
|
||||
|
||||
// destination of transaction. If everything is null, assume cash account.
|
||||
'destination_id' => $this->integerFromValue((string)$object['destination_id']),
|
||||
'destination_name' => $this->clearString((string)$object['destination_name'], false),
|
||||
'destination_iban' => $this->clearString((string)$object['destination_iban'], false),
|
||||
'destination_number' => $this->clearString((string)$object['destination_number'], false),
|
||||
'destination_bic' => $this->clearString((string)$object['destination_bic'], false),
|
||||
|
||||
// budget info
|
||||
'budget_id' => $this->integerFromValue((string)$object['budget_id']),
|
||||
'budget_name' => $this->clearString((string)$object['budget_name'], false),
|
||||
|
||||
// category info
|
||||
'category_id' => $this->integerFromValue((string)$object['category_id']),
|
||||
'category_name' => $this->clearString((string)$object['category_name'], false),
|
||||
|
||||
// journal bill reference. Optional. Will only work for withdrawals
|
||||
'bill_id' => $this->integerFromValue((string)$object['bill_id']),
|
||||
'bill_name' => $this->clearString((string)$object['bill_name'], false),
|
||||
|
||||
// piggy bank reference. Optional. Will only work for transfers
|
||||
'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']),
|
||||
'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name'], false),
|
||||
|
||||
// some other interesting properties
|
||||
'reconciled' => $this->convertBoolean((string)$object['reconciled']),
|
||||
'notes' => $this->clearString((string)$object['notes']),
|
||||
'tags' => $this->arrayFromValue($object['tags']),
|
||||
|
||||
// all custom fields:
|
||||
'internal_reference' => $this->clearString((string)$object['internal_reference'], false),
|
||||
'external_id' => $this->clearString((string)$object['external_id'], false),
|
||||
'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')),
|
||||
'recurrence_id' => $this->integerFromValue($object['recurrence_id']),
|
||||
'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id'], false),
|
||||
'external_url' => $this->clearString((string)$object['external_url'], false),
|
||||
|
||||
'sepa_cc' => $this->clearString((string)$object['sepa_cc'], false),
|
||||
'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op'], false),
|
||||
'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id'], false),
|
||||
'sepa_db' => $this->clearString((string)$object['sepa_db'], false),
|
||||
'sepa_country' => $this->clearString((string)$object['sepa_country'], false),
|
||||
'sepa_ep' => $this->clearString((string)$object['sepa_ep'], false),
|
||||
'sepa_ci' => $this->clearString((string)$object['sepa_ci'], false),
|
||||
'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id'], false),
|
||||
// custom date fields. Must be Carbon objects. Presence is optional.
|
||||
'interest_date' => $this->dateFromValue($object['interest_date']),
|
||||
'book_date' => $this->dateFromValue($object['book_date']),
|
||||
'process_date' => $this->dateFromValue($object['process_date']),
|
||||
'due_date' => $this->dateFromValue($object['due_date']),
|
||||
'payment_date' => $this->dateFromValue($object['payment_date']),
|
||||
'invoice_date' => $this->dateFromValue($object['invoice_date']),
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,6 @@ use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface;
|
||||
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use JsonException;
|
||||
|
||||
|
@@ -64,54 +64,6 @@ class Controller extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param LengthAwarePaginator $paginator
|
||||
* @param AbstractTransformer $transformer
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
final protected function jsonApiList(string $key, LengthAwarePaginator $paginator, AbstractTransformer $transformer): array
|
||||
{
|
||||
$manager = new Manager();
|
||||
$baseUrl = request()->getSchemeAndHttpHost().'/api/v2';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
$objects = $paginator->getCollection();
|
||||
|
||||
// the transformer, at this point, needs to collect information that ALL items in the collection
|
||||
// require, like meta data and stuff like that, and save it for later.
|
||||
$transformer->collectMetaData($objects);
|
||||
|
||||
$resource = new FractalCollection($objects, $transformer, $key);
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return $manager->createData($resource)->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON API object and returns it.
|
||||
*
|
||||
* @param string $key
|
||||
* @param Model $object
|
||||
* @param AbstractTransformer $transformer
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
final protected function jsonApiObject(string $key, Model $object, AbstractTransformer $transformer): array
|
||||
{
|
||||
// create some objects:
|
||||
$manager = new Manager();
|
||||
$baseUrl = request()->getSchemeAndHttpHost().'/api/v2';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
$transformer->collectMetaData(new Collection([$object]));
|
||||
|
||||
$resource = new Item($object, $transformer, $key);
|
||||
|
||||
return $manager->createData($resource)->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate from V1 controller
|
||||
* Method to grab all parameters from the URL.
|
||||
@@ -179,4 +131,52 @@ class Controller extends BaseController
|
||||
|
||||
return $bag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param LengthAwarePaginator $paginator
|
||||
* @param AbstractTransformer $transformer
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
final protected function jsonApiList(string $key, LengthAwarePaginator $paginator, AbstractTransformer $transformer): array
|
||||
{
|
||||
$manager = new Manager();
|
||||
$baseUrl = request()->getSchemeAndHttpHost() . '/api/v2';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
$objects = $paginator->getCollection();
|
||||
|
||||
// the transformer, at this point, needs to collect information that ALL items in the collection
|
||||
// require, like meta data and stuff like that, and save it for later.
|
||||
$transformer->collectMetaData($objects);
|
||||
|
||||
$resource = new FractalCollection($objects, $transformer, $key);
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return $manager->createData($resource)->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON API object and returns it.
|
||||
*
|
||||
* @param string $key
|
||||
* @param Model $object
|
||||
* @param AbstractTransformer $transformer
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
final protected function jsonApiObject(string $key, Model $object, AbstractTransformer $transformer): array
|
||||
{
|
||||
// create some objects:
|
||||
$manager = new Manager();
|
||||
$baseUrl = request()->getSchemeAndHttpHost() . '/api/v2';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
$transformer->collectMetaData(new Collection([$object]));
|
||||
|
||||
$resource = new Item($object, $transformer, $key);
|
||||
|
||||
return $manager->createData($resource)->toArray();
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\AutoBudget;
|
||||
use FireflyIII\Models\AvailableBudget;
|
||||
use FireflyIII\Models\Bill;
|
||||
@@ -39,6 +40,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class CorrectAmounts extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'This command makes sure positive and negative amounts are recorded correctly.';
|
||||
protected $signature = 'firefly-iii:fix-amount-pos-neg';
|
||||
|
||||
@@ -78,7 +81,7 @@ class CorrectAmounts extends Command
|
||||
$set = AutoBudget::where('amount', '<', 0)->get();
|
||||
$count = $set->count();
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: All auto budget amounts are positive.');
|
||||
$this->friendlyPositive('All auto budget amounts are positive.');
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -87,7 +90,7 @@ class CorrectAmounts extends Command
|
||||
$item->amount = app('steam')->positive((string)$item->amount);
|
||||
$item->save();
|
||||
}
|
||||
$this->line(sprintf('Corrected %d auto budget amount(s).', $count));
|
||||
$this->friendlyInfo(sprintf('Corrected %d auto budget amount(s).', $count));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,7 +101,7 @@ class CorrectAmounts extends Command
|
||||
$set = AvailableBudget::where('amount', '<', 0)->get();
|
||||
$count = $set->count();
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: All available budget amounts are positive.');
|
||||
$this->friendlyPositive('All available budget amounts are positive.');
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -107,7 +110,7 @@ class CorrectAmounts extends Command
|
||||
$item->amount = app('steam')->positive((string)$item->amount);
|
||||
$item->save();
|
||||
}
|
||||
$this->line(sprintf('Corrected %d available budget amount(s).', $count));
|
||||
$this->friendlyInfo(sprintf('Corrected %d available budget amount(s).', $count));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,7 +121,7 @@ class CorrectAmounts extends Command
|
||||
$set = Bill::where('amount_min', '<', 0)->orWhere('amount_max', '<', 0)->get();
|
||||
$count = $set->count();
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: All bill amounts are positive.');
|
||||
$this->friendlyPositive('All bill amounts are positive.');
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -128,6 +131,7 @@ class CorrectAmounts extends Command
|
||||
$item->amount_max = app('steam')->positive((string)$item->amount_max);
|
||||
$item->save();
|
||||
}
|
||||
$this->friendlyInfo(sprintf('Corrected %d bill amount(s).', $count));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,7 +142,7 @@ class CorrectAmounts extends Command
|
||||
$set = BudgetLimit::where('amount', '<', 0)->get();
|
||||
$count = $set->count();
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: All budget limit amounts are positive.');
|
||||
$this->friendlyPositive('All budget limit amounts are positive.');
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -147,7 +151,7 @@ class CorrectAmounts extends Command
|
||||
$item->amount = app('steam')->positive((string)$item->amount);
|
||||
$item->save();
|
||||
}
|
||||
$this->line(sprintf('Corrected %d budget limit amount(s).', $count));
|
||||
$this->friendlyInfo(sprintf('Corrected %d budget limit amount(s).', $count));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,7 +162,7 @@ class CorrectAmounts extends Command
|
||||
$set = CurrencyExchangeRate::where('rate', '<', 0)->get();
|
||||
$count = $set->count();
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: All currency exchange rates are positive.');
|
||||
$this->friendlyPositive('All currency exchange rates are positive.');
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -167,7 +171,27 @@ class CorrectAmounts extends Command
|
||||
$item->rate = app('steam')->positive((string)$item->rate);
|
||||
$item->save();
|
||||
}
|
||||
$this->line(sprintf('Corrected %d currency exchange rate(s).', $count));
|
||||
$this->friendlyInfo(sprintf('Corrected %d currency exchange rate(s).', $count));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function fixRepetitions(): void
|
||||
{
|
||||
$set = PiggyBankRepetition::where('currentamount', '<', 0)->get();
|
||||
$count = $set->count();
|
||||
if (0 === $count) {
|
||||
$this->friendlyPositive('All piggy bank repetition amounts are positive.');
|
||||
|
||||
return;
|
||||
}
|
||||
/** @var PiggyBankRepetition $item */
|
||||
foreach ($set as $item) {
|
||||
$item->currentamount = app('steam')->positive((string)$item->currentamount);
|
||||
$item->save();
|
||||
}
|
||||
$this->friendlyInfo(sprintf('Corrected %d piggy bank repetition amount(s).', $count));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,7 +202,7 @@ class CorrectAmounts extends Command
|
||||
$set = PiggyBank::where('targetamount', '<', 0)->get();
|
||||
$count = $set->count();
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: All piggy bank amounts are positive.');
|
||||
$this->friendlyPositive('All piggy bank amounts are positive.');
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -187,7 +211,7 @@ class CorrectAmounts extends Command
|
||||
$item->targetamount = app('steam')->positive((string)$item->targetamount);
|
||||
$item->save();
|
||||
}
|
||||
$this->line(sprintf('Corrected %d piggy bank amount(s).', $count));
|
||||
$this->friendlyInfo(sprintf('Corrected %d piggy bank amount(s).', $count));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,7 +224,7 @@ class CorrectAmounts extends Command
|
||||
->get();
|
||||
$count = $set->count();
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: All recurring transaction amounts are positive.');
|
||||
$this->friendlyPositive('All recurring transaction amounts are positive.');
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -210,27 +234,7 @@ class CorrectAmounts extends Command
|
||||
$item->foreign_amount = app('steam')->positive((string)$item->foreign_amount);
|
||||
$item->save();
|
||||
}
|
||||
$this->line(sprintf('Corrected %d recurring transaction amount(s).', $count));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function fixRepetitions(): void
|
||||
{
|
||||
$set = PiggyBankRepetition::where('currentamount', '<', 0)->get();
|
||||
$count = $set->count();
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: All piggy bank repetition amounts are positive.');
|
||||
|
||||
return;
|
||||
}
|
||||
/** @var PiggyBankRepetition $item */
|
||||
foreach ($set as $item) {
|
||||
$item->currentamount = app('steam')->positive((string)$item->currentamount);
|
||||
$item->save();
|
||||
}
|
||||
$this->line(sprintf('Corrected %d piggy bank repetition amount(s).', $count));
|
||||
$this->friendlyInfo(sprintf('Corrected %d recurring transaction amount(s).', $count));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,11 +254,11 @@ class CorrectAmounts extends Command
|
||||
}
|
||||
}
|
||||
if (0 === $fixed) {
|
||||
$this->info('Correct: All rule trigger amounts are positive.');
|
||||
$this->friendlyPositive('All rule trigger amounts are positive.');
|
||||
|
||||
return;
|
||||
}
|
||||
$this->line(sprintf('Corrected %d rule trigger amount(s).', $fixed));
|
||||
$this->friendlyInfo(sprintf('Corrected %d rule trigger amount(s).', $fixed));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use Artisan;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use Illuminate\Console\Command;
|
||||
use Schema;
|
||||
|
||||
@@ -34,6 +35,8 @@ use Schema;
|
||||
*/
|
||||
class CorrectDatabase extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Will correct the integrity of your database, if necessary.';
|
||||
protected $signature = 'firefly-iii:correct-database';
|
||||
|
||||
@@ -44,7 +47,7 @@ class CorrectDatabase extends Command
|
||||
{
|
||||
// if table does not exist, return false
|
||||
if (!Schema::hasTable('users')) {
|
||||
$this->error('No "users"-table, will not continue.');
|
||||
$this->friendlyError('No "users"-table, will not continue.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -76,7 +79,7 @@ class CorrectDatabase extends Command
|
||||
'firefly-iii:trigger-credit-recalculation',
|
||||
];
|
||||
foreach ($commands as $command) {
|
||||
$this->line(sprintf('Now executing command "%s"', $command));
|
||||
$this->friendlyLine(sprintf('Now executing command "%s"', $command));
|
||||
$this->call($command);
|
||||
}
|
||||
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
@@ -39,6 +40,8 @@ use Illuminate\Support\Collection;
|
||||
*/
|
||||
class CorrectOpeningBalanceCurrencies extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Will make sure that opening balance transaction currencies match the account they\'re for.';
|
||||
protected $signature = 'firefly-iii:fix-ob-currencies';
|
||||
|
||||
@@ -58,16 +61,27 @@ class CorrectOpeningBalanceCurrencies extends Command
|
||||
|
||||
if ($count > 0) {
|
||||
$message = sprintf('Corrected %d opening balance transaction(s).', $count);
|
||||
$this->line($message);
|
||||
$this->friendlyInfo($message);
|
||||
}
|
||||
if (0 === $count) {
|
||||
$message = 'Correct: There was nothing to fix in the opening balance transactions.';
|
||||
$this->info($message);
|
||||
$message = 'There was nothing to fix in the opening balance transactions.';
|
||||
$this->friendlyPositive($message);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
private function getJournals(): Collection
|
||||
{
|
||||
/** @var Collection */
|
||||
return TransactionJournal::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->where('transaction_types.type', TransactionType::OPENING_BALANCE)->get(['transaction_journals.*']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
@@ -80,7 +94,7 @@ class CorrectOpeningBalanceCurrencies extends Command
|
||||
if (null === $account) {
|
||||
$message = sprintf('Transaction journal #%d has no valid account. Can\'t fix this line.', $journal->id);
|
||||
app('log')->warning($message);
|
||||
$this->warn($message);
|
||||
$this->friendlyError($message);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -109,31 +123,6 @@ class CorrectOpeningBalanceCurrencies extends Command
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
* @return TransactionCurrency
|
||||
*/
|
||||
private function getCurrency(Account $account): TransactionCurrency
|
||||
{
|
||||
/** @var AccountRepositoryInterface $repos */
|
||||
$repos = app(AccountRepositoryInterface::class);
|
||||
$repos->setUser($account->user);
|
||||
|
||||
return $repos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUser($account->user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
private function getJournals(): Collection
|
||||
{
|
||||
/** @var Collection */
|
||||
return TransactionJournal::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->where('transaction_types.type', TransactionType::OPENING_BALANCE)->get(['transaction_journals.*']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param TransactionJournal $journal
|
||||
@@ -160,4 +149,18 @@ class CorrectOpeningBalanceCurrencies extends Command
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
* @return TransactionCurrency
|
||||
*/
|
||||
private function getCurrency(Account $account): TransactionCurrency
|
||||
{
|
||||
/** @var AccountRepositoryInterface $repos */
|
||||
$repos = app(AccountRepositoryInterface::class);
|
||||
$repos->setUser($account->user);
|
||||
|
||||
return $repos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUser($account->user);
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -33,6 +34,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class CreateAccessTokens extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
@@ -66,12 +69,12 @@ class CreateAccessTokens extends Command
|
||||
if (null === $pref) {
|
||||
$token = $user->generateAccessToken();
|
||||
app('preferences')->setForUser($user, 'access_token', $token);
|
||||
$this->line(sprintf('Generated access token for user %s', $user->email));
|
||||
$this->friendlyInfo(sprintf('Generated access token for user %s', $user->email));
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: Verified access tokens.');
|
||||
$this->friendlyPositive('Verified access tokens.');
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\LinkType;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
@@ -31,6 +32,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class CreateLinkTypes extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
@@ -67,13 +70,13 @@ class CreateLinkTypes extends Command
|
||||
$link->inward = $values[1];
|
||||
$link->outward = $values[0];
|
||||
++$count;
|
||||
$this->line(sprintf('Created missing link type "%s"', $name));
|
||||
$this->friendlyInfo(sprintf('Created missing link type "%s"', $name));
|
||||
}
|
||||
$link->editable = false;
|
||||
$link->save();
|
||||
}
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: all link types are OK');
|
||||
$this->friendlyPositive('All link types are OK');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
@@ -32,6 +33,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class DeleteEmptyGroups extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Delete empty transaction groups.';
|
||||
protected $signature = 'firefly-iii:delete-empty-groups';
|
||||
|
||||
@@ -50,7 +53,7 @@ class DeleteEmptyGroups extends Command
|
||||
|
||||
$total = count($groupIds);
|
||||
if ($total > 0) {
|
||||
$this->info(sprintf('Deleted %d empty transaction group(s).', $total));
|
||||
$this->friendlyInfo(sprintf('Deleted %d empty transaction group(s).', $total));
|
||||
|
||||
// again, chunks for SQLite.
|
||||
$chunks = array_chunk($groupIds, 500);
|
||||
@@ -59,7 +62,7 @@ class DeleteEmptyGroups extends Command
|
||||
}
|
||||
}
|
||||
if (0 === $total) {
|
||||
$this->info('Correct: verified empty groups.');
|
||||
$this->friendlyInfo('Verified there are no empty groups.');
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use DB;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -35,6 +36,8 @@ use Illuminate\Support\Facades\Log;
|
||||
*/
|
||||
class DeleteEmptyJournals extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
@@ -61,30 +64,6 @@ class DeleteEmptyJournals extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function deleteEmptyJournals(): void
|
||||
{
|
||||
$count = 0;
|
||||
$set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->groupBy('transaction_journals.id')
|
||||
->whereNull('transactions.transaction_journal_id')
|
||||
->get(['transaction_journals.id']);
|
||||
|
||||
foreach ($set as $entry) {
|
||||
try {
|
||||
TransactionJournal::find($entry->id)->delete();
|
||||
} catch (QueryException $e) {
|
||||
Log::info(sprintf('Could not delete entry: %s', $e->getMessage()));
|
||||
}
|
||||
|
||||
|
||||
$this->info(sprintf('Deleted empty transaction journal #%d', $entry->id));
|
||||
++$count;
|
||||
}
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: no empty transaction journals.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete transactions and their journals if they have an uneven number of transactions.
|
||||
*/
|
||||
@@ -107,12 +86,38 @@ class DeleteEmptyJournals extends Command
|
||||
|
||||
|
||||
Transaction::where('transaction_journal_id', (int)$row->transaction_journal_id)->delete();
|
||||
$this->info(sprintf('Deleted transaction journal #%d because it had an uneven number of transactions.', $row->transaction_journal_id));
|
||||
$this->friendlyWarning(
|
||||
sprintf('Deleted transaction journal #%d because it had an uneven number of transactions.', $row->transaction_journal_id)
|
||||
);
|
||||
$total++;
|
||||
}
|
||||
}
|
||||
if (0 === $total) {
|
||||
$this->info('Correct: no uneven transaction journals.');
|
||||
$this->friendlyPositive('No uneven transaction journals.');
|
||||
}
|
||||
}
|
||||
|
||||
private function deleteEmptyJournals(): void
|
||||
{
|
||||
$count = 0;
|
||||
$set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->groupBy('transaction_journals.id')
|
||||
->whereNull('transactions.transaction_journal_id')
|
||||
->get(['transaction_journals.id']);
|
||||
|
||||
foreach ($set as $entry) {
|
||||
try {
|
||||
TransactionJournal::find($entry->id)->delete();
|
||||
} catch (QueryException $e) {
|
||||
Log::info(sprintf('Could not delete entry: %s', $e->getMessage()));
|
||||
}
|
||||
|
||||
|
||||
$this->friendlyInfo(sprintf('Deleted empty transaction journal #%d', $entry->id));
|
||||
++$count;
|
||||
}
|
||||
if (0 === $count) {
|
||||
$this->friendlyPositive('No empty transaction journals.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -34,6 +35,8 @@ use stdClass;
|
||||
*/
|
||||
class DeleteOrphanedTransactions extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
@@ -62,38 +65,6 @@ class DeleteOrphanedTransactions extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function deleteFromOrphanedAccounts(): void
|
||||
{
|
||||
$set
|
||||
= Transaction::leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
|
||||
->whereNotNull('accounts.deleted_at')
|
||||
->get(['transactions.*']);
|
||||
$count = 0;
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($set as $transaction) {
|
||||
// delete journals
|
||||
$journal = TransactionJournal::find((int)$transaction->transaction_journal_id);
|
||||
if ($journal) {
|
||||
$journal->delete();
|
||||
}
|
||||
Transaction::where('transaction_journal_id', (int)$transaction->transaction_journal_id)->delete();
|
||||
$this->line(
|
||||
sprintf(
|
||||
'Deleted transaction journal #%d because account #%d was already deleted.',
|
||||
$transaction->transaction_journal_id,
|
||||
$transaction->account_id
|
||||
)
|
||||
);
|
||||
$count++;
|
||||
}
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: no orphaned accounts.');
|
||||
}
|
||||
}
|
||||
|
||||
private function deleteOrphanedJournals(): void
|
||||
{
|
||||
$set = TransactionJournal::leftJoin('transaction_groups', 'transaction_journals.transaction_group_id', 'transaction_groups.id')
|
||||
@@ -102,15 +73,15 @@ class DeleteOrphanedTransactions extends Command
|
||||
->get(['transaction_journals.id', 'transaction_journals.transaction_group_id']);
|
||||
$count = $set->count();
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: no orphaned journals.');
|
||||
$this->friendlyPositive('No orphaned journals.');
|
||||
return;
|
||||
}
|
||||
if ($count > 0) {
|
||||
$this->info(sprintf('Found %d orphaned journal(s).', $count));
|
||||
$this->friendlyInfo(sprintf('Found %d orphaned journal(s).', $count));
|
||||
foreach ($set as $entry) {
|
||||
$journal = TransactionJournal::withTrashed()->find((int)$entry->id);
|
||||
if (null !== $journal) {
|
||||
$journal->delete();
|
||||
$this->info(
|
||||
$this->friendlyWarning(
|
||||
sprintf(
|
||||
'Journal #%d (part of deleted transaction group #%d) has been deleted as well.',
|
||||
$entry->id,
|
||||
@@ -120,7 +91,6 @@ class DeleteOrphanedTransactions extends Command
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
@@ -143,7 +113,7 @@ class DeleteOrphanedTransactions extends Command
|
||||
$transaction = Transaction::find((int)$entry->transaction_id);
|
||||
if (null !== $transaction) {
|
||||
$transaction->delete();
|
||||
$this->info(
|
||||
$this->friendlyWarning(
|
||||
sprintf(
|
||||
'Transaction #%d (part of deleted transaction journal #%d) has been deleted as well.',
|
||||
$entry->transaction_id,
|
||||
@@ -154,7 +124,39 @@ class DeleteOrphanedTransactions extends Command
|
||||
}
|
||||
}
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: no orphaned transactions.');
|
||||
$this->friendlyPositive('No orphaned transactions.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function deleteFromOrphanedAccounts(): void
|
||||
{
|
||||
$set
|
||||
= Transaction::leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
|
||||
->whereNotNull('accounts.deleted_at')
|
||||
->get(['transactions.*']);
|
||||
$count = 0;
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($set as $transaction) {
|
||||
// delete journals
|
||||
$journal = TransactionJournal::find((int)$transaction->transaction_journal_id);
|
||||
if ($journal) {
|
||||
$journal->delete();
|
||||
}
|
||||
Transaction::where('transaction_journal_id', (int)$transaction->transaction_journal_id)->delete();
|
||||
$this->friendlyWarning(
|
||||
sprintf(
|
||||
'Deleted transaction journal #%d because account #%d was already deleted.',
|
||||
$transaction->transaction_journal_id,
|
||||
$transaction->account_id
|
||||
)
|
||||
);
|
||||
$count++;
|
||||
}
|
||||
if (0 === $count) {
|
||||
$this->friendlyPositive('No orphaned accounts.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -32,6 +33,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class DeleteZeroAmount extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
@@ -57,13 +60,13 @@ class DeleteZeroAmount extends Command
|
||||
$journals = TransactionJournal::whereIn('id', $set)->get();
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$this->info(sprintf('Deleted transaction journal #%d because the amount is zero (0.00).', $journal->id));
|
||||
$this->friendlyWarning(sprintf('Deleted transaction journal #%d because the amount is zero (0.00).', $journal->id));
|
||||
$journal->delete();
|
||||
|
||||
Transaction::where('transaction_journal_id', $journal->id)->delete();
|
||||
}
|
||||
if (0 === $journals->count()) {
|
||||
$this->info('Correct: no zero-amount transaction journals.');
|
||||
$this->friendlyPositive('No zero-amount transaction journals.');
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\AccountMeta;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\Transaction;
|
||||
@@ -30,13 +31,14 @@ use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class EnableCurrencies
|
||||
*/
|
||||
class EnableCurrencies extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Enables all currencies in use.';
|
||||
protected $signature = 'firefly-iii:enable-currencies';
|
||||
|
||||
@@ -85,10 +87,10 @@ class EnableCurrencies extends Command
|
||||
);
|
||||
$disabled = TransactionCurrency::whereIn('id', $found)->where('enabled', false)->count();
|
||||
if ($disabled > 0) {
|
||||
$this->info(sprintf('%d were (was) still disabled. This has been corrected.', $disabled));
|
||||
$this->friendlyInfo(sprintf('%d currencies were (was) disabled while in use by transactions. This has been corrected.', $disabled));
|
||||
}
|
||||
if (0 === $disabled) {
|
||||
$this->info('Correct: All currencies are correctly enabled or disabled.');
|
||||
$this->friendlyPositive('All currencies are correctly enabled or disabled.');
|
||||
}
|
||||
TransactionCurrency::whereIn('id', $found)->update(['enabled' => true]);
|
||||
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -32,6 +33,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class FixAccountOrder extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Make sure account order is correct.';
|
||||
protected $signature = 'firefly-iii:fix-account-order';
|
||||
|
||||
@@ -52,7 +55,7 @@ class FixAccountOrder extends Command
|
||||
$this->repository->resetAccountOrder();
|
||||
}
|
||||
|
||||
$this->info('Correct: All accounts are ordered correctly');
|
||||
$this->friendlyPositive('All accounts are ordered correctly');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Factory\AccountFactory;
|
||||
use FireflyIII\Models\AccountType;
|
||||
@@ -30,14 +31,16 @@ use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Illuminate\Console\Command;
|
||||
use JsonException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use JsonException;
|
||||
|
||||
/**
|
||||
* Class FixAccountTypes
|
||||
*/
|
||||
class FixAccountTypes extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Make sure all journals have the correct from/to account types.';
|
||||
protected $signature = 'firefly-iii:fix-account-types';
|
||||
private int $count;
|
||||
@@ -60,16 +63,89 @@ class FixAccountTypes extends Command
|
||||
$this->inspectJournal($journal);
|
||||
}
|
||||
if (0 === $this->count) {
|
||||
$this->info('Correct: all account types are OK');
|
||||
$this->friendlyPositive('All account types are OK');
|
||||
}
|
||||
if (0 !== $this->count) {
|
||||
Log::debug(sprintf('%d journals had to be fixed.', $this->count));
|
||||
$this->info(sprintf('Acted on %d transaction(s)!', $this->count));
|
||||
$this->friendlyInfo(sprintf('Acted on %d transaction(s)', $this->count));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function inspectJournal(TransactionJournal $journal): void
|
||||
{
|
||||
$transactions = $journal->transactions()->count();
|
||||
if (2 !== $transactions) {
|
||||
Log::debug(sprintf('Journal has %d transactions, so can\'t fix.', $transactions));
|
||||
$this->friendlyError(sprintf('Cannot inspect transaction journal #%d because it has %d transaction(s) instead of 2.', $journal->id, $transactions));
|
||||
|
||||
return;
|
||||
}
|
||||
$type = $journal->transactionType->type;
|
||||
$sourceTransaction = $this->getSourceTransaction($journal);
|
||||
$destTransaction = $this->getDestinationTransaction($journal);
|
||||
$sourceAccount = $sourceTransaction->account;
|
||||
$sourceAccountType = $sourceAccount->accountType->type;
|
||||
$destAccount = $destTransaction->account;
|
||||
$destAccountType = $destAccount->accountType->type;
|
||||
|
||||
if (!array_key_exists($type, $this->expected)) {
|
||||
Log::info(sprintf('No source/destination info for transaction type %s.', $type));
|
||||
$this->friendlyError(sprintf('No source/destination info for transaction type %s.', $type));
|
||||
|
||||
return;
|
||||
}
|
||||
if (!array_key_exists($sourceAccountType, $this->expected[$type])) {
|
||||
Log::debug(sprintf('Going to fix journal #%d', $journal->id));
|
||||
$this->fixJournal($journal, $type, $sourceTransaction, $destTransaction);
|
||||
|
||||
return;
|
||||
}
|
||||
$expectedTypes = $this->expected[$type][$sourceAccountType];
|
||||
if (!in_array($destAccountType, $expectedTypes, true)) {
|
||||
Log::debug(sprintf('Going to fix journal #%d', $journal->id));
|
||||
$this->fixJournal($journal, $type, $sourceTransaction, $destTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return Transaction
|
||||
*/
|
||||
private function getSourceTransaction(TransactionJournal $journal): Transaction
|
||||
{
|
||||
return $journal->transactions->firstWhere('amount', '<', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return Transaction
|
||||
*/
|
||||
private function getDestinationTransaction(TransactionJournal $journal): Transaction
|
||||
{
|
||||
return $journal->transactions->firstWhere('amount', '>', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $type
|
||||
@@ -94,7 +170,7 @@ class FixAccountTypes extends Command
|
||||
$journal->transactionType()->associate($withdrawal);
|
||||
$journal->save();
|
||||
$message = sprintf('Converted transaction #%d from a transfer to a withdrawal.', $journal->id);
|
||||
$this->info($message);
|
||||
$this->friendlyInfo($message);
|
||||
Log::debug($message);
|
||||
// check it again:
|
||||
$this->inspectJournal($journal);
|
||||
@@ -107,7 +183,7 @@ class FixAccountTypes extends Command
|
||||
$journal->transactionType()->associate($deposit);
|
||||
$journal->save();
|
||||
$message = sprintf('Converted transaction #%d from a transfer to a deposit.', $journal->id);
|
||||
$this->info($message);
|
||||
$this->friendlyInfo($message);
|
||||
Log::debug($message);
|
||||
// check it again:
|
||||
$this->inspectJournal($journal);
|
||||
@@ -128,7 +204,7 @@ class FixAccountTypes extends Command
|
||||
$result->id,
|
||||
$result->name
|
||||
);
|
||||
$this->info($message);
|
||||
$this->friendlyWarning($message);
|
||||
Log::debug($message);
|
||||
$this->inspectJournal($journal);
|
||||
break;
|
||||
@@ -148,93 +224,20 @@ class FixAccountTypes extends Command
|
||||
$result->id,
|
||||
$result->name
|
||||
);
|
||||
$this->info($message);
|
||||
$this->friendlyWarning($message);
|
||||
Log::debug($message);
|
||||
$this->inspectJournal($journal);
|
||||
break;
|
||||
default:
|
||||
$message = sprintf('The source account of %s #%d cannot be of type "%s".', $type, $journal->id, $source->account->accountType->type);
|
||||
$this->info($message);
|
||||
$this->friendlyError($message);
|
||||
Log::debug($message);
|
||||
|
||||
$message = sprintf('The destination account of %s #%d cannot be of type "%s".', $type, $journal->id, $dest->account->accountType->type);
|
||||
$this->info($message);
|
||||
$this->friendlyError($message);
|
||||
Log::debug($message);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return Transaction
|
||||
*/
|
||||
private function getDestinationTransaction(TransactionJournal $journal): Transaction
|
||||
{
|
||||
return $journal->transactions->firstWhere('amount', '>', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return Transaction
|
||||
*/
|
||||
private function getSourceTransaction(TransactionJournal $journal): Transaction
|
||||
{
|
||||
return $journal->transactions->firstWhere('amount', '<', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function inspectJournal(TransactionJournal $journal): void
|
||||
{
|
||||
$transactions = $journal->transactions()->count();
|
||||
if (2 !== $transactions) {
|
||||
Log::debug(sprintf('Journal has %d transactions, so can\'t fix.', $transactions));
|
||||
$this->info(sprintf('Cannot inspect transaction journal #%d because it has %d transaction(s) instead of 2.', $journal->id, $transactions));
|
||||
|
||||
return;
|
||||
}
|
||||
$type = $journal->transactionType->type;
|
||||
$sourceTransaction = $this->getSourceTransaction($journal);
|
||||
$destTransaction = $this->getDestinationTransaction($journal);
|
||||
$sourceAccount = $sourceTransaction->account;
|
||||
$sourceAccountType = $sourceAccount->accountType->type;
|
||||
$destAccount = $destTransaction->account;
|
||||
$destAccountType = $destAccount->accountType->type;
|
||||
|
||||
if (!array_key_exists($type, $this->expected)) {
|
||||
Log::info(sprintf('No source/destination info for transaction type %s.', $type));
|
||||
$this->info(sprintf('No source/destination info for transaction type %s.', $type));
|
||||
|
||||
return;
|
||||
}
|
||||
if (!array_key_exists($sourceAccountType, $this->expected[$type])) {
|
||||
Log::debug(sprintf('Going to fix journal #%d', $journal->id));
|
||||
$this->fixJournal($journal, $type, $sourceTransaction, $destTransaction);
|
||||
|
||||
return;
|
||||
}
|
||||
$expectedTypes = $this->expected[$type][$sourceAccountType];
|
||||
if (!in_array($destAccountType, $expectedTypes, true)) {
|
||||
Log::debug(sprintf('Going to fix journal #%d', $journal->id));
|
||||
$this->fixJournal($journal, $type, $sourceTransaction, $destTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->count = 0;
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Preference;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
@@ -36,6 +37,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class FixFrontpageAccounts extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Fixes a preference that may include deleted accounts or accounts of another type.';
|
||||
protected $signature = 'firefly-iii:fix-frontpage-accounts';
|
||||
|
||||
@@ -54,7 +57,7 @@ class FixFrontpageAccounts extends Command
|
||||
$this->fixPreference($preference);
|
||||
}
|
||||
}
|
||||
$this->info('Correct: account preferences are OK');
|
||||
$this->friendlyPositive('Account preferences are OK');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use DB;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
||||
use FireflyIII\Handlers\Events\UpdatedGroupEventHandler;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
@@ -36,6 +37,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class FixGroupAccounts extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Unify the source / destination accounts of split groups.';
|
||||
protected $signature = 'firefly-iii:unify-group-accounts';
|
||||
|
||||
@@ -62,7 +65,7 @@ class FixGroupAccounts extends Command
|
||||
$handler->unifyAccounts($event);
|
||||
}
|
||||
|
||||
$this->info('Correct: updated possible inconsistent transaction groups.');
|
||||
$this->friendlyPositive('Updated possible inconsistent transaction groups.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -34,6 +35,8 @@ use Illuminate\Support\Collection;
|
||||
*/
|
||||
class FixIbans extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Removes spaces from IBANs';
|
||||
protected $signature = 'firefly-iii:fix-ibans';
|
||||
private int $count = 0;
|
||||
@@ -49,12 +52,34 @@ class FixIbans extends Command
|
||||
$this->filterIbans($accounts);
|
||||
$this->countAndCorrectIbans($accounts);
|
||||
if (0 === $this->count) {
|
||||
$this->info('Correct: All IBANs are valid.');
|
||||
$this->friendlyPositive('All IBANs are valid.');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function filterIbans(Collection $accounts): void
|
||||
{
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$iban = $account->iban;
|
||||
if (str_contains($iban, ' ')) {
|
||||
$iban = app('steam')->filterSpaces((string)$account->iban);
|
||||
if ('' !== $iban) {
|
||||
$account->iban = $iban;
|
||||
$account->save();
|
||||
$this->friendlyInfo(sprintf('Removed spaces from IBAN of account #%d', $account->id));
|
||||
$this->count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*
|
||||
@@ -82,7 +107,7 @@ class FixIbans extends Command
|
||||
&& // allowed combination
|
||||
!(AccountType::REVENUE === $set[$userId][$iban] && AccountType::EXPENSE === $type) // also allowed combination.
|
||||
) {
|
||||
$this->line(
|
||||
$this->friendlyWarning(
|
||||
sprintf(
|
||||
'IBAN "%s" is used more than once and will be removed from %s #%d ("%s")',
|
||||
$iban,
|
||||
@@ -102,26 +127,4 @@ class FixIbans extends Command
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function filterIbans(Collection $accounts): void
|
||||
{
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$iban = $account->iban;
|
||||
if (str_contains($iban, ' ')) {
|
||||
$iban = app('steam')->filterSpaces((string)$account->iban);
|
||||
if ('' !== $iban) {
|
||||
$account->iban = $iban;
|
||||
$account->save();
|
||||
$this->line(sprintf('Removed spaces from IBAN of account #%d', $account->id));
|
||||
$this->count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -33,6 +34,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class FixLongDescriptions extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
private const MAX_LENGTH = 1000;
|
||||
protected $description = 'Fixes long descriptions in journals and groups.';
|
||||
protected $signature = 'firefly-iii:fix-long-descriptions';
|
||||
@@ -51,7 +54,7 @@ class FixLongDescriptions extends Command
|
||||
if (strlen($journal->description) > self::MAX_LENGTH) {
|
||||
$journal->description = substr($journal->description, 0, self::MAX_LENGTH);
|
||||
$journal->save();
|
||||
$this->line(sprintf('Truncated description of transaction journal #%d', $journal->id));
|
||||
$this->friendlyWarning(sprintf('Truncated description of transaction journal #%d', $journal->id));
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
@@ -62,12 +65,12 @@ class FixLongDescriptions extends Command
|
||||
if (strlen((string)$group->title) > self::MAX_LENGTH) {
|
||||
$group->title = substr($group->title, 0, self::MAX_LENGTH);
|
||||
$group->save();
|
||||
$this->line(sprintf('Truncated description of transaction group #%d', $group->id));
|
||||
$this->friendlyWarning(sprintf('Truncated description of transaction group #%d', $group->id));
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: all transaction group and journal title lengths are within bounds.');
|
||||
$this->friendlyPositive('All transaction group and journal title lengths are within bounds.');
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -34,6 +35,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class FixPiggies extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Fixes common issues with piggy banks.';
|
||||
protected $signature = 'firefly-iii:fix-piggies';
|
||||
|
||||
@@ -63,10 +66,10 @@ class FixPiggies extends Command
|
||||
}
|
||||
}
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: all piggy bank events are OK.');
|
||||
$this->friendlyPositive('All piggy bank events are OK.');
|
||||
}
|
||||
if (0 !== $count) {
|
||||
$this->line(sprintf('Fixed %d piggy bank event(s).', $count));
|
||||
$this->friendlyInfo(sprintf('Fixed %d piggy bank event(s).', $count));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\Recurrence;
|
||||
use FireflyIII\Models\RecurrenceTransaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
@@ -37,6 +38,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class FixRecurringTransactions extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Fixes recurring transactions with the wrong transaction type.';
|
||||
protected $signature = 'firefly-iii:fix-recurring-transactions';
|
||||
private int $count = 0;
|
||||
@@ -53,12 +56,25 @@ class FixRecurringTransactions extends Command
|
||||
$this->stupidLaravel();
|
||||
$this->correctTransactions();
|
||||
if (0 === $this->count) {
|
||||
$this->info('Correct: all recurring transactions are OK.');
|
||||
$this->friendlyPositive('All recurring transactions are OK.');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->recurringRepos = app(RecurringRepositoryInterface::class);
|
||||
$this->userRepos = app(UserRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -71,6 +87,19 @@ class FixRecurringTransactions extends Command
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
private function processUser(User $user): void
|
||||
{
|
||||
$this->recurringRepos->setUser($user);
|
||||
$recurrences = $this->recurringRepos->get();
|
||||
/** @var Recurrence $recurrence */
|
||||
foreach ($recurrences as $recurrence) {
|
||||
$this->processRecurrence($recurrence);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Recurrence $recurrence
|
||||
*/
|
||||
@@ -93,7 +122,7 @@ class FixRecurringTransactions extends Command
|
||||
$type = $recurrence->transactionType;
|
||||
$link = config(sprintf('firefly.account_to_transaction.%s.%s', $source->accountType->type, $destination->accountType->type));
|
||||
if (null !== $link && strtolower($type->type) !== strtolower($link)) {
|
||||
$this->warn(
|
||||
$this->friendlyWarning(
|
||||
sprintf('Recurring transaction #%d should be a "%s" but is a "%s" and will be corrected.', $recurrence->id, $link, $type->type)
|
||||
);
|
||||
$transactionType = TransactionType::whereType($link)->first();
|
||||
@@ -104,30 +133,4 @@ class FixRecurringTransactions extends Command
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
private function processUser(User $user): void
|
||||
{
|
||||
$this->recurringRepos->setUser($user);
|
||||
$recurrences = $this->recurringRepos->get();
|
||||
/** @var Recurrence $recurrence */
|
||||
foreach ($recurrences as $recurrence) {
|
||||
$this->processRecurrence($recurrence);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->recurringRepos = app(RecurringRepositoryInterface::class);
|
||||
$this->userRepos = app(UserRepositoryInterface::class);
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
@@ -37,6 +38,8 @@ use Illuminate\Support\Collection;
|
||||
*/
|
||||
class FixTransactionTypes extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Make sure all transactions are of the correct type, based on source + dest.';
|
||||
protected $signature = 'firefly-iii:fix-transaction-types';
|
||||
|
||||
@@ -57,28 +60,15 @@ class FixTransactionTypes extends Command
|
||||
}
|
||||
}
|
||||
if ($count > 0) {
|
||||
$this->info('Corrected transaction type of %d transaction journals.', $count);
|
||||
$this->friendlyInfo('Corrected transaction type of %d transaction journals.', $count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
$this->info('Correct: all transaction journals are of the correct transaction type');
|
||||
$this->friendlyPositive('All transaction journals are of the correct transaction type');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $expectedType
|
||||
*/
|
||||
private function changeJournal(TransactionJournal $journal, string $expectedType): void
|
||||
{
|
||||
$type = TransactionType::whereType($expectedType)->first();
|
||||
if (null !== $type) {
|
||||
$journal->transaction_type_id = $type->id;
|
||||
$journal->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all transaction journals.
|
||||
*
|
||||
@@ -102,13 +92,13 @@ class FixTransactionTypes extends Command
|
||||
$source = $this->getSourceAccount($journal);
|
||||
$destination = $this->getDestinationAccount($journal);
|
||||
} catch (FireflyException $e) {
|
||||
$this->error($e->getMessage());
|
||||
$this->friendlyError($e->getMessage());
|
||||
|
||||
return false;
|
||||
}
|
||||
$expectedType = (string)config(sprintf('firefly.account_to_transaction.%s.%s', $source->accountType->type, $destination->accountType->type));
|
||||
if ($expectedType !== $type) {
|
||||
$this->line(
|
||||
$this->friendlyWarning(
|
||||
sprintf(
|
||||
'Transaction journal #%d was of type "%s" but is corrected to "%s" (%s -> %s)',
|
||||
$journal->id,
|
||||
@@ -126,6 +116,36 @@ class FixTransactionTypes extends Command
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return Account
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function getSourceAccount(TransactionJournal $journal): Account
|
||||
{
|
||||
$collection = $journal->transactions->filter(
|
||||
static function (Transaction $transaction) {
|
||||
return $transaction->amount < 0;
|
||||
}
|
||||
);
|
||||
if (0 === $collection->count()) {
|
||||
throw new FireflyException(sprintf('300001: Journal #%d has no source transaction.', $journal->id));
|
||||
}
|
||||
if (1 !== $collection->count()) {
|
||||
throw new FireflyException(sprintf('300002: Journal #%d has multiple source transactions.', $journal->id));
|
||||
}
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $collection->first();
|
||||
/** @var Account|null $account */
|
||||
$account = $transaction->account;
|
||||
if (null === $account) {
|
||||
throw new FireflyException(sprintf('300003: Journal #%d, transaction #%d has no source account.', $journal->id, $transaction->id));
|
||||
}
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
@@ -158,31 +178,14 @@ class FixTransactionTypes extends Command
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return Account
|
||||
* @throws FireflyException
|
||||
* @param string $expectedType
|
||||
*/
|
||||
private function getSourceAccount(TransactionJournal $journal): Account
|
||||
private function changeJournal(TransactionJournal $journal, string $expectedType): void
|
||||
{
|
||||
$collection = $journal->transactions->filter(
|
||||
static function (Transaction $transaction) {
|
||||
return $transaction->amount < 0;
|
||||
}
|
||||
);
|
||||
if (0 === $collection->count()) {
|
||||
throw new FireflyException(sprintf('300001: Journal #%d has no source transaction.', $journal->id));
|
||||
}
|
||||
if (1 !== $collection->count()) {
|
||||
throw new FireflyException(sprintf('300002: Journal #%d has multiple source transactions.', $journal->id));
|
||||
}
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $collection->first();
|
||||
/** @var Account|null $account */
|
||||
$account = $transaction->account;
|
||||
if (null === $account) {
|
||||
throw new FireflyException(sprintf('300003: Journal #%d, transaction #%d has no source account.', $journal->id, $transaction->id));
|
||||
}
|
||||
|
||||
return $account;
|
||||
$type = TransactionType::whereType($expectedType)->first();
|
||||
if (null !== $type) {
|
||||
$journal->transaction_type_id = $type->id;
|
||||
$journal->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use DB;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -34,6 +35,8 @@ use stdClass;
|
||||
*/
|
||||
class FixUnevenAmount extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Fix journals with uneven amounts.';
|
||||
protected $signature = 'firefly-iii:fix-uneven-amount';
|
||||
|
||||
@@ -51,16 +54,32 @@ class FixUnevenAmount extends Command
|
||||
->get(['transaction_journal_id', DB::raw('SUM(amount) AS the_sum')]);
|
||||
/** @var stdClass $entry */
|
||||
foreach ($journals as $entry) {
|
||||
$sum = (string)$entry->the_sum;
|
||||
if (!is_numeric($sum) || '' === $sum || str_contains($sum, 'e') || str_contains($sum, ',')) {
|
||||
$message = sprintf(
|
||||
'Journal #%d has an invalid sum ("%s"). No sure what to do.',
|
||||
$entry->transaction_journal_id,
|
||||
$entry->the_sum
|
||||
);
|
||||
$this->friendlyWarning($message);
|
||||
app('log')->warning($message);
|
||||
$count++;
|
||||
continue;
|
||||
}
|
||||
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);
|
||||
$message = sprintf(
|
||||
'Sum of journal #%d is %s instead of zero.',
|
||||
$entry->transaction_journal_id,
|
||||
$entry->the_sum
|
||||
);
|
||||
$this->friendlyWarning($message);
|
||||
app('log')->warning($message);
|
||||
$this->fixJournal((int)$entry->transaction_journal_id);
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: Database amount integrity is OK');
|
||||
$this->friendlyPositive('Database amount integrity is OK');
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -80,7 +99,7 @@ class FixUnevenAmount extends Command
|
||||
$source = $journal->transactions()->where('amount', '<', 0)->first();
|
||||
|
||||
if (null === $source) {
|
||||
$this->error(
|
||||
$this->friendlyError(
|
||||
sprintf(
|
||||
'Journal #%d ("%s") has no source transaction. It will be deleted to maintain database consistency.',
|
||||
$journal->id ?? 0,
|
||||
@@ -100,7 +119,7 @@ class FixUnevenAmount extends Command
|
||||
$destination = $journal->transactions()->where('amount', '>', 0)->first();
|
||||
|
||||
if (null === $destination) {
|
||||
$this->error(
|
||||
$this->friendlyError(
|
||||
sprintf(
|
||||
'Journal #%d ("%s") has no destination transaction. It will be deleted to maintain database consistency.',
|
||||
$journal->id ?? 0,
|
||||
@@ -118,6 +137,6 @@ class FixUnevenAmount extends Command
|
||||
$destination->save();
|
||||
|
||||
$message = sprintf('Corrected amount in transaction journal #%d', $param);
|
||||
$this->line($message);
|
||||
$this->friendlyInfo($message);
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -32,6 +33,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class RemoveBills extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Remove bills from transactions that shouldn\'t have one.';
|
||||
protected $signature = 'firefly-iii:remove-bills';
|
||||
|
||||
@@ -50,14 +53,14 @@ class RemoveBills extends Command
|
||||
$journals = TransactionJournal::whereNotNull('bill_id')->where('transaction_type_id', '!=', $withdrawal->id)->get();
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$this->line(sprintf('Transaction journal #%d should not be linked to bill #%d.', $journal->id, $journal->bill_id));
|
||||
$this->friendlyWarning(sprintf('Transaction journal #%d will be unlinked from bill #%d.', $journal->id, $journal->bill_id));
|
||||
$journal->bill_id = null;
|
||||
$journal->save();
|
||||
}
|
||||
if ($journals->count() > 0) {
|
||||
$this->info('Fixed all transaction journals so they have correct bill information.');
|
||||
$this->friendlyInfo('Fixed all transaction journals so they have correct bill information.');
|
||||
}
|
||||
$this->info('Correct: verified bills / journals in %s seconds');
|
||||
$this->friendlyPositive('All bills and journals are OK');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use DB;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
@@ -31,6 +32,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class RenameMetaFields extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Rename changed meta fields.';
|
||||
protected $signature = 'firefly-iii:rename-meta-fields';
|
||||
|
||||
@@ -63,10 +66,10 @@ class RenameMetaFields extends Command
|
||||
$this->rename($original, $update);
|
||||
}
|
||||
if (0 === $this->count) {
|
||||
$this->info('Correct: all meta fields are correct.');
|
||||
$this->friendlyPositive('All meta fields are correct.');
|
||||
}
|
||||
if (0 !== $this->count) {
|
||||
$this->info(sprintf('Renamed %d meta field(s).', $this->count));
|
||||
$this->friendlyInfo(sprintf('Renamed %d meta field(s).', $this->count));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -33,6 +34,8 @@ use Illuminate\Support\Facades\Log;
|
||||
*/
|
||||
class TransferBudgets extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Removes budgets from transfers.';
|
||||
protected $signature = 'firefly-iii:fix-transfer-budgets';
|
||||
|
||||
@@ -52,19 +55,19 @@ class TransferBudgets extends Command
|
||||
/** @var TransactionJournal $entry */
|
||||
foreach ($set as $entry) {
|
||||
$message = sprintf('Transaction journal #%d is a %s, so has no longer a budget.', $entry->id, $entry->transactionType->type);
|
||||
$this->info($message);
|
||||
$this->friendlyInfo($message);
|
||||
Log::debug($message);
|
||||
$entry->budgets()->sync([]);
|
||||
$count++;
|
||||
}
|
||||
if (0 === $count) {
|
||||
$message = 'Correct: no invalid budget/journal entries.';
|
||||
$this->info($message);
|
||||
$message = 'No invalid budget/journal entries.';
|
||||
$this->friendlyPositive($message);
|
||||
}
|
||||
if (0 !== $count) {
|
||||
$message = sprintf('Corrected %d invalid budget/journal entries (entry).', $count);
|
||||
Log::debug($message);
|
||||
$this->line($message);
|
||||
$this->friendlyInfo($message);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@@ -7,7 +7,6 @@ namespace FireflyIII\Console\Commands\Correction;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Services\Internal\Support\CreditRecalculateService;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class CorrectionSkeleton
|
||||
@@ -30,6 +29,16 @@ class TriggerCreditCalculation extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function processAccounts(): void
|
||||
{
|
||||
$accounts = Account::leftJoin('account_types', 'accounts.account_type_id', 'account_types.id')
|
||||
->whereIn('account_types.type', config('firefly.valid_liabilities'))
|
||||
->get(['accounts.*']);
|
||||
foreach ($accounts as $account) {
|
||||
$this->processAccount($account);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
@@ -42,14 +51,4 @@ class TriggerCreditCalculation extends Command
|
||||
$object->setAccount($account);
|
||||
$object->recalculate();
|
||||
}
|
||||
|
||||
private function processAccounts(): void
|
||||
{
|
||||
$accounts = Account::leftJoin('account_types', 'accounts.account_type_id', 'account_types.id')
|
||||
->whereIn('account_types.type', config('firefly.valid_liabilities'))
|
||||
->get(['accounts.*']);
|
||||
foreach ($accounts as $account) {
|
||||
$this->processAccount($account);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@ namespace FireflyIII\Console\Commands\Export;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Console\Commands\VerifiesAccessToken;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
@@ -35,14 +36,15 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Support\Export\ExportDataGenerator;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
use InvalidArgumentException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class ExportData
|
||||
*/
|
||||
class ExportData extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
use VerifiesAccessToken;
|
||||
|
||||
/**
|
||||
@@ -86,7 +88,7 @@ class ExportData extends Command
|
||||
{
|
||||
// verify access token
|
||||
if (!$this->verifyAccessToken()) {
|
||||
$this->error('Invalid access token. Check /profile.');
|
||||
$this->friendlyError('Invalid access token. Check /profile.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -99,7 +101,7 @@ class ExportData extends Command
|
||||
try {
|
||||
$options = $this->parseOptions();
|
||||
} catch (FireflyException $e) {
|
||||
$this->error(sprintf('Could not work with your options: %s', $e));
|
||||
$this->friendlyError(sprintf('Could not work with your options: %s', $e));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -122,14 +124,14 @@ class ExportData extends Command
|
||||
$exporter->setExportPiggies($options['export']['piggies']);
|
||||
$data = $exporter->export();
|
||||
if (0 === count($data)) {
|
||||
$this->error('You must export *something*. Use --export-transactions or another option. See docs.firefly-iii.org');
|
||||
$this->friendlyError('You must export *something*. Use --export-transactions or another option. See docs.firefly-iii.org');
|
||||
}
|
||||
$returnCode = 0;
|
||||
if (0 !== count($data)) {
|
||||
try {
|
||||
$this->exportData($options, $data);
|
||||
} catch (FireflyException $e) {
|
||||
$this->error(sprintf('Could not store data: %s', $e->getMessage()));
|
||||
$this->friendlyError(sprintf('Could not store data: %s', $e->getMessage()));
|
||||
|
||||
$returnCode = 1;
|
||||
}
|
||||
@@ -198,7 +200,7 @@ class ExportData extends Command
|
||||
$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')));
|
||||
$this->friendlyError(sprintf('%s date "%s" must be formatted YYYY-MM-DD. Field will be ignored.', $field, $this->option('start')));
|
||||
$error = true;
|
||||
}
|
||||
}
|
||||
@@ -288,11 +290,11 @@ class ExportData extends Command
|
||||
throw new FireflyException(sprintf('File "%s" exists already. Use --force to overwrite.', $file));
|
||||
}
|
||||
if (true === $options['force'] && file_exists($file)) {
|
||||
$this->warn(sprintf('File "%s" exists already but will be replaced.', $file));
|
||||
$this->friendlyWarning(sprintf('File "%s" exists already but will be replaced.', $file));
|
||||
}
|
||||
// continue to write to file.
|
||||
file_put_contents($file, $content);
|
||||
$this->info(sprintf('Wrote %s-export to file "%s".', $key, $file));
|
||||
$this->friendlyPositive(sprintf('Wrote %s-export to file "%s".', $key, $file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,23 +24,52 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Integrity;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\GroupMembership;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\Models\UserRole;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class CreateGroupMemberships
|
||||
*/
|
||||
class CreateGroupMemberships extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '560_create_group_memberships';
|
||||
protected $description = 'Update group memberships';
|
||||
protected $signature = 'firefly-iii:create-group-memberships';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$this->createGroupMemberships();
|
||||
$this->friendlyPositive('Validated group memberships');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function createGroupMemberships(): void
|
||||
{
|
||||
$users = User::get();
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
self::createGroupMembership($user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO move to helper.
|
||||
*
|
||||
@@ -78,31 +107,4 @@ class CreateGroupMemberships extends Command
|
||||
$user->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$this->createGroupMemberships();
|
||||
$this->info('Correct: validated group memberships');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function createGroupMemberships(): void
|
||||
{
|
||||
$users = User::get();
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
self::createGroupMembership($user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Integrity;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
@@ -35,6 +36,8 @@ use stdClass;
|
||||
*/
|
||||
class ReportEmptyObjects extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
@@ -64,51 +67,6 @@ class ReportEmptyObjects extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports on accounts with no transactions.
|
||||
*/
|
||||
private function reportAccounts(): void
|
||||
{
|
||||
$set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
|
||||
->leftJoin('users', 'accounts.user_id', '=', 'users.id')
|
||||
->groupBy(['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email'])
|
||||
->whereNull('transactions.account_id')
|
||||
->get(
|
||||
['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email']
|
||||
);
|
||||
|
||||
/** @var stdClass $entry */
|
||||
foreach ($set as $entry) {
|
||||
$line = 'User #%d (%s) has account #%d ("%s") which has no transactions.';
|
||||
$line = sprintf($line, $entry->user_id, $entry->email, $entry->id, $entry->name);
|
||||
$this->line($line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports on budgets with no budget limits (which makes them pointless).
|
||||
*/
|
||||
private function reportBudgetLimits(): void
|
||||
{
|
||||
$set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id')
|
||||
->leftJoin('users', 'budgets.user_id', '=', 'users.id')
|
||||
->groupBy(['budgets.id', 'budgets.name', 'budgets.encrypted', 'budgets.user_id', 'users.email'])
|
||||
->whereNull('budget_limits.id')
|
||||
->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'budgets.encrypted', 'users.email']);
|
||||
|
||||
/** @var Budget $entry */
|
||||
foreach ($set as $entry) {
|
||||
$line = sprintf(
|
||||
'User #%d (%s) has budget #%d ("%s") which has no budget limits.',
|
||||
$entry->user_id,
|
||||
$entry->email,
|
||||
$entry->id,
|
||||
$entry->name
|
||||
);
|
||||
$this->line($line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Report on budgets with no transactions or journals.
|
||||
*/
|
||||
@@ -130,7 +88,7 @@ class ReportEmptyObjects extends Command
|
||||
$entry->id,
|
||||
$entry->name
|
||||
);
|
||||
$this->line($line);
|
||||
$this->friendlyWarning($line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +113,7 @@ class ReportEmptyObjects extends Command
|
||||
$entry->id,
|
||||
$entry->name
|
||||
);
|
||||
$this->line($line);
|
||||
$this->friendlyWarning($line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +138,52 @@ class ReportEmptyObjects extends Command
|
||||
$entry->id,
|
||||
$entry->tag
|
||||
);
|
||||
$this->line($line);
|
||||
$this->friendlyWarning($line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports on accounts with no transactions.
|
||||
*/
|
||||
private function reportAccounts(): void
|
||||
{
|
||||
$set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
|
||||
->leftJoin('users', 'accounts.user_id', '=', 'users.id')
|
||||
->groupBy(['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email'])
|
||||
->whereNull('transactions.account_id')
|
||||
->get(
|
||||
['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email']
|
||||
);
|
||||
|
||||
/** @var stdClass $entry */
|
||||
foreach ($set as $entry) {
|
||||
$line = 'User #%d (%s) has account #%d ("%s") which has no transactions.';
|
||||
$line = sprintf($line, $entry->user_id, $entry->email, $entry->id, $entry->name);
|
||||
$this->friendlyWarning($line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports on budgets with no budget limits (which makes them pointless).
|
||||
*/
|
||||
private function reportBudgetLimits(): void
|
||||
{
|
||||
$set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id')
|
||||
->leftJoin('users', 'budgets.user_id', '=', 'users.id')
|
||||
->groupBy(['budgets.id', 'budgets.name', 'budgets.encrypted', 'budgets.user_id', 'users.email'])
|
||||
->whereNull('budget_limits.id')
|
||||
->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'budgets.encrypted', 'users.email']);
|
||||
|
||||
/** @var Budget $entry */
|
||||
foreach ($set as $entry) {
|
||||
$line = sprintf(
|
||||
'User #%d (%s) has budget #%d ("%s") which has no budget limits.',
|
||||
$entry->user_id,
|
||||
$entry->email,
|
||||
$entry->id,
|
||||
$entry->name
|
||||
);
|
||||
$this->friendlyWarning($line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\Integrity;
|
||||
|
||||
use Artisan;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use Illuminate\Console\Command;
|
||||
use Schema;
|
||||
|
||||
@@ -34,6 +35,8 @@ use Schema;
|
||||
*/
|
||||
class ReportIntegrity extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
@@ -60,11 +63,10 @@ class ReportIntegrity extends Command
|
||||
'firefly-iii:create-group-memberships',
|
||||
'firefly-iii:report-empty-objects',
|
||||
'firefly-iii:report-sum',
|
||||
'firefly-iii:restore-oauth-keys',
|
||||
'firefly-iii:upgrade-group-information',
|
||||
];
|
||||
foreach ($commands as $command) {
|
||||
$this->line(sprintf('Now executing %s', $command));
|
||||
$this->friendlyLine(sprintf('Now executing %s', $command));
|
||||
$this->call($command);
|
||||
}
|
||||
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Integrity;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -32,6 +33,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class ReportSum extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Report on the total sum of transactions. Must be 0.';
|
||||
protected $signature = 'firefly-iii:report-sum';
|
||||
|
||||
@@ -58,12 +61,17 @@ class ReportSum extends Command
|
||||
/** @var User $user */
|
||||
foreach ($userRepository->all() as $user) {
|
||||
$sum = (string)$user->transactions()->sum('amount');
|
||||
if (!is_numeric($sum)) {
|
||||
$message = sprintf('Error: Transactions for user #%d (%s) have an invalid sum ("%s").', $user->id, $user->email, $sum);
|
||||
$this->friendlyError($message);
|
||||
continue;
|
||||
}
|
||||
if (0 !== bccomp($sum, '0')) {
|
||||
$message = sprintf('Error: Transactions for user #%d (%s) are off by %s!', $user->id, $user->email, $sum);
|
||||
$this->error($message);
|
||||
$this->friendlyError($message);
|
||||
}
|
||||
if (0 === bccomp($sum, '0')) {
|
||||
$this->info(sprintf('Correct: Amount integrity OK for user #%d', $user->id));
|
||||
$this->friendlyPositive(sprintf('Amount integrity OK for user #%d', $user->id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Integrity;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Support\System\OAuthKeys;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
@@ -32,6 +33,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class RestoreOAuthKeys extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Will restore the OAuth keys generated for the system.';
|
||||
protected $signature = 'firefly-iii:restore-oauth-keys';
|
||||
|
||||
@@ -50,9 +53,35 @@ class RestoreOAuthKeys extends Command
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function generateKeys(): void
|
||||
private function restoreOAuthKeys(): void
|
||||
{
|
||||
OAuthKeys::generateKeys();
|
||||
if (!$this->keysInDatabase() && !$this->keysOnDrive()) {
|
||||
$this->generateKeys();
|
||||
$this->storeKeysInDB();
|
||||
$this->friendlyInfo('Generated and stored new keys.');
|
||||
|
||||
return;
|
||||
}
|
||||
if ($this->keysInDatabase() && !$this->keysOnDrive()) {
|
||||
$result = $this->restoreKeysFromDB();
|
||||
if (true === $result) {
|
||||
$this->friendlyInfo('Restored OAuth keys from database.');
|
||||
|
||||
return;
|
||||
}
|
||||
$this->generateKeys();
|
||||
$this->storeKeysInDB();
|
||||
$this->friendlyInfo('Generated and stored new keys.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (!$this->keysInDatabase() && $this->keysOnDrive()) {
|
||||
$this->storeKeysInDB();
|
||||
$this->friendlyInfo('Stored OAuth keys in database.');
|
||||
|
||||
return;
|
||||
}
|
||||
$this->friendlyPositive('OAuth keys are OK');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,43 +103,9 @@ class RestoreOAuthKeys extends Command
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function restoreKeysFromDB(): bool
|
||||
private function generateKeys(): void
|
||||
{
|
||||
return OAuthKeys::restoreKeysFromDB();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function restoreOAuthKeys(): void
|
||||
{
|
||||
if (!$this->keysInDatabase() && !$this->keysOnDrive()) {
|
||||
$this->generateKeys();
|
||||
$this->storeKeysInDB();
|
||||
$this->line('Correct: generated and stored new keys.');
|
||||
|
||||
return;
|
||||
}
|
||||
if ($this->keysInDatabase() && !$this->keysOnDrive()) {
|
||||
$result = $this->restoreKeysFromDB();
|
||||
if (true === $result) {
|
||||
$this->line('Correct: restored OAuth keys from database.');
|
||||
|
||||
return;
|
||||
}
|
||||
$this->generateKeys();
|
||||
$this->storeKeysInDB();
|
||||
$this->line('Correct: generated and stored new keys.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (!$this->keysInDatabase() && $this->keysOnDrive()) {
|
||||
$this->storeKeysInDB();
|
||||
$this->line('Correct: stored OAuth keys in database.');
|
||||
|
||||
return;
|
||||
}
|
||||
$this->line('Correct: OAuth keys are OK');
|
||||
OAuthKeys::generateKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,4 +115,12 @@ class RestoreOAuthKeys extends Command
|
||||
{
|
||||
OAuthKeys::storeKeysInDB();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function restoreKeysFromDB(): bool
|
||||
{
|
||||
return OAuthKeys::restoreKeysFromDB();
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Integrity;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Models\AvailableBudget;
|
||||
@@ -48,6 +49,8 @@ use Illuminate\Database\QueryException;
|
||||
*/
|
||||
class UpdateGroupInformation extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Makes sure that every object is linked to a group';
|
||||
protected $signature = 'firefly-iii:upgrade-group-information';
|
||||
|
||||
@@ -74,7 +77,7 @@ class UpdateGroupInformation extends Command
|
||||
{
|
||||
$group = $user->userGroup;
|
||||
if (null === $group) {
|
||||
$this->warn(sprintf('User "%s" has no group.', $user->email));
|
||||
$this->friendlyWarning(sprintf('User "%s" has no group.', $user->email));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -111,12 +114,12 @@ class UpdateGroupInformation extends Command
|
||||
try {
|
||||
$result = $className::where('user_id', $user->id)->where('user_group_id', null)->update(['user_group_id' => $group->id]);
|
||||
} catch (QueryException $e) {
|
||||
$this->error(sprintf('Could not update group information for "%s" because of error "%s"', $className, $e->getMessage()));
|
||||
$this->friendlyError(sprintf('Could not update group information for "%s" because of error "%s"', $className, $e->getMessage()));
|
||||
|
||||
return;
|
||||
}
|
||||
if (0 !== $result) {
|
||||
$this->info(sprintf('Correct: Moved %d %s objects to the correct group.', $result, str_replace('FireflyIII\\Models\\', '', $className)));
|
||||
$this->friendlyPositive(sprintf('Moved %d %s objects to the correct group.', $result, str_replace('FireflyIII\\Models\\', '', $className)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
85
app/Console/Commands/ShowsFriendlyMessages.php
Normal file
85
app/Console/Commands/ShowsFriendlyMessages.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* ShowsFriendlyMessages.php
|
||||
* Copyright (c) 2023 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Console\Commands;
|
||||
|
||||
/**
|
||||
* Trait ShowsFriendlyMessages
|
||||
*/
|
||||
trait ShowsFriendlyMessages
|
||||
{
|
||||
/**
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function friendlyError(string $message): void
|
||||
{
|
||||
$this->error(sprintf(' [x] %s', trim($message)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function friendlyInfo(string $message): void
|
||||
{
|
||||
$this->friendlyNeutral($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function friendlyNeutral(string $message): void
|
||||
{
|
||||
$this->line(sprintf(' [i] %s', trim($message)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function friendlyLine(string $message): void
|
||||
{
|
||||
$this->line(sprintf(' %s', trim($message)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function friendlyPositive(string $message): void
|
||||
{
|
||||
$this->info(sprintf(' [✓] %s', trim($message)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function friendlyWarning(string $message): void
|
||||
{
|
||||
$this->warn(sprintf(' [!] %s', trim($message)));
|
||||
}
|
||||
|
||||
}
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\System;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use Illuminate\Console\Command;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
@@ -33,6 +34,8 @@ use PDOException;
|
||||
*/
|
||||
class CreateDatabase extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
@@ -54,7 +57,7 @@ class CreateDatabase extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
if ('mysql' !== env('DB_CONNECTION', 'mysql')) {
|
||||
$this->info(sprintf('CreateDB does not apply to "%s", skipped.', env('DB_CONNECTION')));
|
||||
$this->friendlyInfo(sprintf('CreateDB does not apply to "%s", skipped.', env('DB_CONNECTION')));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -67,7 +70,7 @@ class CreateDatabase extends Command
|
||||
if ('' !== env('DB_SOCKET', '')) {
|
||||
$dsn = sprintf('mysql:unix_socket=%s;charset=utf8mb4', env('DB_SOCKET', ''));
|
||||
}
|
||||
$this->info(sprintf('DSN is %s', $dsn));
|
||||
$this->friendlyLine(sprintf('DSN is %s', $dsn));
|
||||
|
||||
$options = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
@@ -79,7 +82,7 @@ class CreateDatabase extends Command
|
||||
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()));
|
||||
$this->friendlyError(sprintf('Error when connecting to DB: %s', $e->getMessage()));
|
||||
}
|
||||
|
||||
// only continue when no error.
|
||||
@@ -96,14 +99,14 @@ class CreateDatabase extends Command
|
||||
}
|
||||
}
|
||||
if (false === $exists && true === $checked) {
|
||||
$this->error(sprintf('Database "%s" does not exist.', env('DB_DATABASE')));
|
||||
$this->friendlyError(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')));
|
||||
$this->friendlyInfo(sprintf('Created database "%s"', env('DB_DATABASE')));
|
||||
}
|
||||
if (true === $exists && true === $checked) {
|
||||
$this->info(sprintf('Database "%s" exists.', env('DB_DATABASE')));
|
||||
$this->friendlyInfo(sprintf('Database "%s" exists.', env('DB_DATABASE')));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\System;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
@@ -36,6 +37,8 @@ use Str;
|
||||
*/
|
||||
class CreateFirstUser extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
@@ -58,14 +61,14 @@ class CreateFirstUser extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
if ('testing' !== env('APP_ENV', 'local')) {
|
||||
$this->error('This command only works in the testing environment.');
|
||||
$this->friendlyError('This command only works in the testing environment.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
$this->stupidLaravel();
|
||||
$count = $this->repository->count();
|
||||
if ($count > 0) {
|
||||
$this->error('Already have more than zero users in DB.');
|
||||
$this->friendlyError('Already have more than zero users in DB.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -81,8 +84,8 @@ class CreateFirstUser extends Command
|
||||
$user->save();
|
||||
$user->setRememberToken(Str::random(60));
|
||||
|
||||
$this->info(sprintf('Created new admin user (ID #%d) with email address "%s" and password "%s".', $user->id, $user->email, $password));
|
||||
$this->error('Change this password.');
|
||||
$this->friendlyInfo(sprintf('Created new admin user (ID #%d) with email address "%s" and password "%s".', $user->id, $user->email, $password));
|
||||
$this->friendlyWarning('Change this password.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\System;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AutoBudget;
|
||||
@@ -51,6 +52,8 @@ use Illuminate\Support\Facades\Log;
|
||||
*/
|
||||
class ForceDecimalSize extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'This command resizes DECIMAL columns in MySQL or PostgreSQL and correct amounts (only MySQL).';
|
||||
protected $signature = 'firefly-iii:force-decimal-size';
|
||||
private string $cast;
|
||||
@@ -96,8 +99,8 @@ class ForceDecimalSize extends Command
|
||||
Log::debug('Now in ForceDecimalSize::handle()');
|
||||
$this->determineDatabaseType();
|
||||
|
||||
$this->error('Running this command is dangerous and can cause data loss.');
|
||||
$this->error('Please do not continue.');
|
||||
$this->friendlyError('Running this command is dangerous and can cause data loss.');
|
||||
$this->friendlyError('Please do not continue.');
|
||||
$question = $this->confirm('Do you want to continue?');
|
||||
if (true === $question) {
|
||||
$this->correctAmounts();
|
||||
@@ -107,6 +110,112 @@ class ForceDecimalSize extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function determineDatabaseType(): void
|
||||
{
|
||||
// switch stuff based on database connection:
|
||||
$this->operator = 'REGEXP';
|
||||
$this->regularExpression = '\'\\\\.[\\\\d]{%d}[1-9]+\'';
|
||||
$this->cast = 'CHAR';
|
||||
if ('pgsql' === config('database.default')) {
|
||||
$this->operator = 'SIMILAR TO';
|
||||
$this->regularExpression = '\'%%\.[\d]{%d}[1-9]+%%\'';
|
||||
$this->cast = 'TEXT';
|
||||
}
|
||||
if ('sqlite' === config('database.default')) {
|
||||
$this->regularExpression = '"\\.[\d]{%d}[1-9]+"';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if a basic check can be done or if it needs to be complicated.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function correctAmounts(): void
|
||||
{
|
||||
// if sqlite, add function?
|
||||
if ('sqlite' === (string)config('database.default')) {
|
||||
DB::connection()->getPdo()->sqliteCreateFunction('REGEXP', function ($pattern, $value) {
|
||||
mb_regex_encoding('UTF-8');
|
||||
$pattern = trim($pattern, '"');
|
||||
|
||||
return (false !== mb_ereg($pattern, (string)$value)) ? 1 : 0;
|
||||
});
|
||||
}
|
||||
|
||||
if (!in_array((string)config('database.default'), ['mysql', 'pgsql', 'sqlite'], true)) {
|
||||
$this->friendlyWarning(sprintf('Skip correcting amounts, does not support "%s"...', (string)config('database.default')));
|
||||
|
||||
return;
|
||||
}
|
||||
$this->correctAmountsByCurrency();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method loops all enabled currencies and then calls the method that will fix all objects in this currency.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function correctAmountsByCurrency(): void
|
||||
{
|
||||
/** @var Collection $enabled */
|
||||
$enabled = TransactionCurrency::whereEnabled(1)->get();
|
||||
/** @var TransactionCurrency $currency */
|
||||
foreach ($enabled as $currency) {
|
||||
$this->correctByCurrency($currency);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method loops the available tables that may need fixing, and calls for the right method that can fix them.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
*
|
||||
* @return void
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function correctByCurrency(TransactionCurrency $currency): void
|
||||
{
|
||||
/**
|
||||
* @var string $name
|
||||
* @var array $fields
|
||||
*/
|
||||
foreach ($this->tables as $name => $fields) {
|
||||
switch ($name) {
|
||||
default:
|
||||
$message = sprintf('Cannot handle table "%s"', $name);
|
||||
$this->friendlyError($message);
|
||||
throw new FireflyException($message);
|
||||
case 'accounts':
|
||||
$this->correctAccountAmounts($currency, $fields);
|
||||
break;
|
||||
case 'auto_budgets':
|
||||
case 'available_budgets':
|
||||
case 'bills':
|
||||
case 'budget_limits':
|
||||
case 'recurrences_transactions':
|
||||
$this->correctGeneric($currency, $name);
|
||||
break;
|
||||
case 'currency_exchange_rates':
|
||||
case 'limit_repetitions':
|
||||
// do nothing
|
||||
break;
|
||||
case 'piggy_bank_events':
|
||||
$this->correctPiggyEventAmounts($currency, $fields);
|
||||
break;
|
||||
case 'piggy_bank_repetitions':
|
||||
$this->correctPiggyRepetitionAmounts($currency, $fields);
|
||||
break;
|
||||
case 'piggy_banks':
|
||||
$this->correctPiggyAmounts($currency, $fields);
|
||||
break;
|
||||
case 'transactions':
|
||||
$this->correctTransactionAmounts($currency);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method loops over all accounts and validates the amounts.
|
||||
*
|
||||
@@ -136,7 +245,7 @@ class ForceDecimalSize extends Command
|
||||
});
|
||||
$result = $query->get(['accounts.*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All accounts in %s', $currency->code));
|
||||
$this->friendlyPositive(sprintf('All accounts in %s are OK', $currency->code));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -150,104 +259,12 @@ class ForceDecimalSize extends Command
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Account #%d has %s with value "%s", this has been corrected to "%s".', $account->id, $field, $value, $correct));
|
||||
$this->friendlyInfo(sprintf('Account #%d has %s with value "%s", this has been corrected to "%s".', $account->id, $field, $value, $correct));
|
||||
Account::find($account->id)->update([$field => $correct]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if a basic check can be done or if it needs to be complicated.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function correctAmounts(): void
|
||||
{
|
||||
// if sqlite, add function?
|
||||
if ('sqlite' === (string)config('database.default')) {
|
||||
DB::connection()->getPdo()->sqliteCreateFunction('REGEXP', function ($pattern, $value) {
|
||||
mb_regex_encoding('UTF-8');
|
||||
$pattern = trim($pattern, '"');
|
||||
|
||||
return (false !== mb_ereg($pattern, (string)$value)) ? 1 : 0;
|
||||
});
|
||||
}
|
||||
|
||||
if (!in_array((string)config('database.default'), ['mysql', 'pgsql', 'sqlite'], true)) {
|
||||
$this->line(sprintf('Skip correcting amounts, does not support "%s"...', (string)config('database.default')));
|
||||
|
||||
return;
|
||||
}
|
||||
$this->correctAmountsByCurrency();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method loops all enabled currencies and then calls the method that will fix all objects in this currency.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function correctAmountsByCurrency(): void
|
||||
{
|
||||
$this->line('Going to correct amounts.');
|
||||
/** @var Collection $enabled */
|
||||
$enabled = TransactionCurrency::whereEnabled(1)->get();
|
||||
/** @var TransactionCurrency $currency */
|
||||
foreach ($enabled as $currency) {
|
||||
$this->correctByCurrency($currency);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method loops the available tables that may need fixing, and calls for the right method that can fix them.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
*
|
||||
* @return void
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function correctByCurrency(TransactionCurrency $currency): void
|
||||
{
|
||||
$this->line(sprintf('Going to correct amounts in currency %s ("%s").', $currency->code, $currency->name));
|
||||
/**
|
||||
* @var string $name
|
||||
* @var array $fields
|
||||
*/
|
||||
foreach ($this->tables as $name => $fields) {
|
||||
switch ($name) {
|
||||
default:
|
||||
$message = sprintf('Cannot handle table "%s"', $name);
|
||||
$this->line($message);
|
||||
throw new FireflyException($message);
|
||||
case 'accounts':
|
||||
$this->correctAccountAmounts($currency, $fields);
|
||||
break;
|
||||
case 'auto_budgets':
|
||||
case 'available_budgets':
|
||||
case 'bills':
|
||||
case 'budget_limits':
|
||||
case 'recurrences_transactions':
|
||||
$this->correctGeneric($currency, $name);
|
||||
break;
|
||||
case 'currency_exchange_rates':
|
||||
case 'limit_repetitions':
|
||||
// do nothing
|
||||
break;
|
||||
case 'piggy_bank_events':
|
||||
$this->correctPiggyEventAmounts($currency, $fields);
|
||||
break;
|
||||
case 'piggy_bank_repetitions':
|
||||
$this->correctPiggyRepetitionAmounts($currency, $fields);
|
||||
break;
|
||||
case 'piggy_banks':
|
||||
$this->correctPiggyAmounts($currency, $fields);
|
||||
break;
|
||||
case 'transactions':
|
||||
$this->correctTransactionAmounts($currency);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fixes all auto budgets in currency $currency.
|
||||
*
|
||||
@@ -279,7 +296,7 @@ class ForceDecimalSize extends Command
|
||||
|
||||
$result = $query->get(['*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All %s in %s', $table, $currency->code));
|
||||
$this->friendlyPositive(sprintf('All %s in %s are OK', $table, $currency->code));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -293,63 +310,12 @@ class ForceDecimalSize extends Command
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('%s #%d has %s with value "%s", this has been corrected to "%s".', $table, $item->id, $field, $value, $correct));
|
||||
$this->friendlyWarning(sprintf('%s #%d has %s with value "%s", this has been corrected to "%s".', $table, $item->id, $field, $value, $correct));
|
||||
$class::find($item->id)->update([$field => $correct]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fixes all piggy banks in currency $currency.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
* @param array $fields
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function correctPiggyAmounts(TransactionCurrency $currency, array $fields): void
|
||||
{
|
||||
$operator = $this->operator;
|
||||
$cast = $this->cast;
|
||||
$regularExpression = $this->regularExpression;
|
||||
|
||||
/** @var Builder $query */
|
||||
$query = PiggyBank::leftJoin('accounts', 'piggy_banks.account_id', '=', 'accounts.id')
|
||||
->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id')
|
||||
->where('account_meta.name', 'currency_id')
|
||||
->where('account_meta.data', json_encode((string)$currency->id))
|
||||
->where(static function (Builder $q) use ($fields, $currency, $operator, $cast, $regularExpression) {
|
||||
foreach ($fields as $field) {
|
||||
$q->orWhere(
|
||||
DB::raw(sprintf('CAST(piggy_banks.%s AS %s)', $field, $cast)),
|
||||
$operator,
|
||||
DB::raw(sprintf($regularExpression, $currency->decimal_places))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$result = $query->get(['piggy_banks.*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All piggy banks in %s', $currency->code));
|
||||
|
||||
return;
|
||||
}
|
||||
/** @var PiggyBank $item */
|
||||
foreach ($result as $item) {
|
||||
foreach ($fields as $field) {
|
||||
$value = $item->$field;
|
||||
if (null === $value) {
|
||||
continue;
|
||||
}
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Piggy bank #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct));
|
||||
PiggyBank::find($item->id)->update([$field => $correct]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fixes all piggy bank events in currency $currency.
|
||||
*
|
||||
@@ -382,7 +348,7 @@ class ForceDecimalSize extends Command
|
||||
|
||||
$result = $query->get(['piggy_bank_events.*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All piggy bank events in %s', $currency->code));
|
||||
$this->friendlyPositive(sprintf('All piggy bank events in %s are OK', $currency->code));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -396,7 +362,9 @@ class ForceDecimalSize extends Command
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Piggy bank event #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct));
|
||||
$this->friendlyWarning(
|
||||
sprintf('Piggy bank event #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct)
|
||||
);
|
||||
PiggyBankEvent::find($item->id)->update([$field => $correct]);
|
||||
}
|
||||
}
|
||||
@@ -434,7 +402,7 @@ class ForceDecimalSize extends Command
|
||||
|
||||
$result = $query->get(['piggy_bank_repetitions.*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All piggy bank repetitions in %s', $currency->code));
|
||||
$this->friendlyPositive(sprintf('All piggy bank repetitions in %s', $currency->code));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -448,12 +416,65 @@ class ForceDecimalSize extends Command
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Piggy bank repetition #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct));
|
||||
$this->friendlyWarning(
|
||||
sprintf('Piggy bank repetition #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct)
|
||||
);
|
||||
PiggyBankRepetition::find($item->id)->update([$field => $correct]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fixes all piggy banks in currency $currency.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
* @param array $fields
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function correctPiggyAmounts(TransactionCurrency $currency, array $fields): void
|
||||
{
|
||||
$operator = $this->operator;
|
||||
$cast = $this->cast;
|
||||
$regularExpression = $this->regularExpression;
|
||||
|
||||
/** @var Builder $query */
|
||||
$query = PiggyBank::leftJoin('accounts', 'piggy_banks.account_id', '=', 'accounts.id')
|
||||
->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id')
|
||||
->where('account_meta.name', 'currency_id')
|
||||
->where('account_meta.data', json_encode((string)$currency->id))
|
||||
->where(static function (Builder $q) use ($fields, $currency, $operator, $cast, $regularExpression) {
|
||||
foreach ($fields as $field) {
|
||||
$q->orWhere(
|
||||
DB::raw(sprintf('CAST(piggy_banks.%s AS %s)', $field, $cast)),
|
||||
$operator,
|
||||
DB::raw(sprintf($regularExpression, $currency->decimal_places))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$result = $query->get(['piggy_banks.*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->friendlyPositive(sprintf('All piggy banks in %s are OK', $currency->code));
|
||||
|
||||
return;
|
||||
}
|
||||
/** @var PiggyBank $item */
|
||||
foreach ($result as $item) {
|
||||
foreach ($fields as $field) {
|
||||
$value = $item->$field;
|
||||
if (null === $value) {
|
||||
continue;
|
||||
}
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->friendlyWarning(sprintf('Piggy bank #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct));
|
||||
PiggyBank::find($item->id)->update([$field => $correct]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fixes all transactions in currency $currency.
|
||||
*
|
||||
@@ -473,7 +494,7 @@ class ForceDecimalSize extends Command
|
||||
|
||||
$result = $query->get(['transactions.*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All transactions in %s', $currency->code));
|
||||
$this->friendlyPositive(sprintf('All transactions in %s are OK', $currency->code));
|
||||
}
|
||||
|
||||
/** @var Transaction $item */
|
||||
@@ -485,7 +506,7 @@ class ForceDecimalSize extends Command
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Transaction #%d has amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct));
|
||||
$this->friendlyWarning(sprintf('Transaction #%d has amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct));
|
||||
Transaction::find($item->id)->update(['amount' => $correct]);
|
||||
}
|
||||
|
||||
@@ -499,7 +520,7 @@ class ForceDecimalSize extends Command
|
||||
|
||||
$result = $query->get(['*']);
|
||||
if (0 === $result->count()) {
|
||||
$this->line(sprintf('Correct: All transactions in foreign currency %s', $currency->code));
|
||||
$this->friendlyPositive(sprintf('All transactions in foreign currency %s are OK', $currency->code));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -512,33 +533,19 @@ class ForceDecimalSize extends Command
|
||||
// fix $field by rounding it down correctly.
|
||||
$pow = pow(10, (int)$currency->decimal_places);
|
||||
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
|
||||
$this->line(sprintf('Transaction #%d has foreign amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct));
|
||||
$this->friendlyWarning(
|
||||
sprintf('Transaction #%d has foreign amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct)
|
||||
);
|
||||
Transaction::find($item->id)->update(['foreign_amount' => $correct]);
|
||||
}
|
||||
}
|
||||
|
||||
private function determineDatabaseType(): void
|
||||
{
|
||||
// switch stuff based on database connection:
|
||||
$this->operator = 'REGEXP';
|
||||
$this->regularExpression = '\'\\\\.[\\\\d]{%d}[1-9]+\'';
|
||||
$this->cast = 'CHAR';
|
||||
if ('pgsql' === config('database.default')) {
|
||||
$this->operator = 'SIMILAR TO';
|
||||
$this->regularExpression = '\'%%\.[\d]{%d}[1-9]+%%\'';
|
||||
$this->cast = 'TEXT';
|
||||
}
|
||||
if ('sqlite' === config('database.default')) {
|
||||
$this->regularExpression = '"\\.[\d]{%d}[1-9]+"';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function updateDecimals(): void
|
||||
{
|
||||
$this->info('Going to force the size of DECIMAL columns. Please hold.');
|
||||
$this->friendlyInfo('Going to force the size of DECIMAL columns. Please hold.');
|
||||
$type = (string)config('database.default');
|
||||
|
||||
/**
|
||||
@@ -548,11 +555,11 @@ class ForceDecimalSize extends Command
|
||||
foreach ($this->tables as $name => $fields) {
|
||||
/** @var string $field */
|
||||
foreach ($fields as $field) {
|
||||
$this->line(sprintf('Updating table "%s", field "%s"...', $name, $field));
|
||||
$this->friendlyLine(sprintf('Updating table "%s", field "%s"...', $name, $field));
|
||||
|
||||
switch ($type) {
|
||||
default:
|
||||
$this->error(sprintf('Cannot handle database type "%s".', $type));
|
||||
$this->friendlyError(sprintf('Cannot handle database type "%s".', $type));
|
||||
|
||||
return;
|
||||
case 'pgsql':
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\System;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Console\Commands\VerifiesAccessToken;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -33,6 +34,7 @@ use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ForceMigration extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
use VerifiesAccessToken;
|
||||
|
||||
/**
|
||||
@@ -58,13 +60,13 @@ class ForceMigration extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
if (!$this->verifyAccessToken()) {
|
||||
$this->error('Invalid access token.');
|
||||
$this->friendlyError('Invalid access token.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->error('Running this command is dangerous and can cause data loss.');
|
||||
$this->error('Please do not continue.');
|
||||
$this->friendlyError('Running this command is dangerous and can cause data loss.');
|
||||
$this->friendlyError('Please do not continue.');
|
||||
$question = $this->confirm('Do you want to continue?');
|
||||
if (true === $question) {
|
||||
$user = $this->getUser();
|
||||
@@ -80,16 +82,16 @@ class ForceMigration extends Command
|
||||
private function forceMigration(): void
|
||||
{
|
||||
DB::commit();
|
||||
$this->line('Dropping "migrations" table...');
|
||||
$this->friendlyLine('Dropping "migrations" table...');
|
||||
sleep(2);
|
||||
Schema::dropIfExists('migrations');
|
||||
$this->line('Re-run all migrations...');
|
||||
$this->friendlyLine('Re-run all migrations...');
|
||||
Artisan::call('migrate', ['--seed' => true]);
|
||||
sleep(2);
|
||||
$this->line('');
|
||||
$this->line('There is a good chance you just saw a lot of error messages.');
|
||||
$this->line('No need to panic yet. First try to access Firefly III (again).');
|
||||
$this->line('The issue, whatever it was, may have been solved now.');
|
||||
$this->line('');
|
||||
$this->friendlyLine('');
|
||||
$this->friendlyWarning('There is a good chance you just saw a lot of error messages.');
|
||||
$this->friendlyWarning('No need to panic yet. First try to access Firefly III (again).');
|
||||
$this->friendlyWarning('The issue, whatever it was, may have been solved now.');
|
||||
$this->friendlyLine('');
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\System;
|
||||
|
||||
use Crypt;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
@@ -37,6 +38,8 @@ use Storage;
|
||||
*/
|
||||
class ScanAttachments extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
@@ -79,7 +82,7 @@ class ScanAttachments extends Command
|
||||
$attachment->md5 = $md5;
|
||||
$attachment->mime = $mime;
|
||||
$attachment->save();
|
||||
$this->line(sprintf('Fixed attachment #%d', $attachment->id));
|
||||
$this->friendlyInfo(sprintf('Fixed attachment #%d', $attachment->id));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\System;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
@@ -31,6 +32,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class SetLatestVersion extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
@@ -52,13 +55,13 @@ class SetLatestVersion extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
if (!$this->option('james-is-cool')) {
|
||||
$this->error('Am too!');
|
||||
$this->friendlyError('Am too!');
|
||||
|
||||
return 0;
|
||||
}
|
||||
app('fireflyconfig')->set('db_version', config('firefly.db_version'));
|
||||
app('fireflyconfig')->set('ff3_version', config('firefly.version'));
|
||||
$this->line('Updated version.');
|
||||
$this->friendlyInfo('Updated version.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -66,6 +66,93 @@ class UpgradeFireflyInstructions extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render upgrade instructions.
|
||||
*/
|
||||
private function updateInstructions(): void
|
||||
{
|
||||
/** @var string $version */
|
||||
$version = config('firefly.version');
|
||||
$config = config('upgrade.text.upgrade');
|
||||
$text = '';
|
||||
foreach (array_keys($config) as $compare) {
|
||||
// if string starts with:
|
||||
if (\str_starts_with($version, $compare)) {
|
||||
$text = $config[$compare];
|
||||
}
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
$this->showLogo();
|
||||
$this->newLine();
|
||||
$this->showLine();
|
||||
|
||||
$this->boxed('');
|
||||
if (null === $text || '' === $text) {
|
||||
$this->boxed(sprintf('Thank you for updating to Firefly III, v%s', $version));
|
||||
$this->boxedInfo('There are no extra upgrade instructions.');
|
||||
$this->boxed('Firefly III should be ready for use.');
|
||||
$this->boxed('');
|
||||
$this->showLine();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->boxed(sprintf('Thank you for updating to Firefly III, v%s!', $version));
|
||||
$this->boxedInfo($text);
|
||||
$this->boxed('');
|
||||
$this->showLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* The logo takes up 8 lines of code. So 8 colors can be used.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function showLogo(): void
|
||||
{
|
||||
$today = date('m-d');
|
||||
$month = date('m');
|
||||
// variation in colors and effects just because I can!
|
||||
// default is Ukraine flag:
|
||||
$colors = ['blue', 'blue', 'blue', 'yellow', 'yellow', 'yellow', 'default', 'default'];
|
||||
|
||||
// 5th of May is Dutch liberation day and 29th of April is Dutch King's Day and September 17 is my birthday.
|
||||
if ('05-01' === $today || '04-29' === $today || '09-17' === $today) {
|
||||
$colors = ['red', 'red', 'red', 'white', 'white', 'blue', 'blue', 'blue'];
|
||||
}
|
||||
|
||||
// National Coming Out Day, International Day Against Homophobia, Biphobia and Transphobia and Pride Month
|
||||
if ('10-11' === $today || '05-17' === $today || '06' === $month) {
|
||||
$colors = ['red', 'bright-red', 'yellow', 'green', 'blue', 'magenta', 'default', 'default'];
|
||||
}
|
||||
|
||||
// International Transgender Day of Visibility
|
||||
if ('03-31' === $today) {
|
||||
$colors = ['bright-blue', 'bright-red', 'white', 'white', 'bright-red', 'bright-blue', 'default', 'default'];
|
||||
}
|
||||
|
||||
$this->line(sprintf('<fg=%s> ______ _ __ _ _____ _____ _____ </>', $colors[0]));
|
||||
$this->line(sprintf('<fg=%s> | ____(_) / _| | |_ _|_ _|_ _| </>', $colors[1]));
|
||||
$this->line(sprintf('<fg=%s> | |__ _ _ __ ___| |_| |_ _ | | | | | | </>', $colors[2]));
|
||||
$this->line(sprintf('<fg=%s> | __| | | \'__/ _ \ _| | | | | | | | | | | </>', $colors[3]));
|
||||
$this->line(sprintf('<fg=%s> | | | | | | __/ | | | |_| | _| |_ _| |_ _| |_ </>', $colors[4]));
|
||||
$this->line(sprintf('<fg=%s> |_| |_|_| \___|_| |_|\__, | |_____|_____|_____| </>', $colors[5]));
|
||||
$this->line(sprintf('<fg=%s> __/ | </>', $colors[6]));
|
||||
$this->line(sprintf('<fg=%s> |___/ </>', $colors[7]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a line.
|
||||
*/
|
||||
private function showLine(): void
|
||||
{
|
||||
$line = '+';
|
||||
$line .= str_repeat('-', 78);
|
||||
$line .= '+';
|
||||
$this->line($line);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a nice box.
|
||||
*
|
||||
@@ -127,90 +214,4 @@ class UpgradeFireflyInstructions extends Command
|
||||
$this->boxed('');
|
||||
$this->showLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a line.
|
||||
*/
|
||||
private function showLine(): void
|
||||
{
|
||||
$line = '+';
|
||||
$line .= str_repeat('-', 78);
|
||||
$line .= '+';
|
||||
$this->line($line);
|
||||
}
|
||||
|
||||
/**
|
||||
* The logo takes up 8 lines of code. So 8 colors can be used.
|
||||
* @return void
|
||||
*/
|
||||
private function showLogo(): void
|
||||
{
|
||||
$today = date('m-d');
|
||||
$month = date('m');
|
||||
// variation in colors and effects just because I can!
|
||||
// default is Ukraine flag:
|
||||
$colors = ['blue', 'blue', 'blue', 'yellow', 'yellow', 'yellow', 'default', 'default'];
|
||||
|
||||
// 5th of May is Dutch liberation day and 29th of April is Dutch King's Day and September 17 is my birthday.
|
||||
if ('05-01' === $today || '04-29' === $today || '09-17' === $today) {
|
||||
$colors = ['red', 'red', 'red', 'white', 'white', 'blue', 'blue', 'blue'];
|
||||
}
|
||||
|
||||
// National Coming Out Day, International Day Against Homophobia, Biphobia and Transphobia and Pride Month
|
||||
if ('10-11' === $today || '05-17' === $today || '06' === $month) {
|
||||
$colors = ['red', 'bright-red', 'yellow', 'green', 'blue', 'magenta', 'default', 'default'];
|
||||
}
|
||||
|
||||
// International Transgender Day of Visibility
|
||||
if ('03-31' === $today) {
|
||||
$colors = ['bright-blue', 'bright-red', 'white', 'white', 'bright-red', 'bright-blue', 'default', 'default'];
|
||||
}
|
||||
|
||||
$this->line(sprintf('<fg=%s> ______ _ __ _ _____ _____ _____ </>', $colors[0]));
|
||||
$this->line(sprintf('<fg=%s> | ____(_) / _| | |_ _|_ _|_ _| </>', $colors[1]));
|
||||
$this->line(sprintf('<fg=%s> | |__ _ _ __ ___| |_| |_ _ | | | | | | </>', $colors[2]));
|
||||
$this->line(sprintf('<fg=%s> | __| | | \'__/ _ \ _| | | | | | | | | | | </>', $colors[3]));
|
||||
$this->line(sprintf('<fg=%s> | | | | | | __/ | | | |_| | _| |_ _| |_ _| |_ </>', $colors[4]));
|
||||
$this->line(sprintf('<fg=%s> |_| |_|_| \___|_| |_|\__, | |_____|_____|_____| </>', $colors[5]));
|
||||
$this->line(sprintf('<fg=%s> __/ | </>', $colors[6]));
|
||||
$this->line(sprintf('<fg=%s> |___/ </>', $colors[7]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Render upgrade instructions.
|
||||
*/
|
||||
private function updateInstructions(): void
|
||||
{
|
||||
/** @var string $version */
|
||||
$version = config('firefly.version');
|
||||
$config = config('upgrade.text.upgrade');
|
||||
$text = '';
|
||||
foreach (array_keys($config) as $compare) {
|
||||
// if string starts with:
|
||||
if (\str_starts_with($version, $compare)) {
|
||||
$text = $config[$compare];
|
||||
}
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
$this->showLogo();
|
||||
$this->newLine();
|
||||
$this->showLine();
|
||||
|
||||
$this->boxed('');
|
||||
if (null === $text || '' === $text) {
|
||||
$this->boxed(sprintf('Thank you for updating to Firefly III, v%s', $version));
|
||||
$this->boxedInfo('There are no extra upgrade instructions.');
|
||||
$this->boxed('Firefly III should be ready for use.');
|
||||
$this->boxed('');
|
||||
$this->showLine();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->boxed(sprintf('Thank you for updating to Firefly III, v%s!', $version));
|
||||
$this->boxedInfo($text);
|
||||
$this->boxed('');
|
||||
$this->showLine();
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\System;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@@ -35,6 +36,8 @@ use Storage;
|
||||
*/
|
||||
class VerifySecurityAlerts extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
@@ -80,23 +83,23 @@ class VerifySecurityAlerts extends Command
|
||||
// depends on level
|
||||
if ('info' === $array['level']) {
|
||||
Log::debug('INFO level alert');
|
||||
$this->info($array['message']);
|
||||
$this->friendlyInfo($array['message']);
|
||||
|
||||
return 0;
|
||||
}
|
||||
if ('warning' === $array['level']) {
|
||||
Log::debug('WARNING level alert');
|
||||
$this->warn('------------------------ :o');
|
||||
$this->warn($array['message']);
|
||||
$this->warn('------------------------ :o');
|
||||
$this->friendlyWarning('------------------------ :o');
|
||||
$this->friendlyWarning($array['message']);
|
||||
$this->friendlyWarning('------------------------ :o');
|
||||
|
||||
return 0;
|
||||
}
|
||||
if ('danger' === $array['level']) {
|
||||
Log::debug('DANGER level alert');
|
||||
$this->error('------------------------ :-(');
|
||||
$this->error($array['message']);
|
||||
$this->error('------------------------ :-(');
|
||||
$this->friendlyError('------------------------ :-(');
|
||||
$this->friendlyError($array['message']);
|
||||
$this->friendlyError('------------------------ :-(');
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -104,8 +107,8 @@ class VerifySecurityAlerts extends Command
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Log::debug('This version is not mentioned.');
|
||||
|
||||
Log::debug(sprintf('No security alerts for version %s', $version));
|
||||
$this->friendlyPositive(sprintf('No security alerts for version %s', $version));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\Tools;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Console\Commands\VerifiesAccessToken;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\AccountType;
|
||||
@@ -43,6 +44,7 @@ use Illuminate\Support\Facades\Log;
|
||||
*/
|
||||
class ApplyRules extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
use VerifiesAccessToken;
|
||||
|
||||
/**
|
||||
@@ -88,7 +90,7 @@ class ApplyRules extends Command
|
||||
$start = microtime(true);
|
||||
$this->stupidLaravel();
|
||||
if (!$this->verifyAccessToken()) {
|
||||
$this->error('Invalid access token.');
|
||||
$this->friendlyError('Invalid access token.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -111,11 +113,11 @@ class ApplyRules extends Command
|
||||
$rulesToApply = $this->getRulesToApply();
|
||||
$count = $rulesToApply->count();
|
||||
if (0 === $count) {
|
||||
$this->error('No rules or rule groups have been included.');
|
||||
$this->warn('Make a selection using:');
|
||||
$this->warn(' --rules=1,2,...');
|
||||
$this->warn(' --rule_groups=1,2,...');
|
||||
$this->warn(' --all_rules');
|
||||
$this->friendlyError('No rules or rule groups have been included.');
|
||||
$this->friendlyWarning('Make a selection using:');
|
||||
$this->friendlyWarning(' --rules=1,2,...');
|
||||
$this->friendlyWarning(' --rule_groups=1,2,...');
|
||||
$this->friendlyWarning(' --all_rules');
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -139,14 +141,14 @@ class ApplyRules extends Command
|
||||
$ruleEngine->addOperator(['type' => 'date_before', 'value' => $this->endDate->format('Y-m-d')]);
|
||||
|
||||
// start running rules.
|
||||
$this->line(sprintf('Will apply %d rule(s) to your transaction(s).', $count));
|
||||
$this->friendlyLine(sprintf('Will apply %d rule(s) to your transaction(s).', $count));
|
||||
|
||||
// file the rule(s)
|
||||
$ruleEngine->fire();
|
||||
|
||||
$this->line('');
|
||||
$this->friendlyLine('');
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->line(sprintf('Done in %s seconds!', $end));
|
||||
$this->friendlyPositive(sprintf('Done in %s seconds!', $end));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -201,7 +203,7 @@ class ApplyRules extends Command
|
||||
{
|
||||
$accountString = $this->option('accounts');
|
||||
if (null === $accountString || '' === $accountString) {
|
||||
$this->error('Please use the --accounts option to indicate the accounts to apply rules to.');
|
||||
$this->friendlyError('Please use the --accounts option to indicate the accounts to apply rules to.');
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -220,7 +222,7 @@ class ApplyRules extends Command
|
||||
}
|
||||
|
||||
if (0 === $finalList->count()) {
|
||||
$this->error('Please make sure all accounts in --accounts are asset accounts or liabilities.');
|
||||
$this->friendlyError('Please make sure all accounts in --accounts are asset accounts or liabilities.');
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -247,7 +249,7 @@ class ApplyRules extends Command
|
||||
$this->ruleGroupSelection[] = $ruleGroup->id;
|
||||
}
|
||||
if (false === $ruleGroup->active) {
|
||||
$this->warn(sprintf('Will ignore inactive rule group #%d ("%s")', $ruleGroup->id, $ruleGroup->title));
|
||||
$this->friendlyWarning(sprintf('Will ignore inactive rule group #%d ("%s")', $ruleGroup->id, $ruleGroup->title));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -25,6 +25,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\Tools;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Support\Cronjobs\AutoBudgetCronjob;
|
||||
use FireflyIII\Support\Cronjobs\BillWarningCronjob;
|
||||
@@ -43,6 +44,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
*/
|
||||
class Cron extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
@@ -68,7 +71,7 @@ class Cron extends Command
|
||||
try {
|
||||
$date = new Carbon($this->option('date'));
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$this->error(sprintf('"%s" is not a valid date', $this->option('date')));
|
||||
$this->friendlyError(sprintf('"%s" is not a valid date', $this->option('date')));
|
||||
}
|
||||
$force = (bool)$this->option('force');
|
||||
|
||||
@@ -81,7 +84,7 @@ class Cron extends Command
|
||||
} catch (FireflyException $e) {
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
$this->error($e->getMessage());
|
||||
$this->friendlyError($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +96,7 @@ class Cron extends Command
|
||||
} catch (FireflyException $e) {
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
$this->error($e->getMessage());
|
||||
$this->friendlyError($e->getMessage());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -104,7 +107,7 @@ class Cron extends Command
|
||||
} catch (FireflyException $e) {
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
$this->error($e->getMessage());
|
||||
$this->friendlyError($e->getMessage());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -115,71 +118,14 @@ class Cron extends Command
|
||||
} catch (FireflyException $e) {
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
$this->error($e->getMessage());
|
||||
$this->friendlyError($e->getMessage());
|
||||
}
|
||||
|
||||
$this->info('More feedback on the cron jobs can be found in the log files.');
|
||||
$this->friendlyInfo('More feedback on the cron jobs can be found in the log files.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
* @param Carbon|null $date
|
||||
*
|
||||
*/
|
||||
private function autoBudgetCronJob(bool $force, ?Carbon $date): void
|
||||
{
|
||||
$autoBudget = new AutoBudgetCronjob();
|
||||
$autoBudget->setForce($force);
|
||||
// set date in cron job:
|
||||
if (null !== $date) {
|
||||
$autoBudget->setDate($date);
|
||||
}
|
||||
|
||||
$autoBudget->fire();
|
||||
|
||||
if ($autoBudget->jobErrored) {
|
||||
$this->error(sprintf('Error in "create auto budgets" cron: %s', $autoBudget->message));
|
||||
}
|
||||
if ($autoBudget->jobFired) {
|
||||
$this->line(sprintf('"Create auto budgets" cron fired: %s', $autoBudget->message));
|
||||
}
|
||||
if ($autoBudget->jobSucceeded) {
|
||||
$this->info(sprintf('"Create auto budgets" cron ran with success: %s', $autoBudget->message));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
* @param Carbon|null $date
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function billWarningCronJob(bool $force, ?Carbon $date): void
|
||||
{
|
||||
$autoBudget = new BillWarningCronjob();
|
||||
$autoBudget->setForce($force);
|
||||
// set date in cron job:
|
||||
if (null !== $date) {
|
||||
$autoBudget->setDate($date);
|
||||
}
|
||||
|
||||
$autoBudget->fire();
|
||||
|
||||
if ($autoBudget->jobErrored) {
|
||||
$this->error(sprintf('Error in "bill warnings" cron: %s', $autoBudget->message));
|
||||
}
|
||||
if ($autoBudget->jobFired) {
|
||||
$this->line(sprintf('"Send bill warnings" cron fired: %s', $autoBudget->message));
|
||||
}
|
||||
if ($autoBudget->jobSucceeded) {
|
||||
$this->info(sprintf('"Send bill warnings" cron ran with success: %s', $autoBudget->message));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
* @param Carbon|null $date
|
||||
@@ -196,13 +142,13 @@ class Cron extends Command
|
||||
$exchangeRates->fire();
|
||||
|
||||
if ($exchangeRates->jobErrored) {
|
||||
$this->error(sprintf('Error in "exchange rates" cron: %s', $exchangeRates->message));
|
||||
$this->friendlyError(sprintf('Error in "exchange rates" cron: %s', $exchangeRates->message));
|
||||
}
|
||||
if ($exchangeRates->jobFired) {
|
||||
$this->line(sprintf('"Exchange rates" cron fired: %s', $exchangeRates->message));
|
||||
$this->friendlyInfo(sprintf('"Exchange rates" cron fired: %s', $exchangeRates->message));
|
||||
}
|
||||
if ($exchangeRates->jobSucceeded) {
|
||||
$this->info(sprintf('"Exchange rates" cron ran with success: %s', $exchangeRates->message));
|
||||
$this->friendlyPositive(sprintf('"Exchange rates" cron ran with success: %s', $exchangeRates->message));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,13 +172,70 @@ class Cron extends Command
|
||||
|
||||
$recurring->fire();
|
||||
if ($recurring->jobErrored) {
|
||||
$this->error(sprintf('Error in "create recurring transactions" cron: %s', $recurring->message));
|
||||
$this->friendlyError(sprintf('Error in "create recurring transactions" cron: %s', $recurring->message));
|
||||
}
|
||||
if ($recurring->jobFired) {
|
||||
$this->line(sprintf('"Create recurring transactions" cron fired: %s', $recurring->message));
|
||||
$this->friendlyInfo(sprintf('"Create recurring transactions" cron fired: %s', $recurring->message));
|
||||
}
|
||||
if ($recurring->jobSucceeded) {
|
||||
$this->info(sprintf('"Create recurring transactions" cron ran with success: %s', $recurring->message));
|
||||
$this->friendlyPositive(sprintf('"Create recurring transactions" cron ran with success: %s', $recurring->message));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
* @param Carbon|null $date
|
||||
*
|
||||
*/
|
||||
private function autoBudgetCronJob(bool $force, ?Carbon $date): void
|
||||
{
|
||||
$autoBudget = new AutoBudgetCronjob();
|
||||
$autoBudget->setForce($force);
|
||||
// set date in cron job:
|
||||
if (null !== $date) {
|
||||
$autoBudget->setDate($date);
|
||||
}
|
||||
|
||||
$autoBudget->fire();
|
||||
|
||||
if ($autoBudget->jobErrored) {
|
||||
$this->friendlyError(sprintf('Error in "create auto budgets" cron: %s', $autoBudget->message));
|
||||
}
|
||||
if ($autoBudget->jobFired) {
|
||||
$this->friendlyInfo(sprintf('"Create auto budgets" cron fired: %s', $autoBudget->message));
|
||||
}
|
||||
if ($autoBudget->jobSucceeded) {
|
||||
$this->friendlyPositive(sprintf('"Create auto budgets" cron ran with success: %s', $autoBudget->message));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
* @param Carbon|null $date
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function billWarningCronJob(bool $force, ?Carbon $date): void
|
||||
{
|
||||
$autoBudget = new BillWarningCronjob();
|
||||
$autoBudget->setForce($force);
|
||||
// set date in cron job:
|
||||
if (null !== $date) {
|
||||
$autoBudget->setDate($date);
|
||||
}
|
||||
|
||||
$autoBudget->fire();
|
||||
|
||||
if ($autoBudget->jobErrored) {
|
||||
$this->friendlyError(sprintf('Error in "bill warnings" cron: %s', $autoBudget->message));
|
||||
}
|
||||
if ($autoBudget->jobFired) {
|
||||
$this->friendlyInfo(sprintf('"Send bill warnings" cron fired: %s', $autoBudget->message));
|
||||
}
|
||||
if ($autoBudget->jobSucceeded) {
|
||||
$this->friendlyPositive(sprintf('"Send bill warnings" cron ran with success: %s', $autoBudget->message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountMeta;
|
||||
@@ -34,14 +35,14 @@ use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
|
||||
/**
|
||||
* Class AccountCurrencies
|
||||
*/
|
||||
class AccountCurrencies extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '480_account_currencies';
|
||||
|
||||
protected $description = 'Give all accounts proper currency info.';
|
||||
@@ -51,7 +52,8 @@ class AccountCurrencies extends Command
|
||||
private UserRepositoryInterface $userRepos;
|
||||
|
||||
/**
|
||||
* Each (asset) account must have a reference to a preferred currency. If the account does not have one, it's forced upon the account.
|
||||
* Each (asset) account must have a reference to a preferred currency. If the account does not have one, it's
|
||||
* forced upon the account.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@@ -59,17 +61,17 @@ class AccountCurrencies extends Command
|
||||
{
|
||||
$this->stupidLaravel();
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
$this->updateAccountCurrencies();
|
||||
|
||||
if (0 === $this->count) {
|
||||
$this->info('Correct: all account currencies are OK.');
|
||||
$this->friendlyPositive('All account currencies are OK.');
|
||||
}
|
||||
if (0 !== $this->count) {
|
||||
$this->line(sprintf('Corrected %d account(s).', $this->count));
|
||||
$this->friendlyInfo(sprintf('Corrected %d account(s).', $this->count));
|
||||
}
|
||||
|
||||
$this->markAsExecuted();
|
||||
@@ -77,23 +79,6 @@ class AccountCurrencies extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
return (bool)$configVar?->data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
@@ -108,48 +93,12 @@ class AccountCurrencies extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param TransactionCurrency $currency
|
||||
* @return bool
|
||||
*/
|
||||
private function updateAccount(Account $account, TransactionCurrency $currency): void
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$this->accountRepos->setUser($account->user);
|
||||
$accountCurrency = (int)$this->accountRepos->getMetaValue($account, 'currency_id');
|
||||
$openingBalance = $this->accountRepos->getOpeningBalance($account);
|
||||
$obCurrency = (int)$openingBalance?->transaction_currency_id;
|
||||
|
||||
// both 0? set to default currency:
|
||||
if (0 === $accountCurrency && 0 === $obCurrency) {
|
||||
AccountMeta::where('account_id', $account->id)->where('name', 'currency_id')->forceDelete();
|
||||
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $currency->id]);
|
||||
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $currency->code));
|
||||
$this->count++;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// account is set to 0, opening balance is not?
|
||||
if (0 === $accountCurrency && $obCurrency > 0) {
|
||||
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $obCurrency]);
|
||||
$this->line(sprintf('Account #%d ("%s") now has a currency setting (#%d).', $account->id, $account->name, $obCurrency));
|
||||
$this->count++;
|
||||
|
||||
return;
|
||||
}
|
||||
// do not match and opening balance id is not null.
|
||||
if ($accountCurrency !== $obCurrency && null !== $openingBalance) {
|
||||
// update opening balance:
|
||||
$openingBalance->transaction_currency_id = $accountCurrency;
|
||||
$openingBalance->save();
|
||||
$openingBalance->transactions->each(
|
||||
static function (Transaction $transaction) use ($accountCurrency) {
|
||||
$transaction->transaction_currency_id = $accountCurrency;
|
||||
$transaction->save();
|
||||
}
|
||||
);
|
||||
$this->line(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name));
|
||||
$this->count++;
|
||||
}
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
return (bool)$configVar?->data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,7 +135,7 @@ class AccountCurrencies extends Command
|
||||
|
||||
if (null === $defaultCurrency) {
|
||||
Log::error(sprintf('Users currency pref "%s" does not exist!', $defaultCurrencyCode));
|
||||
$this->error(sprintf('User has a preference for "%s", but this currency does not exist.', $defaultCurrencyCode));
|
||||
$this->friendlyError(sprintf('User has a preference for "%s", but this currency does not exist.', $defaultCurrencyCode));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -196,4 +145,57 @@ class AccountCurrencies extends Command
|
||||
$this->updateAccount($account, $defaultCurrency);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param TransactionCurrency $currency
|
||||
*/
|
||||
private function updateAccount(Account $account, TransactionCurrency $currency): void
|
||||
{
|
||||
$this->accountRepos->setUser($account->user);
|
||||
$accountCurrency = (int)$this->accountRepos->getMetaValue($account, 'currency_id');
|
||||
$openingBalance = $this->accountRepos->getOpeningBalance($account);
|
||||
$obCurrency = (int)$openingBalance?->transaction_currency_id;
|
||||
|
||||
// both 0? set to default currency:
|
||||
if (0 === $accountCurrency && 0 === $obCurrency) {
|
||||
AccountMeta::where('account_id', $account->id)->where('name', 'currency_id')->forceDelete();
|
||||
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $currency->id]);
|
||||
$this->friendlyInfo(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $currency->code));
|
||||
$this->count++;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// account is set to 0, opening balance is not?
|
||||
if (0 === $accountCurrency && $obCurrency > 0) {
|
||||
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $obCurrency]);
|
||||
$this->friendlyInfo(sprintf('Account #%d ("%s") now has a currency setting (#%d).', $account->id, $account->name, $obCurrency));
|
||||
$this->count++;
|
||||
|
||||
return;
|
||||
}
|
||||
// do not match and opening balance id is not null.
|
||||
if ($accountCurrency !== $obCurrency && null !== $openingBalance) {
|
||||
// update opening balance:
|
||||
$openingBalance->transaction_currency_id = $accountCurrency;
|
||||
$openingBalance->save();
|
||||
$openingBalance->transactions->each(
|
||||
static function (Transaction $transaction) use ($accountCurrency) {
|
||||
$transaction->transaction_currency_id = $accountCurrency;
|
||||
$transaction->save();
|
||||
}
|
||||
);
|
||||
$this->friendlyInfo(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name));
|
||||
$this->count++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -32,6 +33,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
|
||||
class AppendBudgetLimitPeriods extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '550_budget_limit_periods';
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -57,7 +60,7 @@ class AppendBudgetLimitPeriods extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -68,6 +71,30 @@ class AppendBudgetLimitPeriods extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function theresNoLimit(): void
|
||||
{
|
||||
$limits = BudgetLimit::whereNull('period')->get();
|
||||
/** @var BudgetLimit $limit */
|
||||
foreach ($limits as $limit) {
|
||||
$this->fixLimit($limit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetLimit $limit
|
||||
*/
|
||||
@@ -82,7 +109,7 @@ class AppendBudgetLimitPeriods extends Command
|
||||
$limit->start_date->format('Y-m-d'),
|
||||
$limit->end_date->format('Y-m-d')
|
||||
);
|
||||
$this->warn($message);
|
||||
$this->friendlyWarning($message);
|
||||
app('log')->warning($message);
|
||||
|
||||
return;
|
||||
@@ -153,18 +180,6 @@ class AppendBudgetLimitPeriods extends Command
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -172,16 +187,4 @@ class AppendBudgetLimitPeriods extends Command
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function theresNoLimit(): void
|
||||
{
|
||||
$limits = BudgetLimit::whereNull('period')->get();
|
||||
/** @var BudgetLimit $limit */
|
||||
foreach ($limits as $limit) {
|
||||
$this->fixLimit($limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use DB;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
@@ -31,7 +32,6 @@ use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
|
||||
@@ -40,6 +40,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
*/
|
||||
class BackToJournals extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '480_back_to_journals';
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -65,73 +67,25 @@ class BackToJournals extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
if (!$this->isMigrated()) {
|
||||
$this->error('Please run firefly-iii:migrate-to-groups first.');
|
||||
$this->friendlyError('Please run firefly-iii:migrate-to-groups first.');
|
||||
}
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
if (true === $this->option('force')) {
|
||||
$this->warn('Forcing the command.');
|
||||
$this->friendlyWarning('Forcing the command.');
|
||||
}
|
||||
|
||||
|
||||
$this->migrateAll();
|
||||
$this->info('Correct: updated category and budget info for all transaction journals');
|
||||
$this->friendlyInfo('Updated category and budget info for all transaction journals');
|
||||
$this->markAsExecuted();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getIdsForBudgets(): array
|
||||
{
|
||||
$transactions = DB::table('budget_transaction')->distinct()->pluck('transaction_id')->toArray();
|
||||
$array = [];
|
||||
$chunks = array_chunk($transactions, 500);
|
||||
|
||||
foreach ($chunks as $chunk) {
|
||||
$set = DB::table('transactions')->whereIn('transactions.id', $chunk)->pluck('transaction_journal_id')->toArray();
|
||||
$array = array_merge($array, $set);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getIdsForCategories(): array
|
||||
{
|
||||
$transactions = DB::table('category_transaction')->distinct()->pluck('transaction_id')->toArray();
|
||||
$array = [];
|
||||
$chunks = array_chunk($transactions, 500);
|
||||
|
||||
foreach ($chunks as $chunk) {
|
||||
$set = DB::table('transactions')
|
||||
->whereIn('transactions.id', $chunk)
|
||||
->pluck('transaction_journal_id')->toArray();
|
||||
$array = array_merge($array, $set);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
@@ -145,11 +99,15 @@ class BackToJournals extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,13 +135,29 @@ class BackToJournals extends Command
|
||||
$collected = TransactionJournal::whereIn('id', $journalIds)->with(['transactions', 'budgets', 'transactions.budgets'])->get();
|
||||
$journals = $journals->merge($collected);
|
||||
}
|
||||
$this->line(sprintf('Check %d transaction journal(s) for budget info.', count($journals)));
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$this->migrateBudgetsForJournal($journal);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getIdsForBudgets(): array
|
||||
{
|
||||
$transactions = DB::table('budget_transaction')->distinct()->pluck('transaction_id')->toArray();
|
||||
$array = [];
|
||||
$chunks = array_chunk($transactions, 500);
|
||||
|
||||
foreach ($chunks as $chunk) {
|
||||
$set = DB::table('transactions')->whereIn('transactions.id', $chunk)->pluck('transaction_journal_id')->toArray();
|
||||
$array = array_merge($array, $set);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*/
|
||||
@@ -193,7 +167,7 @@ class BackToJournals extends Command
|
||||
/** @var Transaction|null $transaction */
|
||||
$transaction = $journal->transactions->first();
|
||||
if (null === $transaction) {
|
||||
$this->info(sprintf('Transaction journal #%d has no transactions. Will be fixed later.', $journal->id));
|
||||
$this->friendlyInfo(sprintf('Transaction journal #%d has no transactions. Will be fixed later.', $journal->id));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -237,6 +211,25 @@ class BackToJournals extends Command
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getIdsForCategories(): array
|
||||
{
|
||||
$transactions = DB::table('category_transaction')->distinct()->pluck('transaction_id')->toArray();
|
||||
$array = [];
|
||||
$chunks = array_chunk($transactions, 500);
|
||||
|
||||
foreach ($chunks as $chunk) {
|
||||
$set = DB::table('transactions')
|
||||
->whereIn('transactions.id', $chunk)
|
||||
->pluck('transaction_journal_id')->toArray();
|
||||
$array = array_merge($array, $set);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*/
|
||||
@@ -246,7 +239,7 @@ class BackToJournals extends Command
|
||||
/** @var Transaction|null $transaction */
|
||||
$transaction = $journal->transactions->first();
|
||||
if (null === $transaction) {
|
||||
$this->info(sprintf('Transaction journal #%d has no transactions. Will be fixed later.', $journal->id));
|
||||
$this->friendlyInfo(sprintf('Transaction journal #%d has no transactions. Will be fixed later.', $journal->id));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -266,4 +259,12 @@ class BackToJournals extends Command
|
||||
$journal->categories()->sync([(int)$category->id]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -34,6 +35,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
*/
|
||||
class BudgetLimitCurrency extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '480_bl_currency';
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -59,7 +62,7 @@ class BudgetLimitCurrency extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -77,7 +80,7 @@ class BudgetLimitCurrency extends Command
|
||||
$currency = app('amount')->getDefaultCurrencyByUser($user);
|
||||
$budgetLimit->transaction_currency_id = $currency->id;
|
||||
$budgetLimit->save();
|
||||
$this->line(
|
||||
$this->friendlyInfo(
|
||||
sprintf('Budget limit #%d (part of budget "%s") now has a currency setting (%s).', $budgetLimit->id, $budget->name, $currency->name)
|
||||
);
|
||||
$count++;
|
||||
@@ -86,7 +89,7 @@ class BudgetLimitCurrency extends Command
|
||||
}
|
||||
}
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: all budget limits are OK.');
|
||||
$this->friendlyPositive('All budget limits are OK.');
|
||||
}
|
||||
$this->markAsExecuted();
|
||||
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
@@ -36,6 +37,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
*/
|
||||
class CCLiabilities extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '480_cc_liabilities';
|
||||
protected $description = 'Convert old credit card liabilities.';
|
||||
protected $signature = 'firefly-iii:cc-liabilities {--F|force : Force the execution of this command.}';
|
||||
@@ -50,10 +53,8 @@ class CCLiabilities extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -62,7 +63,7 @@ class CCLiabilities extends Command
|
||||
$ccType = AccountType::where('type', AccountType::CREDITCARD)->first();
|
||||
$debtType = AccountType::where('type', AccountType::DEBT)->first();
|
||||
if (null === $ccType || null === $debtType) {
|
||||
$this->info('Correct: no incorrectly stored credit card liabilities.');
|
||||
$this->friendlyPositive('No incorrectly stored credit card liabilities.');
|
||||
$this->markAsExecuted();
|
||||
|
||||
return 0;
|
||||
@@ -72,16 +73,16 @@ class CCLiabilities extends Command
|
||||
foreach ($accounts as $account) {
|
||||
$account->account_type_id = $debtType->id;
|
||||
$account->save();
|
||||
$this->line(sprintf('Converted credit card liability account "%s" (#%d) to generic debt liability.', $account->name, $account->id));
|
||||
$this->friendlyInfo(sprintf('Converted credit card liability account "%s" (#%d) to generic debt liability.', $account->name, $account->id));
|
||||
}
|
||||
if ($accounts->count() > 0) {
|
||||
$this->info('Credit card liability types are no longer supported and have been converted to generic debts. See: https://bit.ly/FF3-credit-cards');
|
||||
$this->friendlyWarning(
|
||||
'Credit card liability types are no longer supported and have been converted to generic debts. See: https://bit.ly/FF3-credit-cards'
|
||||
);
|
||||
}
|
||||
if (0 === $accounts->count()) {
|
||||
$this->info('Correct: no incorrectly stored credit card liabilities.');
|
||||
$this->friendlyPositive('No incorrectly stored credit card liabilities.');
|
||||
}
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Verified credit card liabilities in %s seconds', $end));
|
||||
$this->markAsExecuted();
|
||||
|
||||
return 0;
|
||||
@@ -95,11 +96,7 @@ class CCLiabilities extends Command
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
return (bool)$configVar?->data;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -26,6 +26,7 @@ namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use Crypt;
|
||||
use DB;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Preference;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -41,6 +42,8 @@ use stdClass;
|
||||
*/
|
||||
class DecryptDatabase extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Decrypts the database.';
|
||||
protected $signature = 'firefly-iii:decrypt-all';
|
||||
|
||||
@@ -74,81 +77,6 @@ class DecryptDatabase extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @param string $field
|
||||
*/
|
||||
private function decryptField(string $table, string $field): void
|
||||
{
|
||||
$rows = DB::table($table)->get(['id', $field]);
|
||||
/** @var stdClass $row */
|
||||
foreach ($rows as $row) {
|
||||
$this->decryptRow($table, $field, $row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $value
|
||||
*/
|
||||
private function decryptPreferencesRow(int $id, string $value): void
|
||||
{
|
||||
// try to json_decrypt the value.
|
||||
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. This does not have to be a problem.', $id, $e->getMessage());
|
||||
$this->error($message);
|
||||
app('log')->warning($message);
|
||||
app('log')->warning($value);
|
||||
app('log')->warning($e->getTraceAsString());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var Preference $object */
|
||||
$object = Preference::find((int)$id);
|
||||
if (null !== $object) {
|
||||
$object->data = $newValue;
|
||||
$object->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @param string $field
|
||||
* @param stdClass $row
|
||||
*/
|
||||
private function decryptRow(string $table, string $field, stdClass $row): void
|
||||
{
|
||||
$original = $row->$field;
|
||||
if (null === $original) {
|
||||
return;
|
||||
}
|
||||
$id = (int)$row->id;
|
||||
$value = '';
|
||||
|
||||
try {
|
||||
$value = $this->tryDecrypt($original);
|
||||
} catch (FireflyException $e) {
|
||||
$message = sprintf('Could not decrypt field "%s" in row #%d of table "%s": %s', $field, $id, $table, $e->getMessage());
|
||||
$this->error($message);
|
||||
Log::error($message);
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
|
||||
// A separate routine for preferences table:
|
||||
if ('preferences' === $table) {
|
||||
$this->decryptPreferencesRow($id, $value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($value !== $original) {
|
||||
DB::table($table)->where('id', $id)->update([$field => $value]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @param array $fields
|
||||
@@ -159,14 +87,14 @@ class DecryptDatabase extends Command
|
||||
private function decryptTable(string $table, array $fields): void
|
||||
{
|
||||
if ($this->isDecrypted($table)) {
|
||||
$this->info(sprintf('Correct: no decryption required for table "%s".', $table));
|
||||
$this->friendlyInfo(sprintf('No decryption required for table "%s".', $table));
|
||||
|
||||
return;
|
||||
}
|
||||
foreach ($fields as $field) {
|
||||
$this->decryptField($table, $field);
|
||||
}
|
||||
$this->line(sprintf('Correct: decrypted the data in table "%s".', $table));
|
||||
$this->friendlyPositive(sprintf('Decrypted the data in table "%s".', $table));
|
||||
// mark as decrypted:
|
||||
$configName = sprintf('is_decrypted_%s', $table);
|
||||
app('fireflyconfig')->set($configName, true);
|
||||
@@ -195,6 +123,54 @@ class DecryptDatabase extends Command
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @param string $field
|
||||
*/
|
||||
private function decryptField(string $table, string $field): void
|
||||
{
|
||||
$rows = DB::table($table)->get(['id', $field]);
|
||||
/** @var stdClass $row */
|
||||
foreach ($rows as $row) {
|
||||
$this->decryptRow($table, $field, $row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @param string $field
|
||||
* @param stdClass $row
|
||||
*/
|
||||
private function decryptRow(string $table, string $field, stdClass $row): void
|
||||
{
|
||||
$original = $row->$field;
|
||||
if (null === $original) {
|
||||
return;
|
||||
}
|
||||
$id = (int)$row->id;
|
||||
$value = '';
|
||||
|
||||
try {
|
||||
$value = $this->tryDecrypt($original);
|
||||
} catch (FireflyException $e) {
|
||||
$message = sprintf('Could not decrypt field "%s" in row #%d of table "%s": %s', $field, $id, $table, $e->getMessage());
|
||||
$this->friendlyError($message);
|
||||
Log::error($message);
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
|
||||
// A separate routine for preferences table:
|
||||
if ('preferences' === $table) {
|
||||
$this->decryptPreferencesRow($id, $value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($value !== $original) {
|
||||
DB::table($table)->where('id', $id)->update([$field => $value]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to decrypt data. Will only throw an exception when the MAC is invalid.
|
||||
*
|
||||
@@ -215,4 +191,31 @@ class DecryptDatabase extends Command
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $value
|
||||
*/
|
||||
private function decryptPreferencesRow(int $id, string $value): void
|
||||
{
|
||||
// try to json_decrypt the value.
|
||||
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. This does not have to be a problem.', $id, $e->getMessage());
|
||||
$this->friendlyError($message);
|
||||
app('log')->warning($message);
|
||||
app('log')->warning($value);
|
||||
app('log')->warning($e->getTraceAsString());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var Preference $object */
|
||||
$object = Preference::find((int)$id);
|
||||
if (null !== $object) {
|
||||
$object->data = $newValue;
|
||||
$object->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use DB;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
@@ -32,6 +33,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class FixPostgresSequences extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
@@ -55,7 +58,7 @@ class FixPostgresSequences extends Command
|
||||
if (DB::connection()->getName() !== 'pgsql') {
|
||||
return 0;
|
||||
}
|
||||
$this->line('Going to verify PostgreSQL table sequences.');
|
||||
$this->friendlyLine('Going to verify PostgreSQL table sequences.');
|
||||
$tablesToCheck = [
|
||||
'2fa_tokens',
|
||||
'account_meta',
|
||||
@@ -116,12 +119,12 @@ class FixPostgresSequences extends Command
|
||||
];
|
||||
|
||||
foreach ($tablesToCheck as $tableToCheck) {
|
||||
$this->info(sprintf('Checking the next id sequence for table "%s".', $tableToCheck));
|
||||
$this->friendlyLine(sprintf('Checking the next id sequence for table "%s".', $tableToCheck));
|
||||
|
||||
$highestId = DB::table($tableToCheck)->select(DB::raw('MAX(id)'))->first();
|
||||
$nextId = DB::table($tableToCheck)->select(DB::raw(sprintf('nextval(\'%s_id_seq\')', $tableToCheck)))->first();
|
||||
if (null === $nextId) {
|
||||
$this->line(sprintf('nextval is NULL for table "%s", go to next table.', $tableToCheck));
|
||||
$this->friendlyInfo(sprintf('nextval is NULL for table "%s", go to next table.', $tableToCheck));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -130,14 +133,14 @@ class FixPostgresSequences extends Command
|
||||
$highestId = DB::table($tableToCheck)->select(DB::raw('MAX(id)'))->first();
|
||||
$nextId = DB::table($tableToCheck)->select(DB::raw(sprintf('nextval(\'%s_id_seq\')', $tableToCheck)))->first();
|
||||
if ($nextId->nextval > $highestId->max) {
|
||||
$this->info(sprintf('Table "%s" autoincrement corrected.', $tableToCheck));
|
||||
$this->friendlyInfo(sprintf('Table "%s" autoincrement corrected.', $tableToCheck));
|
||||
}
|
||||
if ($nextId->nextval <= $highestId->max) {
|
||||
$this->warn(sprintf('Arff! The nextval sequence is still all screwed up on table "%s".', $tableToCheck));
|
||||
$this->friendlyWarning(sprintf('Arff! The nextval sequence is still all screwed up on table "%s".', $tableToCheck));
|
||||
}
|
||||
}
|
||||
if ($nextId->nextval >= $highestId->max) {
|
||||
$this->info(sprintf('Table "%s" autoincrement is correct.', $tableToCheck));
|
||||
$this->friendlyPositive(sprintf('Table "%s" autoincrement is correct.', $tableToCheck));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Models\Note;
|
||||
@@ -36,6 +37,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
*/
|
||||
class MigrateAttachments extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '480_migrate_attachments';
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -62,7 +65,7 @@ class MigrateAttachments extends Command
|
||||
{
|
||||
$start = microtime(true);
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -94,13 +97,13 @@ class MigrateAttachments extends Command
|
||||
}
|
||||
}
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: all attachments are OK.');
|
||||
$this->friendlyPositive('All attachments are OK.');
|
||||
}
|
||||
if (0 !== $count) {
|
||||
$this->line(sprintf('Updated %d attachment(s).', $count));
|
||||
$this->friendlyInfo(sprintf('Updated %d attachment(s).', $count));
|
||||
}
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Migrated attachment notes in %s seconds.', $end));
|
||||
$this->friendlyInfo(sprintf('Migrated attachment notes in %s seconds.', $end));
|
||||
$this->markAsExecuted();
|
||||
|
||||
return 0;
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Note;
|
||||
use FireflyIII\Models\TransactionJournalMeta;
|
||||
@@ -36,6 +37,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
*/
|
||||
class MigrateJournalNotes extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '480_migrate_notes';
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -63,7 +66,7 @@ class MigrateJournalNotes extends Command
|
||||
$start = microtime(true);
|
||||
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -88,14 +91,14 @@ class MigrateJournalNotes extends Command
|
||||
}
|
||||
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: No notes to migrate.');
|
||||
$this->friendlyPositive('No notes to migrate.');
|
||||
}
|
||||
if (0 !== $count) {
|
||||
$this->line(sprintf('Migrated %d note(s).', $count));
|
||||
$this->friendlyInfo(sprintf('Migrated %d note(s).', $count));
|
||||
}
|
||||
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Migrated notes in %s seconds.', $end));
|
||||
$this->friendlyInfo(sprintf('Migrated notes in %s seconds.', $end));
|
||||
$this->markAsExecuted();
|
||||
|
||||
return 0;
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Recurrence;
|
||||
use FireflyIII\Models\RecurrenceMeta;
|
||||
@@ -38,6 +39,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
*/
|
||||
class MigrateRecurrenceMeta extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '481_migrate_recurrence_meta';
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -64,17 +67,17 @@ class MigrateRecurrenceMeta extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
$count = $this->migrateMetaData();
|
||||
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: no recurrence meta data migrated.');
|
||||
$this->friendlyPositive('No recurrence meta data migrated.');
|
||||
}
|
||||
if ($count > 0) {
|
||||
$this->line(sprintf('Migrated %d meta data entries', $count));
|
||||
$this->friendlyInfo(sprintf('Migrated %d meta data entries', $count));
|
||||
}
|
||||
|
||||
$this->markAsExecuted();
|
||||
@@ -98,11 +101,20 @@ class MigrateRecurrenceMeta extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return int
|
||||
* @throws JsonException
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
private function migrateMetaData(): int
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
$count = 0;
|
||||
// get all recurrence meta data:
|
||||
$collection = RecurrenceMeta::with('recurrence')->get();
|
||||
/** @var RecurrenceMeta $meta */
|
||||
foreach ($collection as $meta) {
|
||||
$count += $this->migrateEntry($meta);
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,19 +154,10 @@ class MigrateRecurrenceMeta extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @throws JsonException
|
||||
*
|
||||
*/
|
||||
private function migrateMetaData(): int
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
$count = 0;
|
||||
// get all recurrence meta data:
|
||||
$collection = RecurrenceMeta::with('recurrence')->get();
|
||||
/** @var RecurrenceMeta $meta */
|
||||
foreach ($collection as $meta) {
|
||||
$count += $this->migrateEntry($meta);
|
||||
}
|
||||
|
||||
return $count;
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Recurrence;
|
||||
use FireflyIII\Models\RecurrenceTransaction;
|
||||
@@ -37,6 +38,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
*/
|
||||
class MigrateRecurrenceType extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '550_migrate_recurrence_type';
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -62,7 +65,7 @@ class MigrateRecurrenceType extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -74,14 +77,6 @@ class MigrateRecurrenceType extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function getInvalidType(): TransactionType
|
||||
{
|
||||
return TransactionType::whereType(TransactionType::INVALID)->firstOrCreate(['type' => TransactionType::INVALID]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
@@ -90,33 +85,7 @@ class MigrateRecurrenceType extends Command
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
private function migrateRecurrence(Recurrence $recurrence): void
|
||||
{
|
||||
$originalType = (int)$recurrence->transaction_type_id;
|
||||
$newType = $this->getInvalidType();
|
||||
$recurrence->transaction_type_id = $newType->id;
|
||||
$recurrence->save();
|
||||
/** @var RecurrenceTransaction $transaction */
|
||||
foreach ($recurrence->recurrenceTransactions as $transaction) {
|
||||
$transaction->transaction_type_id = $originalType;
|
||||
$transaction->save();
|
||||
}
|
||||
$this->line(sprintf('Updated recurrence #%d to new transaction type model.', $recurrence->id));
|
||||
return (bool)$configVar?->data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,4 +101,38 @@ class MigrateRecurrenceType extends Command
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Recurrence $recurrence
|
||||
* @return void
|
||||
*/
|
||||
private function migrateRecurrence(Recurrence $recurrence): void
|
||||
{
|
||||
$originalType = (int)$recurrence->transaction_type_id;
|
||||
$newType = $this->getInvalidType();
|
||||
$recurrence->transaction_type_id = $newType->id;
|
||||
$recurrence->save();
|
||||
/** @var RecurrenceTransaction $transaction */
|
||||
foreach ($recurrence->recurrenceTransactions as $transaction) {
|
||||
$transaction->transaction_type_id = $originalType;
|
||||
$transaction->save();
|
||||
}
|
||||
$this->friendlyInfo(sprintf('Updated recurrence #%d to new transaction type model.', $recurrence->id));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function getInvalidType(): TransactionType
|
||||
{
|
||||
return TransactionType::whereType(TransactionType::INVALID)->firstOrCreate(['type' => TransactionType::INVALID]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Location;
|
||||
use FireflyIII\Models\Tag;
|
||||
@@ -36,6 +37,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
*/
|
||||
class MigrateTagLocations extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '500_migrate_tag_locations';
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -61,7 +64,7 @@ class MigrateTagLocations extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -71,16 +74,6 @@ class MigrateTagLocations extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tag $tag
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function hasLocationDetails(Tag $tag): bool
|
||||
{
|
||||
return null !== $tag->latitude && null !== $tag->longitude && null !== $tag->zoomLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
@@ -96,12 +89,25 @@ class MigrateTagLocations extends Command
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
private function migrateTagLocations(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
$tags = Tag::get();
|
||||
/** @var Tag $tag */
|
||||
foreach ($tags as $tag) {
|
||||
if ($this->hasLocationDetails($tag)) {
|
||||
$this->migrateLocationDetails($tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tag $tag
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function hasLocationDetails(Tag $tag): bool
|
||||
{
|
||||
return null !== $tag->latitude && null !== $tag->longitude && null !== $tag->zoomLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,14 +128,11 @@ class MigrateTagLocations extends Command
|
||||
$tag->save();
|
||||
}
|
||||
|
||||
private function migrateTagLocations(): void
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
$tags = Tag::get();
|
||||
/** @var Tag $tag */
|
||||
foreach ($tags as $tag) {
|
||||
if ($this->hasLocationDetails($tag)) {
|
||||
$this->migrateLocationDetails($tag);
|
||||
}
|
||||
}
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
}
|
||||
|
@@ -25,7 +25,7 @@ namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use DB;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Factory\TransactionGroupFactory;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
@@ -49,6 +49,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
*/
|
||||
class MigrateToGroups extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '480_migrated_to_groups';
|
||||
protected $description = 'Migrates a pre-4.7.8 transaction structure to the 4.7.8+ transaction structure.';
|
||||
protected $signature = 'firefly-iii:migrate-to-groups {--F|force : Force the migration, even if it fired before.}';
|
||||
@@ -68,13 +70,13 @@ class MigrateToGroups extends Command
|
||||
$this->stupidLaravel();
|
||||
|
||||
if ($this->isMigrated() && true !== $this->option('force')) {
|
||||
$this->info('Correct: database is already migrated.');
|
||||
$this->friendlyInfo('Database is already migrated.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (true === $this->option('force')) {
|
||||
$this->warn('Forcing the migration.');
|
||||
$this->friendlyWarning('Forcing the migration.');
|
||||
}
|
||||
|
||||
|
||||
@@ -82,10 +84,10 @@ class MigrateToGroups extends Command
|
||||
$this->makeGroupsFromAll();
|
||||
|
||||
if (0 !== $this->count) {
|
||||
$this->line(sprintf('Migrated %d transaction journal(s).', $this->count));
|
||||
$this->friendlyInfo(sprintf('Migrated %d transaction journal(s).', $this->count));
|
||||
}
|
||||
if (0 === $this->count) {
|
||||
$this->info('Correct: no journals to migrate to groups.');
|
||||
$this->friendlyPositive('No journals to migrate to groups.');
|
||||
}
|
||||
$this->markAsMigrated();
|
||||
|
||||
@@ -93,122 +95,19 @@ class MigrateToGroups extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Transaction $transaction
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @return Transaction|null
|
||||
|
||||
*/
|
||||
private function findOpposingTransaction(TransactionJournal $journal, Transaction $transaction): ?Transaction
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$set = $journal->transactions->filter(
|
||||
static function (Transaction $subject) use ($transaction) {
|
||||
$amount = (float)$transaction->amount * -1 === (float)$subject->amount; // intentional float
|
||||
$identifier = $transaction->identifier === $subject->identifier;
|
||||
Log::debug(sprintf('Amount the same? %s', var_export($amount, true)));
|
||||
Log::debug(sprintf('ID the same? %s', var_export($identifier, true)));
|
||||
|
||||
return $amount && $identifier;
|
||||
}
|
||||
);
|
||||
|
||||
return $set->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function getDestinationTransactions(TransactionJournal $journal): Collection
|
||||
{
|
||||
return $journal->transactions->filter(
|
||||
static function (Transaction $transaction) {
|
||||
return $transaction->amount > 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $left
|
||||
* @param Transaction $right
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
private function getTransactionBudget(Transaction $left, Transaction $right): ?int
|
||||
{
|
||||
Log::debug('Now in getTransactionBudget()');
|
||||
|
||||
// try to get a budget ID from the left transaction:
|
||||
/** @var Budget|null $budget */
|
||||
$budget = $left->budgets()->first();
|
||||
if (null !== $budget) {
|
||||
Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $left->id));
|
||||
|
||||
return (int)$budget->id;
|
||||
}
|
||||
|
||||
// try to get a budget ID from the right transaction:
|
||||
/** @var Budget|null $budget */
|
||||
$budget = $right->budgets()->first();
|
||||
if (null !== $budget) {
|
||||
Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $right->id));
|
||||
|
||||
return (int)$budget->id;
|
||||
}
|
||||
Log::debug('Neither left or right have a budget, return NULL');
|
||||
|
||||
// if all fails, return NULL.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $left
|
||||
* @param Transaction $right
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
private function getTransactionCategory(Transaction $left, Transaction $right): ?int
|
||||
{
|
||||
Log::debug('Now in getTransactionCategory()');
|
||||
|
||||
// try to get a category ID from the left transaction:
|
||||
/** @var Category|null $category */
|
||||
$category = $left->categories()->first();
|
||||
if (null !== $category) {
|
||||
Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $left->id));
|
||||
|
||||
return (int)$category->id;
|
||||
}
|
||||
|
||||
// try to get a category ID from the left transaction:
|
||||
/** @var Category|null $category */
|
||||
$category = $right->categories()->first();
|
||||
if (null !== $category) {
|
||||
Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $category->id));
|
||||
|
||||
return (int)$category->id;
|
||||
}
|
||||
Log::debug('Neither left or right have a category, return NULL');
|
||||
|
||||
// if all fails, return NULL.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
*/
|
||||
private function giveGroup(array $array): void
|
||||
{
|
||||
$groupId = DB::table('transaction_groups')->insertGetId(
|
||||
[
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
'title' => null,
|
||||
'user_id' => $array['user_id'],
|
||||
]
|
||||
);
|
||||
DB::table('transaction_journals')->where('id', $array['id'])->update(['transaction_group_id' => $groupId]);
|
||||
$this->count++;
|
||||
$this->count = 0;
|
||||
$this->journalRepository = app(JournalRepositoryInterface::class);
|
||||
$this->service = app(JournalDestroyService::class);
|
||||
$this->groupFactory = app(TransactionGroupFactory::class);
|
||||
$this->cliRepository = app(JournalCLIRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,26 +125,6 @@ class MigrateToGroups extends Command
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives all journals without a group a group.
|
||||
*/
|
||||
private function makeGroupsFromAll(): void
|
||||
{
|
||||
$orphanedJournals = $this->cliRepository->getJournalsWithoutGroup();
|
||||
$total = count($orphanedJournals);
|
||||
if ($total > 0) {
|
||||
Log::debug(sprintf('Going to convert %d transaction journals. Please hold..', $total));
|
||||
$this->line(sprintf('Going to convert %d transaction journals. Please hold..', $total));
|
||||
/** @var array $array */
|
||||
foreach ($orphanedJournals as $array) {
|
||||
$this->giveGroup($array);
|
||||
}
|
||||
}
|
||||
if (0 === $total) {
|
||||
$this->info('Correct: no need to convert transaction journals.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
@@ -253,7 +132,7 @@ class MigrateToGroups extends Command
|
||||
{
|
||||
$splitJournals = $this->cliRepository->getSplitJournals();
|
||||
if ($splitJournals->count() > 0) {
|
||||
$this->info(sprintf('Going to convert %d split transaction(s). Please hold..', $splitJournals->count()));
|
||||
$this->friendlyLine(sprintf('Going to convert %d split transaction(s). Please hold..', $splitJournals->count()));
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($splitJournals as $journal) {
|
||||
$this->makeMultiGroup($journal);
|
||||
@@ -320,7 +199,7 @@ class MigrateToGroups extends Command
|
||||
$opposingTr = $this->findOpposingTransaction($journal, $transaction);
|
||||
|
||||
if (null === $opposingTr) {
|
||||
$this->error(
|
||||
$this->friendlyError(
|
||||
sprintf(
|
||||
'Journal #%d has no opposing transaction for transaction #%d. Cannot upgrade this entry.',
|
||||
$journal->id,
|
||||
@@ -393,7 +272,7 @@ class MigrateToGroups extends Command
|
||||
implode(', #', $group->transactionJournals->pluck('id')->toArray())
|
||||
)
|
||||
);
|
||||
$this->line(
|
||||
$this->friendlyInfo(
|
||||
sprintf(
|
||||
'Migrated journal #%d into group #%d with these journals: #%s',
|
||||
$journal->id,
|
||||
@@ -403,6 +282,145 @@ class MigrateToGroups extends Command
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function getDestinationTransactions(TransactionJournal $journal): Collection
|
||||
{
|
||||
return $journal->transactions->filter(
|
||||
static function (Transaction $transaction) {
|
||||
return $transaction->amount > 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Transaction $transaction
|
||||
*
|
||||
* @return Transaction|null
|
||||
*/
|
||||
private function findOpposingTransaction(TransactionJournal $journal, Transaction $transaction): ?Transaction
|
||||
{
|
||||
$set = $journal->transactions->filter(
|
||||
static function (Transaction $subject) use ($transaction) {
|
||||
$amount = (float)$transaction->amount * -1 === (float)$subject->amount; // intentional float
|
||||
$identifier = $transaction->identifier === $subject->identifier;
|
||||
Log::debug(sprintf('Amount the same? %s', var_export($amount, true)));
|
||||
Log::debug(sprintf('ID the same? %s', var_export($identifier, true)));
|
||||
|
||||
return $amount && $identifier;
|
||||
}
|
||||
);
|
||||
|
||||
return $set->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $left
|
||||
* @param Transaction $right
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
private function getTransactionBudget(Transaction $left, Transaction $right): ?int
|
||||
{
|
||||
Log::debug('Now in getTransactionBudget()');
|
||||
|
||||
// try to get a budget ID from the left transaction:
|
||||
/** @var Budget|null $budget */
|
||||
$budget = $left->budgets()->first();
|
||||
if (null !== $budget) {
|
||||
Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $left->id));
|
||||
|
||||
return (int)$budget->id;
|
||||
}
|
||||
|
||||
// try to get a budget ID from the right transaction:
|
||||
/** @var Budget|null $budget */
|
||||
$budget = $right->budgets()->first();
|
||||
if (null !== $budget) {
|
||||
Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $right->id));
|
||||
|
||||
return (int)$budget->id;
|
||||
}
|
||||
Log::debug('Neither left or right have a budget, return NULL');
|
||||
|
||||
// if all fails, return NULL.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $left
|
||||
* @param Transaction $right
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
private function getTransactionCategory(Transaction $left, Transaction $right): ?int
|
||||
{
|
||||
Log::debug('Now in getTransactionCategory()');
|
||||
|
||||
// try to get a category ID from the left transaction:
|
||||
/** @var Category|null $category */
|
||||
$category = $left->categories()->first();
|
||||
if (null !== $category) {
|
||||
Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $left->id));
|
||||
|
||||
return (int)$category->id;
|
||||
}
|
||||
|
||||
// try to get a category ID from the left transaction:
|
||||
/** @var Category|null $category */
|
||||
$category = $right->categories()->first();
|
||||
if (null !== $category) {
|
||||
Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $category->id));
|
||||
|
||||
return (int)$category->id;
|
||||
}
|
||||
Log::debug('Neither left or right have a category, return NULL');
|
||||
|
||||
// if all fails, return NULL.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives all journals without a group a group.
|
||||
*/
|
||||
private function makeGroupsFromAll(): void
|
||||
{
|
||||
$orphanedJournals = $this->cliRepository->getJournalsWithoutGroup();
|
||||
$total = count($orphanedJournals);
|
||||
if ($total > 0) {
|
||||
Log::debug(sprintf('Going to convert %d transaction journals. Please hold..', $total));
|
||||
$this->friendlyInfo(sprintf('Going to convert %d transaction journals. Please hold..', $total));
|
||||
/** @var array $array */
|
||||
foreach ($orphanedJournals as $array) {
|
||||
$this->giveGroup($array);
|
||||
}
|
||||
}
|
||||
if (0 === $total) {
|
||||
$this->friendlyPositive('No need to convert transaction journals.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
*/
|
||||
private function giveGroup(array $array): void
|
||||
{
|
||||
$groupId = DB::table('transaction_groups')->insertGetId(
|
||||
[
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
'title' => null,
|
||||
'user_id' => $array['user_id'],
|
||||
]
|
||||
);
|
||||
DB::table('transaction_journals')->where('id', $array['id'])->update(['transaction_group_id' => $groupId]);
|
||||
$this->count++;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -410,20 +428,4 @@ class MigrateToGroups extends Command
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->count = 0;
|
||||
$this->journalRepository = app(JournalRepositoryInterface::class);
|
||||
$this->service = app(JournalDestroyService::class);
|
||||
$this->groupFactory = app(TransactionGroupFactory::class);
|
||||
$this->cliRepository = app(JournalCLIRepositoryInterface::class);
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Preference;
|
||||
@@ -41,6 +42,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
*/
|
||||
class MigrateToRules extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '480_bills_to_rules';
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -75,11 +78,9 @@ class MigrateToRules extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
$this->stupidLaravel();
|
||||
$start = microtime(true);
|
||||
|
||||
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -92,19 +93,33 @@ class MigrateToRules extends Command
|
||||
}
|
||||
|
||||
if (0 === $this->count) {
|
||||
$this->info('Correct: all bills are OK.');
|
||||
$this->friendlyPositive('All bills are OK.');
|
||||
}
|
||||
if (0 !== $this->count) {
|
||||
$this->line(sprintf('Verified and fixed %d bill(s).', $this->count));
|
||||
$this->friendlyInfo(sprintf('Verified and fixed %d bill(s).', $this->count));
|
||||
}
|
||||
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->info(sprintf('Verified and fixed bills in %s seconds.', $end));
|
||||
$this->markAsExecuted();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->count = 0;
|
||||
$this->userRepository = app(UserRepositoryInterface::class);
|
||||
$this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
|
||||
$this->billRepository = app(BillRepositoryInterface::class);
|
||||
$this->ruleRepository = app(RuleRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
@@ -121,11 +136,38 @@ class MigrateToRules extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate bills to new rule structure for a specific user.
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
private function migrateUser(User $user): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
$this->ruleGroupRepository->setUser($user);
|
||||
$this->billRepository->setUser($user);
|
||||
$this->ruleRepository->setUser($user);
|
||||
|
||||
/** @var Preference $lang */
|
||||
$lang = app('preferences')->getForUser($user, 'language', 'en_US');
|
||||
$groupTitle = (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data);
|
||||
$ruleGroup = $this->ruleGroupRepository->findByTitle($groupTitle);
|
||||
|
||||
if (null === $ruleGroup) {
|
||||
$ruleGroup = $this->ruleGroupRepository->store(
|
||||
[
|
||||
'title' => (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data),
|
||||
'description' => (string)trans('firefly.rulegroup_for_bills_description', [], $lang->data),
|
||||
'active' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
$bills = $this->billRepository->getBills();
|
||||
|
||||
/** @var Bill $bill */
|
||||
foreach ($bills as $bill) {
|
||||
$this->migrateBill($ruleGroup, $bill, $lang);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,53 +242,10 @@ class MigrateToRules extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate bills to new rule structure for a specific user.
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function migrateUser(User $user): void
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
$this->ruleGroupRepository->setUser($user);
|
||||
$this->billRepository->setUser($user);
|
||||
$this->ruleRepository->setUser($user);
|
||||
|
||||
/** @var Preference $lang */
|
||||
$lang = app('preferences')->getForUser($user, 'language', 'en_US');
|
||||
$groupTitle = (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data);
|
||||
$ruleGroup = $this->ruleGroupRepository->findByTitle($groupTitle);
|
||||
|
||||
if (null === $ruleGroup) {
|
||||
$ruleGroup = $this->ruleGroupRepository->store(
|
||||
[
|
||||
'title' => (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data),
|
||||
'description' => (string)trans('firefly.rulegroup_for_bills_description', [], $lang->data),
|
||||
'active' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
$bills = $this->billRepository->getBills();
|
||||
|
||||
/** @var Bill $bill */
|
||||
foreach ($bills as $bill) {
|
||||
$this->migrateBill($ruleGroup, $bill, $lang);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->count = 0;
|
||||
$this->userRepository = app(UserRepositoryInterface::class);
|
||||
$this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
|
||||
$this->billRepository = app(BillRepositoryInterface::class);
|
||||
$this->ruleRepository = app(RuleRepositoryInterface::class);
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
@@ -43,6 +44,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
*/
|
||||
class OtherCurrenciesCorrections extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '480_other_currencies';
|
||||
protected $description = 'Update all journal currency information.';
|
||||
protected $signature = 'firefly-iii:other-currencies {--F|force : Force the execution of this command.}';
|
||||
@@ -66,7 +69,7 @@ class OtherCurrenciesCorrections extends Command
|
||||
$this->stupidLaravel();
|
||||
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -75,34 +78,115 @@ class OtherCurrenciesCorrections extends Command
|
||||
$this->updateOtherJournalsCurrencies();
|
||||
$this->markAsExecuted();
|
||||
|
||||
$this->info('Correct: verified and fixed transaction currencies.');
|
||||
$this->friendlyPositive('Verified and fixed transaction currencies.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @return TransactionCurrency|null
|
||||
|
||||
*/
|
||||
private function getCurrency(Account $account): ?TransactionCurrency
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$accountId = $account->id;
|
||||
if (array_key_exists($accountId, $this->accountCurrencies) && 0 === $this->accountCurrencies[$accountId]) {
|
||||
return null;
|
||||
$this->count = 0;
|
||||
$this->accountCurrencies = [];
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$this->journalRepos = app(JournalRepositoryInterface::class);
|
||||
$this->cliRepos = app(JournalCLIRepositoryInterface::class);
|
||||
}
|
||||
if (array_key_exists($accountId, $this->accountCurrencies) && $this->accountCurrencies[$accountId] instanceof TransactionCurrency) {
|
||||
return $this->accountCurrencies[$accountId];
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
$currency = $this->accountRepos->getAccountCurrency($account);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine verifies that withdrawals, deposits and opening balances have the correct currency settings for
|
||||
* the accounts they are linked to.
|
||||
* Both source and destination must match the respective currency preference of the related asset account.
|
||||
* So FF3 must verify all transactions.
|
||||
*/
|
||||
private function updateOtherJournalsCurrencies(): void
|
||||
{
|
||||
$set = $this->cliRepos->getAllJournals(
|
||||
[TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,]
|
||||
);
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($set as $journal) {
|
||||
$this->updateJournalCurrency($journal);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*/
|
||||
private function updateJournalCurrency(TransactionJournal $journal): void
|
||||
{
|
||||
$this->accountRepos->setUser($journal->user);
|
||||
$this->journalRepos->setUser($journal->user);
|
||||
$this->currencyRepos->setUser($journal->user);
|
||||
$this->cliRepos->setUser($journal->user);
|
||||
|
||||
$leadTransaction = $this->getLeadTransaction($journal);
|
||||
|
||||
if (null === $leadTransaction) {
|
||||
$this->friendlyError(sprintf('Could not reliably determine which transaction is in the lead for transaction journal #%d.', $journal->id));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$account = $leadTransaction->account;
|
||||
$currency = $this->getCurrency($account);
|
||||
if (null === $currency) {
|
||||
$this->accountCurrencies[$accountId] = 0;
|
||||
$this->friendlyError(
|
||||
sprintf(
|
||||
'Account #%d ("%s") has no currency preference, so transaction journal #%d can\'t be corrected',
|
||||
$account->id,
|
||||
$account->name,
|
||||
$journal->id
|
||||
)
|
||||
);
|
||||
$this->count++;
|
||||
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
// fix each transaction:
|
||||
$journal->transactions->each(
|
||||
static function (Transaction $transaction) use ($currency) {
|
||||
if (null === $transaction->transaction_currency_id) {
|
||||
$transaction->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
}
|
||||
$this->accountCurrencies[$accountId] = $currency;
|
||||
|
||||
return $currency;
|
||||
// when mismatch in transaction:
|
||||
if ((int)$transaction->transaction_currency_id !== (int)$currency->id) {
|
||||
$transaction->foreign_currency_id = (int)$transaction->transaction_currency_id;
|
||||
$transaction->foreign_amount = $transaction->amount;
|
||||
$transaction->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
}
|
||||
}
|
||||
);
|
||||
// also update the journal, of course:
|
||||
$journal->transaction_currency_id = $currency->id;
|
||||
$this->count++;
|
||||
$journal->save();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,18 +234,28 @@ class OtherCurrenciesCorrections extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
* @param Account $account
|
||||
*
|
||||
* @return TransactionCurrency|null
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
private function getCurrency(Account $account): ?TransactionCurrency
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
$accountId = $account->id;
|
||||
if (array_key_exists($accountId, $this->accountCurrencies) && 0 === $this->accountCurrencies[$accountId]) {
|
||||
return null;
|
||||
}
|
||||
if (array_key_exists($accountId, $this->accountCurrencies) && $this->accountCurrencies[$accountId] instanceof TransactionCurrency) {
|
||||
return $this->accountCurrencies[$accountId];
|
||||
}
|
||||
$currency = $this->accountRepos->getAccountCurrency($account);
|
||||
if (null === $currency) {
|
||||
$this->accountCurrencies[$accountId] = 0;
|
||||
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
$this->accountCurrencies[$accountId] = $currency;
|
||||
|
||||
return $currency;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,95 +265,4 @@ class OtherCurrenciesCorrections extends Command
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->count = 0;
|
||||
$this->accountCurrencies = [];
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$this->journalRepos = app(JournalRepositoryInterface::class);
|
||||
$this->cliRepos = app(JournalCLIRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*/
|
||||
private function updateJournalCurrency(TransactionJournal $journal): void
|
||||
{
|
||||
$this->accountRepos->setUser($journal->user);
|
||||
$this->journalRepos->setUser($journal->user);
|
||||
$this->currencyRepos->setUser($journal->user);
|
||||
$this->cliRepos->setUser($journal->user);
|
||||
|
||||
$leadTransaction = $this->getLeadTransaction($journal);
|
||||
|
||||
if (null === $leadTransaction) {
|
||||
$this->error(sprintf('Could not reliably determine which transaction is in the lead for transaction journal #%d.', $journal->id));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$account = $leadTransaction->account;
|
||||
$currency = $this->getCurrency($account);
|
||||
if (null === $currency) {
|
||||
$this->error(
|
||||
sprintf(
|
||||
'Account #%d ("%s") has no currency preference, so transaction journal #%d can\'t be corrected',
|
||||
$account->id,
|
||||
$account->name,
|
||||
$journal->id
|
||||
)
|
||||
);
|
||||
$this->count++;
|
||||
|
||||
return;
|
||||
}
|
||||
// fix each transaction:
|
||||
$journal->transactions->each(
|
||||
static function (Transaction $transaction) use ($currency) {
|
||||
if (null === $transaction->transaction_currency_id) {
|
||||
$transaction->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
}
|
||||
|
||||
// when mismatch in transaction:
|
||||
if ((int)$transaction->transaction_currency_id !== (int)$currency->id) {
|
||||
$transaction->foreign_currency_id = (int)$transaction->transaction_currency_id;
|
||||
$transaction->foreign_amount = $transaction->amount;
|
||||
$transaction->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
}
|
||||
}
|
||||
);
|
||||
// also update the journal, of course:
|
||||
$journal->transaction_currency_id = $currency->id;
|
||||
$this->count++;
|
||||
$journal->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine verifies that withdrawals, deposits and opening balances have the correct currency settings for
|
||||
* the accounts they are linked to.
|
||||
* Both source and destination must match the respective currency preference of the related asset account.
|
||||
* So FF3 must verify all transactions.
|
||||
*/
|
||||
private function updateOtherJournalsCurrencies(): void
|
||||
{
|
||||
$set = $this->cliRepos->getAllJournals(
|
||||
[TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,]
|
||||
);
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($set as $journal) {
|
||||
$this->updateJournalCurrency($journal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\AccountMeta;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -34,6 +35,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
*/
|
||||
class RenameAccountMeta extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '480_rename_account_meta';
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -59,7 +62,7 @@ class RenameAccountMeta extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -86,10 +89,10 @@ class RenameAccountMeta extends Command
|
||||
$this->markAsExecuted();
|
||||
|
||||
if (0 === $count) {
|
||||
$this->info('Correct: all account meta is OK.');
|
||||
$this->friendlyPositive('All account meta is OK.');
|
||||
}
|
||||
if (0 !== $count) {
|
||||
$this->line(sprintf('Renamed %d account meta entries (entry).', $count));
|
||||
$this->friendlyInfo(sprintf('Renamed %d account meta entries (entry).', $count));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
@@ -39,6 +40,8 @@ use Schema;
|
||||
*/
|
||||
class TransactionIdentifier extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '480_transaction_identifier';
|
||||
protected $description = 'Fixes transaction identifiers.';
|
||||
protected $signature = 'firefly-iii:transaction-identifiers {--F|force : Force the execution of this command.}';
|
||||
@@ -46,13 +49,14 @@ class TransactionIdentifier extends Command
|
||||
private int $count;
|
||||
|
||||
/**
|
||||
* This method gives all transactions which are part of a split journal (so more than 2) a sort of "order" so they are easier
|
||||
* to easier to match to their counterpart. When a journal is split, it has two or three transactions: -3, -4 and -5 for example.
|
||||
* This method gives all transactions which are part of a split journal (so more than 2) a sort of "order" so they
|
||||
* are easier to easier to match to their counterpart. When a journal is split, it has two or three transactions:
|
||||
* -3, -4 and -5 for example.
|
||||
*
|
||||
* In the database this is reflected as 6 transactions: -3/+3, -4/+4, -5/+5.
|
||||
*
|
||||
* When either of these are the same amount, FF3 can't keep them apart: +3/-3, +3/-3, +3/-3. This happens more often than you would
|
||||
* think. So each set gets a number (1,2,3) to keep them apart.
|
||||
* When either of these are the same amount, FF3 can't keep them apart: +3/-3, +3/-3, +3/-3. This happens more
|
||||
* often than you would think. So each set gets a number (1,2,3) to keep them apart.
|
||||
*
|
||||
* @return int
|
||||
* @throws ContainerExceptionInterface
|
||||
@@ -64,7 +68,7 @@ class TransactionIdentifier extends Command
|
||||
$this->stupidLaravel();
|
||||
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -81,10 +85,10 @@ class TransactionIdentifier extends Command
|
||||
}
|
||||
|
||||
if (0 === $this->count) {
|
||||
$this->line('Correct: all split journal transaction identifiers are OK.');
|
||||
$this->friendlyPositive('All split journal transaction identifiers are OK.');
|
||||
}
|
||||
if (0 !== $this->count) {
|
||||
$this->line(sprintf('Correct: fixed %d split journal transaction identifier(s).', $this->count));
|
||||
$this->friendlyInfo(sprintf('Fixed %d split journal transaction identifier(s).', $this->count));
|
||||
}
|
||||
|
||||
$this->markAsExecuted();
|
||||
@@ -93,33 +97,16 @@ class TransactionIdentifier extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $transaction
|
||||
* @param array $exclude
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
* @return Transaction|null
|
||||
|
||||
*/
|
||||
private function findOpposing(Transaction $transaction, array $exclude): ?Transaction
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
// find opposing:
|
||||
$amount = bcmul((string)$transaction->amount, '-1');
|
||||
|
||||
try {
|
||||
/** @var Transaction $opposing */
|
||||
$opposing = Transaction::where('transaction_journal_id', $transaction->transaction_journal_id)
|
||||
->where('amount', $amount)->where('identifier', '=', 0)
|
||||
->whereNotIn('id', $exclude)
|
||||
->first();
|
||||
} catch (QueryException $e) {
|
||||
Log::error($e->getMessage());
|
||||
$this->error('Firefly III could not find the "identifier" field in the "transactions" table.');
|
||||
$this->error(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version')));
|
||||
$this->error('Please run "php artisan migrate" to add this field to the table.');
|
||||
$this->info('Then, run "php artisan firefly:upgrade-database" to try again.');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $opposing;
|
||||
$this->cliRepository = app(JournalCLIRepositoryInterface::class);
|
||||
$this->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,29 +125,8 @@ class TransactionIdentifier extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->cliRepository = app(JournalCLIRepositoryInterface::class);
|
||||
$this->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grab all positive transactions from this journal that are not deleted. for each one, grab the negative opposing one
|
||||
* which has 0 as an identifier and give it the same identifier.
|
||||
* Grab all positive transactions from this journal that are not deleted. for each one, grab the negative opposing
|
||||
* one which has 0 as an identifier and give it the same identifier.
|
||||
*
|
||||
* @param TransactionJournal $transactionJournal
|
||||
*/
|
||||
@@ -186,4 +152,42 @@ class TransactionIdentifier extends Command
|
||||
++$identifier;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $transaction
|
||||
* @param array $exclude
|
||||
*
|
||||
* @return Transaction|null
|
||||
*/
|
||||
private function findOpposing(Transaction $transaction, array $exclude): ?Transaction
|
||||
{
|
||||
// find opposing:
|
||||
$amount = bcmul((string)$transaction->amount, '-1');
|
||||
|
||||
try {
|
||||
/** @var Transaction $opposing */
|
||||
$opposing = Transaction::where('transaction_journal_id', $transaction->transaction_journal_id)
|
||||
->where('amount', $amount)->where('identifier', '=', 0)
|
||||
->whereNotIn('id', $exclude)
|
||||
->first();
|
||||
} catch (QueryException $e) {
|
||||
Log::error($e->getMessage());
|
||||
$this->friendlyError('Firefly III could not find the "identifier" field in the "transactions" table.');
|
||||
$this->friendlyError(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version')));
|
||||
$this->friendlyError('Please run "php artisan migrate" to add this field to the table.');
|
||||
$this->friendlyError('Then, run "php artisan firefly:upgrade-database" to try again.');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $opposing;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
@@ -41,6 +41,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
*/
|
||||
class TransferCurrenciesCorrections extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '480_transfer_currencies';
|
||||
protected $description = 'Updates transfer currency information.';
|
||||
protected $signature = 'firefly-iii:transfer-currencies {--F|force : Force the execution of this command.}';
|
||||
@@ -66,7 +68,7 @@ class TransferCurrenciesCorrections extends Command
|
||||
$this->stupidLaravel();
|
||||
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -76,207 +78,174 @@ class TransferCurrenciesCorrections extends Command
|
||||
$this->markAsExecuted();
|
||||
|
||||
if (0 === $this->count) {
|
||||
$message = 'Correct: all transfers have correct currency information.';
|
||||
$this->info($message);
|
||||
}
|
||||
if (0 !== $this->count) {
|
||||
$message = sprintf('Verified currency information of %d transfer(s).', $this->count);
|
||||
$this->line($message);
|
||||
$this->friendlyPositive('All transfers have correct currency information.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->friendlyInfo(sprintf('Verified currency information of %d transfer(s).', $this->count));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The destination transaction must have a currency. If not, it will be added by
|
||||
* taking it from the destination account's preference.
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
|
||||
*/
|
||||
private function fixDestNoCurrency(): void
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
if (null === $this->destinationTransaction->transaction_currency_id && null !== $this->destinationCurrency) {
|
||||
$this->destinationTransaction
|
||||
->transaction_currency_id
|
||||
= (int)$this->destinationCurrency->id;
|
||||
$message = sprintf(
|
||||
'Transaction #%d has no currency setting, now set to %s.',
|
||||
$this->destinationTransaction->id,
|
||||
$this->destinationCurrency->code
|
||||
$this->count = 0;
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->cliRepos = app(JournalCLIRepositoryInterface::class);
|
||||
$this->accountCurrencies = [];
|
||||
$this->resetInformation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all the class fields for the current transfer.
|
||||
*
|
||||
|
||||
*/
|
||||
private function resetInformation(): void
|
||||
{
|
||||
$this->sourceTransaction = null;
|
||||
$this->sourceAccount = null;
|
||||
$this->sourceCurrency = null;
|
||||
$this->destinationTransaction = null;
|
||||
$this->destinationAccount = null;
|
||||
$this->destinationCurrency = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine verifies that transfers have the correct currency settings for the accounts they are linked to.
|
||||
* 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.
|
||||
*
|
||||
* Both source and destination must match the respective currency preference. So FF3 must verify ALL
|
||||
* transactions.
|
||||
*/
|
||||
private function startUpdateRoutine(): void
|
||||
{
|
||||
$set = $this->cliRepos->getAllJournals([TransactionType::TRANSFER]);
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($set as $journal) {
|
||||
$this->updateTransferCurrency($journal);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $transfer
|
||||
*/
|
||||
private function updateTransferCurrency(TransactionJournal $transfer): void
|
||||
{
|
||||
$this->resetInformation();
|
||||
|
||||
|
||||
if ($this->isSplitJournal($transfer)) {
|
||||
$this->friendlyWarning(sprintf('Transaction journal #%d is a split journal. Cannot continue.', $transfer->id));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$this->getSourceInformation($transfer);
|
||||
$this->getDestinationInformation($transfer);
|
||||
|
||||
// unexpectedly, either one is null:
|
||||
|
||||
if ($this->isEmptyTransactions()) {
|
||||
$this->friendlyError(sprintf('Source or destination information for transaction journal #%d is null. Cannot fix this one.', $transfer->id));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// both accounts must have currency preference:
|
||||
|
||||
if ($this->isNoCurrencyPresent()) {
|
||||
$this->friendlyError(
|
||||
sprintf('Source or destination accounts for transaction journal #%d have no currency information. Cannot fix this one.', $transfer->id)
|
||||
);
|
||||
$this->line($message);
|
||||
$this->count++;
|
||||
$this->destinationTransaction->save();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// fix source transaction having no currency.
|
||||
$this->fixSourceNoCurrency();
|
||||
|
||||
// fix source transaction having bad currency.
|
||||
$this->fixSourceUnmatchedCurrency();
|
||||
|
||||
// fix destination transaction having no currency.
|
||||
$this->fixDestNoCurrency();
|
||||
|
||||
// fix destination transaction having bad currency.
|
||||
$this->fixDestinationUnmatchedCurrency();
|
||||
|
||||
// remove foreign currency information if not necessary.
|
||||
$this->fixInvalidForeignCurrency();
|
||||
// correct foreign currency info if necessary.
|
||||
$this->fixMismatchedForeignCurrency();
|
||||
|
||||
// restore missing foreign currency amount.
|
||||
$this->fixSourceNullForeignAmount();
|
||||
|
||||
$this->fixDestNullForeignAmount();
|
||||
|
||||
// fix journal itself:
|
||||
$this->fixTransactionJournalCurrency($transfer);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the foreign amount of the destination transaction is null, but that of the other isn't, use this piece of code
|
||||
* to restore it.
|
||||
* Is this a split transaction journal?
|
||||
*
|
||||
* @param TransactionJournal $transfer
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function fixDestNullForeignAmount(): void
|
||||
private function isSplitJournal(TransactionJournal $transfer): bool
|
||||
{
|
||||
if (null === $this->destinationTransaction->foreign_amount && null !== $this->sourceTransaction->foreign_amount) {
|
||||
$this->destinationTransaction->foreign_amount = bcmul((string)$this->sourceTransaction->foreign_amount, '-1');
|
||||
$this->destinationTransaction->save();
|
||||
$this->count++;
|
||||
$this->line(
|
||||
sprintf(
|
||||
'Restored foreign amount of destination transaction #%d to %s',
|
||||
$this->destinationTransaction->id,
|
||||
$this->destinationTransaction->foreign_amount
|
||||
)
|
||||
);
|
||||
}
|
||||
return $transfer->transactions->count() > 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* The destination transaction must have the correct currency. If not, it will be set by
|
||||
* taking it from the destination account's preference.
|
||||
*/
|
||||
private function fixDestinationUnmatchedCurrency(): void
|
||||
{
|
||||
if (null !== $this->destinationCurrency
|
||||
&& null === $this->destinationTransaction->foreign_amount
|
||||
&& (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.',
|
||||
$this->destinationTransaction->id,
|
||||
$this->destinationTransaction->transaction_currency_id,
|
||||
$this->destinationAccount->id,
|
||||
$this->destinationTransaction->amount
|
||||
);
|
||||
$this->line($message);
|
||||
$this->count++;
|
||||
$this->destinationTransaction->transaction_currency_id = (int)$this->destinationCurrency->id;
|
||||
$this->destinationTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the destination account currency is the same as the source currency,
|
||||
* both foreign_amount and foreign_currency_id fields must be NULL
|
||||
* for both transactions (because foreign currency info would not make sense)
|
||||
*/
|
||||
private function fixInvalidForeignCurrency(): void
|
||||
{
|
||||
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;
|
||||
|
||||
$this->destinationTransaction->foreign_amount = null;
|
||||
$this->destinationTransaction->foreign_currency_id = null;
|
||||
|
||||
$this->sourceTransaction->save();
|
||||
$this->destinationTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If destination account currency is different from source account currency,
|
||||
* then both transactions must get the source account's currency as normal currency
|
||||
* and the opposing account's currency as foreign currency.
|
||||
*/
|
||||
private function fixMismatchedForeignCurrency(): void
|
||||
{
|
||||
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;
|
||||
$this->destinationTransaction->foreign_currency_id = $this->destinationCurrency->id;
|
||||
|
||||
$this->sourceTransaction->save();
|
||||
$this->destinationTransaction->save();
|
||||
$this->count++;
|
||||
$this->line(sprintf('Verified foreign currency ID of transaction #%d and #%d', $this->sourceTransaction->id, $this->destinationTransaction->id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The source transaction must have a currency. If not, it will be added by
|
||||
* taking it from the source account's preference.
|
||||
*/
|
||||
private function fixSourceNoCurrency(): void
|
||||
{
|
||||
if (null === $this->sourceTransaction->transaction_currency_id && null !== $this->sourceCurrency) {
|
||||
$this->sourceTransaction
|
||||
->transaction_currency_id
|
||||
= (int)$this->sourceCurrency->id;
|
||||
$message = sprintf(
|
||||
'Transaction #%d has no currency setting, now set to %s.',
|
||||
$this->sourceTransaction->id,
|
||||
$this->sourceCurrency->code
|
||||
);
|
||||
$this->line($message);
|
||||
$this->count++;
|
||||
$this->sourceTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the foreign amount of the source transaction is null, but that of the other isn't, use this piece of code
|
||||
* to restore it.
|
||||
*/
|
||||
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->save();
|
||||
$this->count++;
|
||||
$this->line(
|
||||
sprintf(
|
||||
'Restored foreign amount of source transaction #%d to %s',
|
||||
$this->sourceTransaction->id,
|
||||
$this->sourceTransaction->foreign_amount
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The source transaction must have the correct currency. If not, it will be set by
|
||||
* taking it from the source account's preference.
|
||||
*/
|
||||
private function fixSourceUnmatchedCurrency(): void
|
||||
{
|
||||
if (null !== $this->sourceCurrency
|
||||
&& null === $this->sourceTransaction->foreign_amount
|
||||
&& (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.',
|
||||
$this->sourceTransaction->id,
|
||||
$this->sourceTransaction->transaction_currency_id,
|
||||
$this->sourceAccount->id,
|
||||
$this->sourceTransaction->amount
|
||||
);
|
||||
$this->line($message);
|
||||
$this->count++;
|
||||
$this->sourceTransaction->transaction_currency_id = (int)$this->sourceCurrency->id;
|
||||
$this->sourceTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method makes sure that the transaction journal uses the currency given in the source transaction.
|
||||
* Extract source transaction, source account + source account currency from the journal.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
|
||||
*/
|
||||
private function fixTransactionJournalCurrency(TransactionJournal $journal): void
|
||||
private function getSourceInformation(TransactionJournal $journal): void
|
||||
{
|
||||
if ((int)$journal->transaction_currency_id !== (int)$this->sourceCurrency->id) {
|
||||
$oldCurrencyCode = $journal->transactionCurrency->code ?? '(nothing)';
|
||||
$journal->transaction_currency_id = $this->sourceCurrency->id;
|
||||
$message = sprintf(
|
||||
'Transfer #%d ("%s") has been updated to use %s instead of %s.',
|
||||
$journal->id,
|
||||
$journal->description,
|
||||
$this->sourceCurrency->code,
|
||||
$oldCurrencyCode
|
||||
);
|
||||
$this->count++;
|
||||
$this->line($message);
|
||||
$journal->save();
|
||||
$this->sourceTransaction = $this->getSourceTransaction($journal);
|
||||
$this->sourceAccount = $this->sourceTransaction?->account;
|
||||
$this->sourceCurrency = null === $this->sourceAccount ? null : $this->getCurrency($this->sourceAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $transfer
|
||||
*
|
||||
* @return Transaction|null
|
||||
*/
|
||||
private function getSourceTransaction(TransactionJournal $transfer): ?Transaction
|
||||
{
|
||||
return $transfer->transactions()->where('amount', '<', 0)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -328,30 +297,6 @@ class TransferCurrenciesCorrections extends Command
|
||||
return $transfer->transactions()->where('amount', '>', 0)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract source transaction, source account + source account currency from the journal.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
|
||||
*/
|
||||
private function getSourceInformation(TransactionJournal $journal): void
|
||||
{
|
||||
$this->sourceTransaction = $this->getSourceTransaction($journal);
|
||||
$this->sourceAccount = $this->sourceTransaction?->account;
|
||||
$this->sourceCurrency = null === $this->sourceAccount ? null : $this->getCurrency($this->sourceAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $transfer
|
||||
*
|
||||
* @return Transaction|null
|
||||
*/
|
||||
private function getSourceTransaction(TransactionJournal $transfer): ?Transaction
|
||||
{
|
||||
return $transfer->transactions()->where('amount', '<', 0)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is either the source or destination transaction NULL?
|
||||
*
|
||||
@@ -364,21 +309,6 @@ class TransferCurrenciesCorrections extends Command
|
||||
|| null === $this->destinationAccount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
@@ -388,7 +318,7 @@ class TransferCurrenciesCorrections extends Command
|
||||
if (null === $this->sourceCurrency) {
|
||||
$message = sprintf('Account #%d ("%s") must have currency preference but has none.', $this->sourceAccount->id, $this->sourceAccount->name);
|
||||
Log::error($message);
|
||||
$this->error($message);
|
||||
$this->friendlyError($message);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -401,7 +331,7 @@ class TransferCurrenciesCorrections extends Command
|
||||
$this->destinationAccount->name
|
||||
);
|
||||
Log::error($message);
|
||||
$this->error($message);
|
||||
$this->friendlyError($message);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -410,15 +340,198 @@ class TransferCurrenciesCorrections extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a split transaction journal?
|
||||
*
|
||||
* @param TransactionJournal $transfer
|
||||
*
|
||||
* @return bool
|
||||
* The source transaction must have a currency. If not, it will be added by
|
||||
* taking it from the source account's preference.
|
||||
*/
|
||||
private function isSplitJournal(TransactionJournal $transfer): bool
|
||||
private function fixSourceNoCurrency(): void
|
||||
{
|
||||
return $transfer->transactions->count() > 2;
|
||||
if (null === $this->sourceTransaction->transaction_currency_id && null !== $this->sourceCurrency) {
|
||||
$this->sourceTransaction
|
||||
->transaction_currency_id
|
||||
= (int)$this->sourceCurrency->id;
|
||||
$message = sprintf(
|
||||
'Transaction #%d has no currency setting, now set to %s.',
|
||||
$this->sourceTransaction->id,
|
||||
$this->sourceCurrency->code
|
||||
);
|
||||
$this->friendlyInfo($message);
|
||||
$this->count++;
|
||||
$this->sourceTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The source transaction must have the correct currency. If not, it will be set by
|
||||
* taking it from the source account's preference.
|
||||
*/
|
||||
private function fixSourceUnmatchedCurrency(): void
|
||||
{
|
||||
if (null !== $this->sourceCurrency
|
||||
&& null === $this->sourceTransaction->foreign_amount
|
||||
&& (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.',
|
||||
$this->sourceTransaction->id,
|
||||
$this->sourceTransaction->transaction_currency_id,
|
||||
$this->sourceAccount->id,
|
||||
$this->sourceTransaction->amount
|
||||
);
|
||||
$this->friendlyWarning($message);
|
||||
$this->count++;
|
||||
$this->sourceTransaction->transaction_currency_id = (int)$this->sourceCurrency->id;
|
||||
$this->sourceTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The destination transaction must have a currency. If not, it will be added by
|
||||
* taking it from the destination account's preference.
|
||||
*/
|
||||
private function fixDestNoCurrency(): void
|
||||
{
|
||||
if (null === $this->destinationTransaction->transaction_currency_id && null !== $this->destinationCurrency) {
|
||||
$this->destinationTransaction
|
||||
->transaction_currency_id
|
||||
= (int)$this->destinationCurrency->id;
|
||||
$message = sprintf(
|
||||
'Transaction #%d has no currency setting, now set to %s.',
|
||||
$this->destinationTransaction->id,
|
||||
$this->destinationCurrency->code
|
||||
);
|
||||
$this->friendlyInfo($message);
|
||||
$this->count++;
|
||||
$this->destinationTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The destination transaction must have the correct currency. If not, it will be set by
|
||||
* taking it from the destination account's preference.
|
||||
*/
|
||||
private function fixDestinationUnmatchedCurrency(): void
|
||||
{
|
||||
if (null !== $this->destinationCurrency
|
||||
&& null === $this->destinationTransaction->foreign_amount
|
||||
&& (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.',
|
||||
$this->destinationTransaction->id,
|
||||
$this->destinationTransaction->transaction_currency_id,
|
||||
$this->destinationAccount->id,
|
||||
$this->destinationTransaction->amount
|
||||
);
|
||||
$this->friendlyWarning($message);
|
||||
$this->count++;
|
||||
$this->destinationTransaction->transaction_currency_id = (int)$this->destinationCurrency->id;
|
||||
$this->destinationTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the destination account currency is the same as the source currency,
|
||||
* both foreign_amount and foreign_currency_id fields must be NULL
|
||||
* for both transactions (because foreign currency info would not make sense)
|
||||
*/
|
||||
private function fixInvalidForeignCurrency(): void
|
||||
{
|
||||
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;
|
||||
|
||||
$this->destinationTransaction->foreign_amount = null;
|
||||
$this->destinationTransaction->foreign_currency_id = null;
|
||||
|
||||
$this->sourceTransaction->save();
|
||||
$this->destinationTransaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If destination account currency is different from source account currency,
|
||||
* then both transactions must get the source account's currency as normal currency
|
||||
* and the opposing account's currency as foreign currency.
|
||||
*/
|
||||
private function fixMismatchedForeignCurrency(): void
|
||||
{
|
||||
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;
|
||||
$this->destinationTransaction->foreign_currency_id = $this->destinationCurrency->id;
|
||||
|
||||
$this->sourceTransaction->save();
|
||||
$this->destinationTransaction->save();
|
||||
$this->count++;
|
||||
$this->friendlyInfo(
|
||||
sprintf('Verified foreign currency ID of transaction #%d and #%d', $this->sourceTransaction->id, $this->destinationTransaction->id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the foreign amount of the source transaction is null, but that of the other isn't, use this piece of code
|
||||
* to restore it.
|
||||
*/
|
||||
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->save();
|
||||
$this->count++;
|
||||
$this->friendlyInfo(
|
||||
sprintf(
|
||||
'Restored foreign amount of source transaction #%d to %s',
|
||||
$this->sourceTransaction->id,
|
||||
$this->sourceTransaction->foreign_amount
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the foreign amount of the destination transaction is null, but that of the other isn't, use this piece of code
|
||||
* to restore it.
|
||||
*/
|
||||
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->save();
|
||||
$this->count++;
|
||||
$this->friendlyInfo(
|
||||
sprintf(
|
||||
'Restored foreign amount of destination transaction #%d to %s',
|
||||
$this->destinationTransaction->id,
|
||||
$this->destinationTransaction->foreign_amount
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method makes sure that the transaction journal uses the currency given in the source transaction.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
*/
|
||||
private function fixTransactionJournalCurrency(TransactionJournal $journal): void
|
||||
{
|
||||
if ((int)$journal->transaction_currency_id !== (int)$this->sourceCurrency->id) {
|
||||
$oldCurrencyCode = $journal->transactionCurrency->code ?? '(nothing)';
|
||||
$journal->transaction_currency_id = $this->sourceCurrency->id;
|
||||
$message = sprintf(
|
||||
'Transfer #%d ("%s") has been updated to use %s instead of %s.',
|
||||
$journal->id,
|
||||
$journal->description,
|
||||
$this->sourceCurrency->code,
|
||||
$oldCurrencyCode
|
||||
);
|
||||
$this->count++;
|
||||
$this->friendlyInfo($message);
|
||||
$journal->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -428,115 +541,4 @@ class TransferCurrenciesCorrections extends Command
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all the class fields for the current transfer.
|
||||
*
|
||||
|
||||
*/
|
||||
private function resetInformation(): void
|
||||
{
|
||||
$this->sourceTransaction = null;
|
||||
$this->sourceAccount = null;
|
||||
$this->sourceCurrency = null;
|
||||
$this->destinationTransaction = null;
|
||||
$this->destinationAccount = null;
|
||||
$this->destinationCurrency = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine verifies that transfers have the correct currency settings for the accounts they are linked to.
|
||||
* 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.
|
||||
*
|
||||
* Both source and destination must match the respective currency preference. So FF3 must verify ALL
|
||||
* transactions.
|
||||
*/
|
||||
private function startUpdateRoutine(): void
|
||||
{
|
||||
$set = $this->cliRepos->getAllJournals([TransactionType::TRANSFER]);
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($set as $journal) {
|
||||
$this->updateTransferCurrency($journal);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
|
||||
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
|
||||
* be called from the handle method instead of using the constructor to initialize the command.
|
||||
*
|
||||
|
||||
*/
|
||||
private function stupidLaravel(): void
|
||||
{
|
||||
$this->count = 0;
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->cliRepos = app(JournalCLIRepositoryInterface::class);
|
||||
$this->accountCurrencies = [];
|
||||
$this->resetInformation();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $transfer
|
||||
*/
|
||||
private function updateTransferCurrency(TransactionJournal $transfer): void
|
||||
{
|
||||
$this->resetInformation();
|
||||
|
||||
|
||||
if ($this->isSplitJournal($transfer)) {
|
||||
$this->line(sprintf('Transaction journal #%d is a split journal. Cannot continue.', $transfer->id));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$this->getSourceInformation($transfer);
|
||||
$this->getDestinationInformation($transfer);
|
||||
|
||||
// unexpectedly, either one is null:
|
||||
|
||||
if ($this->isEmptyTransactions()) {
|
||||
$this->error(sprintf('Source or destination information for transaction journal #%d is null. Cannot fix this one.', $transfer->id));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// both accounts must have currency preference:
|
||||
|
||||
if ($this->isNoCurrencyPresent()) {
|
||||
$this->error(
|
||||
sprintf('Source or destination accounts for transaction journal #%d have no currency information. Cannot fix this one.', $transfer->id)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// fix source transaction having no currency.
|
||||
$this->fixSourceNoCurrency();
|
||||
|
||||
// fix source transaction having bad currency.
|
||||
$this->fixSourceUnmatchedCurrency();
|
||||
|
||||
// fix destination transaction having no currency.
|
||||
$this->fixDestNoCurrency();
|
||||
|
||||
// fix destination transaction having bad currency.
|
||||
$this->fixDestinationUnmatchedCurrency();
|
||||
|
||||
// remove foreign currency information if not necessary.
|
||||
$this->fixInvalidForeignCurrency();
|
||||
// correct foreign currency info if necessary.
|
||||
$this->fixMismatchedForeignCurrency();
|
||||
|
||||
// restore missing foreign currency amount.
|
||||
$this->fixSourceNullForeignAmount();
|
||||
|
||||
$this->fixDestNullForeignAmount();
|
||||
|
||||
// fix journal itself:
|
||||
$this->fixTransactionJournalCurrency($transfer);
|
||||
}
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@ namespace FireflyIII\Console\Commands\Upgrade;
|
||||
set_time_limit(0);
|
||||
|
||||
use Artisan;
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
@@ -35,6 +36,8 @@ use Illuminate\Console\Command;
|
||||
*/
|
||||
class UpgradeDatabase extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Upgrades the database to the latest version.';
|
||||
protected $signature = 'firefly-iii:upgrade-database {--F|force : Force all upgrades.}';
|
||||
|
||||
@@ -65,13 +68,14 @@ class UpgradeDatabase extends Command
|
||||
'firefly-iii:upgrade-liabilities',
|
||||
'firefly-iii:liabilities-600',
|
||||
'firefly-iii:budget-limit-periods',
|
||||
'firefly-iii:restore-oauth-keys',
|
||||
];
|
||||
$args = [];
|
||||
if ($this->option('force')) {
|
||||
$args = ['--force' => true];
|
||||
}
|
||||
foreach ($commands as $command) {
|
||||
$this->line(sprintf('Now executing %s', $command));
|
||||
$this->friendlyLine(sprintf('Now executing %s', $command));
|
||||
$this->call($command, $args);
|
||||
}
|
||||
// set new DB version.
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Factory\AccountMetaFactory;
|
||||
use FireflyIII\Models\Account;
|
||||
@@ -33,7 +34,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Support\CreditRecalculateService;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
|
||||
@@ -42,6 +42,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
*/
|
||||
class UpgradeLiabilities extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '560_upgrade_liabilities';
|
||||
protected $description = 'Upgrade liabilities to new 5.6.0 structure.';
|
||||
protected $signature = 'firefly-iii:upgrade-liabilities {--F|force : Force the execution of this command.}';
|
||||
@@ -57,7 +59,7 @@ class UpgradeLiabilities extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -67,6 +69,76 @@ class UpgradeLiabilities extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function upgradeLiabilities(): void
|
||||
{
|
||||
$users = User::get();
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$this->upgradeForUser($user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
private function upgradeForUser(User $user): void
|
||||
{
|
||||
$accounts = $user->accounts()
|
||||
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||
->whereIn('account_types.type', config('firefly.valid_liabilities'))
|
||||
->get(['accounts.*']);
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$this->upgradeLiability($account);
|
||||
$service = app(CreditRecalculateService::class);
|
||||
$service->setAccount($account);
|
||||
$service->recalculate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*/
|
||||
private function upgradeLiability(Account $account): void
|
||||
{
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$repository->setUser($account->user);
|
||||
|
||||
// get opening balance, and correct if necessary.
|
||||
$openingBalance = $repository->getOpeningBalance($account);
|
||||
if (null !== $openingBalance) {
|
||||
// correct if necessary
|
||||
$this->correctOpeningBalance($account, $openingBalance);
|
||||
}
|
||||
|
||||
// add liability direction property (if it does not yet exist!)
|
||||
$value = $repository->getMetaValue($account, 'liability_direction');
|
||||
if (null === $value) {
|
||||
/** @var AccountMetaFactory $factory */
|
||||
$factory = app(AccountMetaFactory::class);
|
||||
$factory->crud($account, 'liability_direction', 'debit');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param TransactionJournal $openingBalance
|
||||
@@ -94,9 +166,9 @@ class UpgradeLiabilities extends Command
|
||||
*
|
||||
* @return Transaction|null
|
||||
*/
|
||||
private function getDestinationTransaction(TransactionJournal $journal): ?Transaction
|
||||
private function getSourceTransaction(TransactionJournal $journal): ?Transaction
|
||||
{
|
||||
return $journal->transactions()->where('amount', '>', 0)->first();
|
||||
return $journal->transactions()->where('amount', '<', 0)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,24 +176,9 @@ class UpgradeLiabilities extends Command
|
||||
*
|
||||
* @return Transaction|null
|
||||
*/
|
||||
private function getSourceTransaction(TransactionJournal $journal): ?Transaction
|
||||
private function getDestinationTransaction(TransactionJournal $journal): ?Transaction
|
||||
{
|
||||
return $journal->transactions()->where('amount', '<', 0)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
return $journal->transactions()->where('amount', '>', 0)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,59 +188,4 @@ class UpgradeLiabilities extends Command
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
private function upgradeForUser(User $user): void
|
||||
{
|
||||
$accounts = $user->accounts()
|
||||
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||
->whereIn('account_types.type', config('firefly.valid_liabilities'))
|
||||
->get(['accounts.*']);
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$this->upgradeLiability($account);
|
||||
$service = app(CreditRecalculateService::class);
|
||||
$service->setAccount($account);
|
||||
$service->recalculate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function upgradeLiabilities(): void
|
||||
{
|
||||
$users = User::get();
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$this->upgradeForUser($user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*/
|
||||
private function upgradeLiability(Account $account): void
|
||||
{
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$repository->setUser($account->user);
|
||||
|
||||
// get opening balance, and correct if necessary.
|
||||
$openingBalance = $repository->getOpeningBalance($account);
|
||||
if (null !== $openingBalance) {
|
||||
// correct if necessary
|
||||
$this->correctOpeningBalance($account, $openingBalance);
|
||||
}
|
||||
|
||||
// add liability direction property (if it does not yet exist!)
|
||||
$value = $repository->getMetaValue($account, 'liability_direction');
|
||||
if (null === $value) {
|
||||
/** @var AccountMetaFactory $factory */
|
||||
$factory = app(AccountMetaFactory::class);
|
||||
$factory->crud($account, 'liability_direction', 'debit');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
@@ -44,6 +45,8 @@ use Psr\Container\NotFoundExceptionInterface;
|
||||
*/
|
||||
class UpgradeLiabilitiesEight extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public const CONFIG_NAME = '600_upgrade_liabilities';
|
||||
protected $description = 'Upgrade liabilities to new 6.0.0 structure.';
|
||||
protected $signature = 'firefly-iii:liabilities-600 {--F|force : Force the execution of this command.}';
|
||||
@@ -59,7 +62,7 @@ class UpgradeLiabilitiesEight extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -69,6 +72,104 @@ class UpgradeLiabilitiesEight extends Command
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function upgradeLiabilities(): void
|
||||
{
|
||||
$users = User::get();
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$this->upgradeForUser($user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
private function upgradeForUser(User $user): void
|
||||
{
|
||||
$accounts = $user->accounts()
|
||||
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||
->whereIn('account_types.type', config('firefly.valid_liabilities'))
|
||||
->get(['accounts.*']);
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$this->upgradeLiability($account);
|
||||
$service = app(CreditRecalculateService::class);
|
||||
$service->setAccount($account);
|
||||
$service->recalculate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*/
|
||||
private function upgradeLiability(Account $account): void
|
||||
{
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$repository->setUser($account->user);
|
||||
|
||||
$direction = $repository->getMetaValue($account, 'liability_direction');
|
||||
if ('credit' === $direction && $this->hasBadOpening($account)) {
|
||||
$this->deleteCreditTransaction($account);
|
||||
$this->reverseOpeningBalance($account);
|
||||
$this->friendlyInfo(sprintf('Corrected opening balance for liability #%d ("%s")', $account->id, $account->name));
|
||||
}
|
||||
if ('credit' === $direction) {
|
||||
$count = $this->deleteTransactions($account);
|
||||
if ($count > 0) {
|
||||
$this->friendlyInfo(sprintf('Removed %d old format transaction(s) for liability #%d ("%s")', $count, $account->id, $account->name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function hasBadOpening(Account $account): bool
|
||||
{
|
||||
$openingBalanceType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first();
|
||||
$liabilityType = TransactionType::whereType(TransactionType::LIABILITY_CREDIT)->first();
|
||||
$openingJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->where('transaction_journals.transaction_type_id', $openingBalanceType->id)
|
||||
->first(['transaction_journals.*']);
|
||||
if (null === $openingJournal) {
|
||||
return false;
|
||||
}
|
||||
$liabilityJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->where('transaction_journals.transaction_type_id', $liabilityType->id)
|
||||
->first(['transaction_journals.*']);
|
||||
if (null === $liabilityJournal) {
|
||||
return false;
|
||||
}
|
||||
if (!$openingJournal->date->isSameDay($liabilityJournal->date)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
@@ -90,6 +191,36 @@ class UpgradeLiabilitiesEight extends Command
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function reverseOpeningBalance(Account $account): void
|
||||
{
|
||||
$openingBalanceType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first();
|
||||
/** @var TransactionJournal $openingJournal */
|
||||
$openingJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->where('transaction_journals.transaction_type_id', $openingBalanceType->id)
|
||||
->first(['transaction_journals.*']);
|
||||
/** @var Transaction|null $source */
|
||||
$source = $openingJournal->transactions()->where('amount', '<', 0)->first();
|
||||
/** @var Transaction|null $dest */
|
||||
$dest = $openingJournal->transactions()->where('amount', '>', 0)->first();
|
||||
if ($source && $dest) {
|
||||
$sourceId = $source->account_id;
|
||||
$destId = $dest->account_id;
|
||||
$dest->account_id = $sourceId;
|
||||
$source->account_id = $destId;
|
||||
$source->save();
|
||||
$dest->save();
|
||||
|
||||
return;
|
||||
}
|
||||
Log::warning('Did not find opening balance.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $account
|
||||
*
|
||||
@@ -131,51 +262,6 @@ class UpgradeLiabilitiesEight extends Command
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function hasBadOpening(Account $account): bool
|
||||
{
|
||||
$openingBalanceType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first();
|
||||
$liabilityType = TransactionType::whereType(TransactionType::LIABILITY_CREDIT)->first();
|
||||
$openingJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->where('transaction_journals.transaction_type_id', $openingBalanceType->id)
|
||||
->first(['transaction_journals.*']);
|
||||
if (null === $openingJournal) {
|
||||
return false;
|
||||
}
|
||||
$liabilityJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->where('transaction_journals.transaction_type_id', $liabilityType->id)
|
||||
->first(['transaction_journals.*']);
|
||||
if (null === $liabilityJournal) {
|
||||
return false;
|
||||
}
|
||||
if (!$openingJournal->date->isSameDay($liabilityJournal->date)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
if (null !== $configVar) {
|
||||
return (bool)$configVar->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -183,87 +269,4 @@ class UpgradeLiabilitiesEight extends Command
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function reverseOpeningBalance(Account $account): void
|
||||
{
|
||||
$openingBalanceType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first();
|
||||
/** @var TransactionJournal $openingJournal */
|
||||
$openingJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->where('transaction_journals.transaction_type_id', $openingBalanceType->id)
|
||||
->first(['transaction_journals.*']);
|
||||
/** @var Transaction|null $source */
|
||||
$source = $openingJournal->transactions()->where('amount', '<', 0)->first();
|
||||
/** @var Transaction|null $dest */
|
||||
$dest = $openingJournal->transactions()->where('amount', '>', 0)->first();
|
||||
if ($source && $dest) {
|
||||
$sourceId = $source->account_id;
|
||||
$destId = $dest->account_id;
|
||||
$dest->account_id = $sourceId;
|
||||
$source->account_id = $destId;
|
||||
$source->save();
|
||||
$dest->save();
|
||||
|
||||
return;
|
||||
}
|
||||
Log::warning('Did not find opening balance.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
private function upgradeForUser(User $user): void
|
||||
{
|
||||
$accounts = $user->accounts()
|
||||
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||
->whereIn('account_types.type', config('firefly.valid_liabilities'))
|
||||
->get(['accounts.*']);
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$this->upgradeLiability($account);
|
||||
$service = app(CreditRecalculateService::class);
|
||||
$service->setAccount($account);
|
||||
$service->recalculate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function upgradeLiabilities(): void
|
||||
{
|
||||
$users = User::get();
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$this->upgradeForUser($user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*/
|
||||
private function upgradeLiability(Account $account): void
|
||||
{
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$repository->setUser($account->user);
|
||||
|
||||
$direction = $repository->getMetaValue($account, 'liability_direction');
|
||||
if ('credit' === $direction && $this->hasBadOpening($account)) {
|
||||
$this->deleteCreditTransaction($account);
|
||||
$this->reverseOpeningBalance($account);
|
||||
$this->line(sprintf('Corrected opening balance for liability #%d ("%s")', $account->id, $account->name));
|
||||
}
|
||||
if ('credit' === $direction) {
|
||||
$count = $this->deleteTransactions($account);
|
||||
if ($count > 0) {
|
||||
$this->line(sprintf('Removed %d old format transaction(s) for liability #%d ("%s")', $count, $account->id, $account->name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@ class UpgradeSkeleton extends Command
|
||||
{
|
||||
$start = microtime(true);
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->info('Correct: this command has already been executed.');
|
||||
$this->info('FRIENDLY This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -40,6 +40,7 @@ class RegisteredUser extends Event
|
||||
|
||||
/**
|
||||
* Create a new event instance. This event is triggered when a new user registers.
|
||||
*
|
||||
* @param User $user
|
||||
*/
|
||||
public function __construct(User $user)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user