Compare commits

...

115 Commits

Author SHA1 Message Date
James Cole
5085a384dc Update changelog. 2021-07-25 19:49:28 +02:00
James Cole
07abfd78e1 Throttle logins. Update changelog. https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-3663 2021-07-25 19:48:34 +02:00
James Cole
8b90d2297d Merge branch 'hotfix/5.5.12' into main 2021-06-03 13:01:20 +02:00
James Cole
28479ef2ed Update version. 2021-06-03 12:54:21 +02:00
James Cole
9b03ae160d Update changelog. 2021-06-03 12:54:15 +02:00
James Cole
5303321952 Fix export vulnerability, found by GitHub user @oomb and disclosed via the excellent huntr.dev platform. 2021-06-03 12:51:31 +02:00
James Cole
906fca7e9e Merge pull request #4815 from firefly-iii/dependabot/npm_and_yarn/frontend/ws-7.4.6
Bump ws from 7.4.5 to 7.4.6 in /frontend
2021-05-29 16:53:03 +02:00
dependabot[bot]
f64e1f3c1b Bump ws from 7.4.5 to 7.4.6 in /frontend
Bumps [ws](https://github.com/websockets/ws) from 7.4.5 to 7.4.6.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.4.5...7.4.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-29 14:52:33 +00:00
James Cole
c8af3684c6 Merge pull request #4812 from firefly-iii/dependabot/npm_and_yarn/ws-7.4.6
Bump ws from 7.4.5 to 7.4.6
2021-05-29 16:51:32 +02:00
dependabot[bot]
e3474bb075 Bump ws from 7.4.5 to 7.4.6
Bumps [ws](https://github.com/websockets/ws) from 7.4.5 to 7.4.6.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.4.5...7.4.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-29 13:54:19 +00:00
James Cole
8140613a29 Merge pull request #4804 from firefly-iii/dependabot/npm_and_yarn/frontend/dns-packet-1.3.4
Bump dns-packet from 1.3.1 to 1.3.4 in /frontend
2021-05-27 14:29:48 +02:00
dependabot[bot]
00d5b4d29d Bump dns-packet from 1.3.1 to 1.3.4 in /frontend
Bumps [dns-packet](https://github.com/mafintosh/dns-packet) from 1.3.1 to 1.3.4.
- [Release notes](https://github.com/mafintosh/dns-packet/releases)
- [Changelog](https://github.com/mafintosh/dns-packet/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mafintosh/dns-packet/compare/v1.3.1...v1.3.4)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-27 11:37:49 +00:00
James Cole
251a347a42 Merge pull request #4803 from firefly-iii/dependabot/npm_and_yarn/dns-packet-1.3.4
Bump dns-packet from 1.3.1 to 1.3.4
2021-05-27 13:37:16 +02:00
dependabot[bot]
7fb090392f Bump dns-packet from 1.3.1 to 1.3.4
Bumps [dns-packet](https://github.com/mafintosh/dns-packet) from 1.3.1 to 1.3.4.
- [Release notes](https://github.com/mafintosh/dns-packet/releases)
- [Changelog](https://github.com/mafintosh/dns-packet/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mafintosh/dns-packet/compare/v1.3.1...v1.3.4)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-27 11:34:37 +00:00
James Cole
13b96bb3b6 Merge branch 'release/5.5.11' into main 2021-05-08 21:35:16 +02:00
James Cole
c318790bbf Update packages. 2021-05-08 21:28:24 +02:00
James Cole
0d969dd42a Rebuild all for new release. 2021-05-08 21:26:50 +02:00
James Cole
4e70601d19 Fix reference. 2021-05-08 21:19:26 +02:00
James Cole
e4a6ff293a Temp version of budget view. 2021-05-08 21:19:04 +02:00
James Cole
d0edae76f2 Build v2 with some fixes for #4735 2021-05-08 18:23:50 +02:00
James Cole
9eaacf30ad Fix #4739 2021-05-08 17:56:36 +02:00
James Cole
40111ed25e Expand API point. 2021-05-07 19:32:08 +02:00
James Cole
506f972c75 Merge branch 'main' into develop 2021-05-04 19:00:52 +02:00
James Cole
843f3c9b45 Switch to another host in the default configuration. 2021-05-04 19:00:33 +02:00
James Cole
6c2c2ca41f Changelog. Temp version. 2021-05-04 18:58:55 +02:00
James Cole
8315734471 Update schedule to daily. 2021-05-03 08:08:47 +02:00
James Cole
2d9b2ab379 Merge pull request #4740 from firefly-iii/dependabot/composer/develop/league/commonmark-1.6.0
Bump league/commonmark from 1.5.8 to 1.6.0
2021-05-03 07:24:07 +02:00
dependabot[bot]
45f20509f4 Bump league/commonmark from 1.5.8 to 1.6.0
Bumps [league/commonmark](https://github.com/thephpleague/commonmark) from 1.5.8 to 1.6.0.
- [Release notes](https://github.com/thephpleague/commonmark/releases)
- [Changelog](https://github.com/thephpleague/commonmark/blob/latest/CHANGELOG-1.x.md)
- [Commits](https://github.com/thephpleague/commonmark/compare/1.5.8...1.6.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-03 05:06:24 +00:00
James Cole
c5b9c79b82 Rebuild frontend. 2021-05-02 15:19:09 +02:00
James Cole
6a196884e5 Merge branch 'main' into develop 2021-05-02 15:13:37 +02:00
James Cole
1d9431795b Add feedback. 2021-05-02 15:12:53 +02:00
James Cole
b3074b2f6a Fix run. 2021-05-02 15:03:56 +02:00
James Cole
3be65ff806 Add manual trigger. 2021-05-02 15:03:09 +02:00
James Cole
8c0005e460 Remove old lock file. 2021-05-02 15:00:56 +02:00
James Cole
c7128dedf2 Lock app. 2021-05-02 15:00:40 +02:00
James Cole
e5736c822d Warn, not info. 2021-05-02 06:40:36 +02:00
James Cole
8f6655f85e Rebuild front 2021-05-01 20:24:36 +02:00
James Cole
dbe827e3c5 Various strict code things. 2021-05-01 20:04:58 +02:00
James Cole
5a7f933a5c Possible fix for #4734 2021-05-01 18:55:49 +02:00
James Cole
54d5f9a9c3 Fix #4707 and fix #4732 2021-05-01 18:54:11 +02:00
James Cole
cc682485fc Fix #4730 2021-05-01 17:59:36 +02:00
James Cole
f8cb8967d9 Fix #4729 2021-05-01 17:52:01 +02:00
James Cole
0480db10ac Merge tag '5.5.10' into develop
5.5.10
2021-05-01 11:40:35 +02:00
James Cole
f52c6f7b00 Merge branch 'release/5.5.10' into main 2021-05-01 11:40:34 +02:00
James Cole
7d1f5f5257 Cast to int 2021-05-01 11:34:11 +02:00
James Cole
ea0942b7fe Update for 5.5.10 2021-05-01 09:47:21 +02:00
James Cole
3298f2d815 Form changes. 2021-05-01 08:47:20 +02:00
James Cole
fae5cdae50 Fix issue in link. 2021-05-01 08:36:19 +02:00
James Cole
8ffe08bfb9 Add more info. 2021-05-01 07:14:43 +02:00
James Cole
1c2b14868b Skip if null 2021-05-01 07:07:32 +02:00
James Cole
7775a0141b Skips pgsql for some reason. 2021-05-01 06:53:42 +02:00
James Cole
831272d971 Repair sequences for #4545 2021-05-01 06:46:36 +02:00
James Cole
c8c4507d4b Merge branch 'main' into develop 2021-04-30 17:58:22 +02:00
James Cole
6a74cd21fb Merge pull request #4727 from firefly-iii/dependabot/composer/laravel/framework-8.40.0
Bump laravel/framework from 8.38.0 to 8.40.0
2021-04-30 06:58:07 +02:00
dependabot[bot]
eb5eca9fa5 Bump laravel/framework from 8.38.0 to 8.40.0
Bumps [laravel/framework](https://github.com/laravel/framework) from 8.38.0 to 8.40.0.
- [Release notes](https://github.com/laravel/framework/releases)
- [Changelog](https://github.com/laravel/framework/blob/8.x/CHANGELOG-8.x.md)
- [Commits](https://github.com/laravel/framework/compare/v8.38.0...v8.40.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-30 04:10:52 +00:00
James Cole
0e702fb334 Merge pull request #4725 from firefly-iii/dependabot/composer/composer/composer-2.0.13
Bump composer/composer from 2.0.12 to 2.0.13
2021-04-30 06:09:41 +02:00
dependabot[bot]
626f97cd65 Bump composer/composer from 2.0.12 to 2.0.13
Bumps [composer/composer](https://github.com/composer/composer) from 2.0.12 to 2.0.13.
- [Release notes](https://github.com/composer/composer/releases)
- [Changelog](https://github.com/composer/composer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/composer/composer/compare/2.0.12...2.0.13)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-29 21:54:42 +00:00
James Cole
f03b0569cf Update packages 2021-04-27 06:50:10 +02:00
James Cole
82f3a37a3e Clean up some code. 2021-04-27 06:45:11 +02:00
James Cole
112a27dbd9 Clean up some code. 2021-04-27 06:42:07 +02:00
James Cole
366eca3173 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2021-04-27 06:24:49 +02:00
James Cole
fab0c5bfd9 Small code cleanup. 2021-04-27 06:23:16 +02:00
James Cole
dc3b923258 Merge pull request #4716 from rubenvanerk/patch-1
Fix link in index
2021-04-26 20:29:43 +02:00
Ruben van Erk
3622d3234a Fix link in index 2021-04-26 20:08:35 +02:00
James Cole
cf2c99d986 Merge pull request #4711 from firefly-iii/dependabot/composer/develop/vimeo/psalm-4.7.1
Bump vimeo/psalm from 4.7.0 to 4.7.1
2021-04-26 08:19:45 +02:00
James Cole
672add8668 Merge pull request #4712 from firefly-iii/dependabot/composer/develop/filp/whoops-2.12.1
Bump filp/whoops from 2.12.0 to 2.12.1
2021-04-26 08:14:29 +02:00
James Cole
d5826861a0 Merge pull request #4713 from firefly-iii/dependabot/npm_and_yarn/frontend/develop/chart.js-3.2.0
Bump chart.js from 3.1.1 to 3.2.0 in /frontend
2021-04-26 08:10:46 +02:00
dependabot[bot]
a8494bd6f0 Bump chart.js from 3.1.1 to 3.2.0 in /frontend
Bumps [chart.js](https://github.com/chartjs/Chart.js) from 3.1.1 to 3.2.0.
- [Release notes](https://github.com/chartjs/Chart.js/releases)
- [Commits](https://github.com/chartjs/Chart.js/compare/v3.1.1...v3.2.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-26 05:32:50 +00:00
dependabot[bot]
7fa0c14f8c Bump filp/whoops from 2.12.0 to 2.12.1
Bumps [filp/whoops](https://github.com/filp/whoops) from 2.12.0 to 2.12.1.
- [Release notes](https://github.com/filp/whoops/releases)
- [Changelog](https://github.com/filp/whoops/blob/master/CHANGELOG.md)
- [Commits](https://github.com/filp/whoops/compare/2.12.0...2.12.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-26 05:09:07 +00:00
dependabot[bot]
9bd22d4252 Bump vimeo/psalm from 4.7.0 to 4.7.1
Bumps [vimeo/psalm](https://github.com/vimeo/psalm) from 4.7.0 to 4.7.1.
- [Release notes](https://github.com/vimeo/psalm/releases)
- [Commits](https://github.com/vimeo/psalm/compare/4.7.0...4.7.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-26 05:08:38 +00:00
James Cole
b78f5bd54a Fix #4708 2021-04-26 06:39:12 +02:00
James Cole
0949a264b8 Fix #4710 2021-04-26 06:18:30 +02:00
James Cole
97a0110931 Merge tag '5.5.9' into develop
5.5.9
2021-04-24 21:37:43 +02:00
James Cole
83ec935ac3 Merge branch 'release/5.5.9' into main 2021-04-24 21:37:42 +02:00
James Cole
e46da428eb Update meta files. 2021-04-24 21:27:36 +02:00
James Cole
26089f992e Remove debug code. 2021-04-24 21:18:15 +02:00
James Cole
13233e0893 Fix #4706 2021-04-24 21:08:46 +02:00
James Cole
da934575a6 Disable unused method. 2021-04-24 06:09:31 +02:00
James Cole
6feb04c800 Rebuild various components. 2021-04-24 05:34:24 +02:00
James Cole
d56f97e86d Files for 5.5.9 2021-04-23 18:48:15 +02:00
James Cole
a78614f198 Align top boxes 2021-04-23 06:15:24 +02:00
James Cole
2b25631611 Fix budget API call 2021-04-23 06:03:06 +02:00
James Cole
98613b5ea6 Update composer 2021-04-22 19:56:52 +02:00
James Cole
105ecc4452 Add debug 2021-04-22 19:56:42 +02:00
James Cole
418a682f7e Remove bad account types. 2021-04-22 19:55:43 +02:00
James Cole
215e5d42a7 Rebuild frontend. 2021-04-22 18:41:44 +02:00
James Cole
21d71bd03c Include user in preference 2021-04-22 18:34:08 +02:00
James Cole
3184a8536e Add another command. 2021-04-22 18:33:53 +02:00
James Cole
ddf9938c00 Fix issue with empty box. 2021-04-22 18:33:37 +02:00
James Cole
388d19b99c Fix #4697 2021-04-22 06:18:46 +02:00
James Cole
ec03017eca Fix possible issue with tag display in bulk editor. 2021-04-21 06:23:12 +02:00
James Cole
0b920b5c64 Catch obscure DB errors. 2021-04-20 07:55:24 +02:00
James Cole
6e0be9a6a2 Rebuild frontend. 2021-04-19 06:49:58 +02:00
James Cole
f1798a1c97 Regenerate frontend. 2021-04-18 12:29:42 +02:00
James Cole
57cb428105 Merge tag '5.5.8' into develop
5.5.8
2021-04-18 07:28:13 +02:00
James Cole
adc52f7b63 Merge branch 'release/5.5.8' into main 2021-04-18 07:28:12 +02:00
James Cole
f8cf02bda1 Make sure debug does timezone right. 2021-04-18 07:28:00 +02:00
James Cole
4b14ad9770 Update packages. 2021-04-18 07:23:05 +02:00
James Cole
2a630b0a50 Rebuild frontend, new translations. 2021-04-18 07:22:10 +02:00
James Cole
1a311664e8 Rebuild frontend. 2021-04-17 20:53:42 +02:00
James Cole
0a4e3edf43 Rebuild frontend, add version to JS calls against caching. 2021-04-17 15:57:51 +02:00
James Cole
9b0b80d1d4 Various fixes for v2 issues in 5.5.7 2021-04-17 15:53:11 +02:00
James Cole
dcd123a9ec Fix #4668 2021-04-17 05:39:56 +02:00
James Cole
e06452d97c Fix #4664 2021-04-16 05:55:23 +02:00
James Cole
e5b4e7afe0 Recreate frontend for correct date #4660 2021-04-15 06:39:07 +02:00
James Cole
0ea22269a0 Fix #4663 2021-04-15 06:15:17 +02:00
James Cole
577dcfa938 Rebuild frontend. 2021-04-14 20:07:39 +02:00
James Cole
99f08da4df Fix #4656 2021-04-13 19:01:43 +02:00
James Cole
2f9724e7ca Fix nullpointers. 2021-04-13 06:26:51 +02:00
James Cole
8cda89569c Update .env.example 2021-04-12 15:50:13 +00:00
James Cole
5421e30293 Add text #4637 2021-04-12 17:25:40 +02:00
James Cole
a2deff0f7a Whoops 2021-04-12 15:28:06 +02:00
James Cole
bff661fe69 Merge branch 'main' into develop 2021-04-12 14:10:52 +02:00
James Cole
f6b890e284 Fix #4652 2021-04-12 14:09:17 +02:00
James Cole
acb8fa522b Merge tag '5.5.7' into develop
5.5.7
2021-04-12 06:30:46 +02:00
276 changed files with 4370 additions and 3025 deletions

View File

@@ -30,4 +30,4 @@ parameters:
- ../bootstrap/app.php
# The level 8 is the highest level. original was 5
level: 2
level: 3

View File

@@ -64,7 +64,7 @@ AUDIT_LOG_LEVEL=info
# Use "mysql" for MySQL and MariaDB.
# Use "sqlite" for SQLite.
DB_CONNECTION=mysql
DB_HOST=fireflyiiidb
DB_HOST=db
DB_PORT=3306
DB_DATABASE=firefly
DB_USERNAME=firefly
@@ -175,6 +175,10 @@ MAP_DEFAULT_ZOOM=6
# For full instructions on these settings please visit:
# https://docs.firefly-iii.org/advanced-installation/authentication
# If you use Docker or similar, you can set this variable from a file by appending it with _FILE
#
# If you enable 'ldap' AND you run Docker, the Docker image will contact packagist.org
# This is necessary to download the required packages.
#
LOGIN_PROVIDER=eloquent
# It's also possible to change the way users are authenticated. You could use Authelia for example.

35
.github/lock.yml vendored
View File

@@ -1,35 +0,0 @@
# Configuration for Lock Threads - https://github.com/dessant/lock-threads
# Number of days of inactivity before a closed issue or pull request is locked
daysUntilLock: 90
# Skip issues and pull requests created before a given timestamp. Timestamp must
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
skipCreatedBefore: false
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
exemptLabels: []
# Label to add before locking, such as `outdated`. Set to `false` to disable
lockLabel: false
# Comment to post before locking. Set to `false` to disable
lockComment: false
# Assign `resolved` as the reason for locking. Set to `false` to disable
setLockReason: true
# Limit to only `issues` or `pulls`
# only: issues
# Optionally, specify configuration settings just for `issues` or `pulls`
# issues:
# exemptLabels:
# - help-wanted
# lockLabel: outdated
# pulls:
# daysUntilLock: 30
# Repository to extend settings from
# _extends: repo

19
.github/workflows/lock.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: Lock old issues
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * *'
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v2
with:
github-token: ${{ github.token }}
issue-lock-inactive-days: '90'
issue-lock-comment: >
This issue has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new issue for related bugs.

View File

@@ -1,24 +1,5 @@
<?php
declare(strict_types=1);
/*
* AccountController.php
* Copyright (c) 2021 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\Api\V1\Controllers\Data\Bulk;

View File

@@ -1,24 +1,5 @@
<?php
declare(strict_types=1);
/*
* MoveTransactionsRequest.php
* Copyright (c) 2021 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\Api\V1\Requests\Data\Bulk;

View File

@@ -76,8 +76,8 @@ class StoreRequest extends FormRequest
'currency_code' => 'exists:transaction_currencies,code',
// auto budget info
'auto_budget_type' => 'in:reset,rollover,none',
'auto_budget_amount' => 'min:0|max:1000000000',
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
'auto_budget_amount' => 'numeric|min:0|max:1000000000|required_if:auto_budget_type,reset|required_if:auto_budget_type,rollover',
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly|required_if:auto_budget_type,reset|required_if:auto_budget_type,rollover',
];
}

View File

@@ -76,6 +76,7 @@ class CorrectDatabase extends Command
'firefly-iii:fix-recurring-transactions',
'firefly-iii:restore-oauth-keys',
'firefly-iii:fix-transaction-types',
'firefly-iii:fix-frontpage-accounts'
];
foreach ($commands as $command) {
$this->line(sprintf('Now executing %s', $command));

View File

@@ -71,6 +71,7 @@ class DeleteEmptyJournals extends Command
->groupBy('transactions.transaction_journal_id')
->get([DB::raw('COUNT(transactions.transaction_journal_id) as the_count'), 'transaction_journal_id']);
$total = 0;
/** @var Transaction $row */
foreach ($set as $row) {
$count = (int)$row->the_count;
if (1 === $count % 2) {

View File

@@ -0,0 +1,106 @@
<?php
/*
* FixFrontpageAccounts.php
* Copyright (c) 2021 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Preference;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\User;
use Illuminate\Console\Command;
/**
* Class FixFrontpageAccounts
*/
class FixFrontpageAccounts extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Fixes a preference that may include deleted accounts or accounts of another type.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:fix-frontpage-accounts';
/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
$start = microtime(true);
$users = User::get();
/** @var User $user */
foreach ($users as $user) {
$preference = Preferences::getForUser($user, 'frontPageAccounts', null);
if (null !== $preference) {
$this->fixPreference($preference);
}
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verifying account preferences took %s seconds', $end));
return 0;
}
/**
* @param Preference $preference
*/
private function fixPreference(Preference $preference): void
{
$fixed = [];
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
if (null === $preference->user) {
return;
}
$repository->setUser($preference->user);
$data = $preference->data;
if (is_array($data)) {
/** @var string $accountId */
foreach ($data as $accountId) {
$accountId = (int)$accountId;
$account = $repository->findNull($accountId);
if (null !== $account) {
if (
in_array($account->accountType->type, [AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE], true)
&& true === $account->active
) {
$fixed[] = $account->id;
continue;
}
}
}
}
Preferences::setForUser($preference->user, 'frontPageAccounts', $fixed);
}
}

View File

@@ -60,6 +60,7 @@ class FixGroupAccounts extends Command
$res = TransactionJournal
::groupBy('transaction_group_id')
->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]);
/** @var TransactionJournal $journal */
foreach ($res as $journal) {
if ((int)$journal->the_count > 1) {
$groups[] = (int)$journal->transaction_group_id;

View File

@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction;
use DB;
use Illuminate\Console\Command;
/**
* Class FixPostgresSequences
*/
class FixPostgresSequences extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Fixes issues with PostgreSQL sequences.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:fix-pgsql-sequences';
/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
if (DB::connection()->getName() !== 'pgsql') {
$this->info('Command executed successfully.');
return 0;
}
$this->line('Going to verify PostgreSQL table sequences.');
$tablesToCheck = [
'2fa_tokens',
'account_meta',
'account_types',
'accounts',
'attachments',
'auto_budgets',
'available_budgets',
'bills',
'budget_limits',
'budget_transaction',
'budget_transaction_journal',
'budgets',
'categories',
'category_transaction',
'category_transaction_journal',
'configuration',
'currency_exchange_rates',
'export_jobs',
'failed_jobs',
'group_journals',
'import_jobs',
'jobs',
'journal_links',
'journal_meta',
'limit_repetitions',
'link_types',
'locations',
'migrations',
'notes',
'oauth_clients',
'oauth_personal_access_clients',
'object_groups',
'permissions',
'piggy_bank_events',
'piggy_bank_repetitions',
'piggy_banks',
'preferences',
'recurrences',
'recurrences_meta',
'recurrences_repetitions',
'recurrences_transactions',
'roles',
'rt_meta',
'rule_actions',
'rule_groups',
'rule_triggers',
'rules',
'tag_transaction_journal',
'tags',
'telemetry',
'transaction_currencies',
'transaction_groups',
'transaction_journals',
'transaction_types',
'transactions',
'users',
'webhook_attempts',
'webhook_messages',
'webhooks',
];
foreach ($tablesToCheck as $tableToCheck) {
$this->info(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));
continue;
}
if ($nextId->nextval < $highestId->max) {
DB::select(sprintf('SELECT setval(\'%s_id_seq\', %d)', $tableToCheck, $highestId->max));
$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));
}
if ($nextId->nextval <= $highestId->max) {
$this->warn(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));
}
}
return 0;
}
}

View File

@@ -177,7 +177,7 @@ class DecryptDatabase extends Command
/**
* Tries to decrypt data. Will only throw an exception when the MAC is invalid.
*
* @param $value
* @param mixed $value
*
* @return string
* @throws FireflyException

View File

@@ -28,6 +28,7 @@ use Carbon\Carbon;
use Exception;
use FireflyIII\Console\Commands\VerifiesAccessToken;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
@@ -162,9 +163,8 @@ class ExportData extends Command
*/
private function parseOptions(): array
{
$start = $this->getDateParameter('start');
$end = $this->getDateParameter('end');
exit;
$start = $this->getDateParameter('start');
$end = $this->getDateParameter('end');
$accounts = $this->getAccountsParameter();
$export = $this->getExportDirectory();
@@ -242,7 +242,7 @@ class ExportData extends Command
$accounts = $this->accountRepository->getAccountsByType($types);
}
// filter accounts,
/** @var AccountType $account */
/** @var Account $account */
foreach ($accounts as $account) {
if (in_array($account->accountType->type, $types, true)) {
$final->push($account);

View File

@@ -64,7 +64,7 @@ class BackToJournals extends Command
$this->error('Please run firefly-iii:migrate-to-groups first.');
}
if ($this->isExecuted() && true !== $this->option('force')) {
$this->info('This command has already been executed.');
$this->warn('This command has already been executed.');
return 0;
}
@@ -144,8 +144,7 @@ class BackToJournals extends Command
$chunks = array_chunk($transactions, 500);
foreach ($chunks as $chunk) {
$set = DB::table('transactions')->whereIn('transactions.id', $chunk)
->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray();
$set = DB::table('transactions')->whereIn('transactions.id', $chunk)->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray();
$array = array_merge($array, $set);
}

View File

@@ -414,7 +414,7 @@ class MigrateToGroups extends Command
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 $journal */
/** @var array $array */
foreach ($orphanedJournals as $array) {
$this->giveGroup($array);
}

View File

@@ -95,6 +95,7 @@ class UpgradeDatabase extends Command
'firefly-iii:fix-recurring-transactions',
'firefly-iii:unify-group-accounts',
'firefly-iii:fix-transaction-types',
'firefly-iii:fix-frontpage-accounts',
// two report commands
'firefly-iii:report-empty-objects',
@@ -129,6 +130,11 @@ class UpgradeDatabase extends Command
$result = Artisan::output();
echo $result;
$this->line('Fix PostgreSQL sequences.');
Artisan::call('firefly-iii:fix-pgsql-sequences');
$result = Artisan::output();
echo $result;
$this->line('Now decrypting the database (if necessary)...');
Artisan::call('firefly-iii:decrypt-all');
$result = Artisan::output();

View File

@@ -48,38 +48,38 @@ class GracefulNotFoundHandler extends ExceptionHandler
* Render an exception into an HTTP response.
*
* @param Request $request
* @param Exception $exception
* @param Throwable $e
*
* @return mixed
* @throws Exception
* @throws Throwable
*/
public function render($request, Throwable $exception)
public function render($request, Throwable $e)
{
$route = $request->route();
if (null === $route) {
return parent::render($request, $exception);
return parent::render($request, $e);
}
$name = $route->getName();
if (!auth()->check()) {
return parent::render($request, $exception);
return parent::render($request, $e);
}
switch ($name) {
default:
Log::warning(sprintf('GracefulNotFoundHandler cannot handle route with name "%s"', $name));
return parent::render($request, $exception);
return parent::render($request, $e);
case 'accounts.show':
case 'accounts.show.all':
return $this->handleAccount($request, $exception);
return $this->handleAccount($request, $e);
case 'transactions.show':
return $this->handleGroup($request, $exception);
return $this->handleGroup($request, $e);
case 'attachments.show':
case 'attachments.edit':
case 'attachments.download':
case 'attachments.view':
// redirect to original attachment holder.
return $this->handleAttachment($request, $exception);
return $this->handleAttachment($request, $e);
break;
case 'bills.show':
$request->session()->reflash();
@@ -131,7 +131,7 @@ class GracefulNotFoundHandler extends ExceptionHandler
return redirect(route('index'));
}
return parent::render($request, $exception);
return parent::render($request, $e);
}
}
@@ -141,7 +141,7 @@ class GracefulNotFoundHandler extends ExceptionHandler
* @param Throwable $exception
*
* @return Redirector|Response
* @throws Exception
* @throws Throwable
*/
private function handleAccount(Request $request, Throwable $exception)
{
@@ -165,11 +165,11 @@ class GracefulNotFoundHandler extends ExceptionHandler
}
/**
* @param Throwable $request
* @param Exception $exception
* @param Request $request
* @param Throwable $exception
*
* @return RedirectResponse|\Illuminate\Http\Response|Redirector|Response
* @throws Exception
* @throws Throwable
*/
private function handleGroup(Request $request, Throwable $exception)
{
@@ -209,7 +209,7 @@ class GracefulNotFoundHandler extends ExceptionHandler
* @param Throwable $exception
*
* @return RedirectResponse|Redirector|Response
* @throws Exception
* @throws Throwable
*/
private function handleAttachment(Request $request, Throwable $exception)
{

View File

@@ -46,7 +46,7 @@ class TransactionCurrencyFactory
public function create(array $data): TransactionCurrency
{
try {
/** @var TransactionCurrency $currency */
/** @var TransactionCurrency $result */
$result = TransactionCurrency::create(
[
'name' => $data['name'],

View File

@@ -543,7 +543,7 @@ class TransactionJournalFactory
'data' => (string)($data[$field] ?? ''),
];
Log::debug(sprintf('Going to store meta-field "%s", with value "%s".', $set['name'], $set['data']));
//Log::debug(sprintf('Going to store meta-field "%s", with value "%s".', $set['name'], $set['data']));
/** @var TransactionJournalMetaFactory $factory */
$factory = app(TransactionJournalMetaFactory::class);

View File

@@ -41,7 +41,7 @@ class TransactionJournalMetaFactory
*/
public function updateOrCreate(array $data): ?TransactionJournalMeta
{
Log::debug('In updateOrCreate()');
//Log::debug('In updateOrCreate()');
$value = $data['data'];
/** @var TransactionJournalMeta $entry */
$entry = $data['journal']->transactionJournalMeta()->where('name', $data['name'])->first();
@@ -61,7 +61,7 @@ class TransactionJournalMetaFactory
$value = $data['data']->toW3cString();
}
if ('' === (string)$value) {
Log::debug('Is an empty string.');
// Log::debug('Is an empty string.');
// don't store blank strings.
if (null !== $entry) {
Log::debug('Will not store empty strings, delete meta value');

View File

@@ -101,7 +101,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface
*/
private function getWebhooks(): Collection
{
return $this->user->webhooks()->where('active', 1)->where('trigger', $this->trigger)->get(['webhooks.*']);
return $this->user->webhooks()->where('active', true)->where('trigger', $this->trigger)->get(['webhooks.*']);
}
/**

View File

@@ -207,7 +207,7 @@ trait MetaCollection
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
}
$this->query->where('journal_meta.name', '=', 'external_id');
$this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $externalId));
$this->query->where('journal_meta.data', '=', sprintf('%s', $externalId));
return $this;
}

View File

@@ -497,7 +497,7 @@ class GroupCollector implements GroupCollectorInterface
->where(
static function (EloquentBuilder $q1) {
$q1->where('attachments.attachable_type', TransactionJournal::class);
$q1->where('attachments.uploaded', 1);
$q1->where('attachments.uploaded', true);
$q1->orWhereNull('attachments.attachable_type');
}
);
@@ -699,7 +699,7 @@ class GroupCollector implements GroupCollectorInterface
$result = $this->convertToInteger($result);
$result['reconciled'] = 1 === (int)$result['reconciled'];
if (array_key_exists('tag_id', $result)) { // assume the other fields are present as well.
if (array_key_exists('tag_id', $result) && null !== $result['tag_id']) { // assume the other fields are present as well.
$tagId = (int)$augumentedJournal['tag_id'];
$tagDate = null;
try {

View File

@@ -549,8 +549,6 @@ interface GroupCollectorInterface
public function withoutCategory(): GroupCollectorInterface;
/**
* @param string $value
*
* @return GroupCollectorInterface
*/
public function withoutNotes(): GroupCollectorInterface;

View File

@@ -91,7 +91,7 @@ class ReportHelper implements ReportHelperInterface
'paid_moments' => [],
];
/** @var Carbon $start */
/** @var Carbon $expectedStart */
foreach ($expectedDates as $expectedStart) {
$expectedEnd = app('navigation')->endOfX($expectedStart, $bill->repeat_freq, null);

View File

@@ -39,9 +39,7 @@ use Log;
*/
class LinkController extends Controller
{
/** @var LinkTypeRepositoryInterface */
private $repository;
private LinkTypeRepositoryInterface $repository;
/**
* LinkController constructor.

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Admin;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Update\UpdateTrait;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
@@ -62,8 +63,7 @@ class UpdateController extends Controller
* Show page with update options.
*
* @return Factory|View
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws FireflyException
*/
public function index()
{

View File

@@ -29,6 +29,7 @@ use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Providers\RouteServiceProvider;
use Illuminate\Contracts\View\Factory;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
@@ -48,7 +49,7 @@ use Symfony\Component\HttpFoundation\Response;
*/
class LoginController extends Controller
{
use AuthenticatesUsers;
use AuthenticatesUsers, ThrottlesLogins;
/**
* Where to redirect users after login.

View File

@@ -59,7 +59,7 @@ class TwoFactorController extends Controller
{
/** @var array $mfaHistory */
$mfaHistory = Preferences::get('mfa_history', [])->data;
$mfaCode = $request->get('one_time_password');
$mfaCode = (string)$request->get('one_time_password');
// is in history? then refuse to use it.
if ($this->inMFAHistory($mfaCode, $mfaHistory)) {

View File

@@ -159,7 +159,6 @@ class IndexController extends Controller
continue;
}
/** @var TransactionCurrency $currency */
$currencyId = $bill['currency_id'];
$sums[$groupOrder][$currencyId] = $sums[$groupOrder][$currencyId] ?? [
'currency_id' => $currencyId,

View File

@@ -135,16 +135,21 @@ class BudgetLimitController extends Controller
if (null === $currency || null === $budget) {
throw new FireflyException('No valid currency or budget.');
}
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
$amount = (string)$request->get('amount');
$start->startOfDay();
$end->startOfDay();
if ('' === $amount) {
return response()->json([]);
}
Log::debug(sprintf('Start: %s, end: %s', $start->format('Y-m-d'), $end->format('Y-m-d')));
$limit = $this->blRepository->find($budget, $currency, $start, $end);
if (null !== $limit) {
$limit->amount = $request->get('amount');
$limit->amount = $amount;
$limit->save();
}
if (null === $limit) {
@@ -154,7 +159,7 @@ class BudgetLimitController extends Controller
'currency_id' => (int)$request->get('transaction_currency_id'),
'start_date' => $start,
'end_date' => $end,
'amount' => $request->get('amount'),
'amount' => $amount,
]
);
}
@@ -176,7 +181,7 @@ class BudgetLimitController extends Controller
return response()->json($array);
}
return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')]));
return response()->json([]);
}
/**
@@ -187,7 +192,10 @@ class BudgetLimitController extends Controller
*/
public function update(Request $request, BudgetLimit $budgetLimit): JsonResponse
{
$amount = $request->get('amount');
$amount = (string)$request->get('amount');
if ('' === $amount) {
$amount = '0';
}
$limit = $this->blRepository->update($budgetLimit, ['amount' => $amount]);
$array = $limit->toArray();

View File

@@ -144,7 +144,7 @@ class ShowController extends Controller
*/
public function show(Request $request, Budget $budget)
{
/** @var Carbon $start */
/** @var Carbon $allStart */
$allStart = session('first', Carbon::now()->startOfYear());
$allEnd = today();
$page = (int)$request->get('page');

View File

@@ -108,7 +108,7 @@ class BudgetController extends Controller
$currencies = [];
$defaultEntries = [];
while ($end >= $loopStart) {
/** @var Carbon $currentEnd */
/** @var Carbon $loopEnd */
$loopEnd = app('navigation')->endOfPeriod($loopStart, $step);
$spent = $this->opsRepository->sumExpenses($loopStart, $loopEnd, null, $collection);
$label = trim(app('navigation')->periodShow($loopStart, $step));

View File

@@ -76,7 +76,7 @@ class CategoryController extends Controller
$cache->addProperty('chart.category.all');
$cache->addProperty($category->id);
if ($cache->has()) {
return response()->json($cache->get());
return response()->json($cache->get());
}
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
@@ -125,7 +125,7 @@ class CategoryController extends Controller
$cache->addProperty($end);
$cache->addProperty('chart.category.frontpage');
if ($cache->has()) {
return response()->json($cache->get());
return response()->json($cache->get());
}
$frontPageGenerator = new FrontpageChartGenerator($start, $end);
@@ -270,7 +270,7 @@ class CategoryController extends Controller
$cache->addProperty('chart.category.period.no-cat');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return response()->json($cache->get());
return response()->json($cache->get());
}
$data = $this->reportPeriodChart($accounts, $start, $end, null);
@@ -283,8 +283,8 @@ class CategoryController extends Controller
* Chart for a specific period.
* TODO test method, for category refactor.
*
* @param Category $category
* @param $date
* @param Category $category
* @param Carbon $date
*
* @return JsonResponse
*/
@@ -294,7 +294,7 @@ class CategoryController extends Controller
$start = app('navigation')->startOfPeriod($date, $range);
$end = session()->get('end');
if ($end < $start) {
[$end, $start] = [$start, $end];
[$end, $start] = [$start, $end];
}
$cache = new CacheProperties;
@@ -303,7 +303,7 @@ class CategoryController extends Controller
$cache->addProperty($category->id);
$cache->addProperty('chart.category.period-chart');
if ($cache->has()) {
return response()->json($cache->get());
return response()->json($cache->get());
}
/** @var WholePeriodChartGenerator $chartGenerator */

View File

@@ -140,10 +140,8 @@ class CategoryReportController extends Controller
* @param Collection $categories
* @param Carbon $start
* @param Carbon $end
* @param string $others
*
* @return JsonResponse
*
*/
public function categoryIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse
{

View File

@@ -80,9 +80,9 @@ class PiggyBankController extends Controller
$locale = app('steam')->getLocale();
// get first event or start date of piggy bank or today
$startDate = $piggyBank->start_date ?? today(config('app.timezone'));
$startDate = $piggyBank->startdate ?? today(config('app.timezone'));
/** @var PiggyBankEvent $first */
/** @var PiggyBankEvent $firstEvent */
$firstEvent = $set->first();
$firstDate = null === $firstEvent ? new Carbon : $firstEvent->date;

View File

@@ -52,12 +52,10 @@ class TransactionController extends Controller
}
/**
* @param string $objectType
* @param Carbon $start
* @param Carbon $end
*
* @return JsonResponse
* @throws FireflyException
*/
public function budgets(Carbon $start, Carbon $end)
{

View File

@@ -32,7 +32,7 @@ class HelpController extends Controller
/**
* Show help for a route.
*
* @param $route
* @param string $route
*
* @return JsonResponse
*/

View File

@@ -30,54 +30,9 @@ use Illuminate\Http\Request;
/**
* Class AutoCompleteController.
*
* TODO autocomplete for transaction types.
*
*/
class AutoCompleteController extends Controller
{
/**
* Searches in the titles of all transaction journals.
* The result is limited to the top 15 unique results.
*
* If the query is numeric, it will append the journal with that particular ID.
*
* @param Request $request
*
* @return JsonResponse
*/
public function allJournalsWithID(Request $request): JsonResponse
{
$search = (string)$request->get('search');
/** @var JournalRepositoryInterface $repository */
$repository = app(JournalRepositoryInterface::class);
/** @var TransactionGroupRepositoryInterface $groupRepos */
$groupRepos = app(TransactionGroupRepositoryInterface::class);
$result = $repository->searchJournalDescriptions($search);
$array = [];
if (is_numeric($search)) {
// search for group, not journal.
$firstResult = $groupRepos->find((int)$search);
if (null !== $firstResult) {
// group may contain multiple journals, each a result:
foreach ($firstResult->transactionJournals as $journal) {
$array[] = $journal->toArray();
}
}
}
// if not numeric, search ahead!
// limit and unique
$limited = $result->slice(0, 15);
$array = array_merge($array, $limited->toArray());
foreach ($array as $index => $item) {
// give another key for consistency
$array[$index]['name'] = sprintf('#%d: %s', $item['transaction_group_id'], $item['description']);
}
return response()->json($array);
}
}

View File

@@ -191,7 +191,7 @@ class PreferencesController extends Controller
// same for locale:
if (!auth()->user()->hasRole('demo')) {
/** @var Preference $currentLocale */
/** @var Preference $locale */
$locale = $request->get('locale');
app('preferences')->set('locale', $locale);
}

View File

@@ -354,7 +354,7 @@ class ProfileController extends Controller
$user = auth()->user();
$isInternalAuth = $this->internalAuth;
$isInternalIdentity = $this->internalIdentity;
$count = DB::table('oauth_clients')->where('personal_access_client', 1)->whereNull('user_id')->count();
$count = DB::table('oauth_clients')->where('personal_access_client', true)->whereNull('user_id')->count();
$subTitle = $user->email;
$userId = $user->id;
$enabled2FA = null !== $user->mfa_secret;

View File

@@ -148,13 +148,13 @@ class CreateController extends Controller
RecurrenceRepetition::WEEKEND_TO_MONDAY => (string)trans('firefly.jump_to_monday'),
];
/** @var Transaction $source */
/** @var Transaction $dest */
// fill prefilled with journal info
$type = strtolower($journal->transactionType->type);
/** @var Transaction $source */
$source = $journal->transactions()->where('amount', '<', 0)->first();
/** @var Transaction $dest */
$dest = $journal->transactions()->where('amount', '>', 0)->first();
$category = $journal->categories()->first() ? $journal->categories()->first()->name : '';
$budget = $journal->budgets()->first() ? $journal->budgets()->first()->id : 0;

View File

@@ -122,7 +122,7 @@ class TagController extends Controller
foreach ($earned as $currency) {
$currencyId = $currency['currency_id'];
/** @var array $category */
/** @var array $tag */
foreach ($currency['tags'] as $tag) {
foreach ($tag['transaction_journals'] as $journal) {
$destinationId = $journal['destination_account_id'];

View File

@@ -73,12 +73,10 @@ class IndexController extends Controller
*/
public function index()
{
/** @var User $user */
$user = auth()->user();
$this->createDefaultRuleGroup();
$this->createDefaultRule();
$this->ruleGroupRepos->resetOrder();
$ruleGroups = $this->ruleGroupRepos->getRuleGroupsWithRules(null);
$ruleGroups = $this->ruleGroupRepos->getAllRuleGroupsWithRules(null);
return prefixView('rules.index', compact('ruleGroups'));
}

View File

@@ -158,6 +158,7 @@ class SelectController extends Controller
$rule->ruleTriggers = $triggers;
// create new rule engine:
/** @var RuleEngineInterface $newRuleEngine */
$newRuleEngine = app(RuleEngineInterface::class);
// set rules:

View File

@@ -64,6 +64,7 @@ class InstallController extends Controller
$this->upgradeCommands = [
// there are 3 initial commands
'migrate' => ['--seed' => true, '--force' => true],
'firefly-iii:fix-pgsql-sequences' => [],
'firefly-iii:decrypt-all' => [],
'firefly-iii:restore-oauth-keys' => [],
'generate-keys' => [], // an exception :(
@@ -105,6 +106,7 @@ class InstallController extends Controller
'firefly-iii:fix-recurring-transactions' => [],
'firefly-iii:unify-group-accounts' => [],
'firefly-iii:fix-transaction-types' => [],
'firefly-iii:fix-frontpage-accounts' => [],
// final command to set latest version in DB
'firefly-iii:set-latest-version' => ['--james-is-cool' => true],

View File

@@ -100,7 +100,7 @@ class CreateController extends Controller
$repository = app(AccountRepositoryInterface::class);
$cash = $repository->getCashAccount();
$preFilled = session()->has('preFilled') ? session('preFilled') : [];
$subTitle = (string)trans('breadcrumbs.create_new_transaction');
$subTitle = (string)trans(sprintf('breadcrumbs.create_%s', strtolower((string)$objectType)));
$subTitleIcon = 'fa-plus';
$optionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data;
$allowedOpposingTypes = config('firefly.allowed_opposing_types');

View File

@@ -77,6 +77,10 @@ class IndexController extends Controller
*/
public function index(Request $request, string $objectType, Carbon $start = null, Carbon $end = null)
{
if('transfers' === $objectType) {
$objectType = 'transfer';
}
$subTitleIcon = config('firefly.transactionIconsByType.' . $objectType);
$types = config('firefly.transactionTypesByType.' . $objectType);
$page = (int)$request->get('page');

View File

@@ -34,7 +34,7 @@ use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/** @var int The headers to check. */
protected $headers = Request::HEADER_X_FORWARDED_ALL;
//protected $headers = Request::HEADER_X_FORWARDED_ALL;
/**
* TrustProxies constructor.

View File

@@ -65,7 +65,7 @@ class BudgetFormStoreRequest extends FormRequest
'active' => 'numeric|between:0,1',
'auto_budget_type' => 'numeric|between:0,2',
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
'auto_budget_amount' => 'min:0|max:1000000000',
'auto_budget_amount' => 'min:0|max:1000000000|required_if:auto_budget_type,1|required_if:auto_budget_type,2',
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
];
}

View File

@@ -75,7 +75,7 @@ class BudgetFormUpdateRequest extends FormRequest
'active' => 'numeric|between:0,1',
'auto_budget_type' => 'numeric|between:0,2',
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
'auto_budget_amount' => 'min:0|max:1000000000',
'auto_budget_amount' => 'min:0|max:1000000000|required_if:auto_budget_type,1|required_if:auto_budget_type,2',
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
];
}

View File

@@ -22,6 +22,7 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Carbon\Carbon;
use Eloquent;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
@@ -87,6 +88,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @method static Builder|Account withTrashed()
* @method static Builder|Account withoutTrashed()
* @mixin Eloquent
* @property Carbon $lastActivityDate
*/
class Account extends Model
{

View File

@@ -74,6 +74,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @method static Builder|Budget withTrashed()
* @method static Builder|Budget withoutTrashed()
* @mixin Eloquent
* @property string $email
*/
class Budget extends Model
{

View File

@@ -44,6 +44,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property int $user_id
* @property string $name
* @property Carbon $lastActivity
* @property bool $encrypted
* @property-read Collection|\FireflyIII\Models\Attachment[] $attachments
* @property-read int|null $attachments_count

View File

@@ -40,6 +40,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @property string $name
* @property string $outward
* @property string $inward
* @property int $journalCount
* @property bool $editable
* @property-read Collection|\FireflyIII\Models\TransactionJournalLink[] $transactionJournalLinks
* @property-read int|null $transaction_journal_links_count

View File

@@ -22,7 +22,6 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Carbon\Carbon;
use Eloquent;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder;
@@ -33,13 +32,13 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* FireflyIII\Models\Preference
*
* @property int $id
* @property int $id
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property int $user_id
* @property string $name
* @property int|string|array|null $data
* @property-read User $user
* @property int $user_id
* @property string $name
* @property int|string|array|null $data
* @property-read User $user
* @method static Builder|Preference newModelQuery()
* @method static Builder|Preference newQuery()
* @method static Builder|Preference query()
@@ -73,19 +72,29 @@ class Preference extends Model
*
* @param string $value
*
* @throws NotFoundHttpException
* @return Preference
* @throws NotFoundHttpException
*/
public static function routeBinder(string $value): Preference
{
if (auth()->check()) {
/** @var User $user */
$user = auth()->user();
/** @var Preference $preference */
/** @var Preference|null $preference */
$preference = $user->preferences()->where('name', $value)->first();
if (null !== $preference) {
return $preference;
}
$default = config('firefly.default_preferences');
if (array_key_exists($value, $default)) {
$preference = new Preference;
$preference->name = $value;
$preference->data = $default[$value];
$preference->user_id = $user->id;
$preference->save();
return $preference;
}
}
throw new NotFoundHttpException;
}

View File

@@ -83,6 +83,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @method static \Illuminate\Database\Query\Builder|Transaction withTrashed()
* @method static \Illuminate\Database\Query\Builder|Transaction withoutTrashed()
* @mixin Eloquent
* @property int $the_count
*/
class Transaction extends Model
{

View File

@@ -114,6 +114,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @mixin Eloquent
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Location[] $locations
* @property-read int|null $locations_count
* @property int $the_count
*/
class TransactionJournal extends Model
{

View File

@@ -123,24 +123,14 @@ class AccountRepository implements AccountRepositoryInterface
*/
public function findByIbanNull(string $iban, array $types): ?Account
{
$query = $this->user->accounts()->where('iban', '!=', '')->whereNotNull('iban');
$query = $this->user->accounts()->where('accounts.iban', $iban);
if (0!==count($types)) {
if (0 !== count($types)) {
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$query->whereIn('account_types.type', $types);
}
// TODO a loop like this is no longer necessary
$accounts = $query->get(['accounts.*']);
/** @var Account $account */
foreach ($accounts as $account) {
if ($account->iban === $iban) {
return $account;
}
}
return null;
return $query->first(['accounts.*']);
}
/**
@@ -269,7 +259,7 @@ class AccountRepository implements AccountRepositoryInterface
if (0 !== count($types)) {
$query->accountTypeIn($types);
}
$query->where('active', 1);
$query->where('active', true);
$query->orderBy('accounts.account_type_id', 'ASC');
$query->orderBy('accounts.order', 'ASC');
$query->orderBy('accounts.name', 'ASC');
@@ -650,7 +640,7 @@ class AccountRepository implements AccountRepositoryInterface
public function searchAccount(string $query, array $types, int $limit): Collection
{
$dbQuery = $this->user->accounts()
->where('active', 1)
->where('active', true)
->orderBy('accounts.order', 'ASC')
->orderBy('accounts.account_type_id', 'ASC')
->orderBy('accounts.name', 'ASC')
@@ -678,8 +668,8 @@ class AccountRepository implements AccountRepositoryInterface
public function searchAccountNr(string $query, array $types, int $limit): Collection
{
$dbQuery = $this->user->accounts()->distinct()
->leftJoin('account_meta', 'accounts.id', 'account_meta.account_id')
->where('accounts.active', 1)
->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id')
->where('accounts.active', true)
->orderBy('accounts.order', 'ASC')
->orderBy('accounts.account_type_id', 'ASC')
->orderBy('accounts.name', 'ASC')
@@ -747,4 +737,29 @@ class AccountRepository implements AccountRepositoryInterface
return $service->update($account, $data);
}
/**
* @inheritDoc
*/
public function findByAccountNumber(string $number, array $types): ?Account
{
$dbQuery = $this->user
->accounts()
->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id')
->where('accounts.active', true)
->where(
function (EloquentBuilder $q1) use ($number) {
$json = json_encode($number);
$q1->where('account_meta.name', '=', 'account_number');
$q1->where('account_meta.data', '=', $json);
}
);
if (0 !== count($types)) {
$dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$dbQuery->whereIn('account_types.type', $types);
}
return $dbQuery->first(['accounts.*']);
}
}

View File

@@ -46,6 +46,7 @@ interface AccountRepositoryInterface
*/
public function count(array $types): int;
/**
* Moved here from account CRUD.
*
@@ -65,6 +66,14 @@ interface AccountRepositoryInterface
*/
public function expandWithDoubles(Collection $accounts): Collection;
/**
* @param string $number
* @param array $types
*
* @return Account|null
*/
public function findByAccountNumber(string $number, array $types): ?Account;
/**
* @param string $iban
* @param array $types

View File

@@ -261,6 +261,12 @@ class OperationsRepository implements OperationsRepositoryInterface
$collector->setSourceAccounts($opposing);
}
}
if(TransactionType::TRANSFER === $type) {
// supports only accounts, not opposing.
if(null !== $accounts) {
$collector->setAccounts($accounts);
}
}
if (null !== $currency) {
$collector->setCurrency($currency);

View File

@@ -166,7 +166,7 @@ class BillRepository implements BillRepositoryInterface
public function getActiveBills(): Collection
{
return $this->user->bills()
->where('active', 1)
->where('active', true)
->orderBy('bills.name', 'ASC')
->get(['bills.*', DB::raw('((bills.amount_min + bills.amount_max) / 2) AS expectedAmount'),]);
}

View File

@@ -98,10 +98,10 @@ class BudgetRepository implements BudgetRepositoryInterface
$budgets = $this->getBudgets();
/** @var Budget $budget */
foreach ($budgets as $budget) {
DB::table('budget_transaction')->where('budget_id', $budget->id)->delete();
DB::table('budget_transaction_journal')->where('budget_id', $budget->id)->delete();
RecurrenceTransactionMeta::where('name', 'budget_id')->where('value', $budget->id)->delete();
RuleAction::where('action_type', 'set_budget')->where('action_value', $budget->id)->delete();
DB::table('budget_transaction')->where('budget_id', (int)$budget->id)->delete();
DB::table('budget_transaction_journal')->where('budget_id', (int)$budget->id)->delete();
RecurrenceTransactionMeta::where('name', 'budget_id')->where('value', (string)$budget->id)->delete();
RuleAction::where('action_type', 'set_budget')->where('action_value', (string)$budget->id)->delete();
$budget->delete();
}
}
@@ -197,7 +197,7 @@ class BudgetRepository implements BudgetRepositoryInterface
*/
public function getActiveBudgets(): Collection
{
return $this->user->budgets()->where('active', 1)
return $this->user->budgets()->where('active', true)
->orderBy('order', 'ASC')
->orderBy('name', 'ASC')
->get();
@@ -282,7 +282,7 @@ class BudgetRepository implements BudgetRepositoryInterface
$search->where('name', 'LIKE', sprintf('%%%s%%', $query));
}
$search->orderBy('order', 'ASC')
->orderBy('name', 'ASC')->where('active', 1);
->orderBy('name', 'ASC')->where('active', true);
return $search->take($limit)->get();
}
@@ -328,7 +328,7 @@ class BudgetRepository implements BudgetRepositoryInterface
Log::error($e->getTraceAsString());
throw new FireflyException('400002: Could not store budget.', 0, $e);
}
if (!array_key_exists('auto_budget_type', $data)) {
if (!array_key_exists('auto_budget_type', $data) || !array_key_exists('auto_budget_amount', $data) || !array_key_exists('auto_budget_period', $data)) {
return $newBudget;
}
$type = $data['auto_budget_type'];

View File

@@ -448,7 +448,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
*/
public function searchCurrency(string $search, int $limit): Collection
{
$query = TransactionCurrency::where('enabled', 1);
$query = TransactionCurrency::where('enabled', true);
if ('' !== $search) {
$query->where('name', 'LIKE', sprintf('%%%s%%', $search));
}

View File

@@ -211,8 +211,8 @@ class RuleRepository implements RuleRepositoryInterface
{
$collection = $this->user->rules()
->leftJoin('rule_groups', 'rule_groups.id', '=', 'rules.rule_group_id')
->where('rules.active', 1)
->where('rule_groups.active', 1)
->where('rules.active', true)
->where('rule_groups.active', true)
->orderBy('rule_groups.order', 'ASC')
->orderBy('rules.order', 'ASC')
->orderBy('rules.id', 'ASC')
@@ -238,8 +238,8 @@ class RuleRepository implements RuleRepositoryInterface
{
$collection = $this->user->rules()
->leftJoin('rule_groups', 'rule_groups.id', '=', 'rules.rule_group_id')
->where('rules.active', 1)
->where('rule_groups.active', 1)
->where('rules.active', true)
->where('rule_groups.active', true)
->orderBy('rule_groups.order', 'ASC')
->orderBy('rules.order', 'ASC')
->orderBy('rules.id', 'ASC')

View File

@@ -150,7 +150,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
*/
public function getActiveGroups(): Collection
{
return $this->user->ruleGroups()->with(['rules'])->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get(['rule_groups.*']);
return $this->user->ruleGroups()->with(['rules'])->where('rule_groups.active', true)->orderBy('order', 'ASC')->get(['rule_groups.*']);
}
/**
@@ -161,7 +161,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
public function getActiveRules(RuleGroup $group): Collection
{
return $group->rules()
->where('rules.active', 1)
->where('rules.active', true)
->get(['rules.*']);
}
@@ -176,7 +176,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rule_triggers.trigger_type', 'user_action')
->where('rule_triggers.trigger_value', 'store-journal')
->where('rules.active', 1)
->where('rules.active', true)
->get(['rules.*']);
}
@@ -191,7 +191,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rule_triggers.trigger_type', 'user_action')
->where('rule_triggers.trigger_value', 'update-journal')
->where('rules.active', 1)
->where('rules.active', true)
->get(['rules.*']);
}
@@ -258,6 +258,58 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
);
}
/**
* @param string|null $filter
*
* @return Collection
*/
public function getAllRuleGroupsWithRules(?string $filter): Collection
{
$groups = $this->user->ruleGroups()
->orderBy('order', 'ASC')
->with(
[
'rules' => static function (HasMany $query) {
$query->orderBy('order', 'ASC');
},
'rules.ruleTriggers' => static function (HasMany $query) {
$query->orderBy('order', 'ASC');
},
'rules.ruleActions' => static function (HasMany $query) {
$query->orderBy('order', 'ASC');
},
]
)->get();
if (null === $filter) {
return $groups;
}
Log::debug(sprintf('Will filter getRuleGroupsWithRules on "%s".', $filter));
return $groups->map(
function (RuleGroup $group) use ($filter) {
Log::debug(sprintf('Now filtering group #%d', $group->id));
// filter the rules in the rule group:
$group->rules = $group->rules->filter(
function (Rule $rule) use ($filter) {
Log::debug(sprintf('Now filtering rule #%d', $rule->id));
foreach ($rule->ruleTriggers as $trigger) {
if ('user_action' === $trigger->trigger_type && $filter === $trigger->trigger_value) {
Log::debug(sprintf('Rule #%d triggers on %s, include it.', $rule->id, $filter));
return true;
}
}
Log::debug(sprintf('Rule #%d does not trigger on %s, do not include it.', $rule->id, $filter));
return false;
}
);
return $group;
}
);
}
/**
* @param RuleGroup $group
*
@@ -274,7 +326,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
*/
public function maxOrder(): int
{
return (int)$this->user->ruleGroups()->where('active', 1)->max('order');
return (int)$this->user->ruleGroups()->where('active', true)->max('order');
}
/**
@@ -285,7 +337,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
$this->user->ruleGroups()->where('active', false)->update(['order' => 0]);
$set = $this->user
->ruleGroups()
->where('active', 1)
->where('active', true)
->whereNull('deleted_at')
->orderBy('order', 'ASC')
->orderBy('title', 'DESC')

View File

@@ -114,6 +114,15 @@ interface RuleGroupRepositoryInterface
*/
public function getRuleGroupsWithRules(?string $filter): Collection;
/**
* Also inactive groups.
*
* @param string|null $filter
*
* @return Collection
*/
public function getAllRuleGroupsWithRules(?string $filter): Collection;
/**
* @param RuleGroup $group
*

View File

@@ -102,7 +102,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
$journals = $group->transactionJournals->pluck('id')->toArray();
$set = Attachment::whereIn('attachable_id', $journals)
->where('attachable_type', TransactionJournal::class)
->where('uploaded', 1)
->where('uploaded', true)
->whereNull('deleted_at')->get();
$result = [];

View File

@@ -79,6 +79,7 @@ trait JournalServiceTrait
$result = $this->findAccountById($data, $expectedTypes[$transactionType]);
$result = $this->findAccountByName($result, $data, $expectedTypes[$transactionType]);
$result = $this->findAccountByIban($result, $data, $expectedTypes[$transactionType]);
$result = $this->findAccountByNumber($result, $data, $expectedTypes[$transactionType]);
$result = $this->createAccount($result, $data, $expectedTypes[$transactionType][0]);
return $this->getCashAccount($result, $data, $expectedTypes[$transactionType]);
@@ -95,7 +96,7 @@ trait JournalServiceTrait
$search = null;
// first attempt, find by ID.
if (null !== $data['id']) {
$search = $this->accountRepository->findNull($data['id']);
$search = $this->accountRepository->findNull((int) $data['id']);
if (null !== $search && in_array($search->accountType->type, $types, true)) {
Log::debug(
sprintf('Found "account_id" object: #%d, "%s" of type %s', $search->id, $search->name, $search->accountType->type)
@@ -160,6 +161,34 @@ trait JournalServiceTrait
return $account;
}
/**
* @param Account|null $account
* @param array $data
* @param array $types
*
* @return Account|null
*/
private function findAccountByNumber(?Account $account, array $data, array $types): ?Account
{
// third attempt, find by account number
if (null === $account && null !== $data['number']) {
Log::debug(sprintf('Searching for account number "%s".', $data['number']));
// find by preferred type.
$source = $this->accountRepository->findByAccountNumber((string) $data['number'], [$types[0]]);
// or any expected type.
$source = $source ?? $this->accountRepository->findByAccountNumber((string) $data['number'], $types);
if (null !== $source) {
Log::debug(sprintf('Found account: #%d, %s', $source->id, $source->name));
$account = $source;
}
}
return $account;
}
/**
* @param Account|null $account
* @param array $data

View File

@@ -136,6 +136,15 @@ class AccountUpdateService
$account->account_type_id = $type->id;
}
}
// set liability, alternative method used in v1 layout:
if ($this->isLiability($account) && array_key_exists('account_type_id', $data)) {
$type = AccountType::find((int)$data['account_type_id']);
if (null !== $type && in_array($type->type, config('firefly.valid_liabilities'), true)) {
$account->account_type_id = $type->id;
}
}
// update virtual balance (could be set to zero if empty string).
if (array_key_exists('virtual_balance', $data) && null !== $data['virtual_balance']) {

View File

@@ -292,7 +292,7 @@ class JournalUpdateService
$validator->setTransactionType($expectedType);
$validator->setUser($this->transactionJournal->user);
$validator->source = $this->getValidSourceAccount();
$result = $validator->validateDestination($destId, $destName, null);
$result = $validator->validateDestination($destId, $destName, null);
Log::debug(sprintf('hasValidDestinationAccount(%d, "%s") will return %s', $destId, $destName, var_export($result, true)));
// TODO typeOverrule: the account validator may have another opinion on the transaction type.
@@ -527,6 +527,10 @@ class JournalUpdateService
Log::debug('Will update budget.');
$this->storeBudget($this->transactionJournal, new NullArrayObject($this->data));
}
// is transfer? remove budget
if (TransactionType::TRANSFER === $this->transactionJournal->transactionType->type) {
$this->transactionJournal->budgets()->sync([]);
}
}
/**

View File

@@ -46,7 +46,7 @@ class BudgetList implements BinderInterface
if (auth()->check()) {
if ('allBudgets' === $value) {
return auth()->user()->budgets()->where('active', 1)
return auth()->user()->budgets()->where('active', true)
->orderBy('order', 'ASC')
->orderBy('name', 'ASC')
->get();
@@ -63,7 +63,7 @@ class BudgetList implements BinderInterface
/** @var Collection $collection */
$collection = auth()->user()->budgets()
->where('active', 1)
->where('active', true)
->whereIn('id', $list)
->get();

View File

@@ -61,7 +61,7 @@ trait RuleManagement
],
[
'type' => 'from_account_is',
'value' => (string)trans('firefly.default_rule_trigger_from_account'),
'value' => (string)trans('firefly.default_rule_trigger_source_account'),
'stop_processing' => false,
],

View File

@@ -153,7 +153,7 @@ class Preferences
*/
public function getForUser(User $user, string $name, $default = null): ?Preference
{
$preference = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data', 'updated_at', 'created_at']);
$preference = Preference::where('user_id', $user->id)->where('name', $name)->first(['id','user_id', 'name', 'data', 'updated_at', 'created_at']);
if (null !== $preference && null === $preference->data) {
try {
$preference->delete();

View File

@@ -56,8 +56,8 @@ class SetDestinationAccount implements ActionInterface
*/
public function actOnArray(array $journal): bool
{
$user = User::find($journal['user_id']);
$type = $journal['transaction_type_type'];
$user = User::find($journal['user_id']);
$type = $journal['transaction_type_type'];
/** @var TransactionJournal|null $object */
$object = $user->transactionJournals()->find((int)$journal['transaction_journal_id']);
$this->repository = app(AccountRepositoryInterface::class);
@@ -108,8 +108,9 @@ class SetDestinationAccount implements ActionInterface
}
// if this is a withdrawal, the new destination account must be a expense account and may be created:
// or it is a liability, in which case it must be returned.
if (TransactionType::WITHDRAWAL === $type) {
$newAccount = $this->findExpenseAccount();
$newAccount = $this->findWithdrawalDestinationAccount();
}
Log::debug(sprintf('New destination account is #%d ("%s").', $newAccount->id, $newAccount->name));
@@ -145,9 +146,10 @@ class SetDestinationAccount implements ActionInterface
/**
* @return Account
*/
private function findExpenseAccount(): Account
private function findWithdrawalDestinationAccount(): Account
{
$account = $this->repository->findByName($this->action->action_value, [AccountType::EXPENSE]);
$allowed = config('firefly.expected_source_types.destination.Withdrawal');
$account = $this->repository->findByName($this->action->action_value, $allowed);
if (null === $account) {
$data = [
'name' => $this->action->action_value,

View File

@@ -105,8 +105,9 @@ class SetSourceAccount implements ActionInterface
}
// if this is a deposit, the new source account must be a revenue account and may be created:
// or its a liability
if (TransactionType::DEPOSIT === $type) {
$newAccount = $this->findRevenueAccount();
$newAccount = $this->findDepositSourceAccount();
}
Log::debug(sprintf('New source account is #%d ("%s").', $newAccount->id, $newAccount->name));
@@ -140,7 +141,7 @@ class SetSourceAccount implements ActionInterface
/**
* @return Account
*/
private function findRevenueAccount(): Account
private function findDepositSourceAccount(): Account
{
$allowed = config('firefly.expected_source_types.source.Deposit');
$account = $this->repository->findByName($this->action->action_value, $allowed);

View File

@@ -145,6 +145,7 @@ class SearchRuleEngine implements RuleEngineInterface
*/
public function setRules(Collection $rules): void
{
Log::debug(__METHOD__);
foreach ($rules as $rule) {
if ($rule instanceof Rule) {
@@ -174,6 +175,11 @@ class SearchRuleEngine implements RuleEngineInterface
private function fireRule(Rule $rule): bool
{
Log::debug(sprintf('Now going to fire rule #%d', $rule->id));
if (false === $rule->active) {
Log::debug(sprintf('Rule #%d is not active!', $rule->id));
return false;
}
if (true === $rule->strict) {
Log::debug(sprintf('Rule #%d is a strict rule.', $rule->id));
@@ -222,8 +228,16 @@ class SearchRuleEngine implements RuleEngineInterface
{
Log::debug(sprintf('Now in findStrictRule(#%d)', $rule->id ?? 0));
$searchArray = [];
/** @var Collection $triggers */
$triggers = $rule->ruleTriggers;
/** @var RuleTrigger $ruleTrigger */
foreach ($rule->ruleTriggers()->where('active',1)->get() as $ruleTrigger) {
foreach ($triggers as $ruleTrigger) {
if (false === $ruleTrigger->active) {
continue;
}
// if needs no context, value is different:
$needsContext = config(sprintf('firefly.search.operators.%s.needs_context', $ruleTrigger->trigger_type)) ?? true;
if (false === $needsContext) {
@@ -236,6 +250,7 @@ class SearchRuleEngine implements RuleEngineInterface
}
}
// add local operators:
foreach ($this->operators as $operator) {
Log::debug(sprintf('SearchRuleEngine:: add local added operator: %s:"%s"', $operator['type'], $operator['value']));
@@ -368,7 +383,7 @@ class SearchRuleEngine implements RuleEngineInterface
{
Log::debug(sprintf('SearchRuleEngine:: Will now execute actions on transaction journal #%d', $transaction['transaction_journal_id']));
/** @var RuleAction $ruleAction */
foreach ($rule->ruleActions()->where('active',1)->get() as $ruleAction) {
foreach ($rule->ruleActions()->where('active', true)->get() as $ruleAction) {
$break = $this->processRuleAction($ruleAction, $transaction);
if (true === $break) {
break;
@@ -443,8 +458,15 @@ class SearchRuleEngine implements RuleEngineInterface
// start a search query for individual each trigger:
$total = new Collection;
$count = 0;
/** @var Collection $triggers */
$triggers = $rule->ruleTriggers;
/** @var RuleTrigger $ruleTrigger */
foreach ($rule->ruleTriggers()->where('active',1)->get() as $ruleTrigger) {
foreach ($triggers as $ruleTrigger) {
if (false === $ruleTrigger->active) {
continue;
}
if ('user_action' === $ruleTrigger->trigger_type) {
Log::debug('Skip trigger type.');
continue;

View File

@@ -114,7 +114,7 @@ class AccountTransformer extends AbstractTransformer
'opening_balance' => $openingBalance,
'opening_balance_date' => $openingBalanceDate,
'liability_type' => $liabilityType,
'interest' => (float)$interest,
'interest' => $interest,
'interest_period' => $interestPeriod,
'include_net_worth' => $includeNetWorth,
'longitude' => $longitude,

View File

@@ -48,16 +48,21 @@ trait ValidatesAutoBudgetRequest
return;
}
// basic float check:
if (!is_numeric($amount)) {
$validator->errors()->add('auto_budget_amount', (string)trans('validation.amount_required_for_auto_budget'));
return;
}
if ('' === $amount) {
$validator->errors()->add('auto_budget_amount', (string)trans('validation.amount_required_for_auto_budget'));
}
if (null !== $amount && 1 !== bccomp((string)$amount, '0')) {
if (1 !== bccomp((string)$amount, '0')) {
$validator->errors()->add('auto_budget_amount', (string)trans('validation.auto_budget_amount_positive'));
}
if ('' === $period) {
$validator->errors()->add('auto_budget_period', (string)trans('validation.auto_budget_period_mandatory'));
}
if (null !== $amount && null !== $currencyId && null !== $currencyCode && '' === $currencyCode && 0 === $currencyId) {
if (null !== $currencyId && null !== $currencyCode && '' === $currencyCode && 0 === $currencyId) {
$validator->errors()->add('auto_budget_amount', (string)trans('validation.require_currency_info'));
}
}

View File

@@ -2,6 +2,64 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 5.5.13 - 2021-07-25
### Security
- This version of Firefly III fixes [CVE-2021-3663](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-3663)
## 5.5.12 - 2021-06-03
⚠️ On July 1st 2021 the Docker tag will change to `fireflyiii/core`. You can already start using the new tag.
### Security
- This version of Firefly III fixes a security vulnerability in the export routine. You are advised to upgrade as soon as possible. All credits to the excellent @oomb.
## 5.5.11 - 2021-05-08
⚠️ On July 1st 2021 the Docker tag will change to `fireflyiii/core`. You can already start using the new tag.
### Fixed
- [Issue 4707](https://github.com/firefly-iii/firefly-iii/issues/4707) [issue 4732](https://github.com/firefly-iii/firefly-iii/issues/4732) Rule tests were broken, and matching transactions were not visible.
- [Issue 4729](https://github.com/firefly-iii/firefly-iii/issues/4729) Top boxes were no longer visible.
- [Issue 4730](https://github.com/firefly-iii/firefly-iii/issues/4730) Second split transaction had today's date
- [Issue 4734](https://github.com/firefly-iii/firefly-iii/issues/4734) Potential fixes for PostgreSQL and PHP 7.4.18.
- [Issue 4739](https://github.com/firefly-iii/firefly-iii/issues/4739) Was not possible to change liability type.
## 5.5.10 - 2021-05-01
### Changed
- [Issue 4708](https://github.com/firefly-iii/firefly-iii/issues/4708) When searching for the external ID, Firefly III will now only return the exact match.
### Fixed
- [Issue 4545](https://github.com/firefly-iii/firefly-iii/issues/4545) Rare but annoying issue with PostgreSQL increments will be repaired during image boot time. Thanks @jaylenw!
- [Issue 4710](https://github.com/firefly-iii/firefly-iii/issues/4710) Some rule actions could not handle liabilities.
- [Issue 4715](https://github.com/firefly-iii/firefly-iii/issues/4715) Fixed some titles.
- [Issue 4720](https://github.com/firefly-iii/firefly-iii/issues/4720) Could not remove a split in the new layout.
## 5.5.9 (API 1.5.2) 2021-04-24
This update fixes some of the more annoying issues in the new experimental v2 layout (see also [GitHub](https://github.com/firefly-iii/firefly-iii/issues/4618)), but some minor other issues as well.
### Fixed
- Dashboard preferences would some times retain old or bad data.
### API
- [Issue 4697](https://github.com/firefly-iii/firefly-iii/issues/4697) Submitting an existing account with an account number only would store it as a new account.
- [Issue 4706](https://github.com/firefly-iii/firefly-iii/issues/4706) Account interest was a float and not a string.
- Store Budget API call would not properly handle auto budgets.
## 5.5.8 (API 1.5.2) 2021-04-17
This update fixes some of the more annoying issues in the new experimental v2 layout (see also [GitHub](https://github.com/firefly-iii/firefly-iii/issues/4618)), but some minor other issues as well.
### Fixed
- [Issue 4656](https://github.com/firefly-iii/firefly-iii/issues/4656) [issue 4660](https://github.com/firefly-iii/firefly-iii/issues/4660) Various fixes in the v2 layout.
- [Issue 4663](https://github.com/firefly-iii/firefly-iii/issues/4663) It was possible to assign a budget to a transfer.
- [Issue 4664](https://github.com/firefly-iii/firefly-iii/issues/4664) Null pointer in bulk editor
- [Issue 4668](https://github.com/firefly-iii/firefly-iii/issues/4668) Inactive rule groups would not be listed.
## 5.5.7 (API 1.5.2) 2021-04-11
### Added

View File

@@ -115,8 +115,7 @@
"phpstan/phpstan-deprecation-rules": "^0.12.5",
"phpunit/phpunit": "^9.2",
"roave/security-advisories": "dev-master",
"thecodingmachine/phpstan-strict-rules": "^0.12.0",
"vimeo/psalm": "^4.1"
"thecodingmachine/phpstan-strict-rules": "^0.12.0"
},
"suggest": {
"adldap2/adldap2-laravel": "If you want to login using LDAP.",
@@ -155,6 +154,7 @@
],
"post-update-cmd": [
"@php artisan cache:clear",
"@php artisan firefly-iii:fix-pgsql-sequences",
"@php artisan firefly-iii:decrypt-all",
"@php artisan firefly-iii:transaction-identifiers",
@@ -192,6 +192,7 @@
"@php artisan firefly-iii:fix-recurring-transactions",
"@php artisan firefly-iii:unify-group-accounts",
"@php artisan firefly-iii:fix-transaction-types",
"@php artisan firefly-iii:fix-frontpage-accounts",
"@php artisan firefly-iii:report-empty-objects",
"@php artisan firefly-iii:report-sum",

1174
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -95,12 +95,12 @@ return [
],
'feature_flags' => [
'export' => true,
'telemetry' => true,
'telemetry' => false,
'webhooks' => false,
'handle_debts' => true,
],
'version' => '5.5.7',
'version' => '5.5.13',
'api_version' => '1.5.2',
'db_version' => 16,
'maxUploadSize' => 1073741824, // 1 GB
@@ -631,8 +631,7 @@ return [
'expected_source_types' => [
'source' => [
TransactionTypeModel::WITHDRAWAL => [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE],
TransactionTypeModel::DEPOSIT => [AccountType::REVENUE, AccountType::CASH, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE,
AccountType::INITIAL_BALANCE, AccountType::RECONCILIATION,],
TransactionTypeModel::DEPOSIT => [AccountType::REVENUE, AccountType::CASH, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE],
TransactionTypeModel::TRANSFER => [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE],
TransactionTypeModel::OPENING_BALANCE => [AccountType::INITIAL_BALANCE, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT,
AccountType::MORTGAGE,],
@@ -850,4 +849,11 @@ return [
'valid_asset_fields' => ['account_role', 'account_number', 'currency_id', 'BIC', 'include_net_worth'],
'valid_cc_fields' => ['account_role', 'cc_monthly_payment_date', 'cc_type', 'account_number', 'currency_id', 'BIC', 'include_net_worth'],
'valid_account_fields' => ['account_number', 'currency_id', 'BIC', 'interest', 'interest_period', 'include_net_worth', 'liability_direction'],
'default_preferences' => [
'frontPageAccounts' => [],
'listPageSize' => 50,
'currencyPreference' => 'EUR',
'language' => 'en_US',
'locale' => 'equal',
],
];

View File

@@ -47,7 +47,7 @@ yarn prod
# mv public/css ../public/v2
# also copy fonts
cp -r fonts ../public
#cp -r fonts ../public/v2/css
# remove built stuff
rm -rf public
rm -rf public/

View File

@@ -4,6 +4,7 @@
"/public/js/accounts/delete.js": "/public/js/accounts/delete.js",
"/public/js/accounts/show.js": "/public/js/accounts/show.js",
"/public/js/accounts/create.js": "/public/js/accounts/create.js",
"/public/js/budgets/index.js": "/public/js/budgets/index.js",
"/public/js/transactions/create.js": "/public/js/transactions/create.js",
"/public/js/transactions/edit.js": "/public/js/transactions/edit.js",
"/public/js/transactions/index.js": "/public/js/transactions/index.js",

View File

@@ -11,6 +11,7 @@
},
"devDependencies": {
"axios": "^0.21",
"date-fns": "^2.21.1",
"laravel-mix": "^6.0.6",
"lodash": "^4.17.19",
"lodash.clonedeep": "^4.5.0",
@@ -29,7 +30,7 @@
"admin-lte": "^3.1.0",
"bootstrap": "^4.6.0",
"bootstrap-vue": "^2.21.2",
"chart.js": "^3.0.2",
"chart.js": "^3.2.0",
"icheck-bootstrap": "^3.0.1",
"jquery-ui": "^1.12.1",
"leaflet": "^1.7.1",

View File

@@ -133,6 +133,7 @@
import {mapGetters} from "vuex";
import Sortable from "sortablejs";
import format from "date-fns/format";
export default {
name: "Index",
@@ -370,9 +371,10 @@ export default {
}
);
}));
promises.push(axios.get('./api/v1/accounts/' + acct.id + '?date=' + this.start.toISOString().split('T')[0]));
promises.push(axios.get('./api/v1/accounts/' + acct.id + '?date=' + this.end.toISOString().split('T')[0]));
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
promises.push(axios.get('./api/v1/accounts/' + acct.id + '?date=' + startStr));
promises.push(axios.get('./api/v1/accounts/' + acct.id + '?date=' + endStr));
Promise.all(promises).then(responses => {
let index = responses[0].index;

View File

@@ -0,0 +1,114 @@
<!--
- Index.vue
- Copyright (c) 2021 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/>.
-->
<template>
<div>
<p>
<span class="d-block">(all)</span>
<span class="d-none d-xl-block">xl</span>
<span class="d-none d-lg-block d-xl-none">lg</span>
<span class="d-none d-md-block d-lg-none">md</span>
<span class="d-none d-sm-block d-md-none">sm</span>
<span class="d-block d-sm-none">xs</span>
</p>
<div class="row">
<div class="col-xl-2 col-lg-4 col-md-4 col-sm-4 col-6">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Budgets</h3>
</div>
<div class="card-body">
Budget X<br>
Budget Y<br>
Budget X<br>
Budget Y<br>
Budget X<br>
Budget Y<br>
Budget X<br>
Budget Y<br>
</div>
</div>
</div>
<div class="col-xl-10 col-lg-8 col-md-8 col-sm-8 col-6">
<div class="container-fluid" style="overflow:scroll;">
<div class="d-flex flex-row flex-nowrap">
<div class="card card-body-budget" v-for="n in 5">
<div class="card-header">
<h3 class="card-title">Maand yXz</h3>
</div>
<div class="card-body">
Some text<br>
Some text<br>
Some text<br>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Index",
created() {
}
}
</script>
<style scoped>
.card-body-budget {
min-width: 300px;
margin-right: 5px;
}
.holder-titles {
display: flex;
flex-direction: column-reverse;
}
.title-block {
border: 1px red solid;
}
.holder-blocks {
display: flex;
flex-direction: column-reverse;
}
.budget-block {
border: 1px blue solid;
}
.budget-block-unused {
border: 1px green solid;
}
.budget-block-unset {
border: 1px purple solid;
}
</style>

View File

@@ -102,11 +102,9 @@ export default {
},
ticks: {
callback: function (value, index, values) {
//return this.getLabelForValue(value);
let dateObj = new Date(this.getLabelForValue(value));
let options = {year: 'numeric', month: 'long', day: 'numeric'};
let str = new Intl.DateTimeFormat(localStorage.locale, options).format(dateObj);
return str;
let dateObj = new Date(this.getLabelForValue(value).split('T')[0]);
return new Intl.DateTimeFormat(localStorage.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(dateObj);
//return str;
// // //console.log();
// // //return self.formatLabel(value, 20);
// // return self.formatLabel(str, 20);

View File

@@ -27,7 +27,7 @@
<td style="vertical-align: middle">
<div class="progress progress active">
<div :aria-valuenow="budgetLimit.pctGreen" :style="'width: '+ budgetLimit.pctGreen + '%;'"
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-success progress-bar-striped" role="progressbar">
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-success" role="progressbar">
<span v-if="budgetLimit.pctGreen > 35">
{{ $t('firefly.spent_x_of_y', {amount: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.spent), total: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.amount)}) }}
<!-- -->
@@ -37,14 +37,14 @@
</div>
<div :aria-valuenow="budgetLimit.pctOrange" :style="'width: '+ budgetLimit.pctOrange + '%;'"
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-warning progress-bar-striped" role="progressbar">
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-warning" role="progressbar">
<span v-if="budgetLimit.pctRed <= 50 && budgetLimit.pctOrange > 35">
{{ $t('firefly.spent_x_of_y', {amount: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.spent), total: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.amount)}) }}
</span>
</div>
<div :aria-valuenow="budgetLimit.pctRed" :style="'width: '+ budgetLimit.pctRed + '%;'"
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-danger progress-bar-striped" role="progressbar">
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-danger" role="progressbar">
<span v-if="budgetLimit.pctOrange <= 50 && budgetLimit.pctRed > 35" class="text-muted">
{{ $t('firefly.spent_x_of_y', {amount: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.spent), total: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.amount)}) }}
</span>

View File

@@ -46,7 +46,8 @@
class="btn btn-secondary"
@click="resetDate"
><i class="fas fa-history"></i></button>
<button id="dropdownMenuButton" :title="$t('firefly.select_period')" aria-expanded="false" aria-haspopup="true" class="btn btn-secondary dropdown-toggle"
<button id="dropdownMenuButton" :title="$t('firefly.select_period')" aria-expanded="false" aria-haspopup="true"
class="btn btn-secondary dropdown-toggle"
data-toggle="dropdown"
type="button">
<i class="fas fa-list"></i>
@@ -78,8 +79,23 @@
import {createNamespacedHelpers} from "vuex";
import Vue from "vue";
import DatePicker from "v-calendar/lib/components/date-picker.umd";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
import subDays from 'date-fns/subDays';
import addDays from 'date-fns/addDays';
import addMonths from 'date-fns/addMonths';
import startOfDay from 'date-fns/startOfDay';
import endOfDay from 'date-fns/endOfDay';
import startOfWeek from 'date-fns/startOfWeek';
import endOfWeek from 'date-fns/endOfWeek';
import endOfMonth from 'date-fns/endOfMonth';
import format from 'date-fns/format';
import startOfQuarter from 'date-fns/startOfQuarter';
import subMonths from 'date-fns/subMonths';
import endOfQuarter from 'date-fns/endOfQuarter';
import subQuarters from 'date-fns/subQuarters';
import addQuarters from 'date-fns/addQuarters';
import startOfMonth from 'date-fns/startOfMonth';
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
Vue.component('date-picker', DatePicker)
@@ -130,50 +146,400 @@ export default {
this.generatePeriods()
return false;
},
generatePeriods: function () {
this.periods = [];
// create periods.
let today;
let end;
today = new Date(this.range.start);
// previous month
firstDayOfMonth = new Date(today.getFullYear(), today.getMonth()-1, 1);
lastDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 0);
generateDaily: function () {
let today = new Date(this.range.start);
// yesterday
this.periods.push(
{
start: firstDayOfMonth.toDateString(),
end: lastDayOfMonth.toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long'}).format(firstDayOfMonth)
start: startOfDay(subDays(today, 1)).toDateString(),
end: endOfDay(subDays(today, 1)).toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(subDays(today, 1))
}
);
// today
this.periods.push(
{
start: startOfDay(today).toDateString(),
end: endOfDay(today).toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(today)
}
);
// tomorrow:
this.periods.push(
{
start: startOfDay(addDays(today, 1)).toDateString(),
end: endOfDay(addDays(today, 1)).toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(addDays(today, 1))
}
);
// The Day After Tomorrow dun-dun-dun!
this.periods.push(
{
start: startOfDay(addDays(today, 2)).toDateString(),
end: endOfDay(addDays(today, 2)).toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(addDays(today, 2))
}
);
},
generateWeekly: function () {
//console.log('weekly');
let today = new Date(this.range.start);
//console.log('Today is ' + today);
let start = startOfDay(startOfWeek(subDays(today, 7), {weekStartsOn: 1}));
let end = endOfDay(endOfWeek(subDays(today, 7), {weekStartsOn: 1}));
let dateFormat = this.$t('config.week_in_year_fns');
//console.log('Date format: "'+dateFormat+'"');
let title = format(start, dateFormat);
// last week
// console.log('Last week');
// console.log(start);
// console.log(end);
// console.log(title);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// this week
start = startOfDay(startOfWeek(today, {weekStartsOn: 1}));
end = endOfDay(endOfWeek(today, {weekStartsOn: 1}));
title = format(start, dateFormat);
// console.log('This week');
// console.log(start);
// console.log(end);
// console.log(title);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// next week
start = startOfDay(startOfWeek(addDays(today, 7), {weekStartsOn: 1}));
end = endOfDay(endOfWeek(addDays(today, 7), {weekStartsOn: 1}));
title = format(start, dateFormat);
// console.log('Next week');
// console.log(start);
// console.log(end);
// console.log(title);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
},
generateMonthly: function () {
let today = new Date(this.range.start);
// previous month
let start = startOfDay(startOfMonth(subMonths(today, 1)));
let end = endOfDay(endOfMonth(subMonths(today, 1)));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long'}).format(start)
}
);
// this month
firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
lastDayOfMonth = new Date(today.getFullYear(), today.getMonth()+1, 0);
start = startOfDay(startOfMonth(today));
end = endOfDay(endOfMonth(today));
this.periods.push(
{
start: firstDayOfMonth.toDateString(),
end: lastDayOfMonth.toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long'}).format(firstDayOfMonth)
start: start.toDateString(),
end: end.toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long'}).format(start)
}
);
// next month
let firstDayOfMonth = new Date(today.getFullYear(), today.getMonth()+1, 1);
let lastDayOfMonth = new Date(today.getFullYear(), today.getMonth()+2, 0);
start = startOfDay(startOfMonth(addMonths(today, 1)));
end = endOfDay(endOfMonth(addMonths(today, 1)));
this.periods.push(
{
start: firstDayOfMonth.toDateString(),
end: lastDayOfMonth.toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long'}).format(firstDayOfMonth)
start: start.toDateString(),
end: end.toDateString(),
title: new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long'}).format(start)
}
);
},
generateQuarterly: function () {
let today = new Date(this.range.start);
// last quarter
let start = startOfDay(startOfQuarter(subQuarters(today, 1)));
let end = endOfDay(endOfQuarter(subQuarters(today, 1)));
let dateFormat = this.$t('config.quarter_fns');
let title = format(start, dateFormat);
// last week
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// this quarter
start = startOfDay(startOfQuarter(today));
end = endOfDay(endOfQuarter(today));
title = format(start, dateFormat);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// next quarter
start = startOfDay(startOfQuarter(addQuarters(today, 1)));
end = endOfDay(endOfQuarter(addQuarters(today, 1)));
title = format(start, dateFormat);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
},
generateHalfYearly: function () {
let today = new Date(this.range.start);
let start;
let end;
let title = 'todo';
let half = 1;
// its currently first half of year:
if (today.getMonth() <= 5) {
// previous year, last half:
start = today;
start.setFullYear(start.getFullYear() - 1);
start.setMonth(6);
start.setDate(1);
start = startOfDay(start);
end = start;
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
half = 2;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// this year, first half:
start = today;
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = today;
end.setMonth(5);
end.setDate(30);
end = endOfDay(start);
half = 1;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// this year, second half:
start = today;
start.setMonth(6);
start.setDate(1);
start = startOfDay(start);
end = start;
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
half = 2;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
return;
}
// this year, first half:
start = today;
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = start;
end.setMonth(5);
end.setDate(30);
end = endOfDay(end);
half = 1;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// this year, current (second) half:
start = today;
start.setMonth(6);
start.setDate(1);
start = startOfDay(start);
end = today;
end.setMonth(11);
end.setDate(31);
end = endOfDay(start);
half = 2;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
// next year, first half:
start = today;
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = start;
end.setMonth(5);
end.setDate(30);
end = endOfDay(end);
half = 1;
title = format(start, this.$t('config.half_year_fns', {half: half}));
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: title
}
);
},
generateYearly: function () {
let today = new Date(this.range.start);
let start;
let end;
let title;
// last year
start = new Date(today);
start.setFullYear(start.getFullYear() - 1);
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setFullYear(end.getFullYear() - 1);
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: start.getFullYear()
}
);
// this year
start = new Date(today);
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: start.getFullYear()
}
);
// next year
start = new Date(today);
start.setFullYear(start.getFullYear() + 1);
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setFullYear(end.getFullYear() + 1);
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
this.periods.push(
{
start: start.toDateString(),
end: end.toDateString(),
title: start.getFullYear()
}
);
},
generatePeriods: function () {
this.periods = [];
//console.log('The view range is "' + this.viewRange + '".');
switch (this.viewRange) {
case '1D':
this.generateDaily();
break;
case '1W':
this.generateWeekly();
break;
case '1M':
this.generateMonthly();
break;
case '3M':
this.generateQuarterly();
break;
case '6M':
this.generateHalfYearly();
break;
case '1Y':
this.generateYearly();
break;
}
// last 7 days
today = new Date;
end = new Date;
let today = new Date;
let end = new Date;
end.setDate(end.getDate() - 7);
this.periods.push(
{

View File

@@ -33,9 +33,6 @@
<div v-if="error" class="text-center">
<i class="fas fa-exclamation-triangle text-danger"></i>
</div>
<div v-if="timezoneDifference" class="text-muted small">
{{ $t('firefly.timezone_difference', {local: localTimeZone, system: systemTimeZone}) }}
</div>
</div>
<div class="card-footer">
<a class="btn btn-default button-sm" href="./accounts/asset"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_asset_accounts') }}</a>
@@ -49,12 +46,11 @@ import DataConverter from "../charts/DataConverter";
import DefaultLineOptions from "../charts/DefaultLineOptions";
import {mapGetters} from "vuex";
import * as ChartJs from 'chart.js'
import format from "date-fns/format";
ChartJs.Chart.register.apply(null, Object.values(ChartJs).filter((chartClass) => (chartClass.id)));
export default {
name: "MainAccount",
components: {}, // MainAccountChart
@@ -63,27 +59,20 @@ export default {
loading: true,
error: false,
ready: false,
initialised: false,
dataCollection: {},
chartOptions: {},
_chart: null,
localTimeZone: '',
systemTimeZone: '',
}
},
created() {
this.ready = true;
this.chartOptions = DefaultLineOptions.methods.getDefaultOptions();
this.localTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
this.systemTimeZone = this.timezone;
this.ready = true;
},
computed: {
...mapGetters('dashboard/index',['start', 'end']),
...mapGetters('root',['timezone']),
...mapGetters('dashboard/index', ['start', 'end']),
'datesReady': function () {
return null !== this.start && null !== this.end && this.ready;
},
timezoneDifference: function() {
return this.localTimeZone !== this.systemTimeZone;
}
},
watch: {
@@ -93,18 +82,20 @@ export default {
}
},
start: function () {
//this.initialiseChart();
this.updateChart();
},
end: function () {
//this.initialiseChart();
this.updateChart();
},
},
methods: {
initialiseChart: function () {
this.loading = true;
this.error = false;
let startStr = this.start.toISOString().split('T')[0];
let endStr = this.end.toISOString().split('T')[0];
//let startStr = this.start.toISOString().split('T')[0];
//let endStr = this.end.toISOString().split('T')[0];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
let url = './api/v1/chart/account/overview?start=' + startStr + '&end=' + endStr;
axios.get(url)
.then(response => {
@@ -116,18 +107,39 @@ export default {
this.drawChart();
})
.catch(error => {
// console.log('Has error!');
// console.log(error);
console.log('Has error!');
console.log(error);
this.error = true;
});
},
drawChart: function () {
this._chart = new ChartJs.Chart(this.$refs.canvas.getContext('2d'), {
type: 'line',
data: this.dataCollection,
options: this.chartOptions
}
);
//console.log('drawChart');
if ('undefined' !== typeof this._chart) {
// console.log('update!');
this._chart.data = this.dataCollection;
this._chart.update();
this.initialised = true;
}
if ('undefined' === typeof this._chart) {
// console.log('new!');
this._chart = new ChartJs.Chart(this.$refs.canvas.getContext('2d'), {
type: 'line',
data: this.dataCollection,
options: this.chartOptions
}
);
this.initialised = true;
}
},
updateChart: function () {
// console.log('updateChart');
if (this.initialised) {
// console.log('MUST Update chart!');
// reset some vars so it wont trigger again:
this.initialised = false;
this.initialiseChart();
}
}
},
}

View File

@@ -75,6 +75,7 @@
<script>
import {createNamespacedHelpers} from "vuex";
import format from "date-fns/format";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
@@ -123,7 +124,7 @@ export default {
initialiseList: function () {
this.loading = true;
this.accounts = [];
axios.get('./api/v1/preferences/frontpageAccounts')
axios.get('./api/v1/preferences/frontPageAccounts')
.then(response => {
this.loadAccounts(response);
}
@@ -162,8 +163,10 @@ export default {
);
},
loadTransactions(key, accountId) {
let startStr = this.start.toISOString().split('T')[0];
let endStr = this.end.toISOString().split('T')[0];
// let startStr = this.start.toISOString().split('T')[0];
// let endStr = this.end.toISOString().split('T')[0];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
axios.get('./api/v1/accounts/' + accountId + '/transactions?page=1&limit=10&start=' + startStr + '&end=' + endStr)
.then(response => {
this.accounts[key].transactions = response.data.data;

View File

@@ -77,6 +77,7 @@
</template>
<script>
import {createNamespacedHelpers} from "vuex";
import format from "date-fns/format";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
export default {
@@ -125,8 +126,10 @@ export default {
initialiseBills: function () {
this.loading = true;
this.bills = [];
let startStr = this.start.toISOString().split('T')[0];
let endStr = this.end.toISOString().split('T')[0];
// let startStr = this.start.toISOString().split('T')[0];
// let endStr = this.end.toISOString().split('T')[0];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
axios.get('./api/v1/bills?start=' + startStr + '&end=' + endStr)
.then(response => {

View File

@@ -68,6 +68,7 @@
<script>
import BudgetListGroup from "./BudgetListGroup";
import {createNamespacedHelpers} from "vuex";
import format from "date-fns/format";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
@@ -136,8 +137,10 @@ export default {
other: [],
};
this.loading = true;
let startStr = this.start.toISOString().split('T')[0];
let endStr = this.end.toISOString().split('T')[0];
// let startStr = this.start.toISOString().split('T')[0];
// let endStr = this.end.toISOString().split('T')[0];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
axios.get('./api/v1/budgets?start=' + startStr + '&end=' + endStr)
.then(response => {
this.parseBudgets(response.data);
@@ -172,8 +175,10 @@ export default {
this.getBudgetLimits();
},
getBudgetLimits() {
let startStr = this.start.toISOString().split('T')[0];
let endStr = this.end.toISOString().split('T')[0];
// let startStr = this.start.toISOString().split('T')[0];
// let endStr = this.end.toISOString().split('T')[0];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
axios.get('./api/v1/budget-limits?start=' + startStr + '&end=' + endStr)
.then(response => {
this.parseBudgetLimits(response.data);

View File

@@ -42,7 +42,7 @@
<thead>
<tr>
<th scope="col">{{ $t('firefly.category') }}</th>
<th scope="col">{{ $t('firefly.spent') }}</th>
<th scope="col">{{ $t('firefly.spent') }} / {{ $t('firefly.earned') }}</th>
</tr>
</thead>
<tbody>
@@ -90,6 +90,7 @@
<script>
import {createNamespacedHelpers} from "vuex";
import format from "date-fns/format";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
@@ -142,8 +143,10 @@ export default {
this.spent = 0;
this.earned = 0;
this.loading = true;
let startStr = this.start.toISOString().split('T')[0];
let endStr = this.end.toISOString().split('T')[0];
// let startStr = this.start.toISOString().split('T')[0];
// let endStr = this.end.toISOString().split('T')[0];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
this.getCategoryPage(startStr, endStr, 1);
},
getCategoryPage: function (start, end, page) {

View File

@@ -41,8 +41,8 @@
<caption style="display:none;">{{ $t('firefly.revenue_accounts') }}</caption>
<thead>
<tr>
<th scope="col">{{ $t('firefly.category') }}</th>
<th scope="col">{{ $t('firefly.spent') }}</th>
<th scope="col">{{ $t('firefly.account') }}</th>
<th scope="col">{{ $t('firefly.earned') }}</th>
</tr>
</thead>
<tbody>
@@ -51,7 +51,7 @@
<td class="align-middle">
<div v-if="entry.pct > 0" class="progress">
<div :aria-valuenow="entry.pct" :style="{ width: entry.pct + '%'}" aria-valuemax="100"
aria-valuemin="0" class="progress-bar progress-bar-striped bg-success"
aria-valuemin="0" class="progress-bar bg-success"
role="progressbar">
<span v-if="entry.pct > 20">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float) }}
@@ -75,6 +75,7 @@
<script>
import {createNamespacedHelpers} from "vuex";
import format from "date-fns/format";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
@@ -126,8 +127,10 @@ export default {
this.loading = true;
this.income = [];
this.error = false;
let startStr = this.start.toISOString().split('T')[0];
let endStr = this.end.toISOString().split('T')[0];
// let startStr = this.start.toISOString().split('T')[0];
// let endStr = this.end.toISOString().split('T')[0];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
axios.get('./api/v1/insight/income/revenue?start=' + startStr + '&end=' + endStr)
.then(response => {
// do something with response.

View File

@@ -41,7 +41,7 @@
<caption style="display:none;">{{ $t('firefly.expense_accounts') }}</caption>
<thead>
<tr>
<th scope="col">{{ $t('firefly.category') }}</th>
<th scope="col">{{ $t('firefly.account') }}</th>
<th scope="col">{{ $t('firefly.spent') }}</th>
</tr>
</thead>
@@ -51,7 +51,7 @@
<td class="align-middle">
<div v-if="entry.pct > 0" class="progress">
<div :aria-valuenow="entry.pct" :style="{ width: entry.pct + '%'}" aria-valuemax="100"
aria-valuemin="0" class="progress-bar progress-bar-striped bg-danger"
aria-valuemin="0" class="progress-bar bg-danger"
role="progressbar">
<span v-if="entry.pct > 20">
{{ Intl.NumberFormat(locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float) }}
@@ -75,6 +75,7 @@
<script>
import {createNamespacedHelpers} from "vuex";
import format from "date-fns/format";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
@@ -125,8 +126,10 @@ export default {
this.loading = true;
this.error = false;
this.expenses = [];
let startStr = this.start.toISOString().split('T')[0];
let endStr = this.end.toISOString().split('T')[0];
// let startStr = this.start.toISOString().split('T')[0];
// let endStr = this.end.toISOString().split('T')[0];
let startStr = format(this.start, 'y-MM-dd');
let endStr = format(this.end, 'y-MM-dd');
axios.get('./api/v1/insight/expense/expense?start=' + startStr + '&end=' + endStr)
.then(response => {
// do something with response.

View File

@@ -58,7 +58,7 @@
<td>
<div class="progress-group">
<div class="progress progress-sm">
<div v-if="piggy.attributes.pct < 100" :style="{'width': piggy.attributes.pct + '%'}" class="progress-bar progress-bar-striped primary"></div>
<div v-if="piggy.attributes.pct < 100" :style="{'width': piggy.attributes.pct + '%'}" class="progress-bar primary"></div>
<div v-if="100 === piggy.attributes.pct" :style="{'width': piggy.attributes.pct + '%'}"
class="progress-bar progress-bar-striped bg-success"></div>
</div>

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