mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-08-16 19:04:36 +00:00
Compare commits
260 Commits
v6.1.11
...
develop-20
Author | SHA1 | Date | |
---|---|---|---|
|
7170931464 | ||
|
c1b5a1a13e | ||
|
a6265ce8ab | ||
|
90109917df | ||
|
0acd54c2b7 | ||
|
c96226b9b4 | ||
|
6d143f1624 | ||
|
93324d1154 | ||
|
a39f0e1891 | ||
|
822f609a22 | ||
|
cd7ddd1c61 | ||
|
0b63ba26bb | ||
|
94d70cdb62 | ||
|
acb3831c8b | ||
|
c9d9ecede4 | ||
|
4eb5873353 | ||
|
7ca39fdb21 | ||
|
b8d1d7a8c0 | ||
|
1af79eab30 | ||
|
03be2704ce | ||
|
34baea66a7 | ||
|
0638d109d0 | ||
|
561e228a2d | ||
|
cb5d856769 | ||
|
04fe5d1fc4 | ||
|
45e9d4f8de | ||
|
73fdbb6202 | ||
|
e49dbefddd | ||
|
fc5143337a | ||
|
4b3eb6dace | ||
|
c741b2a819 | ||
|
cebfaa32bf | ||
|
d356d39d43 | ||
|
7d9f22d3f4 | ||
|
c6c8f282e2 | ||
|
6a64420721 | ||
|
fcde4e2488 | ||
|
aa5c4c20e9 | ||
|
794e31e487 | ||
|
16bf186312 | ||
|
45c722e786 | ||
|
36d9e5c3fe | ||
|
8d614de67f | ||
|
d17da670ab | ||
|
5bf4df9ad8 | ||
|
07db6b59ce | ||
|
e16f1cf4ee | ||
|
4c80d929ca | ||
|
16364d9859 | ||
|
c24f6acb2c | ||
|
4bd19e0627 | ||
|
69d839997a | ||
|
c02c027f4f | ||
|
b14606625e | ||
|
222d7b56c7 | ||
|
b7f7bf42b2 | ||
|
0cbd64d31a | ||
|
60bdae47c4 | ||
|
ca4b38d905 | ||
|
3674465f53 | ||
|
b951d4130c | ||
|
7992b810fd | ||
|
c1c0afa40b | ||
|
56c9026299 | ||
|
021ddfc36b | ||
|
5bd72f6428 | ||
|
feabfe54f0 | ||
|
565409b486 | ||
|
f57366da5f | ||
|
064217ccb0 | ||
|
fa3ccbda33 | ||
|
f43aadf02d | ||
|
3b8a4d3e9b | ||
|
3d410556ef | ||
|
f15ca1d0a1 | ||
|
7002463c54 | ||
|
649f876437 | ||
|
3cfd178cbd | ||
|
cefbaafa19 | ||
|
a8c88800c4 | ||
|
9d1a127200 | ||
|
3fdde2d1c8 | ||
|
cdc0b8dd2c | ||
|
1a1e06e6e8 | ||
|
6d39b8468c | ||
|
bdee3947b2 | ||
|
2317037655 | ||
|
dcea6b757b | ||
|
bd7fe92818 | ||
|
850e47d8db | ||
|
96fe62400f | ||
|
5d07fcdcb6 | ||
|
fd5d2d57a8 | ||
|
8e7d42201f | ||
|
f26bd3cb31 | ||
|
36915cdace | ||
|
8a5cecd2a0 | ||
|
78da1b22bb | ||
|
6d970a9794 | ||
|
8bb7739f05 | ||
|
7788bb4b33 | ||
|
2ecb4bb3b7 | ||
|
4a783d3c3c | ||
|
e16645ae87 | ||
|
9d3189be7e | ||
|
07fca78293 | ||
|
82080501c7 | ||
|
d93d6bfc66 | ||
|
a41326ef94 | ||
|
90b77845c3 | ||
|
57af80d820 | ||
|
fc4d5a1dfd | ||
|
8ab9ab8d21 | ||
|
75674b5793 | ||
|
a7d6f26051 | ||
|
eb540ce148 | ||
|
a3077fe43b | ||
|
63bb84d375 | ||
|
e5f5aa628e | ||
|
c54f84dc8e | ||
|
c2e562623c | ||
|
c8d5e8a9dc | ||
|
963f017be3 | ||
|
0e0eeb736f | ||
|
e8d9b8fa49 | ||
|
c166b9242e | ||
|
8ff8efced2 | ||
|
0b4fb9a806 | ||
|
ba9fef9410 | ||
|
f7d94d17cd | ||
|
1fea9c6817 | ||
|
a88c8bedbe | ||
|
fbf3468053 | ||
|
2a3ba9799e | ||
|
d121aad28f | ||
|
dc808fa807 | ||
|
a1be6ff62b | ||
|
20dc5b0256 | ||
|
edd54e23c5 | ||
|
1238df8784 | ||
|
b8c62652b0 | ||
|
54b2d02f63 | ||
|
47faf89a5c | ||
|
b7fb5a3854 | ||
|
a384b4202a | ||
|
99ecac0ce4 | ||
|
6102982456 | ||
|
b88e981b4b | ||
|
827263b03e | ||
|
d44e74d334 | ||
|
911f46c590 | ||
|
7d42c4ee5d | ||
|
ea89f6177f | ||
|
74291b3870 | ||
|
2c4f2082fe | ||
|
d8d58cc29b | ||
|
85b17e4035 | ||
|
83de5667b3 | ||
|
5fffe873c6 | ||
|
78c09c82d6 | ||
|
704abc315d | ||
|
86b4965458 | ||
|
d2dc0c2bf0 | ||
|
9f4894bbb5 | ||
|
76a8675a34 | ||
|
6988301da1 | ||
|
109cd37211 | ||
|
284ff4d1b0 | ||
|
bc0ab7af99 | ||
|
a17bc7258f | ||
|
87911c2438 | ||
|
746f1fd300 | ||
|
9e5faf919f | ||
|
cc6cbe6605 | ||
|
dc6d708897 | ||
|
6189d24b98 | ||
|
f6e28dc88f | ||
|
75ea035630 | ||
|
4cdb14301d | ||
|
9f95221ba3 | ||
|
e3a67be412 | ||
|
5749b642ce | ||
|
baff7c67f9 | ||
|
ccc005942f | ||
|
5b83c33039 | ||
|
cc32578c5f | ||
|
80f410835b | ||
|
b537a3145d | ||
|
bfa1fcbaf8 | ||
|
56243907c4 | ||
|
5928dd72e6 | ||
|
c6bf0ff1cd | ||
|
19d1cf192b | ||
|
37d7dc7e3e | ||
|
95a3a194b8 | ||
|
3542387188 | ||
|
da1b002a64 | ||
|
46daee28e7 | ||
|
9ade5635d4 | ||
|
e14e80f33c | ||
|
0c824e21c8 | ||
|
fab1c68569 | ||
|
c1534657f2 | ||
|
39841de680 | ||
|
43a720b62b | ||
|
5ec54de29e | ||
|
397e37f344 | ||
|
b6f84c2b99 | ||
|
843f86fc66 | ||
|
0e8e364074 | ||
|
bbccbef578 | ||
|
ee11a8e3a0 | ||
|
e8618047bd | ||
|
f104b76f73 | ||
|
cb701d8506 | ||
|
70a334c56e | ||
|
e6b2db1e29 | ||
|
e8dffa0052 | ||
|
c4f0512f39 | ||
|
3268019d0c | ||
|
a0ef6a1fc8 | ||
|
99d0098b20 | ||
|
a7a54c042c | ||
|
c44e48a793 | ||
|
53b501ca73 | ||
|
322f70bcca | ||
|
35559c077b | ||
|
590ffe7c76 | ||
|
8a2d8f148e | ||
|
4f0e15e07d | ||
|
7463861e0c | ||
|
1e70fa28be | ||
|
26c6ca470b | ||
|
5e54034e0e | ||
|
25873ef734 | ||
|
1092b04b22 | ||
|
01ce74dd72 | ||
|
41430d8386 | ||
|
01eb19169c | ||
|
cfaa7d7c68 | ||
|
14d3312a10 | ||
|
87be478dd8 | ||
|
0b6877a20e | ||
|
7186f0ef60 | ||
|
538933691e | ||
|
46c49ddbd8 | ||
|
bcfb134b6e | ||
|
57981f1cf9 | ||
|
0310186fb7 | ||
|
4dcb38290e | ||
|
2f5c37048b | ||
|
370c8b16ae | ||
|
af0555592a | ||
|
9c07ddaed6 | ||
|
bb7355a566 | ||
|
1d48347f8c | ||
|
060b76ca9c | ||
|
2b2b9b6f7a | ||
|
f3dd05a0c0 | ||
|
47a91aa273 |
@@ -19,6 +19,8 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;
|
||||
|
||||
$current = __DIR__;
|
||||
|
||||
$paths = [
|
||||
@@ -35,6 +37,7 @@ $finder = PhpCsFixer\Finder::create()
|
||||
|
||||
|
||||
$config = new PhpCsFixer\Config();
|
||||
$config->setParallelConfig(ParallelConfigFactory::detect());
|
||||
return $config->setRules(
|
||||
[
|
||||
// rule sets
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"require": {
|
||||
"friendsofphp/php-cs-fixer": "^3.12"
|
||||
}
|
||||
"require": {
|
||||
"friendsofphp/php-cs-fixer": "^3.12"
|
||||
}
|
||||
}
|
||||
|
886
.ci/php-cs-fixer/composer.lock
generated
886
.ci/php-cs-fixer/composer.lock
generated
File diff suppressed because it is too large
Load Diff
19
.ci/phpcs.sh
19
.ci/phpcs.sh
@@ -20,23 +20,8 @@
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# Install composer packages
|
||||
#composer install --no-scripts --no-ansi
|
||||
|
||||
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
|
||||
# enable test .env file.
|
||||
# cp .ci/.env.ci .env
|
||||
|
||||
OUTPUT_FORMAT=txt
|
||||
EXTRA_PARAMS=""
|
||||
|
||||
if [[ $GITHUB_ACTIONS = "true" ]]
|
||||
then
|
||||
OUTPUT_FORMAT=txt
|
||||
EXTRA_PARAMS=""
|
||||
fi
|
||||
|
||||
# clean up php code
|
||||
cd $SCRIPT_DIR/php-cs-fixer
|
||||
composer update --quiet
|
||||
@@ -44,8 +29,8 @@ rm -f .php-cs-fixer.cache
|
||||
PHP_CS_FIXER_IGNORE_ENV=true
|
||||
./vendor/bin/php-cs-fixer fix \
|
||||
--config $SCRIPT_DIR/php-cs-fixer/.php-cs-fixer.php \
|
||||
--format=$OUTPUT_FORMAT \
|
||||
--allow-risky=yes $EXTRA_PARAMS
|
||||
--format=txt \
|
||||
--allow-risky=yes
|
||||
|
||||
EXIT_CODE=$?
|
||||
|
||||
|
28
.ci/phpmd/composer.lock
generated
28
.ci/phpmd/composer.lock
generated
@@ -9,16 +9,16 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "composer/pcre",
|
||||
"version": "3.1.2",
|
||||
"version": "3.1.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/pcre.git",
|
||||
"reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace"
|
||||
"reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/pcre/zipball/4775f35b2d70865807c89d32c8e7385b86eb0ace",
|
||||
"reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace",
|
||||
"url": "https://api.github.com/repos/composer/pcre/zipball/5b16e25a5355f1f3afdfc2f954a0a80aec4826a8",
|
||||
"reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -60,7 +60,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/composer/pcre/issues",
|
||||
"source": "https://github.com/composer/pcre/tree/3.1.2"
|
||||
"source": "https://github.com/composer/pcre/tree/3.1.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -76,20 +76,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-03-07T15:38:35+00:00"
|
||||
"time": "2024-03-19T10:26:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/xdebug-handler",
|
||||
"version": "3.0.3",
|
||||
"version": "3.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/xdebug-handler.git",
|
||||
"reference": "ced299686f41dce890debac69273b47ffe98a40c"
|
||||
"reference": "4f988f8fdf580d53bdb2d1278fe93d1ed5462255"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c",
|
||||
"reference": "ced299686f41dce890debac69273b47ffe98a40c",
|
||||
"url": "https://api.github.com/repos/composer/xdebug-handler/zipball/4f988f8fdf580d53bdb2d1278fe93d1ed5462255",
|
||||
"reference": "4f988f8fdf580d53bdb2d1278fe93d1ed5462255",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -100,7 +100,7 @@
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.1",
|
||||
"symfony/phpunit-bridge": "^6.0"
|
||||
"phpunit/phpunit": "^8.5 || ^9.6 || ^10.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@@ -124,9 +124,9 @@
|
||||
"performance"
|
||||
],
|
||||
"support": {
|
||||
"irc": "irc://irc.freenode.org/composer",
|
||||
"irc": "ircs://irc.libera.chat:6697/composer",
|
||||
"issues": "https://github.com/composer/xdebug-handler/issues",
|
||||
"source": "https://github.com/composer/xdebug-handler/tree/3.0.3"
|
||||
"source": "https://github.com/composer/xdebug-handler/tree/3.0.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -142,7 +142,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-02-25T21:32:43+00:00"
|
||||
"time": "2024-03-26T18:29:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pdepend/pdepend",
|
||||
|
@@ -19,9 +19,9 @@
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<ruleset name="pcsg-generated-ruleset"
|
||||
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
name="pcsg-generated-ruleset"
|
||||
xmlns="http://pmd.sf.net/ruleset/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
|
||||
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
|
||||
<description>Firefly III ruleset.</description>
|
||||
|
26
.env.example
26
.env.example
@@ -111,7 +111,10 @@ PGSQL_SSL_CERT=null
|
||||
PGSQL_SSL_KEY=null
|
||||
PGSQL_SSL_CRL_FILE=null
|
||||
|
||||
# more PostgreSQL settings
|
||||
# For postgresql 15 and up, setting this to public will no longer work as expected, becasuse the
|
||||
# 'public' schema is without grants. This can be worked around by having a super user grant those
|
||||
# necessary privileges, but in security conscious setups that's not viable.
|
||||
# You will need to set this to the schema you want to use.
|
||||
PGSQL_SCHEMA=public
|
||||
|
||||
# If you're looking for performance improvements, you could install memcached or redis
|
||||
@@ -296,27 +299,6 @@ DKR_BUILD_LOCALE=false
|
||||
# Won't significantly speed up things.
|
||||
DKR_CHECK_SQLITE=true
|
||||
|
||||
# Run database creation and migration commands. Disable this only if you're 100% sure the DB exists
|
||||
# and is up to date.
|
||||
DKR_RUN_MIGRATION=true
|
||||
|
||||
# Run database upgrade commands. Disable this only when you're 100% sure your DB is up-to-date
|
||||
# with the latest fixes (outside of migrations!)
|
||||
DKR_RUN_UPGRADE=true
|
||||
|
||||
# Verify database integrity. Includes all data checks and verifications.
|
||||
# Disabling this makes Firefly III assume your DB is intact.
|
||||
DKR_RUN_VERIFY=true
|
||||
|
||||
# Run database reporting commands. When disabled, Firefly III won't go over your data to report current state.
|
||||
# Disabling this should have no impact on data integrity or safety but it won't warn you of possible issues.
|
||||
DKR_RUN_REPORT=true
|
||||
|
||||
# Generate OAuth2 keys.
|
||||
# When disabled, Firefly III won't attempt to generate OAuth2 Passport keys. This won't be an issue, IFF (if and only if)
|
||||
# you had previously generated keys already and they're stored in your database for restoration.
|
||||
DKR_RUN_PASSPORT_INSTALL=true
|
||||
|
||||
# Leave the following configuration vars as is.
|
||||
# Unless you like to tinker and know what you're doing.
|
||||
APP_NAME=FireflyIII
|
||||
|
18
.github/pull_request_template.md
vendored
18
.github/pull_request_template.md
vendored
@@ -1,13 +1,19 @@
|
||||
<!--
|
||||
Before you create a new PR, please consider:
|
||||
Thank you for submitting new code to Firefly III, or any of the related projects. Please read the following rules carefully.
|
||||
|
||||
1) Pull requests for the MAIN branch will be closed.
|
||||
2) DO NOT include translations in your PR. Only English US sentences.
|
||||
- Please do not submit solutions for problems that are not already reported in an issue.
|
||||
- Unfortunately, Firefly III can't be your learning experience. If you're new to all of this, please open an issue first.
|
||||
- Please do not open PRs to "discuss" possible solutions or to "get feedback" on your code. I simply don't have time for that.
|
||||
- Pull requests for the MAIN branch will be closed.
|
||||
- DO NOT include translated strings in your PR.
|
||||
|
||||
If it feels necessary to open an issue first, please do so, before you open a PR.
|
||||
|
||||
See also: https://docs.firefly-iii.org/explanation/support/#contributing-code
|
||||
|
||||
Thanks.
|
||||
-->
|
||||
|
||||
Fixes issue # (if relevant)
|
||||
|
||||
This PR fixes issue # (if relevant).
|
||||
|
||||
Changes in this pull request:
|
||||
|
||||
|
58
.github/stale.yml
vendored
58
.github/stale.yml
vendored
@@ -1,58 +0,0 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 14
|
||||
|
||||
# Number of days of inactivity before a stale Issue or Pull Request is closed.
|
||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||
daysUntilClose: 14
|
||||
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
||||
# - "[Status] Maybe Later"
|
||||
exemptLabels:
|
||||
- enhancement
|
||||
- feature
|
||||
- bug
|
||||
- announcement
|
||||
- "layout-v3"
|
||||
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: false
|
||||
|
||||
# Set to true to ignore issues in a milestone (defaults to false)
|
||||
exemptMilestones: false
|
||||
|
||||
# Label to use when marking as stale
|
||||
staleLabel: stale
|
||||
|
||||
# Comment to post when marking as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
|
||||
# Comment to post when removing the stale label.
|
||||
# unmarkComment: >
|
||||
# Your comment here.
|
||||
|
||||
# Comment to post when closing a stale Issue or Pull Request.
|
||||
# closeComment: >
|
||||
# Your comment here.
|
||||
|
||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
||||
limitPerRun: 30
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
# only: issues
|
||||
|
||||
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
|
||||
# pulls:
|
||||
# daysUntilStale: 30
|
||||
# markComment: >
|
||||
# This pull request has been automatically marked as stale because it has not had
|
||||
# recent activity. It will be closed if no further activity occurs. Thank you
|
||||
# for your contributions.
|
||||
|
||||
# issues:
|
||||
# exemptLabels:
|
||||
# - confirmed
|
2
.github/workflows/cleanup.yml
vendored
2
.github/workflows/cleanup.yml
vendored
@@ -7,7 +7,7 @@ permissions:
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
- cron: '0 1 * * *'
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
prune:
|
||||
|
4
.github/workflows/close-duplicates.yml
vendored
4
.github/workflows/close-duplicates.yml
vendored
@@ -3,7 +3,7 @@ name: "Issues - Command to close duplicate issues"
|
||||
# the workflow to execute on is comments that are newly created
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
types: [ created ]
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
close_duplicates:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: github/command@v1.1.0
|
||||
- uses: github/command@v1.2.0
|
||||
id: command
|
||||
with:
|
||||
allowed_contexts: "issue"
|
||||
|
4
.github/workflows/debug-info-actions.yml
vendored
4
.github/workflows/debug-info-actions.yml
vendored
@@ -3,9 +3,9 @@ name: 'Issues - Respond to hidden commands'
|
||||
# the workflow to execute on is comments that are newly created
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited]
|
||||
types: [ opened, edited ]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
types: [ created ]
|
||||
|
||||
# permissions needed for reacting to IssueOps commands on issues and PRs
|
||||
permissions:
|
||||
|
6
.github/workflows/label-actions.yml
vendored
6
.github/workflows/label-actions.yml
vendored
@@ -2,11 +2,11 @@ name: 'Issues - Reply to specific labels'
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled, unlabeled]
|
||||
types: [ labeled, unlabeled ]
|
||||
pull_request_target:
|
||||
types: [labeled, unlabeled]
|
||||
types: [ labeled, unlabeled ]
|
||||
discussion:
|
||||
types: [labeled, unlabeled]
|
||||
types: [ labeled, unlabeled ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
2
.github/workflows/lock.yml
vendored
2
.github/workflows/lock.yml
vendored
@@ -3,7 +3,7 @@ name: 'Issues - Lock old issues'
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
- cron: '0 2 * * *'
|
||||
|
||||
concurrency:
|
||||
group: lock-threads
|
||||
|
159
.github/workflows/release.yml
vendored
159
.github/workflows/release.yml
vendored
@@ -4,11 +4,11 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version to release'
|
||||
description: 'Release "v1.2.3" or "develop"'
|
||||
required: true
|
||||
default: 'develop'
|
||||
schedule:
|
||||
- cron: '15 0 * * MON,THU'
|
||||
- cron: '0 3 * * MON,THU'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
CROWDIN_TOKEN: ${{ secrets.CROWDIN_TOKEN }}
|
||||
- name: Cleanup translations
|
||||
id: cleanup-transactions
|
||||
uses: JC5/firefly-iii-dev@v34
|
||||
uses: JC5/firefly-iii-dev@main
|
||||
with:
|
||||
action: 'ff3:crowdin-warning'
|
||||
output: ''
|
||||
@@ -60,16 +60,25 @@ jobs:
|
||||
GH_TOKEN: ''
|
||||
- name: Cleanup changelog
|
||||
id: cleanup-changelog
|
||||
uses: JC5/firefly-iii-dev@v34
|
||||
uses: JC5/firefly-iii-dev@main
|
||||
with:
|
||||
action: 'ff3:changelog'
|
||||
output: ''
|
||||
env:
|
||||
FIREFLY_III_ROOT: /github/workspace
|
||||
GH_TOKEN: ${{ secrets.CHANGELOG_TOKEN }}
|
||||
- name: "Create THANKS.md"
|
||||
id: thank-you
|
||||
uses: JC5/firefly-iii-dev@main
|
||||
with:
|
||||
action: 'ff3:thank-you'
|
||||
output: ''
|
||||
env:
|
||||
FIREFLY_III_ROOT: /github/workspace
|
||||
GH_TOKEN: ''
|
||||
- name: Extract changelog
|
||||
id: extract-changelog
|
||||
uses: JC5/firefly-iii-dev@v34
|
||||
uses: JC5/firefly-iii-dev@main
|
||||
with:
|
||||
action: 'ff3:extract-changelog'
|
||||
output: 'output'
|
||||
@@ -78,7 +87,7 @@ jobs:
|
||||
GH_TOKEN: ""
|
||||
- name: Replace version
|
||||
id: replace-version
|
||||
uses: JC5/firefly-iii-dev@v34
|
||||
uses: JC5/firefly-iii-dev@main
|
||||
with:
|
||||
action: 'ff3:version'
|
||||
output: ''
|
||||
@@ -88,7 +97,7 @@ jobs:
|
||||
FF_III_VERSION: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
|
||||
- name: Generate JSON v1
|
||||
id: json-v1
|
||||
uses: JC5/firefly-iii-dev@v34
|
||||
uses: JC5/firefly-iii-dev@main
|
||||
with:
|
||||
action: 'ff3:json-translations v1'
|
||||
output: ''
|
||||
@@ -97,7 +106,7 @@ jobs:
|
||||
GH_TOKEN: ''
|
||||
- name: Generate JSON v2
|
||||
id: json-v2
|
||||
uses: JC5/firefly-iii-dev@v34
|
||||
uses: JC5/firefly-iii-dev@main
|
||||
with:
|
||||
action: 'ff3:json-translations v2'
|
||||
output: ''
|
||||
@@ -106,50 +115,76 @@ jobs:
|
||||
GH_TOKEN: ''
|
||||
- name: Code cleanup
|
||||
id: code-cleanup
|
||||
uses: JC5/firefly-iii-dev@v34
|
||||
uses: JC5/firefly-iii-dev@main
|
||||
with:
|
||||
action: 'ff3:code'
|
||||
output: ''
|
||||
env:
|
||||
FIREFLY_III_ROOT: /github/workspace
|
||||
GH_TOKEN: ''
|
||||
- name: Build new JS
|
||||
- name: Build JS
|
||||
run: |
|
||||
pwd
|
||||
npm install
|
||||
npm run build
|
||||
- name: Build old JS
|
||||
id: old-js
|
||||
uses: JC5/firefly-iii-dev@v34
|
||||
with:
|
||||
action: 'ff3:old-js'
|
||||
output: ''
|
||||
env:
|
||||
FIREFLY_III_ROOT: /github/workspace
|
||||
GH_TOKEN: ''
|
||||
npm update
|
||||
npm run prod --workspace=v1
|
||||
npm run build --workspace=v2
|
||||
- name: Run CI
|
||||
run: |
|
||||
rm -rf vendor composer.lock
|
||||
composer validate --strict
|
||||
composer update --no-dev --no-scripts --no-plugins -q
|
||||
sudo chown -R runner:docker resources/lang
|
||||
.ci/phpcs.sh
|
||||
- name: Import GPG key
|
||||
uses: crazy-max/ghaction-import-gpg@v6
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.PASSPHRASE }}
|
||||
- name: Release
|
||||
run: |
|
||||
# do some configuration
|
||||
sudo timedatectl set-timezone Europe/Amsterdam
|
||||
git config user.name github-actions
|
||||
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
||||
git config advice.addIgnoredFile false
|
||||
|
||||
# set some variables
|
||||
releaseName=$version
|
||||
originalName=$version
|
||||
zipName=FireflyIII-$version.zip
|
||||
tarName=FireflyIII-$version.tar.gz
|
||||
|
||||
# update composer (again)
|
||||
composer update --no-dev --no-scripts --no-plugins
|
||||
composer dump-autoload
|
||||
|
||||
# if this is a develop build, slightly different variable names.
|
||||
if [[ "develop" == "$version" ]]; then
|
||||
[[ -z $(git status --untracked-files=normal --porcelain) ]] && echo "this branch is clean, no need to push..." && exit 0;
|
||||
releaseName=$version-$(date +'%Y%m%d')
|
||||
originalName=$releaseName
|
||||
zipName=FireflyIII-develop.zip
|
||||
tarName=FireflyIII-develop.tar.gz
|
||||
fi
|
||||
|
||||
# in both cases, if the release or tag already exists, add ".1" until it no longer exists.
|
||||
tagFound=true
|
||||
tagCount=1
|
||||
while [ "$tagFound" = true ]
|
||||
do
|
||||
if [ $(git tag -l "$releaseName") ]; then
|
||||
echo "Tag $releaseName exists already."
|
||||
releaseName="$originalName"."$tagCount"
|
||||
echo "Tag for release is now $releaseName"
|
||||
tagCount=$((tagCount+1))
|
||||
else
|
||||
echo "Tag $releaseName does not exist, can continue"
|
||||
tagFound=false
|
||||
fi
|
||||
done
|
||||
echo "Will use tag and release name $releaseName."
|
||||
|
||||
# add all content, except output.txt (this contains the changelog and/or the download instructions)
|
||||
echo 'Add all and reset output.txt'
|
||||
git add -A
|
||||
if test -f "output.txt"; then
|
||||
git reset output.txt
|
||||
@@ -157,29 +192,101 @@ jobs:
|
||||
git commit -m "Auto commit for release '$version' on $(date +'%Y-%m-%d')" || true
|
||||
git push
|
||||
|
||||
# zip everything
|
||||
zip -rq $zipName . -x "*.git*" "*.ci*" "*.github*" "*node_modules*"
|
||||
# zip and tar everything
|
||||
echo 'Zip and tar...'
|
||||
zip -rq $zipName . -x "*.git*" "*.ci*" "*.github*" "*node_modules*" "*output.txt*"
|
||||
touch $tarName
|
||||
tar --exclude=$tarName --exclude=$zipName --exclude='./.git' --exclude='./.ci' --exclude='./.github' --exclude='./node_modules' --exclude='./output.txt' -czf $tarName .
|
||||
|
||||
# add sha256 sum
|
||||
echo 'Sha sum ...'
|
||||
sha256sum -b $zipName > $zipName.sha256
|
||||
sha256sum -b $tarName > $tarName.sha256
|
||||
|
||||
# add signatures:
|
||||
gpg --armor --detach-sign $zipName
|
||||
gpg --armor --detach-sign $tarName
|
||||
|
||||
# create a development (nightly) release:
|
||||
if [[ "develop" == "$version" ]]; then
|
||||
echo 'Develop release.'
|
||||
# add text to output.txt (instructions)
|
||||
rm output.txt
|
||||
echo "Bi-weekly development release of Firefly III with the latest fixes, translations and features. Docker users can find this release under the \`develop\` tag." >> output.txt
|
||||
echo "" >> output.txt
|
||||
echo "This release was created on **$(date +'%Y-%m-%d')** and may contain unexpected bugs. Data loss is rare but is not impossible. The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/)." >> output.txt
|
||||
echo "" >> output.txt
|
||||
echo "* Please read the installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/)" >> output.txt
|
||||
echo "* Or read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)" >> output.txt
|
||||
echo "" >> output.txt
|
||||
echo ":warning: Please be careful with this pre-release, as it may not work as expected." >> output.txt
|
||||
|
||||
# create the release:
|
||||
echo "Create nightly release."
|
||||
git tag -a $releaseName -m "Nightly development release '$version' on $(date +'%Y-%m-%d')"
|
||||
git push origin $releaseName
|
||||
gh release create $releaseName -p --verify-tag \
|
||||
-t "Development release for $(date +'%Y-%m-%d')" \
|
||||
-n "Bi-weekly development release of Firefly III with the latest fixes, translations and features. This release was created on **$(date +'%Y-%m-%d')** and may contain bugs. Use at your own risk. Docker users can find this release under the \`develop\` tag."
|
||||
-F output.txt
|
||||
|
||||
# add zip file to release.
|
||||
gh release upload $releaseName $zipName
|
||||
gh release upload $releaseName $tarName
|
||||
|
||||
# add sha256 sum to release
|
||||
gh release upload $releaseName $zipName.sha256
|
||||
gh release upload $releaseName $tarName.sha256
|
||||
|
||||
# add signatures to release
|
||||
gh release upload $releaseName $zipName.asc
|
||||
gh release upload $releaseName $tarName.asc
|
||||
|
||||
# get current HEAD and add as file to the release
|
||||
HEAD=$(git rev-parse HEAD)
|
||||
echo $HEAD > HEAD.txt
|
||||
gh release upload $releaseName HEAD.txt
|
||||
else
|
||||
echo 'MAIN (real) release'
|
||||
sudo chown -R runner:docker output.txt
|
||||
# add text to output.txt (more instructions)
|
||||
echo '' >> output.txt
|
||||
echo '### Instructions' >> output.txt
|
||||
echo '' >> output.txt
|
||||
echo "* Installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/)" >> output.txt
|
||||
echo "* Or read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)" >> output.txt
|
||||
echo "* The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/)." >> output.txt
|
||||
|
||||
echo "Create default release."
|
||||
git tag -a $releaseName -m "Here be changelog"
|
||||
git push origin $releaseName
|
||||
gh release create $releaseName -F output.txt -t "$releaseName" --verify-tag
|
||||
# add zip file to release.
|
||||
|
||||
# add archive files to release
|
||||
gh release upload $releaseName $zipName
|
||||
gh release upload $releaseName $tarName
|
||||
|
||||
# add sha256 sums to release
|
||||
gh release upload $releaseName $zipName.sha256
|
||||
gh release upload $releaseName $tarName.sha256
|
||||
|
||||
# add signatures to release
|
||||
gh release upload $releaseName $zipName.asc
|
||||
gh release upload $releaseName $tarName.asc
|
||||
|
||||
# get current HEAD and add as file to the release
|
||||
HEAD=$(git rev-parse HEAD)
|
||||
echo $HEAD > HEAD.txt
|
||||
gh release upload $releaseName HEAD.txt
|
||||
|
||||
# remove all temporary files
|
||||
rm output.txt
|
||||
rm HEAD.txt
|
||||
rm $zipName
|
||||
rm $zipName.sha256
|
||||
rm $tarName
|
||||
rm $tarName.sha256
|
||||
|
||||
# merge main back into develop
|
||||
git checkout develop
|
||||
git merge main
|
||||
git push
|
||||
|
14
.github/workflows/stale.yml
vendored
14
.github/workflows/stale.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: "Issues - Mark and close stale issues"
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 1 * * *"
|
||||
- cron: "0 4 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
@@ -17,17 +17,17 @@ jobs:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: >
|
||||
Hi there!
|
||||
|
||||
stale-issue-message: |
|
||||
Hi there!
|
||||
|
||||
This is an automatic reply. `Share and enjoy`
|
||||
|
||||
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
|
||||
|
||||
Thank you for your contributions.
|
||||
stale-pr-message: >
|
||||
Hi there!
|
||||
|
||||
stale-pr-message: |
|
||||
Hi there!
|
||||
|
||||
This is an automatic reply. `Share and enjoy`
|
||||
|
||||
This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
|
||||
|
8
.gitignore
vendored
8
.gitignore
vendored
@@ -10,3 +10,11 @@ coverage.xml
|
||||
|
||||
# ignore generated files.
|
||||
public/build
|
||||
|
||||
# ignore v1 build files
|
||||
resources/assets/v1/node_modules
|
||||
resources/assets/v1/build
|
||||
|
||||
# ignore v2 build files
|
||||
resources/assets/v2/node_modules
|
||||
resources/assets/v2/build
|
||||
|
193
THANKS.md
Executable file
193
THANKS.md
Executable file
@@ -0,0 +1,193 @@
|
||||
# Thank you! :tada: :heart: :tada:
|
||||
|
||||
Over time, many people have contributed to Firefly III. Their efforts are not always visible, but always remembered and appreciated.
|
||||
Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution.
|
||||
|
||||
## 2024
|
||||
- imlonghao
|
||||
- Rahman Yusuf
|
||||
- Michael Thomas
|
||||
- WardenJakx
|
||||
- kuilin
|
||||
- Stevie Robinson
|
||||
- luzpaz
|
||||
- Lemuel Roberto Bonifácio
|
||||
- maureenferreira
|
||||
|
||||
## 2023
|
||||
- tieu1991
|
||||
- Maxco10
|
||||
- zqye
|
||||
- Mateus Pereira
|
||||
- josephbadow
|
||||
- Christian Desktop
|
||||
- Edgars
|
||||
- Hannah K
|
||||
- noxonad
|
||||
- Kaijia Feng
|
||||
- Marc Ordinas i Llopis
|
||||
- Kuba Turek
|
||||
- Julien Stébenne
|
||||
|
||||
## 2022
|
||||
- Johannes Zellner
|
||||
- Janne Heß
|
||||
- charlesteets
|
||||
- Nathan PERIER
|
||||
- Jan Willhaus
|
||||
- canoine
|
||||
- Rick Cuddy
|
||||
- James
|
||||
- Hugo Meyronneinc
|
||||
- naveen
|
||||
- neilnaveen
|
||||
- naveensrinivasan
|
||||
- Federico Micelli
|
||||
- George Hahn
|
||||
|
||||
## 2021
|
||||
- StillLoading
|
||||
- Igor Rzegocki
|
||||
- Lorenzo Breda
|
||||
- Hosh
|
||||
- Flightkick
|
||||
- alex6480
|
||||
- VREEdom
|
||||
- Hamza FADIL
|
||||
- Kasper Læssø Sørensen
|
||||
- Alex
|
||||
- Jeroen De Meerleer
|
||||
- Ruben van Erk
|
||||
- Fabian Zimmermann
|
||||
- Mirko Berger
|
||||
- KaihatsuOnline
|
||||
- MihataBG
|
||||
|
||||
## 2020
|
||||
- Hannes Körber
|
||||
- Julien Cassagne
|
||||
- bu4ak
|
||||
- Viktor Yakovlev
|
||||
- Oliver Kaufmann
|
||||
- Arvind Chembarpu
|
||||
- GrayStrider
|
||||
- psychowood
|
||||
- Hosh Sadiq
|
||||
- emansih
|
||||
- Aniruddha Maru
|
||||
- johnny
|
||||
- sephrat
|
||||
- bpatath
|
||||
- Florian Dupret
|
||||
- Maxim Kurbatov
|
||||
- Lucas Guima
|
||||
- Sandro
|
||||
- Ruben Verhoef
|
||||
- Daniel Idzerda
|
||||
- Calum Smith
|
||||
- Agraphie
|
||||
- Tomer Shvueli
|
||||
- Tomer S
|
||||
|
||||
## 2019
|
||||
- Pascal Jungblut
|
||||
- Justyn Shull
|
||||
- Timendum
|
||||
- Nicolas Lœuillet
|
||||
- Dominic Guhl
|
||||
- Melroy van den Berg
|
||||
- Henning Stein
|
||||
- Jan Klepek
|
||||
- Jonathan
|
||||
- Geoffrey “Frogeye” Preud'homme
|
||||
- Michael Fix
|
||||
- Juraj Mlich
|
||||
- Eddybrando Vásquez
|
||||
- hulloanson
|
||||
- Will Rouesnel
|
||||
- lastlink
|
||||
- Mr. Funk
|
||||
- Simon Taddiken
|
||||
- Joris
|
||||
- Bastiaan Nijkamp
|
||||
|
||||
## 2018
|
||||
- a1ex4
|
||||
- Daniel Quah
|
||||
- Marco Lourenço
|
||||
- Dennis Enderink
|
||||
- Luca Bognolo
|
||||
- Mike Conway
|
||||
- Ben
|
||||
- Mathieu Post
|
||||
- George Hertz
|
||||
- HamuZ HamuZ
|
||||
- David Meiseles
|
||||
- Erik Gelderblom
|
||||
- Luca Vallerini
|
||||
- Clemens Wijnekus
|
||||
- Jacob Weisz
|
||||
- Mateusz Gozdek
|
||||
- anmol26s
|
||||
- Kevin Hellemun
|
||||
- Shashank M Chakravarthy
|
||||
- Nico Schreiner
|
||||
- Paul Sohier
|
||||
- Brenden Conte
|
||||
- Ben Yanke
|
||||
- Andrew Prokhorenkov
|
||||
- devlearner
|
||||
- Kelvin
|
||||
- J'informatique
|
||||
|
||||
## 2017
|
||||
- Victor Mosin
|
||||
- Justin
|
||||
- Hugo van Duijn
|
||||
- Lukas Winkler
|
||||
- Marcin Szymanski
|
||||
- Jens Kat
|
||||
- koziolek
|
||||
- jleeong
|
||||
- Simon Hanna
|
||||
- richard & xeli.eu
|
||||
- Sergey Besedin
|
||||
- Welbert Serra
|
||||
- Joris de Vries
|
||||
- Patrick Kostjens
|
||||
- Enrico Lamperti
|
||||
- Christian Musa
|
||||
- Enno Lohmeier
|
||||
|
||||
## 2016
|
||||
- Sander
|
||||
- Toon Schoenmakers
|
||||
- Telyn
|
||||
- Sander Kleykens
|
||||
- Tom van der Werf
|
||||
- Matthew Peck
|
||||
- Sander Mulders
|
||||
- Bonno Nachtegaal-Karels
|
||||
- Niek Haarman
|
||||
- Edwin
|
||||
- Thijs Alkemade
|
||||
- zjean
|
||||
- Graham Miller
|
||||
- Robert Horlings
|
||||
- leander091
|
||||
|
||||
## 2015
|
||||
- Antonio Spinelli
|
||||
- Colin O'Dell
|
||||
- RonaldvanMeer
|
||||
- Richard Ebbers
|
||||
- Balazs Varkonyi
|
||||
- Niek van der Kooy
|
||||
- Ilya Kil
|
||||
|
||||
## 2014
|
||||
- Stewart Malik
|
||||
- Graham Campbell
|
||||
|
||||
|
||||
Thank you for all your support!
|
@@ -83,17 +83,17 @@ class AccountController extends Controller
|
||||
// user's preferences
|
||||
$defaultSet = $this->repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray();
|
||||
|
||||
/** @var Preference $frontPage */
|
||||
$frontPage = app('preferences')->get('frontPageAccounts', $defaultSet);
|
||||
/** @var Preference $frontpage */
|
||||
$frontpage = app('preferences')->get('frontpageAccounts', $defaultSet);
|
||||
$default = app('amount')->getDefaultCurrency();
|
||||
|
||||
if (!(is_array($frontPage->data) && count($frontPage->data) > 0)) {
|
||||
$frontPage->data = $defaultSet;
|
||||
$frontPage->save();
|
||||
if (!(is_array($frontpage->data) && count($frontpage->data) > 0)) {
|
||||
$frontpage->data = $defaultSet;
|
||||
$frontpage->save();
|
||||
}
|
||||
|
||||
// get accounts:
|
||||
$accounts = $this->repository->getAccountsById($frontPage->data);
|
||||
$accounts = $this->repository->getAccountsById($frontpage->data);
|
||||
$chartData = [];
|
||||
|
||||
/** @var Account $account */
|
||||
|
@@ -72,6 +72,12 @@ class UpdateController extends Controller
|
||||
app('log')->debug('Now in update routine for transaction group!');
|
||||
$data = $request->getAll();
|
||||
|
||||
// Fixes 8750.
|
||||
$transactions = $data['transactions'] ?? [];
|
||||
foreach ($transactions as $index => $info) {
|
||||
unset($data['transactions'][$index]['type']);
|
||||
}
|
||||
|
||||
$transactionGroup = $this->groupRepository->update($transactionGroup, $data);
|
||||
$manager = $this->getManager();
|
||||
|
||||
|
@@ -32,6 +32,7 @@ use FireflyIII\Models\Preference;
|
||||
use FireflyIII\Transformers\PreferenceTransformer;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection as FractalCollection;
|
||||
use League\Fractal\Resource\Item;
|
||||
@@ -84,6 +85,10 @@ class PreferencesController extends Controller
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
|
||||
if ('currencyPreference' === $preference->name) {
|
||||
throw new FireflyException('Please use api/v1/currencies/default instead.');
|
||||
}
|
||||
|
||||
/** @var PreferenceTransformer $transformer */
|
||||
$transformer = app(PreferenceTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
@@ -93,6 +98,32 @@ class PreferencesController extends Controller
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO This endpoint is not documented.
|
||||
*
|
||||
* Return a single preference by name.
|
||||
*/
|
||||
public function showList(Collection $collection): JsonResponse
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
$count = $collection->count();
|
||||
$pageSize = $this->parameters->get('limit');
|
||||
$preferences = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
|
||||
// make paginator:
|
||||
$paginator = new LengthAwarePaginator($preferences, $count, $pageSize, $this->parameters->get('page'));
|
||||
$paginator->setPath(route('api.v1.preferences.show-list').$this->buildParams());
|
||||
|
||||
/** @var PreferenceTransformer $transformer */
|
||||
$transformer = app(PreferenceTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($preferences, $transformer, self::RESOURCE_KEY);
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* This endpoint is documented at:
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/preferences/storePreference
|
||||
@@ -103,6 +134,11 @@ class PreferencesController extends Controller
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
$data = $request->getAll();
|
||||
|
||||
if ('currencyPreference' === $data['name']) {
|
||||
throw new FireflyException('Please use api/v1/currencies/default instead.');
|
||||
}
|
||||
|
||||
$pref = app('preferences')->set($data['name'], $data['data']);
|
||||
|
||||
/** @var PreferenceTransformer $transformer */
|
||||
@@ -122,6 +158,10 @@ class PreferencesController extends Controller
|
||||
*/
|
||||
public function update(PreferenceUpdateRequest $request, Preference $preference): JsonResponse
|
||||
{
|
||||
if ('currencyPreference' === $preference->name) {
|
||||
throw new FireflyException('Please use api/v1/currencies/default instead.');
|
||||
}
|
||||
|
||||
$manager = $this->getManager();
|
||||
$data = $request->getAll();
|
||||
$pref = app('preferences')->set($preference->name, $data['data']);
|
||||
|
@@ -63,7 +63,7 @@ class StoreRequest extends FormRequest
|
||||
'order' => $this->convertInteger('order'),
|
||||
'currency_code' => $this->convertString('currency_code'),
|
||||
'virtual_balance' => $this->convertString('virtual_balance'),
|
||||
'iban' => $this->convertString('iban'),
|
||||
'iban' => $this->convertIban('iban'),
|
||||
'BIC' => $this->convertString('bic'),
|
||||
'account_number' => $this->convertString('account_number'),
|
||||
'account_role' => $this->convertString('account_role'),
|
||||
|
@@ -51,7 +51,7 @@ class UpdateRequest extends FormRequest
|
||||
'include_net_worth' => ['include_net_worth', 'boolean'],
|
||||
'account_type_name' => ['type', 'convertString'],
|
||||
'virtual_balance' => ['virtual_balance', 'convertString'],
|
||||
'iban' => ['iban', 'convertString'],
|
||||
'iban' => ['iban', 'convertIban'],
|
||||
'BIC' => ['bic', 'convertString'],
|
||||
'account_number' => ['account_number', 'convertString'],
|
||||
'account_role' => ['account_role', 'convertString'],
|
||||
|
@@ -71,8 +71,9 @@ class StoreRequest extends FormRequest
|
||||
if (is_array($triggers)) {
|
||||
foreach ($triggers as $trigger) {
|
||||
$return[] = [
|
||||
'type' => $trigger['type'],
|
||||
'value' => $trigger['value'],
|
||||
'type' => $trigger['type'] ?? '',
|
||||
'value' => $trigger['value'] ?? null,
|
||||
'prohibited' => $this->convertBoolean((string)($trigger['prohibited'] ?? 'false')),
|
||||
'active' => $this->convertBoolean((string)($trigger['active'] ?? 'true')),
|
||||
'stop_processing' => $this->convertBoolean((string)($trigger['stop_processing'] ?? 'false')),
|
||||
];
|
||||
|
@@ -54,17 +54,13 @@ class TestRequest extends FormRequest
|
||||
|
||||
private function getDate(string $field): ?Carbon
|
||||
{
|
||||
$value = $this->query($field);
|
||||
$value = $this->query($field);
|
||||
if (is_array($value)) {
|
||||
return null;
|
||||
}
|
||||
$value = (string)$value;
|
||||
$result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10));
|
||||
if (false === $result) {
|
||||
return null;
|
||||
}
|
||||
$value = (string)$value;
|
||||
|
||||
return $result;
|
||||
return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10));
|
||||
}
|
||||
|
||||
private function getAccounts(): array
|
||||
|
@@ -48,17 +48,13 @@ class TriggerRequest extends FormRequest
|
||||
|
||||
private function getDate(string $field): ?Carbon
|
||||
{
|
||||
$value = $this->query($field);
|
||||
$value = $this->query($field);
|
||||
if (is_array($value)) {
|
||||
return null;
|
||||
}
|
||||
$value = (string)$value;
|
||||
$result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10));
|
||||
if (false === $result) {
|
||||
return null;
|
||||
}
|
||||
$value = (string)$value;
|
||||
|
||||
return $result;
|
||||
return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10));
|
||||
}
|
||||
|
||||
private function getAccounts(): array
|
||||
|
@@ -81,10 +81,12 @@ class UpdateRequest extends FormRequest
|
||||
if (is_array($triggers)) {
|
||||
foreach ($triggers as $trigger) {
|
||||
$active = array_key_exists('active', $trigger) ? $trigger['active'] : true;
|
||||
$prohibited = array_key_exists('prohibited', $trigger) ? $trigger['prohibited'] : false;
|
||||
$stopProcessing = array_key_exists('stop_processing', $trigger) ? $trigger['stop_processing'] : false;
|
||||
$return[] = [
|
||||
'type' => $trigger['type'],
|
||||
'value' => $trigger['value'],
|
||||
'prohibited' => $prohibited,
|
||||
'active' => $active,
|
||||
'stop_processing' => $stopProcessing,
|
||||
];
|
||||
|
@@ -48,17 +48,13 @@ class TestRequest extends FormRequest
|
||||
|
||||
private function getDate(string $field): ?Carbon
|
||||
{
|
||||
$value = $this->query($field);
|
||||
$value = $this->query($field);
|
||||
if (is_array($value)) {
|
||||
return null;
|
||||
}
|
||||
$value = (string)$value;
|
||||
$result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10));
|
||||
if (false === $result) {
|
||||
return null;
|
||||
}
|
||||
$value = (string)$value;
|
||||
|
||||
return $result;
|
||||
return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10));
|
||||
}
|
||||
|
||||
private function getAccounts(): array
|
||||
|
@@ -48,17 +48,13 @@ class TriggerRequest extends FormRequest
|
||||
|
||||
private function getDate(string $field): ?Carbon
|
||||
{
|
||||
$value = $this->query($field);
|
||||
$value = $this->query($field);
|
||||
if (is_array($value)) {
|
||||
return null;
|
||||
}
|
||||
$value = (string)$value;
|
||||
$result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10));
|
||||
if (false === $result) {
|
||||
return null;
|
||||
}
|
||||
$value = (string)$value;
|
||||
|
||||
return $result;
|
||||
return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10));
|
||||
}
|
||||
|
||||
private function getAccounts(): array
|
||||
|
@@ -103,14 +103,14 @@ class StoreRequest extends FormRequest
|
||||
// source of transaction. If everything is null, assume cash account.
|
||||
'source_id' => $this->integerFromValue((string)$object['source_id']),
|
||||
'source_name' => $this->clearString((string)$object['source_name']),
|
||||
'source_iban' => $this->clearString((string)$object['source_iban']),
|
||||
'source_iban' => $this->clearIban((string)$object['source_iban']),
|
||||
'source_number' => $this->clearString((string)$object['source_number']),
|
||||
'source_bic' => $this->clearString((string)$object['source_bic']),
|
||||
|
||||
// destination of transaction. If everything is null, assume cash account.
|
||||
'destination_id' => $this->integerFromValue((string)$object['destination_id']),
|
||||
'destination_name' => $this->clearString((string)$object['destination_name']),
|
||||
'destination_iban' => $this->clearString((string)$object['destination_iban']),
|
||||
'destination_iban' => $this->clearIban((string)$object['destination_iban']),
|
||||
'destination_number' => $this->clearString((string)$object['destination_number']),
|
||||
'destination_bic' => $this->clearString((string)$object['destination_bic']),
|
||||
|
||||
|
@@ -28,22 +28,21 @@ use FireflyIII\Api\V2\Controllers\Controller;
|
||||
use FireflyIII\Api\V2\Request\Autocomplete\AutocompleteRequest;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface;
|
||||
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||
use FireflyIII\Models\AccountBalance;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class AccountController
|
||||
*/
|
||||
class AccountController extends Controller
|
||||
{
|
||||
use AccountFilter;
|
||||
|
||||
private AdminAccountRepositoryInterface $adminRepository;
|
||||
private array $balanceTypes;
|
||||
private AccountRepositoryInterface $repository;
|
||||
private AccountRepositoryInterface $repository;
|
||||
private TransactionCurrency $default;
|
||||
private ExchangeRateConverter $converter;
|
||||
|
||||
/**
|
||||
* AccountController constructor.
|
||||
@@ -53,83 +52,88 @@ class AccountController extends Controller
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->adminRepository = app(AdminAccountRepositoryInterface::class);
|
||||
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
if (null !== $userGroup) {
|
||||
$this->adminRepository->setUserGroup($userGroup);
|
||||
}
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
$this->default = app('amount')->getDefaultCurrency();
|
||||
$this->converter = app(ExchangeRateConverter::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
$this->balanceTypes = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Documentation for this endpoint:
|
||||
* TODO list of checks
|
||||
* 1. use dates from ParameterBag
|
||||
* 2. Request validates dates
|
||||
* 3. Request includes user_group_id
|
||||
* 4. Endpoint is documented.
|
||||
* 5. Collector uses user_group_id
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @throws FireflyException
|
||||
* Documentation: https://api-docs.firefly-iii.org/?urls.primaryName=2.1.0%20(v2)#/autocomplete/getAccountsAC
|
||||
*/
|
||||
public function accounts(AutocompleteRequest $request): JsonResponse
|
||||
{
|
||||
$data = $request->getData();
|
||||
$types = $data['types'];
|
||||
$query = $data['query'];
|
||||
$date = $this->parameters->get('date') ?? today(config('app.timezone'));
|
||||
$result = $this->adminRepository->searchAccount((string)$query, $types, $data['limit']);
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$groupedResult = [];
|
||||
$allItems = [];
|
||||
$queryParameters = $request->getParameters();
|
||||
$result = $this->repository->searchAccount($queryParameters['query'], $queryParameters['account_types'], $queryParameters['size']);
|
||||
$return = [];
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($result as $account) {
|
||||
$nameWithBalance = $account->name;
|
||||
$currency = $this->repository->getAccountCurrency($account) ?? $defaultCurrency;
|
||||
|
||||
if (in_array($account->accountType->type, $this->balanceTypes, true)) {
|
||||
$balance = app('steam')->balance($account, $date);
|
||||
$nameWithBalance = sprintf('%s (%s)', $account->name, app('amount')->formatAnything($currency, $balance, false));
|
||||
}
|
||||
$type = (string)trans(sprintf('firefly.%s', $account->accountType->type));
|
||||
$groupedResult[$type] ??= [
|
||||
'group ' => $type,
|
||||
'items' => [],
|
||||
];
|
||||
$allItems[] = [
|
||||
'id' => (string)$account->id,
|
||||
'value' => (string)$account->id,
|
||||
'name' => $account->name,
|
||||
'name_with_balance' => $nameWithBalance,
|
||||
'label' => $nameWithBalance,
|
||||
'type' => $account->accountType->type,
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_name' => $currency->name,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
];
|
||||
$return[] = $this->parseAccount($account);
|
||||
}
|
||||
|
||||
usort(
|
||||
$allItems,
|
||||
static function (array $left, array $right): int {
|
||||
$order = [AccountType::ASSET, AccountType::REVENUE, AccountType::EXPENSE];
|
||||
$posLeft = (int)array_search($left['type'], $order, true);
|
||||
$posRight = (int)array_search($right['type'], $order, true);
|
||||
return response()->json($return);
|
||||
}
|
||||
|
||||
return $posLeft - $posRight;
|
||||
private function parseAccount(Account $account): array
|
||||
{
|
||||
$currency = $this->repository->getAccountCurrency($account);
|
||||
|
||||
return [
|
||||
'id' => (string) $account->id,
|
||||
'title' => $account->name,
|
||||
'meta' => [
|
||||
'type' => $account->accountType->type,
|
||||
'currency_id' => null === $currency ? null : (string) $currency->id,
|
||||
'currency_code' => $currency?->code,
|
||||
'currency_symbol' => $currency?->symbol,
|
||||
'currency_decimal_places' => $currency?->decimal_places,
|
||||
'account_balances' => $this->getAccountBalances($account),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function getAccountBalances(Account $account): array
|
||||
{
|
||||
$return = [];
|
||||
$balances = $this->repository->getAccountBalances($account);
|
||||
|
||||
/** @var AccountBalance $balance */
|
||||
foreach ($balances as $balance) {
|
||||
try {
|
||||
$return[] = $this->parseAccountBalance($balance);
|
||||
} catch (FireflyException $e) {
|
||||
Log::error(sprintf('Could not parse convert account balance: %s', $e->getMessage()));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return response()->json($allItems);
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function parseAccountBalance(AccountBalance $balance): array
|
||||
{
|
||||
$currency = $balance->transactionCurrency;
|
||||
|
||||
return [
|
||||
'title' => $balance->title,
|
||||
'native_amount' => $this->converter->convert($currency, $this->default, today(), $balance->balance),
|
||||
'amount' => app('steam')->bcround($balance->balance, $currency->decimal_places),
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
'native_currency_id' => (string) $this->default->id,
|
||||
'native_currency_code' => $this->default->code,
|
||||
'native_currency_symbol' => $this->default->symbol,
|
||||
'native_currency_decimal_places' => $this->default->decimal_places,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -45,11 +45,7 @@ class CategoryController extends Controller
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->repository = app(CategoryRepositoryInterface::class);
|
||||
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
if (null !== $userGroup) {
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
}
|
||||
$this->repository->setUserGroup($this->validateUserGroup($request));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@@ -57,23 +53,18 @@ class CategoryController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Documentation for this endpoint:
|
||||
* TODO list of checks
|
||||
* 1. use dates from ParameterBag
|
||||
* 2. Request validates dates
|
||||
* 3. Request includes user_group_id
|
||||
* 4. Endpoint is documented.
|
||||
* 5. Collector uses user_group_id
|
||||
* Documentation: https://api-docs.firefly-iii.org/?urls.primaryName=2.1.0%20(v2)#/autocomplete/getCategoriesAC
|
||||
*/
|
||||
public function categories(AutocompleteRequest $request): JsonResponse
|
||||
{
|
||||
$data = $request->getData();
|
||||
$result = $this->repository->searchCategory($data['query'], $this->parameters->get('limit'));
|
||||
$filtered = $result->map(
|
||||
$queryParameters = $request->getParameters();
|
||||
$result = $this->repository->searchCategory($queryParameters['query'], $queryParameters['size']);
|
||||
$filtered = $result->map(
|
||||
static function (Category $item) {
|
||||
return [
|
||||
'id' => (string)$item->id,
|
||||
'name' => $item->name,
|
||||
'id' => (string)$item->id,
|
||||
'title' => $item->name,
|
||||
'meta' => [],
|
||||
];
|
||||
}
|
||||
);
|
||||
|
@@ -45,11 +45,7 @@ class TagController extends Controller
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->repository = app(TagRepositoryInterface::class);
|
||||
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
if (null !== $userGroup) {
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
}
|
||||
$this->repository->setUserGroup($this->validateUserGroup($request));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@@ -57,25 +53,20 @@ class TagController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Documentation for this endpoint:
|
||||
* TODO list of checks
|
||||
* 1. use dates from ParameterBag
|
||||
* 2. Request validates dates
|
||||
* 3. Request includes user_group_id
|
||||
* 4. Endpoint is documented.
|
||||
* 5. Collector uses user_group_id
|
||||
* Documentation: https://api-docs.firefly-iii.org/?urls.primaryName=2.1.0%20(v2)#/autocomplete/getTagsAC
|
||||
*/
|
||||
public function tags(AutocompleteRequest $request): JsonResponse
|
||||
{
|
||||
$data = $request->getData();
|
||||
$result = $this->repository->searchTag($data['query'], $data['limit']);
|
||||
$filtered = $result->map(
|
||||
$queryParameters = $request->getParameters();
|
||||
$result = $this->repository->searchTag($queryParameters['query'], $queryParameters['size']);
|
||||
$filtered = $result->map(
|
||||
static function (Tag $item) {
|
||||
return [
|
||||
'id' => (string)$item->id,
|
||||
'name' => $item->tag,
|
||||
'value' => (string)$item->id,
|
||||
'id' => (string) $item->id,
|
||||
'title' => $item->tag,
|
||||
'value' => (string) $item->id,
|
||||
'label' => $item->tag,
|
||||
'meta' => [],
|
||||
];
|
||||
}
|
||||
);
|
||||
|
@@ -45,11 +45,7 @@ class TransactionController extends Controller
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->repository = app(JournalRepositoryInterface::class);
|
||||
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
if (null !== $userGroup) {
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
}
|
||||
$this->repository->setUserGroup($this->validateUserGroup($request));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@@ -57,30 +53,25 @@ class TransactionController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Documentation for this endpoint:
|
||||
* TODO list of checks
|
||||
* 1. use dates from ParameterBag
|
||||
* 2. Request validates dates
|
||||
* 3. Request includes user_group_id
|
||||
* 4. Endpoint is documented.
|
||||
* 5. Collector uses user_group_id
|
||||
* Documentation: https://api-docs.firefly-iii.org/?urls.primaryName=2.1.0%20(v2)#/autocomplete/getTransactionsAC
|
||||
*/
|
||||
public function transactionDescriptions(AutocompleteRequest $request): JsonResponse
|
||||
{
|
||||
$data = $request->getData();
|
||||
$result = $this->repository->searchJournalDescriptions($data['query'], $data['limit']);
|
||||
$queryParameters = $request->getParameters();
|
||||
$result = $this->repository->searchJournalDescriptions($queryParameters['query'], $queryParameters['size']);
|
||||
|
||||
// limit and unique
|
||||
$filtered = $result->unique('description');
|
||||
$array = [];
|
||||
$filtered = $result->unique('description');
|
||||
$array = [];
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($filtered as $journal) {
|
||||
$array[] = [
|
||||
'id' => (string)$journal->id,
|
||||
'transaction_group_id' => (string)$journal->transaction_group_id,
|
||||
'name' => $journal->description,
|
||||
'description' => $journal->description,
|
||||
'id' => (string) $journal->id,
|
||||
'title' => $journal->description,
|
||||
'meta' => [
|
||||
'transaction_group_id' => (string) $journal->transaction_group_id,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -53,10 +53,7 @@ class AccountController extends Controller
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
if (null !== $userGroup) {
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
}
|
||||
$this->repository->setUserGroup($this->validateUserGroup($request));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@@ -75,8 +72,6 @@ class AccountController extends Controller
|
||||
* TODO validate and set user_group_id from request
|
||||
*
|
||||
* @throws FireflyException
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function dashboard(DashboardChartRequest $request): JsonResponse
|
||||
{
|
||||
@@ -98,14 +93,14 @@ class AccountController extends Controller
|
||||
// user's preferences
|
||||
if (0 === $accounts->count()) {
|
||||
$defaultSet = $this->repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT])->pluck('id')->toArray();
|
||||
$frontPage = app('preferences')->get('frontPageAccounts', $defaultSet);
|
||||
$frontpage = app('preferences')->get('frontpageAccounts', $defaultSet);
|
||||
|
||||
if (!(is_array($frontPage->data) && count($frontPage->data) > 0)) {
|
||||
$frontPage->data = $defaultSet;
|
||||
$frontPage->save();
|
||||
if (!(is_array($frontpage->data) && count($frontpage->data) > 0)) {
|
||||
$frontpage->data = $defaultSet;
|
||||
$frontpage->save();
|
||||
}
|
||||
|
||||
$accounts = $this->repository->getAccountsById($frontPage->data);
|
||||
$accounts = $this->repository->getAccountsById($frontpage->data);
|
||||
}
|
||||
|
||||
// both options are overruled by "preselected"
|
||||
|
@@ -27,6 +27,7 @@ namespace FireflyIII\Api\V2\Controllers\Chart;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Api\V2\Controllers\Controller;
|
||||
use FireflyIII\Api\V2\Request\Chart\BalanceChartRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
@@ -42,6 +43,7 @@ use Illuminate\Support\Collection;
|
||||
class BalanceController extends Controller
|
||||
{
|
||||
use CleansChartData;
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
/**
|
||||
* The code is practically a duplicate of ReportController::operations.
|
||||
|
@@ -64,12 +64,9 @@ class BudgetController extends Controller
|
||||
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
|
||||
$this->opsRepository = app(OperationsRepositoryInterface::class);
|
||||
$this->currency = app('amount')->getDefaultCurrency();
|
||||
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
if (null !== $userGroup) {
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
$this->opsRepository->setUserGroup($userGroup);
|
||||
}
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
$this->opsRepository->setUserGroup($userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@@ -124,11 +121,11 @@ class BudgetController extends Controller
|
||||
foreach ($rows as $row) {
|
||||
$current = [
|
||||
'label' => $budget->name,
|
||||
'currency_id' => (string)$row['currency_id'],
|
||||
'currency_id' => (string) $row['currency_id'],
|
||||
'currency_code' => $row['currency_code'],
|
||||
'currency_name' => $row['currency_name'],
|
||||
'currency_decimal_places' => $row['currency_decimal_places'],
|
||||
'native_currency_id' => (string)$row['native_currency_id'],
|
||||
'native_currency_id' => (string) $row['native_currency_id'],
|
||||
'native_currency_code' => $row['native_currency_code'],
|
||||
'native_currency_name' => $row['native_currency_name'],
|
||||
'native_currency_decimal_places' => $row['native_currency_decimal_places'],
|
||||
@@ -189,12 +186,12 @@ class BudgetController extends Controller
|
||||
foreach ($array as $currencyId => $block) {
|
||||
$this->currencies[$currencyId] ??= TransactionCurrency::find($currencyId);
|
||||
$return[$currencyId] ??= [
|
||||
'currency_id' => (string)$currencyId,
|
||||
'currency_id' => (string) $currencyId,
|
||||
'currency_code' => $block['currency_code'],
|
||||
'currency_name' => $block['currency_name'],
|
||||
'currency_symbol' => $block['currency_symbol'],
|
||||
'currency_decimal_places' => (int)$block['currency_decimal_places'],
|
||||
'native_currency_id' => (string)$this->currency->id,
|
||||
'currency_decimal_places' => (int) $block['currency_decimal_places'],
|
||||
'native_currency_id' => (string) $this->currency->id,
|
||||
'native_currency_code' => $this->currency->code,
|
||||
'native_currency_name' => $this->currency->name,
|
||||
'native_currency_symbol' => $this->currency->symbol,
|
||||
|
@@ -57,10 +57,7 @@ class CategoryController extends Controller
|
||||
function ($request, $next) {
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
if (null !== $userGroup) {
|
||||
$this->accountRepos->setUserGroup($userGroup);
|
||||
}
|
||||
$this->accountRepos->setUserGroup($this->validateUserGroup($request));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@@ -100,25 +97,25 @@ class CategoryController extends Controller
|
||||
|
||||
/** @var array $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$currencyId = (int)$journal['currency_id'];
|
||||
$currencyId = (int) $journal['currency_id'];
|
||||
$currency = $currencies[$currencyId] ?? $this->currencyRepos->find($currencyId);
|
||||
$currencies[$currencyId] = $currency;
|
||||
$categoryName = null === $journal['category_name'] ? (string)trans('firefly.no_category') : $journal['category_name'];
|
||||
$categoryName = null === $journal['category_name'] ? (string) trans('firefly.no_category') : $journal['category_name'];
|
||||
$amount = app('steam')->positive($journal['amount']);
|
||||
$nativeAmount = $converter->convert($default, $currency, $journal['date'], $amount);
|
||||
$key = sprintf('%s-%s', $categoryName, $currency->code);
|
||||
if ((int)$journal['foreign_currency_id'] === $default->id) {
|
||||
if ((int) $journal['foreign_currency_id'] === $default->id) {
|
||||
$nativeAmount = app('steam')->positive($journal['foreign_amount']);
|
||||
}
|
||||
// create arrays
|
||||
$return[$key] ??= [
|
||||
'label' => $categoryName,
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_name' => $currency->name,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
'native_currency_id' => (string)$default->id,
|
||||
'native_currency_id' => (string) $default->id,
|
||||
'native_currency_code' => $default->code,
|
||||
'native_currency_name' => $default->name,
|
||||
'native_currency_symbol' => $default->symbol,
|
||||
@@ -138,7 +135,7 @@ class CategoryController extends Controller
|
||||
|
||||
// order by native amount
|
||||
usort($return, static function (array $a, array $b) {
|
||||
return (float)$a['native_amount'] < (float)$b['native_amount'] ? 1 : -1;
|
||||
return (float) $a['native_amount'] < (float) $b['native_amount'] ? 1 : -1;
|
||||
});
|
||||
$converter->summarize();
|
||||
|
||||
|
@@ -27,6 +27,7 @@ namespace FireflyIII\Api\V2\Controllers;
|
||||
use Carbon\Carbon;
|
||||
use Carbon\Exceptions\InvalidDateException;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
|
||||
use FireflyIII\Transformers\V2\AbstractTransformer;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -55,6 +56,7 @@ class Controller extends BaseController
|
||||
|
||||
protected const string CONTENT_TYPE = 'application/vnd.api+json';
|
||||
protected ParameterBag $parameters;
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@@ -120,6 +122,9 @@ class Controller extends BaseController
|
||||
$obj = null;
|
||||
}
|
||||
}
|
||||
if (null !== $date && 'end' === $field) {
|
||||
$obj->endOfDay();
|
||||
}
|
||||
$bag->set($field, $obj);
|
||||
}
|
||||
|
||||
@@ -152,6 +157,9 @@ class Controller extends BaseController
|
||||
{
|
||||
$manager = new Manager();
|
||||
$baseUrl = request()->getSchemeAndHttpHost().'/api/v2';
|
||||
|
||||
// TODO add stuff to path?
|
||||
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
$objects = $paginator->getCollection();
|
||||
|
@@ -25,17 +25,19 @@ namespace FireflyIII\Api\V2\Controllers\Model\Account;
|
||||
|
||||
use FireflyIII\Api\V2\Controllers\Controller;
|
||||
use FireflyIII\Api\V2\Request\Model\Account\IndexRequest;
|
||||
use FireflyIII\Api\V2\Request\Model\Transaction\InfiniteListRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Transformers\V2\AccountTransformer;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class IndexController extends Controller
|
||||
{
|
||||
public const string RESOURCE_KEY = 'accounts';
|
||||
public const string RESOURCE_KEY = 'accounts';
|
||||
|
||||
private AccountRepositoryInterface $repository;
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY, UserRoleEnum::MANAGE_TRANSACTIONS];
|
||||
|
||||
/**
|
||||
* AccountController constructor.
|
||||
@@ -48,9 +50,7 @@ class IndexController extends Controller
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
// new way of user group validation
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
if (null !== $userGroup) {
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
}
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@@ -58,21 +58,35 @@ class IndexController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO see autocomplete/accountcontroller for list.
|
||||
* TODO the sort instructions need proper repeatable documentation.
|
||||
* TODO see autocomplete/account controller for list.
|
||||
*/
|
||||
public function index(IndexRequest $request): JsonResponse
|
||||
{
|
||||
$this->repository->resetAccountOrder();
|
||||
$types = $request->getAccountTypes();
|
||||
$instructions = $request->getSortInstructions('accounts');
|
||||
$accounts = $this->repository->getAccountsByType($types, $instructions);
|
||||
$pageSize = $this->parameters->get('limit');
|
||||
$count = $accounts->count();
|
||||
$accounts = $accounts->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
$paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page'));
|
||||
$transformer = new AccountTransformer();
|
||||
$types = $request->getAccountTypes();
|
||||
$sorting = $request->getSortInstructions('accounts');
|
||||
$filters = $request->getFilterInstructions('accounts');
|
||||
$accounts = $this->repository->getAccountsByType($types, $sorting, $filters);
|
||||
$pageSize = $this->parameters->get('limit');
|
||||
$count = $accounts->count();
|
||||
|
||||
$this->parameters->set('sort', $instructions);
|
||||
// depending on the sort parameters, this list must not be split, because the
|
||||
// order is calculated in the account transformer and by that time it's too late.
|
||||
$first = array_key_first($sorting);
|
||||
$disablePagination = in_array($first, ['last_activity', 'balance', 'balance_difference'], true);
|
||||
Log::debug(sprintf('Will disable pagination in account index v2? %s', var_export($disablePagination, true)));
|
||||
if (!$disablePagination) {
|
||||
$accounts = $accounts->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
}
|
||||
$paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page'));
|
||||
$transformer = new AccountTransformer();
|
||||
|
||||
$this->parameters->set('disablePagination', $disablePagination);
|
||||
$this->parameters->set('pageSize', $pageSize);
|
||||
$this->parameters->set('sort', $sorting);
|
||||
|
||||
$this->parameters->set('filters', $filters);
|
||||
$transformer->setParameters($this->parameters); // give params to transformer
|
||||
|
||||
return response()
|
||||
@@ -80,25 +94,4 @@ class IndexController extends Controller
|
||||
->header('Content-Type', self::CONTENT_TYPE)
|
||||
;
|
||||
}
|
||||
|
||||
public function infiniteList(InfiniteListRequest $request): JsonResponse
|
||||
{
|
||||
$this->repository->resetAccountOrder();
|
||||
|
||||
// get accounts of the specified type, and return.
|
||||
$types = $request->getAccountTypes();
|
||||
|
||||
// get from repository
|
||||
$accounts = $this->repository->getAccountsInOrder($types, $request->getSortInstructions('accounts'), $request->getStartRow(), $request->getEndRow());
|
||||
$total = $this->repository->countAccounts($types);
|
||||
$count = $request->getEndRow() - $request->getStartRow();
|
||||
$paginator = new LengthAwarePaginator($accounts, $total, $count, $this->parameters->get('page'));
|
||||
$transformer = new AccountTransformer();
|
||||
$transformer->setParameters($this->parameters); // give params to transformer
|
||||
|
||||
return response()
|
||||
->json($this->jsonApiList(self::RESOURCE_KEY, $paginator, $transformer))
|
||||
->header('Content-Type', self::CONTENT_TYPE)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
@@ -25,7 +25,9 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Api\V2\Controllers\Model\Account;
|
||||
|
||||
use FireflyIII\Api\V2\Controllers\Controller;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Transformers\V2\AccountTransformer;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
@@ -36,6 +38,29 @@ use Illuminate\Http\JsonResponse;
|
||||
*/
|
||||
class ShowController extends Controller
|
||||
{
|
||||
public const string RESOURCE_KEY = 'accounts';
|
||||
|
||||
private AccountRepositoryInterface $repository;
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY, UserRoleEnum::MANAGE_TRANSACTIONS];
|
||||
|
||||
/**
|
||||
* AccountController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
// new way of user group validation
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO this endpoint is not yet reachable.
|
||||
*/
|
||||
|
@@ -45,11 +45,7 @@ class UpdateController extends Controller
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
// new way of user group validation
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
if (null !== $userGroup) {
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
}
|
||||
$this->repository->setUserGroup($this->validateUserGroup($request));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
@@ -46,12 +46,7 @@ class IndexController extends Controller
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->repository = app(BillRepositoryInterface::class);
|
||||
|
||||
// new way of user group validation
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
if (null !== $userGroup) {
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
}
|
||||
$this->repository->setUserGroup($this->validateUserGroup($request));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
@@ -46,12 +46,7 @@ class ShowController extends Controller
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->repository = app(BillRepositoryInterface::class);
|
||||
|
||||
// new way of user group validation
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
if (null !== $userGroup) {
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
}
|
||||
$this->repository->setUserGroup($this->validateUserGroup($request));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
@@ -45,11 +45,7 @@ class SumController extends Controller
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->repository = app(BillRepositoryInterface::class);
|
||||
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
if (null !== $userGroup) {
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
}
|
||||
$this->repository->setUserGroup($this->validateUserGroup($request));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
@@ -46,11 +46,7 @@ class IndexController extends Controller
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->repository = app(PiggyBankRepositoryInterface::class);
|
||||
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
if (null !== $userGroup) {
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
}
|
||||
$this->repository->setUserGroup($this->validateUserGroup($request));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
@@ -77,13 +77,11 @@ class BasicController extends Controller
|
||||
$this->opsRepository = app(OperationsRepositoryInterface::class);
|
||||
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
if (null !== $userGroup) {
|
||||
$this->abRepository->setUserGroup($userGroup);
|
||||
$this->accountRepository->setUserGroup($userGroup);
|
||||
$this->billRepository->setUserGroup($userGroup);
|
||||
$this->budgetRepository->setUserGroup($userGroup);
|
||||
$this->opsRepository->setUserGroup($userGroup);
|
||||
}
|
||||
$this->abRepository->setUserGroup($userGroup);
|
||||
$this->accountRepository->setUserGroup($userGroup);
|
||||
$this->billRepository->setUserGroup($userGroup);
|
||||
$this->budgetRepository->setUserGroup($userGroup);
|
||||
$this->opsRepository->setUserGroup($userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@@ -298,7 +296,7 @@ class BasicController extends Controller
|
||||
app('log')->debug(sprintf('Amount left is %s', $left));
|
||||
|
||||
// how much left per day?
|
||||
$days = (int) $today->diffInDays($end, true) + 1;
|
||||
$days = (int)$today->diffInDays($end, true) + 1;
|
||||
$perDay = '0';
|
||||
$perDayNative = '0';
|
||||
if (0 !== $days && bccomp($left, '0') > -1) {
|
||||
|
@@ -52,10 +52,8 @@ class NetWorthController extends Controller
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
// new way of user group validation
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
if (null !== $userGroup) {
|
||||
$this->netWorth->setUserGroup($userGroup);
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
}
|
||||
$this->netWorth->setUserGroup($userGroup);
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
73
app/Api/V2/Controllers/UserGroup/IndexController.php
Normal file
73
app/Api/V2/Controllers/UserGroup/IndexController.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/*
|
||||
* IndexController.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V2\Controllers\UserGroup;
|
||||
|
||||
use FireflyIII\Api\V2\Controllers\Controller;
|
||||
use FireflyIII\Api\V2\Request\Model\Account\IndexRequest;
|
||||
use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface;
|
||||
use FireflyIII\Transformers\V2\UserGroupTransformer;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
|
||||
class IndexController extends Controller
|
||||
{
|
||||
public const string RESOURCE_KEY = 'user_groups';
|
||||
|
||||
private UserGroupRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
* AccountController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->repository = app(UserGroupRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO see autocomplete/accountcontroller for list.
|
||||
*/
|
||||
public function index(IndexRequest $request): JsonResponse
|
||||
{
|
||||
$administrations = $this->repository->get();
|
||||
$pageSize = $this->parameters->get('limit');
|
||||
$count = $administrations->count();
|
||||
$administrations = $administrations->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
$paginator = new LengthAwarePaginator($administrations, $count, $pageSize, $this->parameters->get('page'));
|
||||
$transformer = new UserGroupTransformer();
|
||||
|
||||
$transformer->setParameters($this->parameters); // give params to transformer
|
||||
|
||||
return response()
|
||||
->json($this->jsonApiList(self::RESOURCE_KEY, $paginator, $transformer))
|
||||
->header('Content-Type', self::CONTENT_TYPE)
|
||||
;
|
||||
}
|
||||
}
|
@@ -79,4 +79,12 @@ class UpdateController extends Controller
|
||||
->header('Content-Type', self::CONTENT_TYPE)
|
||||
;
|
||||
}
|
||||
|
||||
public function useUserGroup(UserGroup $userGroup): JsonResponse
|
||||
{
|
||||
// group validation is already in place, so can just update the user.
|
||||
$this->repository->useUserGroup($userGroup);
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
}
|
||||
|
@@ -23,47 +23,68 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V2\Request\Autocomplete;
|
||||
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\JsonApi\Rules\IsValidFilter;
|
||||
use FireflyIII\JsonApi\Rules\IsValidPage;
|
||||
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||
use FireflyIII\Support\Http\Api\ParsesQueryFilters;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use LaravelJsonApi\Core\Query\QueryParameters;
|
||||
use LaravelJsonApi\Validation\Rule as JsonApiRule;
|
||||
|
||||
/**
|
||||
* Class AutocompleteRequest
|
||||
*/
|
||||
class AutocompleteRequest extends FormRequest
|
||||
{
|
||||
use AccountFilter;
|
||||
use ChecksLogin;
|
||||
use ConvertsDataTypes;
|
||||
use ParsesQueryFilters;
|
||||
|
||||
protected array $acceptedRoles = [UserRoleEnum::MANAGE_TRANSACTIONS];
|
||||
|
||||
public function getData(): array
|
||||
/**
|
||||
* Loops over all possible query parameters (these are shared over ALL auto complete requests)
|
||||
* and returns a validated array of parameters.
|
||||
*
|
||||
* The advantage is a single class. But you may also submit "account types" to an endpoint that doesn't use these.
|
||||
*/
|
||||
public function getParameters(): array
|
||||
{
|
||||
$types = $this->convertString('types');
|
||||
$array = [];
|
||||
if ('' !== $types) {
|
||||
$array = explode(',', $types);
|
||||
}
|
||||
$limit = $this->convertInteger('limit');
|
||||
$limit = 0 === $limit ? 10 : $limit;
|
||||
|
||||
// remove 'initial balance' and another from allowed types. its internal
|
||||
$array = array_diff($array, [AccountType::INITIAL_BALANCE, AccountType::RECONCILIATION]);
|
||||
$queryParameters = QueryParameters::cast($this->all());
|
||||
|
||||
return [
|
||||
'types' => $array,
|
||||
'query' => $this->convertString('query'),
|
||||
'date' => $this->getCarbonDate('date'),
|
||||
'limit' => $limit,
|
||||
'date' => $this->dateOrToday($queryParameters, 'date'),
|
||||
'query' => $this->arrayOfStrings($queryParameters, 'query'),
|
||||
'size' => $this->integerFromQueryParams($queryParameters, 'size', 50),
|
||||
'account_types' => $this->getAccountTypeParameter($this->arrayOfStrings($queryParameters, 'account_types')),
|
||||
];
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'limit' => 'min:0|max:1337',
|
||||
'fields' => JsonApiRule::notSupported(),
|
||||
'filter' => ['nullable', 'array', new IsValidFilter(['query', 'date', 'account_types'])],
|
||||
'include' => JsonApiRule::notSupported(),
|
||||
'page' => ['nullable', 'array', new IsValidPage(['size'])],
|
||||
'sort' => JsonApiRule::notSupported(),
|
||||
];
|
||||
}
|
||||
|
||||
private function getAccountTypeParameter(mixed $types): array
|
||||
{
|
||||
if (is_string($types) && str_contains($types, ',')) {
|
||||
$types = explode(',', $types);
|
||||
}
|
||||
if (!is_iterable($types)) {
|
||||
$types = [$types];
|
||||
}
|
||||
$return = [];
|
||||
foreach ($types as $type) {
|
||||
$return = array_merge($return, $this->mapAccountTypes($type));
|
||||
}
|
||||
|
||||
return array_unique($return);
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V2\Request\Chart;
|
||||
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
@@ -39,6 +40,7 @@ class BalanceChartRequest extends FormRequest
|
||||
use ChecksLogin;
|
||||
use ConvertsDataTypes;
|
||||
use ValidatesUserGroupTrait;
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
/**
|
||||
* Get all data from the request.
|
||||
|
112
app/Api/V2/Request/Chart/ChartRequest.php
Normal file
112
app/Api/V2/Request/Chart/ChartRequest.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/*
|
||||
* DashboardChartRequest.php
|
||||
* Copyright (c) 2023 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V2\Request\Chart;
|
||||
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\JsonApi\Rules\IsValidFilter;
|
||||
use FireflyIII\Support\Http\Api\ParsesQueryFilters;
|
||||
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\Validator;
|
||||
use LaravelJsonApi\Core\Query\QueryParameters;
|
||||
use LaravelJsonApi\Validation\Rule as JsonApiRule;
|
||||
|
||||
/**
|
||||
* Class ChartRequest
|
||||
*/
|
||||
class ChartRequest extends FormRequest
|
||||
{
|
||||
use ChecksLogin;
|
||||
use ConvertsDataTypes;
|
||||
use ParsesQueryFilters;
|
||||
use ValidatesUserGroupTrait;
|
||||
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
public function getParameters(): array
|
||||
{
|
||||
$queryParameters = QueryParameters::cast($this->all());
|
||||
|
||||
return [
|
||||
'start' => $this->dateOrToday($queryParameters, 'start'),
|
||||
'end' => $this->dateOrToday($queryParameters, 'end'),
|
||||
|
||||
// preselected heeft maar een paar toegestane waardes.
|
||||
|
||||
// 'query' => $this->arrayOfStrings($queryParameters, 'query'),
|
||||
// 'size' => $this->integerFromQueryParams($queryParameters,'size', 50),
|
||||
// 'account_types' => $this->getAccountTypeParameter($this->arrayOfStrings($queryParameters, 'account_types')),
|
||||
];
|
||||
}
|
||||
|
||||
// return [
|
||||
// 'accounts' => $this->getAccountList(),
|
||||
// 'preselected' => $this->convertString('preselected'),
|
||||
// ];
|
||||
// }
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'fields' => JsonApiRule::notSupported(),
|
||||
'filter' => ['nullable', 'array', new IsValidFilter(['start', 'end', 'preselected', 'accounts'])],
|
||||
'include' => JsonApiRule::notSupported(),
|
||||
'page' => JsonApiRule::notSupported(),
|
||||
'sort' => JsonApiRule::notSupported(),
|
||||
];
|
||||
|
||||
// return [
|
||||
// 'start' => 'required|date|after:1900-01-01|before:2099-12-31',
|
||||
// 'end' => 'required|date|after_or_equal:start|before:2099-12-31|after:1900-01-01',
|
||||
// 'preselected' => sprintf('in:%s', implode(',', config('firefly.preselected_accounts'))),
|
||||
// 'accounts.*' => 'exists:accounts,id',
|
||||
// ];
|
||||
}
|
||||
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
static function (Validator $validator): void {
|
||||
// validate transaction query data.
|
||||
$data = $validator->getData();
|
||||
if (!array_key_exists('accounts', $data)) {
|
||||
// $validator->errors()->add('accounts', trans('validation.filled', ['attribute' => 'accounts']));
|
||||
return;
|
||||
}
|
||||
if (!is_array($data['accounts'])) {
|
||||
$validator->errors()->add('accounts', trans('validation.filled', ['attribute' => 'accounts']));
|
||||
}
|
||||
}
|
||||
);
|
||||
if ($validator->fails()) {
|
||||
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
|
||||
}
|
||||
}
|
||||
}
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V2\Request\Chart;
|
||||
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
@@ -39,6 +40,8 @@ class DashboardChartRequest extends FormRequest
|
||||
use ConvertsDataTypes;
|
||||
use ValidatesUserGroupTrait;
|
||||
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
/**
|
||||
* Get all data from the request.
|
||||
*/
|
||||
|
@@ -27,6 +27,7 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use FireflyIII\Support\Request\GetFilterInstructions;
|
||||
use FireflyIII\Support\Request\GetSortInstructions;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
@@ -40,18 +41,16 @@ class IndexRequest extends FormRequest
|
||||
use AccountFilter;
|
||||
use ChecksLogin;
|
||||
use ConvertsDataTypes;
|
||||
use GetFilterInstructions;
|
||||
use GetSortInstructions;
|
||||
|
||||
public function getAccountTypes(): array
|
||||
{
|
||||
$type = (string)$this->get('type', 'default');
|
||||
$type = (string) $this->get('type', 'default');
|
||||
|
||||
return $this->mapAccountTypes($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all data from the request.
|
||||
*/
|
||||
public function getDate(): Carbon
|
||||
{
|
||||
return $this->getCarbonDate('date');
|
||||
@@ -63,7 +62,9 @@ class IndexRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'date' => 'date|after:1900-01-01|before:2099-12-31',
|
||||
'date' => 'date|after:1900-01-01|before:2099-12-31',
|
||||
'start' => 'date|after:1900-01-01|before:2099-12-31|before:end|required_with:end',
|
||||
'end' => 'date|after:1900-01-01|before:2099-12-31|after:start|required_with:start',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V2\Request\UserGroup;
|
||||
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\Rules\IsDefaultUserGroupName;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
@@ -53,7 +54,7 @@ class UpdateRequest extends FormRequest
|
||||
$userGroup = $this->route()->parameter('userGroup');
|
||||
|
||||
return [
|
||||
'title' => sprintf('required|min:1|max:255|unique:user_groups,title,%d', $userGroup->id),
|
||||
'title' => ['required', 'min:1', 'max:255', sprintf('unique:user_groups,title,%d', $userGroup->id), new IsDefaultUserGroupName($userGroup)],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
32
app/Console/Commands/Correction/CorrectAccountBalance.php
Normal file
32
app/Console/Commands/Correction/CorrectAccountBalance.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Support\Models\AccountBalanceCalculator;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class CorrectionSkeleton
|
||||
*/
|
||||
class CorrectAccountBalance extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
protected $description = 'Recalculate all account balance amounts';
|
||||
|
||||
protected $signature = 'firefly-iii:correct-account-balance';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$this->correctBalanceAmounts();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function correctBalanceAmounts(): void
|
||||
{
|
||||
AccountBalanceCalculator::recalculateAll();
|
||||
}
|
||||
}
|
@@ -73,6 +73,8 @@ class CorrectDatabase extends Command
|
||||
// new!
|
||||
'firefly-iii:unify-group-accounts',
|
||||
'firefly-iii:trigger-credit-recalculation',
|
||||
'firefly-iii:migrate-preferences',
|
||||
'firefly-iii:correct-account-balance',
|
||||
];
|
||||
foreach ($commands as $command) {
|
||||
$this->friendlyLine(sprintf('Now executing command "%s"', $command));
|
||||
|
@@ -50,7 +50,7 @@ class FixFrontpageAccounts extends Command
|
||||
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$preference = app('preferences')->getForUser($user, 'frontPageAccounts');
|
||||
$preference = app('preferences')->getForUser($user, 'frontpageAccounts');
|
||||
if (null !== $preference) {
|
||||
$this->fixPreference($preference);
|
||||
}
|
||||
@@ -83,6 +83,6 @@ class FixFrontpageAccounts extends Command
|
||||
}
|
||||
}
|
||||
}
|
||||
app('preferences')->setForUser($preference->user, 'frontPageAccounts', $fixed);
|
||||
app('preferences')->setForUser($preference->user, 'frontpageAccounts', $fixed);
|
||||
}
|
||||
}
|
||||
|
@@ -60,15 +60,13 @@ class FixIbans extends Command
|
||||
{
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$iban = $account->iban;
|
||||
if (str_contains($iban, ' ')) {
|
||||
$iban = app('steam')->filterSpaces((string)$account->iban);
|
||||
if ('' !== $iban) {
|
||||
$account->iban = $iban;
|
||||
$account->save();
|
||||
$this->friendlyInfo(sprintf('Removed spaces from IBAN of account #%d', $account->id));
|
||||
++$this->count;
|
||||
}
|
||||
$iban = (string) $account->iban;
|
||||
$newIban = app('steam')->filterSpaces($iban);
|
||||
if ('' !== $iban && $iban !== $newIban) {
|
||||
$account->iban = $newIban;
|
||||
$account->save();
|
||||
$this->friendlyInfo(sprintf('Removed spaces from IBAN of account #%d', $account->id));
|
||||
++$this->count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,7 +79,7 @@ class FixIbans extends Command
|
||||
foreach ($accounts as $account) {
|
||||
$userId = $account->user_id;
|
||||
$set[$userId] ??= [];
|
||||
$iban = (string)$account->iban;
|
||||
$iban = (string) $account->iban;
|
||||
if ('' === $iban) {
|
||||
continue;
|
||||
}
|
||||
|
66
app/Console/Commands/Correction/MigratePreferences.php
Normal file
66
app/Console/Commands/Correction/MigratePreferences.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* MigratePreferences.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Models\Preference;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Command\Command as CommandAlias;
|
||||
|
||||
class MigratePreferences extends Command
|
||||
{
|
||||
protected $description = 'Give Firefly III preferences a user group ID so they can be made administration specific.';
|
||||
|
||||
protected $signature = 'firefly-iii:migrate-preferences';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$items = config('firefly.admin_specific_prefs');
|
||||
$users = User::get();
|
||||
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$count = 0;
|
||||
foreach ($items as $item) {
|
||||
$preference = Preference::where('name', $item)->where('user_id', $user->id)->first();
|
||||
if (null === $preference) {
|
||||
continue;
|
||||
}
|
||||
if (null !== $preference->user_group_id) {
|
||||
$preference->user_group_id = $user->user_group_id;
|
||||
$preference->save();
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
if ($count > 0) {
|
||||
$this->info(sprintf('Migrated %d preference(s) for user #%d ("%s").', $count, $user->id, $user->email));
|
||||
}
|
||||
}
|
||||
|
||||
return CommandAlias::SUCCESS;
|
||||
}
|
||||
}
|
@@ -191,7 +191,7 @@ class ExportData extends Command
|
||||
$this->friendlyError(sprintf('%s date "%s" must be formatted YYYY-MM-DD. Field will be ignored.', $field, $this->option('start')));
|
||||
$error = true;
|
||||
}
|
||||
if (false === $date) {
|
||||
if (null === $date) {
|
||||
$this->friendlyError(sprintf('%s date "%s" must be formatted YYYY-MM-DD.', $field, $this->option('start')));
|
||||
|
||||
throw new FireflyException(sprintf('%s date "%s" must be formatted YYYY-MM-DD.', $field, $this->option('start')));
|
||||
|
54
app/Console/Commands/System/LaravelPassportKeys.php
Normal file
54
app/Console/Commands/System/LaravelPassportKeys.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* LaravelPassportKeys.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Console\Commands\System;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Symfony\Component\Console\Command\Command as CommandAlias;
|
||||
|
||||
class LaravelPassportKeys extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
protected $description = 'Calls the Laravel "passport:keys" but doesn\'t exit 1.';
|
||||
protected $signature = 'firefly-iii:laravel-passport-keys';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
Artisan::call('passport:keys --no-interaction', []);
|
||||
$result = Artisan::output();
|
||||
if (str_contains($result, 'Encryption keys already exist')) {
|
||||
$this->friendlyInfo('Encryption keys exist already.');
|
||||
|
||||
return CommandAlias::SUCCESS;
|
||||
}
|
||||
$this->friendlyPositive('Encryption keys have been created, nice!');
|
||||
|
||||
return CommandAlias::SUCCESS;
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
/*
|
||||
* ApplyRules.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -16,7 +16,7 @@
|
||||
* 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/>.
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
@@ -285,7 +285,7 @@ class ApplyRules extends Command
|
||||
if (null !== $endString && '' !== $endString) {
|
||||
$inputEnd = Carbon::createFromFormat('Y-m-d', $endString);
|
||||
}
|
||||
if (false === $inputEnd || false === $inputStart) {
|
||||
if (null === $inputEnd || null === $inputStart) {
|
||||
Log::error('Could not parse start or end date in verifyInputDate().');
|
||||
|
||||
return;
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
/*
|
||||
* Cron.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -17,7 +17,7 @@
|
||||
* 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/>.
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
@@ -69,6 +69,7 @@ class UpgradeDatabase extends Command
|
||||
'firefly-iii:create-group-memberships',
|
||||
'firefly-iii:upgrade-group-information',
|
||||
'firefly-iii:upgrade-currency-preferences',
|
||||
'firefly-iii:correct-database',
|
||||
];
|
||||
$args = [];
|
||||
if ($this->option('force')) {
|
||||
|
49
app/Entities/AccountBalance.php
Normal file
49
app/Entities/AccountBalance.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/*
|
||||
* AccountBalance.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Entities;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
|
||||
class AccountBalance
|
||||
{
|
||||
public string $id;
|
||||
public string $amount;
|
||||
public string $currencyId;
|
||||
|
||||
public static function fromArray(): self
|
||||
{
|
||||
$balance = new self();
|
||||
$balance->id = (string) random_int(1, 1000);
|
||||
$balance->name = (string) random_int(1, 1000);
|
||||
$balance->amount = (string) random_int(1, 1000);
|
||||
$balance->currencyId = '1';
|
||||
|
||||
return $balance;
|
||||
}
|
||||
|
||||
public function getAccount(): Account
|
||||
{
|
||||
return Account::inRandomOrder()->first();
|
||||
}
|
||||
}
|
@@ -41,7 +41,6 @@ enum UserRoleEnum: string
|
||||
// manage other financial objects:
|
||||
case MANAGE_BUDGETS = 'mng_budgets';
|
||||
case MANAGE_PIGGY_BANKS = 'mng_piggies';
|
||||
case MANAGE_REPETITIONS = 'mng_reps';
|
||||
case MANAGE_SUBSCRIPTIONS = 'mng_subscriptions';
|
||||
case MANAGE_RULES = 'mng_rules';
|
||||
case MANAGE_RECURRING = 'mng_recurring';
|
||||
@@ -51,7 +50,7 @@ enum UserRoleEnum: string
|
||||
// view and generate reports
|
||||
case VIEW_REPORTS = 'view_reports';
|
||||
|
||||
// view memberships. needs FULL to manage them.
|
||||
// view memberships AND roles. needs FULL to manage them.
|
||||
case VIEW_MEMBERSHIPS = 'view_memberships';
|
||||
|
||||
// everything the creator can, except remove/change original creator and delete group
|
||||
|
@@ -25,6 +25,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Exceptions;
|
||||
|
||||
use FireflyIII\Jobs\MailError;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
@@ -35,6 +36,7 @@ use Illuminate\Session\TokenMismatchException;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Validation\ValidationException as LaravelValidationException;
|
||||
use Laravel\Passport\Exceptions\OAuthServerException as LaravelOAuthException;
|
||||
use LaravelJsonApi\Core\Exceptions\JsonApiException;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -62,6 +64,7 @@ class Handler extends ExceptionHandler
|
||||
HttpException::class,
|
||||
SuspiciousOperationException::class,
|
||||
BadHttpHeaderException::class,
|
||||
JsonApiException::class,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -98,6 +101,13 @@ class Handler extends ExceptionHandler
|
||||
return response()->json(['message' => 'Resource not found', 'exception' => 'NotFoundHttpException'], 404);
|
||||
}
|
||||
|
||||
if ($e instanceof AuthorizationException && $expectsJson) {
|
||||
// somehow Laravel handler does not catch this:
|
||||
app('log')->debug('Return JSON unauthorized error.');
|
||||
|
||||
return response()->json(['message' => $e->getMessage(), 'exception' => 'AuthorizationException'], 401);
|
||||
}
|
||||
|
||||
if ($e instanceof AuthenticationException && $expectsJson) {
|
||||
// somehow Laravel handler does not catch this:
|
||||
app('log')->debug('Return JSON unauthenticated error.');
|
||||
|
@@ -405,7 +405,7 @@ class UserEventHandler
|
||||
}
|
||||
// clean up old entries (6 months)
|
||||
$carbon = Carbon::createFromFormat('Y-m-d H:i:s', $preference[$index]['time']);
|
||||
if (false !== $carbon && $carbon->diffInMonths(today(), true) > 6) {
|
||||
if (null !== $carbon && $carbon->diffInMonths(today(), true) > 6) {
|
||||
app('log')->debug(sprintf('Entry for %s is very old, remove it.', $row['ip']));
|
||||
unset($preference[$index]);
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Handlers\Observer;
|
||||
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Support\Models\AccountBalanceCalculator;
|
||||
|
||||
/**
|
||||
* Class TransactionObserver
|
||||
@@ -35,4 +36,16 @@ class TransactionObserver
|
||||
app('log')->debug('Observe "deleting" of a transaction.');
|
||||
$transaction?->transactionJournal?->delete();
|
||||
}
|
||||
|
||||
public function updated(Transaction $transaction): void
|
||||
{
|
||||
app('log')->debug('Observe "updated" of a transaction.');
|
||||
AccountBalanceCalculator::recalculateForJournal($transaction->transactionJournal);
|
||||
}
|
||||
|
||||
public function created(Transaction $transaction): void
|
||||
{
|
||||
app('log')->debug('Observe "created" of a transaction.');
|
||||
AccountBalanceCalculator::recalculateForJournal($transaction->transactionJournal);
|
||||
}
|
||||
}
|
||||
|
@@ -145,13 +145,13 @@ class CreateController extends Controller
|
||||
Log::channel('audit')->info('Stored new account.', $data);
|
||||
|
||||
// update preferences if necessary:
|
||||
$frontPage = app('preferences')->get('frontPageAccounts', [])->data;
|
||||
if (!is_array($frontPage)) {
|
||||
$frontPage = [];
|
||||
$frontpage = app('preferences')->get('frontpageAccounts', [])->data;
|
||||
if (!is_array($frontpage)) {
|
||||
$frontpage = [];
|
||||
}
|
||||
if (AccountType::ASSET === $account->accountType->type) {
|
||||
$frontPage[] = $account->id;
|
||||
app('preferences')->set('frontPageAccounts', $frontPage);
|
||||
$frontpage[] = $account->id;
|
||||
app('preferences')->set('frontpageAccounts', $frontpage);
|
||||
}
|
||||
|
||||
// store attachment(s):
|
||||
|
@@ -106,7 +106,7 @@ class ShowController extends Controller
|
||||
$periods = $this->getAccountPeriodOverview($account, $firstTransaction, $end);
|
||||
|
||||
// if layout = v2, overrule the page title.
|
||||
if ('v1' !== config('firefly.layout')) {
|
||||
if ('v1' !== config('view.layout')) {
|
||||
$subTitle = (string)trans('firefly.all_journals_for_account', ['name' => $account->name]);
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Api\V3\Controllers;
|
||||
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\JsonApi\V3\AccountBalances\AccountBalanceSchema;
|
||||
use FireflyIII\Models\Account;
|
||||
use Illuminate\Contracts\Support\Responsable;
|
||||
use LaravelJsonApi\Core\Facades\JsonApi;
|
||||
use LaravelJsonApi\Core\Responses\DataResponse;
|
||||
use LaravelJsonApi\Laravel\Http\Controllers\Actions;
|
||||
use LaravelJsonApi\Laravel\Http\Requests\AnonymousQuery;
|
||||
|
||||
class AccountController extends Controller
|
||||
{
|
||||
use Actions\AttachRelationship;
|
||||
use Actions\Destroy;
|
||||
use Actions\DetachRelationship;
|
||||
use Actions\FetchMany;
|
||||
use Actions\FetchOne;
|
||||
use Actions\FetchRelated;
|
||||
use Actions\FetchRelationship;
|
||||
use Actions\Store;
|
||||
use Actions\Update;
|
||||
use Actions\UpdateRelationship;
|
||||
|
||||
public function readAccountBalances(AnonymousQuery $query, AccountBalanceSchema $schema, Account $account): Responsable
|
||||
{
|
||||
$schema = JsonApi::server()->schemas()->schemaFor('account-balances');
|
||||
|
||||
$models = $schema
|
||||
->repository()
|
||||
->queryAll()
|
||||
->withRequest($query)
|
||||
->withAccount($account)
|
||||
->get()
|
||||
;
|
||||
|
||||
return DataResponse::make($models);
|
||||
}
|
||||
}
|
@@ -31,6 +31,7 @@ use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\View\View;
|
||||
|
||||
/**
|
||||
@@ -106,6 +107,8 @@ class ForgotPasswordController extends Controller
|
||||
}
|
||||
$host = request()->host();
|
||||
if ($configuredHost !== $host) {
|
||||
Log::error(sprintf('Host header is "%s", APP_URL is "%s".', $host, $configuredHost));
|
||||
|
||||
throw new FireflyException('The Host-header does not match the host in the APP_URL environment variable. Please make sure these match. See also: https://bit.ly/FF3-host-header');
|
||||
}
|
||||
}
|
||||
|
@@ -128,7 +128,7 @@ class BudgetLimitController extends Controller
|
||||
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
|
||||
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
|
||||
|
||||
if (false === $start || false === $end) {
|
||||
if (null === $start || null === $end) {
|
||||
return response()->json([]);
|
||||
}
|
||||
|
||||
@@ -235,12 +235,12 @@ class BudgetLimitController extends Controller
|
||||
new Collection([$budgetLimit->budget]),
|
||||
$budgetLimit->transactionCurrency
|
||||
);
|
||||
$daysLeft = $this->activeDaysLeft($limit->start_date, $limit->end_date);
|
||||
$array['spent'] = $spentArr[$budgetLimit->transactionCurrency->id]['sum'] ?? '0';
|
||||
$array['left_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, bcadd($array['spent'], $array['amount']));
|
||||
$array['amount_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, $limit['amount']);
|
||||
$array['days_left'] = (string)$this->activeDaysLeft($limit->start_date, $limit->end_date);
|
||||
// left per day:
|
||||
$array['left_per_day'] = bcdiv(bcadd($array['spent'], $array['amount']), $array['days_left']);
|
||||
$array['days_left'] = (string)$daysLeft;
|
||||
$array['left_per_day'] = 0 === $daysLeft ? bcadd($array['spent'], $array['amount']) : bcdiv(bcadd($array['spent'], $array['amount']), $array['days_left']);
|
||||
|
||||
// left per day formatted.
|
||||
$array['amount'] = app('steam')->bcround($limit['amount'], $limit->transactionCurrency->decimal_places);
|
||||
|
@@ -301,14 +301,14 @@ class AccountController extends Controller
|
||||
$end = clone session('end', today(config('app.timezone'))->endOfMonth());
|
||||
$defaultSet = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray();
|
||||
app('log')->debug('Default set is ', $defaultSet);
|
||||
$frontPage = app('preferences')->get('frontPageAccounts', $defaultSet);
|
||||
$frontPageArray = !is_array($frontPage->data) ? [] : $frontPage->data;
|
||||
app('log')->debug('Frontpage preference set is ', $frontPageArray);
|
||||
if (0 === count($frontPageArray)) {
|
||||
app('preferences')->set('frontPageAccounts', $defaultSet);
|
||||
$frontpage = app('preferences')->get('frontpageAccounts', $defaultSet);
|
||||
$frontpageArray = !is_array($frontpage->data) ? [] : $frontpage->data;
|
||||
app('log')->debug('Frontpage preference set is ', $frontpageArray);
|
||||
if (0 === count($frontpageArray)) {
|
||||
app('preferences')->set('frontpageAccounts', $defaultSet);
|
||||
app('log')->debug('frontpage set is empty!');
|
||||
}
|
||||
$accounts = $repository->getAccountsById($frontPageArray);
|
||||
$accounts = $repository->getAccountsById($frontpageArray);
|
||||
|
||||
return response()->json($this->accountBalanceChart($accounts, $start, $end));
|
||||
}
|
||||
|
@@ -116,8 +116,8 @@ class CategoryController extends Controller
|
||||
return response()->json($cache->get());
|
||||
}
|
||||
|
||||
$frontPageGenerator = new FrontpageChartGenerator($start, $end);
|
||||
$chartData = $frontPageGenerator->generate();
|
||||
$frontpageGenerator = new FrontpageChartGenerator($start, $end);
|
||||
$chartData = $frontpageGenerator->generate();
|
||||
$data = $this->generator->multiSet($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
|
@@ -70,7 +70,7 @@ abstract class Controller extends BaseController
|
||||
$logoutUrl = config('firefly.custom_logout_url');
|
||||
|
||||
// overrule v2 layout back to v1.
|
||||
if ('true' === request()->get('force_default_layout') && 'v2' === config('firefly.layout')) {
|
||||
if ('true' === request()->get('force_default_layout') && 'v2' === config('view.layout')) {
|
||||
app('view')->getFinder()->setPaths([realpath(base_path('resources/views'))]); // @phpstan-ignore-line
|
||||
}
|
||||
|
||||
|
@@ -86,14 +86,15 @@ class DebugController extends Controller
|
||||
{
|
||||
app('preferences')->mark();
|
||||
$request->session()->forget(['start', 'end', '_previous', 'viewRange', 'range', 'is_custom_range', 'temp-mfa-secret', 'temp-mfa-codes']);
|
||||
app('log')->debug('Call cache:clear...');
|
||||
|
||||
Artisan::call('cache:clear');
|
||||
app('log')->debug('Call config:clear...');
|
||||
Artisan::call('config:clear');
|
||||
app('log')->debug('Call route:clear...');
|
||||
Artisan::call('route:clear');
|
||||
app('log')->debug('Call twig:clean...');
|
||||
Artisan::call('view:clear');
|
||||
|
||||
// also do some recalculations.
|
||||
Artisan::call('firefly-iii:correct-account-balance');
|
||||
Artisan::call('firefly-iii:trigger-credit-recalculation');
|
||||
|
||||
try {
|
||||
Artisan::call('twig:clean');
|
||||
@@ -101,7 +102,6 @@ class DebugController extends Controller
|
||||
throw new FireflyException($e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
app('log')->debug('Call view:clear...');
|
||||
Artisan::call('view:clear');
|
||||
|
||||
return redirect(route('index'));
|
||||
|
@@ -79,17 +79,17 @@ class HomeController extends Controller
|
||||
app('log')->error(sprintf('End could not parse date string "%s" so ignore it.', $stringEnd));
|
||||
$end = Carbon::now()->endOfMonth();
|
||||
}
|
||||
if (false === $start) {
|
||||
if (null === $start) {
|
||||
$start = Carbon::now()->startOfMonth();
|
||||
}
|
||||
if (false === $end) {
|
||||
if (null === $end) {
|
||||
$end = Carbon::now()->endOfMonth();
|
||||
}
|
||||
|
||||
$label = $request->get('label');
|
||||
$isCustomRange = false;
|
||||
|
||||
app('log')->debug('Received dateRange', ['start' => $stringStart, 'end' => $stringEnd, 'label' => $request->get('label')]);
|
||||
app('log')->debug('dateRange: Received dateRange', ['start' => $stringStart, 'end' => $stringEnd, 'label' => $request->get('label')]);
|
||||
// check if the label is "everything" or "Custom range" which will betray
|
||||
// a possible problem with the budgets.
|
||||
if ($label === (string)trans('firefly.everything') || $label === (string)trans('firefly.customRange')) {
|
||||
@@ -99,7 +99,7 @@ class HomeController extends Controller
|
||||
|
||||
$diff = $start->diffInDays($end, true) + 1;
|
||||
|
||||
if ($diff > 50) {
|
||||
if ($diff > 366) {
|
||||
$request->session()->flash('warning', (string)trans('firefly.warning_much_data', ['days' => (int)$diff]));
|
||||
}
|
||||
|
||||
@@ -120,19 +120,34 @@ class HomeController extends Controller
|
||||
*/
|
||||
public function index(AccountRepositoryInterface $repository): mixed
|
||||
{
|
||||
$types = config('firefly.accountTypesByIdentifier.asset');
|
||||
$count = $repository->count($types);
|
||||
$types = config('firefly.accountTypesByIdentifier.asset');
|
||||
$count = $repository->count($types);
|
||||
Log::channel('audit')->info('User visits homepage.');
|
||||
|
||||
if (0 === $count) {
|
||||
return redirect(route('new-user.index'));
|
||||
}
|
||||
|
||||
if ('v1' === (string)config('view.layout')) {
|
||||
return $this->indexV1($repository);
|
||||
}
|
||||
if ('v2' === (string)config('view.layout')) {
|
||||
return $this->indexV2();
|
||||
}
|
||||
|
||||
throw new FireflyException('Invalid layout configuration');
|
||||
}
|
||||
|
||||
private function indexV1(AccountRepositoryInterface $repository): mixed
|
||||
{
|
||||
$types = config('firefly.accountTypesByIdentifier.asset');
|
||||
$count = $repository->count($types);
|
||||
$subTitle = (string)trans('firefly.welcome_back');
|
||||
$transactions = [];
|
||||
$frontPage = app('preferences')->getFresh('frontPageAccounts', $repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray());
|
||||
$frontPageArray = $frontPage->data;
|
||||
if (!is_array($frontPageArray)) {
|
||||
$frontPageArray = [];
|
||||
$frontpage = app('preferences')->getFresh('frontpageAccounts', $repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray());
|
||||
$frontpageArray = $frontpage->data;
|
||||
if (!is_array($frontpageArray)) {
|
||||
$frontpageArray = [];
|
||||
}
|
||||
|
||||
/** @var Carbon $start */
|
||||
@@ -140,13 +155,11 @@ class HomeController extends Controller
|
||||
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', today(config('app.timezone'))->endOfMonth());
|
||||
$accounts = $repository->getAccountsById($frontPageArray);
|
||||
$accounts = $repository->getAccountsById($frontpageArray);
|
||||
$today = today(config('app.timezone'));
|
||||
$accounts = $accounts->sortBy('order'); // sort frontpage accounts by order
|
||||
|
||||
// sort frontpage accounts by order
|
||||
$accounts = $accounts->sortBy('order');
|
||||
|
||||
app('log')->debug('Frontpage accounts are ', $frontPageArray);
|
||||
app('log')->debug('Frontpage accounts are ', $frontpageArray);
|
||||
|
||||
/** @var BillRepositoryInterface $billRepository */
|
||||
$billRepository = app(BillRepositoryInterface::class);
|
||||
@@ -166,4 +179,18 @@ class HomeController extends Controller
|
||||
|
||||
return view('index', compact('count', 'subTitle', 'transactions', 'billCount', 'start', 'end', 'today'));
|
||||
}
|
||||
|
||||
private function indexV2(): mixed
|
||||
{
|
||||
$subTitle = (string)trans('firefly.welcome_back');
|
||||
|
||||
$start = session('start', today(config('app.timezone'))->startOfMonth());
|
||||
$end = session('end', today(config('app.timezone'))->endOfMonth());
|
||||
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
event(new RequestedVersionCheckStatus($user));
|
||||
|
||||
return view('index', compact('subTitle', 'start', 'end'));
|
||||
}
|
||||
}
|
||||
|
@@ -130,7 +130,7 @@ class BoxController extends Controller
|
||||
$boxTitle = (string)trans('firefly.left_to_spend');
|
||||
$activeDaysLeft = $this->activeDaysLeft($start, $end); // see method description.
|
||||
$display = 1; // not overspent
|
||||
$leftPerDayAmount = bcdiv($leftToSpendAmount, (string)$activeDaysLeft);
|
||||
$leftPerDayAmount = 0 === $activeDaysLeft ? $leftToSpendAmount : bcdiv($leftToSpendAmount, (string)$activeDaysLeft);
|
||||
app('log')->debug(sprintf('Left to spend per day is %s', $leftPerDayAmount));
|
||||
}
|
||||
}
|
||||
|
@@ -81,7 +81,7 @@ class RecurrenceController extends Controller
|
||||
$skip = $skip < 0 || $skip > 31 ? 0 : $skip;
|
||||
$weekend = $weekend < 1 || $weekend > 4 ? 1 : $weekend;
|
||||
|
||||
if (false === $start || false === $end || false === $firstDate || false === $endDate) {
|
||||
if (null === $start || null === $end || null === $firstDate || null === $endDate) {
|
||||
return response()->json();
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ class RecurrenceController extends Controller
|
||||
$actualEnd = clone $end;
|
||||
|
||||
if ('until_date' === $endsAt) {
|
||||
$actualEnd = $endDate ?? clone $end;
|
||||
$actualEnd = $endDate;
|
||||
$occurrences = $this->recurring->getOccurrencesInRange($repetition, $actualStart, $actualEnd);
|
||||
}
|
||||
if ('times' === $endsAt) {
|
||||
@@ -155,7 +155,7 @@ class RecurrenceController extends Controller
|
||||
} catch (InvalidFormatException $e) {
|
||||
$date = Carbon::today(config('app.timezone'));
|
||||
}
|
||||
if (false === $date) {
|
||||
if (null === $date) {
|
||||
return response()->json();
|
||||
}
|
||||
$date->startOfDay();
|
||||
|
@@ -115,7 +115,7 @@ class NewUserController extends Controller
|
||||
|
||||
// store frontpage preferences:
|
||||
$accounts = $this->repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray();
|
||||
app('preferences')->set('frontPageAccounts', $accounts);
|
||||
app('preferences')->set('frontpageAccounts', $accounts);
|
||||
|
||||
// mark.
|
||||
app('preferences')->mark();
|
||||
|
@@ -89,10 +89,10 @@ class PreferencesController extends Controller
|
||||
/** @var array<int, int> $accountIds */
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
$viewRange = app('navigation')->getViewRange(false);
|
||||
$frontPageAccountsPref = app('preferences')->get('frontPageAccounts', $accountIds);
|
||||
$frontPageAccounts = $frontPageAccountsPref->data;
|
||||
if (!is_array($frontPageAccounts)) {
|
||||
$frontPageAccounts = $accountIds;
|
||||
$frontpageAccountsPref = app('preferences')->get('frontpageAccounts', $accountIds);
|
||||
$frontpageAccounts = $frontpageAccountsPref->data;
|
||||
if (!is_array($frontpageAccounts)) {
|
||||
$frontpageAccounts = $accountIds;
|
||||
}
|
||||
$language = app('steam')->getLanguage();
|
||||
$languages = config('firefly.languages');
|
||||
@@ -128,8 +128,8 @@ class PreferencesController extends Controller
|
||||
$locales = ['equal' => (string)trans('firefly.equal_to_language')] + $locales;
|
||||
// an important fallback is that the frontPageAccount array gets refilled automatically
|
||||
// when it turns up empty.
|
||||
if (0 === count($frontPageAccounts)) {
|
||||
$frontPageAccounts = $accountIds;
|
||||
if (0 === count($frontpageAccounts)) {
|
||||
$frontpageAccounts = $accountIds;
|
||||
}
|
||||
|
||||
// for the demo user, the slackUrl is automatically emptied.
|
||||
@@ -139,7 +139,7 @@ class PreferencesController extends Controller
|
||||
$slackUrl = '';
|
||||
}
|
||||
|
||||
return view('preferences.index', compact('language', 'groupedAccounts', 'isDocker', 'frontPageAccounts', 'languages', 'darkMode', 'availableDarkModes', 'notifications', 'slackUrl', 'locales', 'locale', 'tjOptionalFields', 'viewRange', 'customFiscalYear', 'listPageSize', 'fiscalYearStart'));
|
||||
return view('preferences.index', compact('language', 'groupedAccounts', 'isDocker', 'frontpageAccounts', 'languages', 'darkMode', 'availableDarkModes', 'notifications', 'slackUrl', 'locales', 'locale', 'tjOptionalFields', 'viewRange', 'customFiscalYear', 'listPageSize', 'fiscalYearStart'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,12 +155,12 @@ class PreferencesController extends Controller
|
||||
public function postIndex(Request $request)
|
||||
{
|
||||
// front page accounts
|
||||
$frontPageAccounts = [];
|
||||
if (is_array($request->get('frontPageAccounts')) && count($request->get('frontPageAccounts')) > 0) {
|
||||
foreach ($request->get('frontPageAccounts') as $id) {
|
||||
$frontPageAccounts[] = (int)$id;
|
||||
$frontpageAccounts = [];
|
||||
if (is_array($request->get('frontpageAccounts')) && count($request->get('frontpageAccounts')) > 0) {
|
||||
foreach ($request->get('frontpageAccounts') as $id) {
|
||||
$frontpageAccounts[] = (int)$id;
|
||||
}
|
||||
app('preferences')->set('frontPageAccounts', $frontPageAccounts);
|
||||
app('preferences')->set('frontpageAccounts', $frontpageAccounts);
|
||||
}
|
||||
|
||||
// extract notifications:
|
||||
@@ -223,8 +223,8 @@ class PreferencesController extends Controller
|
||||
|
||||
// same for locale:
|
||||
if (!auth()->user()->hasRole('demo')) {
|
||||
/** @var Preference $locale */
|
||||
$locale = $request->get('locale');
|
||||
$locale = (string) $request->get('locale');
|
||||
$locale = '' === $locale ? null : $locale;
|
||||
app('preferences')->set('locale', $locale);
|
||||
}
|
||||
|
||||
|
@@ -23,7 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Events\UserChangedEmail;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Exceptions\ValidationException;
|
||||
@@ -467,9 +466,7 @@ class ProfileController extends Controller
|
||||
if (is_array($secret)) {
|
||||
$secret = null;
|
||||
}
|
||||
if (is_int($secret)) {
|
||||
$secret = (string)$secret;
|
||||
}
|
||||
$secret = (string)$secret;
|
||||
|
||||
$repository->setMFACode($user, $secret);
|
||||
|
||||
|
45
app/Http/Controllers/UserGroup/CreateController.php
Normal file
45
app/Http/Controllers/UserGroup/CreateController.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/*
|
||||
* CreateController.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\UserGroup;
|
||||
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Foundation\Application;
|
||||
|
||||
class CreateController extends Controller
|
||||
{
|
||||
/**
|
||||
* @return Application|Factory|\Illuminate\Contracts\Foundation\Application|View
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$title = (string)trans('firefly.administrations_page_title');
|
||||
$subTitle = (string)trans('firefly.administrations_page_create_sub_title');
|
||||
$mainTitleIcon = 'fa-book';
|
||||
app('log')->debug(sprintf('Now at %s', __METHOD__));
|
||||
|
||||
return view('administrations.create')->with(compact('title', 'subTitle', 'mainTitleIcon'));
|
||||
}
|
||||
}
|
46
app/Http/Controllers/UserGroup/EditController.php
Normal file
46
app/Http/Controllers/UserGroup/EditController.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/*
|
||||
* CreateController.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\UserGroup;
|
||||
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Foundation\Application;
|
||||
|
||||
class EditController extends Controller
|
||||
{
|
||||
/**
|
||||
* @return Application|Factory|\Illuminate\Contracts\Foundation\Application|View
|
||||
*/
|
||||
public function edit(UserGroup $userGroup)
|
||||
{
|
||||
$title = (string)trans('firefly.administrations_page_title');
|
||||
$subTitle = (string)trans('firefly.administrations_page_edit_sub_title', ['title' => $userGroup->title]);
|
||||
$mainTitleIcon = 'fa-book';
|
||||
app('log')->debug(sprintf('Now at %s', __METHOD__));
|
||||
|
||||
return view('administrations.edit')->with(compact('title', 'subTitle', 'mainTitleIcon'));
|
||||
}
|
||||
}
|
47
app/Http/Controllers/UserGroup/IndexController.php
Normal file
47
app/Http/Controllers/UserGroup/IndexController.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/*
|
||||
* IndexController.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\UserGroup;
|
||||
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class IndexController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show all administrations.
|
||||
*
|
||||
* @return Factory|View
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$title = (string)trans('firefly.administrations_page_title');
|
||||
$subTitle = (string)trans('firefly.administrations_page_sub_title');
|
||||
$mainTitleIcon = 'fa-book';
|
||||
app('log')->debug(sprintf('Now at %s', __METHOD__));
|
||||
|
||||
return view('administrations.index')->with(compact('title', 'subTitle', 'mainTitleIcon'));
|
||||
}
|
||||
}
|
@@ -25,6 +25,7 @@ namespace FireflyIII\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Vite;
|
||||
use Barryvdh\Debugbar\Facades\Debugbar;
|
||||
|
||||
/**
|
||||
* Class SecureHeaders
|
||||
@@ -43,6 +44,9 @@ class SecureHeaders
|
||||
// generate and share nonce.
|
||||
$nonce = base64_encode(random_bytes(16));
|
||||
Vite::useCspNonce($nonce);
|
||||
if (class_exists('Barryvdh\Debugbar\Facades\Debugbar')) {
|
||||
Debugbar::getJavascriptRenderer()->setCspNonce($nonce);
|
||||
}
|
||||
app('view')->share('JS_NONCE', $nonce);
|
||||
|
||||
$response = $next($request);
|
||||
@@ -55,14 +59,29 @@ class SecureHeaders
|
||||
"base-uri 'self'",
|
||||
"font-src 'self' data:",
|
||||
sprintf("connect-src 'self' %s", $trackingScriptSrc),
|
||||
sprintf("img-src 'self' 'nonce-%1s'", $nonce),
|
||||
sprintf("img-src 'self' data: 'nonce-%1s' ", $nonce),
|
||||
"manifest-src 'self'",
|
||||
];
|
||||
|
||||
// overrule in development mode
|
||||
if (true === env('IS_LOCAL_DEV')) {
|
||||
$csp = [
|
||||
"default-src 'none'",
|
||||
"object-src 'none'",
|
||||
sprintf("script-src 'unsafe-eval' 'strict-dynamic' 'nonce-%1s' https://firefly.sd.internal/_debugbar/assets", $nonce),
|
||||
"style-src 'unsafe-inline' 'self' https://10.0.0.15:5173/",
|
||||
"base-uri 'self'",
|
||||
"font-src 'self' data: https://10.0.0.15:5173/",
|
||||
sprintf("connect-src 'self' %s https://10.0.0.15:5173/ wss://10.0.0.15:5173/", $trackingScriptSrc),
|
||||
sprintf("img-src 'self' data: 'nonce-%1s'", $nonce),
|
||||
"manifest-src 'self'",
|
||||
];
|
||||
}
|
||||
|
||||
$route = $request->route();
|
||||
$customUrl = '';
|
||||
$authGuard = (string)config('firefly.authentication_guard');
|
||||
$logoutUrl = (string)config('firefly.custom_logout_url');
|
||||
$authGuard = (string) config('firefly.authentication_guard');
|
||||
$logoutUrl = (string) config('firefly.custom_logout_url');
|
||||
if ('remote_user_guard' === $authGuard && '' !== $logoutUrl) {
|
||||
$customUrl = $logoutUrl;
|
||||
}
|
||||
@@ -110,8 +129,8 @@ class SecureHeaders
|
||||
*/
|
||||
private function getTrackingScriptSource(): string
|
||||
{
|
||||
if ('' !== (string)config('firefly.tracker_site_id') && '' !== (string)config('firefly.tracker_url')) {
|
||||
return (string)config('firefly.tracker_url');
|
||||
if ('' !== (string) config('firefly.tracker_site_id') && '' !== (string) config('firefly.tracker_url')) {
|
||||
return (string) config('firefly.tracker_url');
|
||||
}
|
||||
|
||||
return '';
|
||||
|
@@ -57,7 +57,7 @@ class AccountFormRequest extends FormRequest
|
||||
'account_type_name' => $this->convertString('objectType'),
|
||||
'currency_id' => $this->convertInteger('currency_id'),
|
||||
'virtual_balance' => $this->convertString('virtual_balance'),
|
||||
'iban' => $this->convertString('iban'),
|
||||
'iban' => $this->convertIban('iban'),
|
||||
'BIC' => $this->convertString('BIC'),
|
||||
'account_number' => $this->convertString('account_number'),
|
||||
'account_role' => $this->convertString('account_role'),
|
||||
|
@@ -29,6 +29,7 @@ use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
@@ -100,7 +101,7 @@ class DownloadExchangeRates implements ShouldQueue
|
||||
|
||||
try {
|
||||
$res = $client->get($url);
|
||||
} catch (RequestException $e) {
|
||||
} catch (ConnectException|RequestException $e) {
|
||||
app('log')->warning(sprintf('Trying to grab "%s" resulted in error "%d".', $url, $e->getMessage()));
|
||||
|
||||
return;
|
||||
@@ -119,7 +120,7 @@ class DownloadExchangeRates implements ShouldQueue
|
||||
return;
|
||||
}
|
||||
$date = Carbon::createFromFormat('Y-m-d', $json['date'], config('app.timezone'));
|
||||
if (false === $date) {
|
||||
if (null === $date) {
|
||||
return;
|
||||
}
|
||||
$this->saveRates($currency, $date, $json['rates']);
|
||||
|
@@ -27,6 +27,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Message;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Mailer\Exception\TransportException;
|
||||
|
||||
/**
|
||||
@@ -68,6 +69,14 @@ class MailError extends Job implements ShouldQueue
|
||||
$args['user'] = $this->userData;
|
||||
$args['ip'] = $this->ipAddress;
|
||||
$args['token'] = config('firefly.ipinfo_token');
|
||||
|
||||
// limit number of error mails that can be sent.
|
||||
if ($this->reachedLimit()) {
|
||||
Log::info('MailError: reached limit, not sending email.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->attempts() < 3 && '' !== $email) {
|
||||
try {
|
||||
\Mail::send(
|
||||
@@ -96,4 +105,62 @@ class MailError extends Job implements ShouldQueue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function reachedLimit(): bool
|
||||
{
|
||||
Log::debug('reachedLimit()');
|
||||
$types = [
|
||||
'5m' => ['limit' => 5, 'reset' => 5 * 60],
|
||||
'1h' => ['limit' => 15, 'reset' => 60 * 60],
|
||||
'24h' => ['limit' => 15, 'reset' => 24 * 60 * 60],
|
||||
];
|
||||
$file = storage_path('framework/cache/error-count.json');
|
||||
$directory = storage_path('framework/cache');
|
||||
$limits = [];
|
||||
|
||||
if (!is_writable($directory)) {
|
||||
Log::error(sprintf('MailError: cannot write to "%s", cannot rate limit errors!', $directory));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file_exists($file)) {
|
||||
Log::debug(sprintf('Wrote new file in "%s"', $file));
|
||||
file_put_contents($file, json_encode($limits, JSON_PRETTY_PRINT));
|
||||
}
|
||||
if (file_exists($file)) {
|
||||
Log::debug(sprintf('Read file in "%s"', $file));
|
||||
$limits = json_decode((string)file_get_contents($file), true);
|
||||
}
|
||||
// limit reached?
|
||||
foreach ($types as $type => $info) {
|
||||
Log::debug(sprintf('Now checking limit "%s"', $type), $info);
|
||||
if (!array_key_exists($type, $limits)) {
|
||||
Log::debug(sprintf('Limit "%s" reset to zero, did not exist yet.', $type));
|
||||
$limits[$type] = [
|
||||
'time' => time(),
|
||||
'sent' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
if (time() - $limits[$type]['time'] > $info['reset']) {
|
||||
Log::debug(sprintf('Time past for this limit is %d seconds, exceeding %d seconds. Reset to zero.', time() - $limits[$type]['time'], $info['reset']));
|
||||
$limits[$type] = [
|
||||
'time' => time(),
|
||||
'sent' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
if ($limits[$type]['sent'] > $info['limit']) {
|
||||
Log::warning(sprintf('Sent %d emails in %s, return true.', $limits[$type]['sent'], $type));
|
||||
|
||||
return true;
|
||||
}
|
||||
++$limits[$type]['sent'];
|
||||
}
|
||||
file_put_contents($file, json_encode($limits, JSON_PRETTY_PRINT));
|
||||
Log::debug('No limits reached, return FALSE.');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -128,7 +128,7 @@ class WarnAboutBills implements ShouldQueue
|
||||
$today = clone $this->date;
|
||||
$carbon = clone $bill->{$field};
|
||||
|
||||
return (int) $today->diffInDays($carbon);
|
||||
return (int)$today->diffInDays($carbon);
|
||||
}
|
||||
|
||||
private function sendWarning(Bill $bill, string $field): void
|
||||
|
53
app/JsonApi/Rules/IsValidFilter.php
Normal file
53
app/JsonApi/Rules/IsValidFilter.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/*
|
||||
* IsValidFilter.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\JsonApi\Rules;
|
||||
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
class IsValidFilter implements ValidationRule
|
||||
{
|
||||
private array $allowed;
|
||||
|
||||
public function __construct(array $keys)
|
||||
{
|
||||
$this->allowed = $keys;
|
||||
$this->allowed[] = 'user_group_id';
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function validate(string $attribute, mixed $value, \Closure $fail): void
|
||||
{
|
||||
if ('filter' !== $attribute) {
|
||||
$fail('validation.bad_api_filter')->translate();
|
||||
}
|
||||
if (!is_array($value)) {
|
||||
$value = explode(',', $value);
|
||||
}
|
||||
foreach ($value as $key => $val) {
|
||||
if (!in_array($key, $this->allowed, true)) {
|
||||
$fail('validation.bad_api_filter')->translate(['filter' => $key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
52
app/JsonApi/Rules/IsValidPage.php
Normal file
52
app/JsonApi/Rules/IsValidPage.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/*
|
||||
* IsValidFilter.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\JsonApi\Rules;
|
||||
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
class IsValidPage implements ValidationRule
|
||||
{
|
||||
private array $allowed;
|
||||
|
||||
public function __construct(array $keys)
|
||||
{
|
||||
$this->allowed = $keys;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function validate(string $attribute, mixed $value, \Closure $fail): void
|
||||
{
|
||||
if ('page' !== $attribute) {
|
||||
$fail('validation.bad_api_filter')->translate();
|
||||
}
|
||||
if (!is_array($value)) {
|
||||
$value = explode(',', $value);
|
||||
}
|
||||
foreach ($value as $key => $val) {
|
||||
if (!in_array($key, $this->allowed, true)) {
|
||||
$fail('validation.bad_api_page')->translate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
app/JsonApi/V3/AccountBalances/AccountBalanceRepository.php
Normal file
45
app/JsonApi/V3/AccountBalances/AccountBalanceRepository.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/*
|
||||
* AccountBalanceRepository.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\JsonApi\V3\AccountBalances;
|
||||
|
||||
use FireflyIII\Entities\AccountBalance;
|
||||
use LaravelJsonApi\Contracts\Store\QueriesAll;
|
||||
use LaravelJsonApi\NonEloquent\AbstractRepository;
|
||||
|
||||
class AccountBalanceRepository extends AbstractRepository implements QueriesAll
|
||||
{
|
||||
#[\Override]
|
||||
public function find(string $resourceId): ?object
|
||||
{
|
||||
return AccountBalance::fromArray();
|
||||
}
|
||||
|
||||
public function queryAll(): Capabilities\AccountBalanceQuery
|
||||
{
|
||||
return Capabilities\AccountBalanceQuery::make()
|
||||
->withServer($this->server)
|
||||
->withSchema($this->schema)
|
||||
;
|
||||
}
|
||||
}
|
44
app/JsonApi/V3/AccountBalances/AccountBalanceResource.php
Normal file
44
app/JsonApi/V3/AccountBalances/AccountBalanceResource.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\JsonApi\V3\AccountBalances;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use LaravelJsonApi\Core\Resources\JsonApiResource;
|
||||
|
||||
class AccountBalanceResource extends JsonApiResource
|
||||
{
|
||||
/**
|
||||
* Get the resource id.
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return $this->resource->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource's attributes.
|
||||
*
|
||||
* @param null|Request $request
|
||||
*/
|
||||
public function attributes($request): iterable
|
||||
{
|
||||
return [
|
||||
'name' => $this->resource->amount,
|
||||
'amount' => $this->resource->amount,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource's relationships.
|
||||
*
|
||||
* @param null|Request $request
|
||||
*/
|
||||
public function relationships($request): iterable
|
||||
{
|
||||
return [
|
||||
$this->relation('account')->withData($this->resource->getAccount()),
|
||||
];
|
||||
}
|
||||
}
|
50
app/JsonApi/V3/AccountBalances/AccountBalanceSchema.php
Normal file
50
app/JsonApi/V3/AccountBalances/AccountBalanceSchema.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\JsonApi\V3\AccountBalances;
|
||||
|
||||
use FireflyIII\Entities\AccountBalance;
|
||||
use LaravelJsonApi\Core\Schema\Schema;
|
||||
use LaravelJsonApi\Eloquent\Fields\Relations\HasOne;
|
||||
use LaravelJsonApi\NonEloquent\Fields\Attribute;
|
||||
use LaravelJsonApi\NonEloquent\Fields\ID;
|
||||
|
||||
class AccountBalanceSchema extends Schema
|
||||
{
|
||||
/**
|
||||
* The model the schema corresponds to.
|
||||
*/
|
||||
public static string $model = AccountBalance::class;
|
||||
|
||||
/**
|
||||
* Get the resource fields.
|
||||
*/
|
||||
public function fields(): array
|
||||
{
|
||||
return [
|
||||
ID::make(),
|
||||
Attribute::make('name'),
|
||||
Attribute::make('amount'),
|
||||
HasOne::make('account'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource filters.
|
||||
*/
|
||||
public function filters(): array
|
||||
{
|
||||
return [
|
||||
// Filter::make('id'),
|
||||
];
|
||||
}
|
||||
|
||||
public function repository(): AccountBalanceRepository
|
||||
{
|
||||
return AccountBalanceRepository::make()
|
||||
->withServer($this->server)
|
||||
->withSchema($this)
|
||||
;
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/*
|
||||
* AccountBalanceQuery.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\JsonApi\V3\AccountBalances\Capabilities;
|
||||
|
||||
use FireflyIII\Entities\AccountBalance;
|
||||
use FireflyIII\Models\Account;
|
||||
use LaravelJsonApi\NonEloquent\Capabilities\QueryAll;
|
||||
|
||||
class AccountBalanceQuery extends QueryAll
|
||||
{
|
||||
private Account $account;
|
||||
|
||||
/**
|
||||
* QuerySites constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function get(): iterable
|
||||
{
|
||||
return [
|
||||
AccountBalance::fromArray(),
|
||||
AccountBalance::fromArray(),
|
||||
AccountBalance::fromArray(),
|
||||
AccountBalance::fromArray(),
|
||||
];
|
||||
}
|
||||
|
||||
public function withAccount(Account $account): self
|
||||
{
|
||||
$this->account = $account;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user