mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-08-16 10:54:39 +00:00
Compare commits
220 Commits
develop-20
...
v6.1.18
Author | SHA1 | Date | |
---|---|---|---|
|
f52978e71f | ||
|
3a3358124d | ||
|
929808c633 | ||
|
a78df574f3 | ||
|
875cad16b6 | ||
|
7bc30192ca | ||
|
a1a8968e98 | ||
|
6abb74a038 | ||
|
2d7d05e985 | ||
|
d426e09474 | ||
|
72d55cb953 | ||
|
73ad865581 | ||
|
fefb52beb7 | ||
|
abd503543b | ||
|
e3eb550581 | ||
|
46b780758e | ||
|
b2c3ee9779 | ||
|
dca899bcee | ||
|
9667b8a948 | ||
|
661f225fe7 | ||
|
4c6fe0c8de | ||
|
78f457950e | ||
|
d831cc8df2 | ||
|
7056406afc | ||
|
c85cfcf3e6 | ||
|
db06d06789 | ||
|
a28b990cd1 | ||
|
dab4bfa7a6 | ||
|
6575236f2b | ||
|
ad582c8806 | ||
|
452e9cb953 | ||
|
a64f137b39 | ||
|
c067d6aab0 | ||
|
119b9920a6 | ||
|
99ed54fce8 | ||
|
2ea57cdd38 | ||
|
bb94bdfdaf | ||
|
4de8398cc2 | ||
|
e6e8cd5d8a | ||
|
0b200309ba | ||
|
a184548912 | ||
|
c987191212 | ||
|
7009b444d9 | ||
|
06551d5367 | ||
|
a20622ac0c | ||
|
ca38117fca | ||
|
9478f78d4f | ||
|
5c2397bbae | ||
|
92fefef816 | ||
|
d3ced65524 | ||
|
29eb748831 | ||
|
76df3d5f33 | ||
|
252076ec1f | ||
|
bbec28591f | ||
|
075a360ba6 | ||
|
477524a8ae | ||
|
dfe055732d | ||
|
78b611a18d | ||
|
367bdf65e6 | ||
|
3fc9caa31a | ||
|
95a41fcab7 | ||
|
58b409fc00 | ||
|
3eaaac09ad | ||
|
bcb672920c | ||
|
79b91e25c2 | ||
|
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 |
@@ -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
|
||||
|
912
.ci/php-cs-fixer/composer.lock
generated
912
.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=$?
|
||||
|
||||
|
21
.env.example
21
.env.example
@@ -299,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
|
||||
|
8
.github/pull_request_template.md
vendored
8
.github/pull_request_template.md
vendored
@@ -1,13 +1,13 @@
|
||||
<!--
|
||||
Thank you for submitting new code to Firefly III, or any of the related projects. Please read the following rules carefully.
|
||||
|
||||
- Do not submit solutions for problems that are not already reported in an issue
|
||||
- Firefly III can't be your learning experience. If you're new to all of this, please go be new somewhere else
|
||||
- Do not open PRs to "discuss" possible solutions or to "get feedback" on your code. I don't have time for that.
|
||||
- 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.
|
||||
|
||||
Perhaps open an issue first, before you open a 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
|
||||
|
||||
|
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/close-duplicates.yml
vendored
2
.github/workflows/close-duplicates.yml
vendored
@@ -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"
|
||||
|
63
.github/workflows/release.yml
vendored
63
.github/workflows/release.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
required: true
|
||||
default: 'develop'
|
||||
schedule:
|
||||
- cron: '0 3 * * MON,THU'
|
||||
- cron: '0 3 * * MON'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
php-version: '8.3'
|
||||
extensions: mbstring, intl, zip, bcmath
|
||||
- name: crowdin action
|
||||
uses: crowdin/github-action@v1
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
upload_sources: true
|
||||
download_translations: true
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
CROWDIN_TOKEN: ${{ secrets.CROWDIN_TOKEN }}
|
||||
- name: Cleanup translations
|
||||
id: cleanup-transactions
|
||||
uses: JC5/firefly-iii-dev@v36
|
||||
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@v36
|
||||
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@v36
|
||||
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@v36
|
||||
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@v36
|
||||
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@v36
|
||||
uses: JC5/firefly-iii-dev@main
|
||||
with:
|
||||
action: 'ff3:json-translations v2'
|
||||
output: ''
|
||||
@@ -106,34 +115,30 @@ jobs:
|
||||
GH_TOKEN: ''
|
||||
- name: Code cleanup
|
||||
id: code-cleanup
|
||||
uses: JC5/firefly-iii-dev@v36
|
||||
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: |
|
||||
npm install
|
||||
npm run prod --workspace=v1
|
||||
npm run build --workspace=v2
|
||||
npm update
|
||||
npm run build
|
||||
- name: Build old JS
|
||||
id: old-js
|
||||
uses: JC5/firefly-iii-dev@v36
|
||||
with:
|
||||
action: 'ff3:old-js'
|
||||
output: ''
|
||||
env:
|
||||
FIREFLY_III_ROOT: /github/workspace
|
||||
GH_TOKEN: ''
|
||||
- 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
|
||||
@@ -149,7 +154,6 @@ jobs:
|
||||
tarName=FireflyIII-$version.tar.gz
|
||||
|
||||
# update composer (again)
|
||||
composer validate --strict
|
||||
composer update --no-dev --no-scripts --no-plugins
|
||||
composer dump-autoload
|
||||
|
||||
@@ -199,6 +203,10 @@ jobs:
|
||||
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.'
|
||||
@@ -206,7 +214,7 @@ jobs:
|
||||
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." >> 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
|
||||
@@ -229,6 +237,10 @@ jobs:
|
||||
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
|
||||
@@ -242,6 +254,7 @@ jobs:
|
||||
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"
|
||||
@@ -256,6 +269,10 @@ jobs:
|
||||
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
|
||||
|
4
.github/workflows/stale.yml
vendored
4
.github/workflows/stale.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: >
|
||||
stale-issue-message: |
|
||||
Hi there!
|
||||
|
||||
This is an automatic reply. `Share and enjoy`
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
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: >
|
||||
stale-pr-message: |
|
||||
Hi there!
|
||||
|
||||
This is an automatic reply. `Share and enjoy`
|
||||
|
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
|
||||
|
194
THANKS.md
Executable file
194
THANKS.md
Executable file
@@ -0,0 +1,194 @@
|
||||
# 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
|
||||
- Steve Wasiura
|
||||
- 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!
|
@@ -73,7 +73,8 @@ class UpdateController extends Controller
|
||||
$data = $request->getAll();
|
||||
|
||||
// Fixes 8750.
|
||||
foreach ($data['transactions'] as $index => $info) {
|
||||
$transactions = $data['transactions'] ?? [];
|
||||
foreach ($transactions as $index => $info) {
|
||||
unset($data['transactions'][$index]['type']);
|
||||
}
|
||||
|
||||
|
@@ -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'],
|
||||
|
@@ -58,7 +58,7 @@ class StoreRequest extends FormRequest
|
||||
$models = config('firefly.valid_attachment_models');
|
||||
$models = array_map(
|
||||
static function (string $className) {
|
||||
return str_replace('FireflyIII\\Models\\', '', $className);
|
||||
return str_replace('FireflyIII\Models\\', '', $className);
|
||||
},
|
||||
$models
|
||||
);
|
||||
|
@@ -60,7 +60,7 @@ class UpdateRequest extends FormRequest
|
||||
$models = config('firefly.valid_attachment_models');
|
||||
$models = array_map(
|
||||
static function (string $className) {
|
||||
return str_replace('FireflyIII\\Models\\', '', $className);
|
||||
return str_replace('FireflyIII\Models\\', '', $className);
|
||||
},
|
||||
$models
|
||||
);
|
||||
|
@@ -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')),
|
||||
];
|
||||
|
@@ -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,
|
||||
];
|
||||
|
@@ -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,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -24,18 +24,17 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V2\Controllers\Chart;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Api\V2\Controllers\Controller;
|
||||
use FireflyIII\Api\V2\Request\Chart\DashboardChartRequest;
|
||||
use FireflyIII\Api\V2\Request\Chart\ChartRequest;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\Chart\ChartData;
|
||||
use FireflyIII\Support\Http\Api\CleansChartData;
|
||||
use FireflyIII\Support\Http\Api\CollectsAccountsFromFilter;
|
||||
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class AccountController
|
||||
@@ -43,9 +42,12 @@ use Illuminate\Support\Collection;
|
||||
class AccountController extends Controller
|
||||
{
|
||||
use CleansChartData;
|
||||
use CollectsAccountsFromFilter;
|
||||
use ValidatesUserGroupTrait;
|
||||
|
||||
private AccountRepositoryInterface $repository;
|
||||
private ChartData $chartData;
|
||||
private TransactionCurrency $default;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@@ -53,10 +55,9 @@ 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));
|
||||
$this->chartData = new ChartData();
|
||||
$this->default = app('amount')->getDefaultCurrency();
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@@ -64,107 +65,76 @@ class AccountController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* This endpoint is documented at
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v2)#/charts/getChartAccountOverview
|
||||
*
|
||||
* The native currency is the preferred currency on the page /currencies.
|
||||
*
|
||||
* If a transaction has foreign currency = native currency, the foreign amount will be used, no conversion
|
||||
* will take place.
|
||||
*
|
||||
* TODO validate and set user_group_id from request
|
||||
* TODO fix documentation
|
||||
*
|
||||
* @throws FireflyException
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function dashboard(DashboardChartRequest $request): JsonResponse
|
||||
public function dashboard(ChartRequest $request): JsonResponse
|
||||
{
|
||||
/** @var Carbon $start */
|
||||
$start = $this->parameters->get('start');
|
||||
$queryParameters = $request->getParameters();
|
||||
$accounts = $this->getAccountList($queryParameters);
|
||||
|
||||
/** @var Carbon $end */
|
||||
$end = $this->parameters->get('end');
|
||||
$end->endOfDay();
|
||||
|
||||
/** @var TransactionCurrency $default */
|
||||
$default = app('amount')->getDefaultCurrency();
|
||||
$params = $request->getAll();
|
||||
|
||||
/** @var Collection $accounts */
|
||||
$accounts = $params['accounts'];
|
||||
$chartData = [];
|
||||
|
||||
// user's preferences
|
||||
if (0 === $accounts->count()) {
|
||||
$defaultSet = $this->repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT])->pluck('id')->toArray();
|
||||
$frontpage = app('preferences')->get('frontpageAccounts', $defaultSet);
|
||||
|
||||
if (!(is_array($frontpage->data) && count($frontpage->data) > 0)) {
|
||||
$frontpage->data = $defaultSet;
|
||||
$frontpage->save();
|
||||
}
|
||||
|
||||
$accounts = $this->repository->getAccountsById($frontpage->data);
|
||||
}
|
||||
|
||||
// both options are overruled by "preselected"
|
||||
if ('all' === $params['preselected']) {
|
||||
$accounts = $this->repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]);
|
||||
}
|
||||
if ('assets' === $params['preselected']) {
|
||||
$accounts = $this->repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]);
|
||||
}
|
||||
if ('liabilities' === $params['preselected']) {
|
||||
$accounts = $this->repository->getAccountsByType([AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]);
|
||||
}
|
||||
// move date to end of day
|
||||
$queryParameters['start']->startOfDay();
|
||||
$queryParameters['end']->endOfDay();
|
||||
|
||||
// loop each account, and collect info:
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$currency = $this->repository->getAccountCurrency($account);
|
||||
if (null === $currency) {
|
||||
$currency = $default;
|
||||
}
|
||||
$currentSet = [
|
||||
'label' => $account->name,
|
||||
// the currency that belongs to the account.
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
|
||||
// the default currency of the user (could be the same!)
|
||||
'native_currency_id' => (string)$default->id,
|
||||
'native_currency_code' => $default->code,
|
||||
'native_currency_symbol' => $default->symbol,
|
||||
'native_currency_decimal_places' => $default->decimal_places,
|
||||
'start' => $start->toAtomString(),
|
||||
'end' => $end->toAtomString(),
|
||||
'period' => '1D',
|
||||
'entries' => [],
|
||||
'native_entries' => [],
|
||||
];
|
||||
$currentStart = clone $start;
|
||||
$range = app('steam')->balanceInRange($account, $start, clone $end, $currency);
|
||||
$rangeConverted = app('steam')->balanceInRangeConverted($account, $start, clone $end, $default);
|
||||
|
||||
$previous = array_values($range)[0];
|
||||
$previousConverted = array_values($rangeConverted)[0];
|
||||
while ($currentStart <= $end) {
|
||||
$format = $currentStart->format('Y-m-d');
|
||||
$label = $currentStart->toAtomString();
|
||||
$balance = array_key_exists($format, $range) ? $range[$format] : $previous;
|
||||
$balanceConverted = array_key_exists($format, $rangeConverted) ? $rangeConverted[$format] : $previousConverted;
|
||||
$previous = $balance;
|
||||
$previousConverted = $balanceConverted;
|
||||
|
||||
$currentStart->addDay();
|
||||
$currentSet['entries'][$label] = $balance;
|
||||
$currentSet['native_entries'][$label] = $balanceConverted;
|
||||
}
|
||||
$chartData[] = $currentSet;
|
||||
$this->renderAccountData($queryParameters, $account);
|
||||
}
|
||||
|
||||
return response()->json($this->clean($chartData));
|
||||
return response()->json($this->chartData->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function renderAccountData(array $params, Account $account): void
|
||||
{
|
||||
$currency = $this->repository->getAccountCurrency($account);
|
||||
if (null === $currency) {
|
||||
$currency = $this->default;
|
||||
}
|
||||
$currentSet = [
|
||||
'label' => $account->name,
|
||||
|
||||
// the currency that belongs to the account.
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
|
||||
// the default currency of the user (could be the same!)
|
||||
'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,
|
||||
'date' => $params['start']->toAtomString(),
|
||||
'start' => $params['start']->toAtomString(),
|
||||
'end' => $params['end']->toAtomString(),
|
||||
'period' => '1D',
|
||||
'entries' => [],
|
||||
'native_entries' => [],
|
||||
];
|
||||
$currentStart = clone $params['start'];
|
||||
$range = app('steam')->balanceInRange($account, $params['start'], clone $params['end'], $currency);
|
||||
$rangeConverted = app('steam')->balanceInRangeConverted($account, $params['start'], clone $params['end'], $this->default);
|
||||
|
||||
$previous = array_values($range)[0];
|
||||
$previousConverted = array_values($rangeConverted)[0];
|
||||
while ($currentStart <= $params['end']) {
|
||||
$format = $currentStart->format('Y-m-d');
|
||||
$label = $currentStart->toAtomString();
|
||||
$balance = array_key_exists($format, $range) ? $range[$format] : $previous;
|
||||
$balanceConverted = array_key_exists($format, $rangeConverted) ? $rangeConverted[$format] : $previousConverted;
|
||||
$previous = $balance;
|
||||
$previousConverted = $balanceConverted;
|
||||
|
||||
$currentStart->addDay();
|
||||
$currentSet['entries'][$label] = $balance;
|
||||
$currentSet['native_entries'][$label] = $balanceConverted;
|
||||
}
|
||||
$this->chartData->add($currentSet);
|
||||
}
|
||||
}
|
||||
|
@@ -24,17 +24,18 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V2\Controllers\Chart;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Api\V2\Controllers\Controller;
|
||||
use FireflyIII\Api\V2\Request\Chart\BalanceChartRequest;
|
||||
use FireflyIII\Api\V2\Request\Chart\ChartRequest;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\Chart\ChartData;
|
||||
use FireflyIII\Support\Http\Api\AccountBalanceGrouped;
|
||||
use FireflyIII\Support\Http\Api\CleansChartData;
|
||||
use FireflyIII\Support\Http\Api\CollectsAccountsFromFilter;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class BalanceController
|
||||
@@ -42,6 +43,30 @@ use Illuminate\Support\Collection;
|
||||
class BalanceController extends Controller
|
||||
{
|
||||
use CleansChartData;
|
||||
use CollectsAccountsFromFilter;
|
||||
|
||||
private AccountRepositoryInterface $repository;
|
||||
private GroupCollectorInterface $collector;
|
||||
private ChartData $chartData;
|
||||
private TransactionCurrency $default;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->collector = app(GroupCollectorInterface::class);
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
$this->collector->setUserGroup($userGroup);
|
||||
$this->chartData = new ChartData();
|
||||
$this->default = app('amount')->getDefaultCurrency();
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The code is practically a duplicate of ReportController::operations.
|
||||
@@ -52,50 +77,43 @@ class BalanceController extends Controller
|
||||
* If the transaction being processed is already in native currency OR if the
|
||||
* foreign amount is in the native currency, the amount will not be converted.
|
||||
*
|
||||
* TODO validate and set user_group_id
|
||||
* TODO collector set group, not user
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function balance(BalanceChartRequest $request): JsonResponse
|
||||
public function balance(ChartRequest $request): JsonResponse
|
||||
{
|
||||
$params = $request->getAll();
|
||||
$queryParameters = $request->getParameters();
|
||||
$accounts = $this->getAccountList($queryParameters);
|
||||
|
||||
/** @var Carbon $start */
|
||||
$start = $this->parameters->get('start');
|
||||
|
||||
/** @var Carbon $end */
|
||||
$end = $this->parameters->get('end');
|
||||
$end->endOfDay();
|
||||
|
||||
/** @var Collection $accounts */
|
||||
$accounts = $params['accounts'];
|
||||
|
||||
/** @var string $preferredRange */
|
||||
$preferredRange = $params['period'];
|
||||
// move date to end of day
|
||||
$queryParameters['start']->startOfDay();
|
||||
$queryParameters['end']->endOfDay();
|
||||
|
||||
// prepare for currency conversion and data collection:
|
||||
/** @var TransactionCurrency $default */
|
||||
$default = app('amount')->getDefaultCurrency();
|
||||
$default = app('amount')->getDefaultCurrency();
|
||||
|
||||
// get journals for entire period:
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setRange($start, $end)->withAccountInformation();
|
||||
$collector->setXorAccounts($accounts);
|
||||
$collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::RECONCILIATION, TransactionType::TRANSFER]);
|
||||
$journals = $collector->getExtractedJournals();
|
||||
|
||||
$object = new AccountBalanceGrouped();
|
||||
$object->setPreferredRange($preferredRange);
|
||||
$this->collector->setRange($queryParameters['start'], $queryParameters['end'])
|
||||
->withAccountInformation()
|
||||
->setXorAccounts($accounts)
|
||||
->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::RECONCILIATION, TransactionType::TRANSFER])
|
||||
;
|
||||
$journals = $this->collector->getExtractedJournals();
|
||||
|
||||
$object = new AccountBalanceGrouped();
|
||||
$object->setPreferredRange($queryParameters['period']);
|
||||
$object->setDefault($default);
|
||||
$object->setAccounts($accounts);
|
||||
$object->setJournals($journals);
|
||||
$object->setStart($start);
|
||||
$object->setEnd($end);
|
||||
$object->setStart($queryParameters['start']);
|
||||
$object->setEnd($queryParameters['end']);
|
||||
$object->groupByCurrencyAndPeriod();
|
||||
$chartData = $object->convertToChartData();
|
||||
$data = $object->convertToChartData();
|
||||
foreach ($data as $entry) {
|
||||
$this->chartData->add($entry);
|
||||
}
|
||||
|
||||
return response()->json($this->clean($chartData));
|
||||
return response()->json($this->chartData->render());
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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.
|
||||
|
118
app/Api/V2/Request/Chart/ChartRequest.php
Normal file
118
app/Api/V2/Request/Chart/ChartRequest.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?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\Rules\IsFilterValueIn;
|
||||
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' => $this->stringFromQueryParams($queryParameters, 'preselected', 'empty'),
|
||||
'period' => $this->stringFromQueryParams($queryParameters, 'period', '1M'),
|
||||
'accounts' => $this->arrayOfStrings($queryParameters, 'accounts'),
|
||||
// preselected heeft maar een paar toegestane waardes, dat moet ook goed gaan.
|
||||
// 'query' => $this->arrayOfStrings($queryParameters, 'query'),
|
||||
// 'size' => $this->integerFromQueryParams($queryParameters,'size', 50),
|
||||
// 'account_types' => $this->getAccountTypeParameter($this->arrayOfStrings($queryParameters, 'account_types')),
|
||||
];
|
||||
// collect accounts based on this list?
|
||||
}
|
||||
|
||||
// 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']),
|
||||
new IsFilterValueIn('preselected', config('firefly.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',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -80,7 +80,7 @@ class CreateGroupMemberships extends Command
|
||||
// check if membership exists
|
||||
$userGroup = UserGroup::where('title', $user->email)->first();
|
||||
if (null === $userGroup) {
|
||||
$userGroup = UserGroup::create(['title' => $user->email, 'default_administration' => true]);
|
||||
$userGroup = UserGroup::create(['title' => $user->email]);
|
||||
}
|
||||
|
||||
$userRole = UserRole::where('title', UserRoleEnum::OWNER->value)->first();
|
||||
|
@@ -115,7 +115,7 @@ class UpdateGroupInformation extends Command
|
||||
return;
|
||||
}
|
||||
if (0 !== $result) {
|
||||
$this->friendlyPositive(sprintf('User #%d: Moved %d %s objects to the correct group.', $user->id, $result, str_replace('FireflyIII\\Models\\', '', $className)));
|
||||
$this->friendlyPositive(sprintf('User #%d: Moved %d %s objects to the correct group.', $user->id, $result, str_replace('FireflyIII\Models\\', '', $className)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -111,7 +111,7 @@ class ForceDecimalSize extends Command
|
||||
{
|
||||
// switch stuff based on database connection:
|
||||
$this->operator = 'REGEXP';
|
||||
$this->regularExpression = '\'\\\\.[\\\\d]{%d}[1-9]+\'';
|
||||
$this->regularExpression = '\'\\\.[\\\d]{%d}[1-9]+\'';
|
||||
$this->cast = 'CHAR';
|
||||
if ('pgsql' === config('database.default')) {
|
||||
$this->operator = 'SIMILAR TO';
|
||||
@@ -119,7 +119,7 @@ class ForceDecimalSize extends Command
|
||||
$this->cast = 'TEXT';
|
||||
}
|
||||
if ('sqlite' === config('database.default')) {
|
||||
$this->regularExpression = '"\\.[\d]{%d}[1-9]+"';
|
||||
$this->regularExpression = '"\.[\d]{%d}[1-9]+"';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ class ForceDecimalSize extends Command
|
||||
/** @var string $field */
|
||||
foreach ($fields as $field) {
|
||||
$value = $item->{$field};
|
||||
if (null === $value) {
|
||||
if (null === $value || '' === $value) {
|
||||
continue;
|
||||
}
|
||||
// fix $field by rounding it down correctly.
|
||||
|
69
app/Console/Commands/Upgrade/CorrectAccountBalance.php
Normal file
69
app/Console/Commands/Upgrade/CorrectAccountBalance.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/*
|
||||
* CorrectAccountBalance.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\Console\Commands\Upgrade;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Support\Models\AccountBalanceCalculator;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class CorrectionSkeleton
|
||||
*/
|
||||
class CorrectAccountBalance extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
public const string CONFIG_NAME = '610_correct_balances';
|
||||
protected $description = 'Recalculate all account balance amounts';
|
||||
protected $signature = 'firefly-iii:correct-account-balance {--F|force : Force the execution of this command.}';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||
$this->friendlyInfo('This command has already been executed.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
$this->correctBalanceAmounts();
|
||||
$this->markAsExecuted();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function correctBalanceAmounts(): void
|
||||
{
|
||||
AccountBalanceCalculator::recalculateAll();
|
||||
}
|
||||
|
||||
private function isExecuted(): bool
|
||||
{
|
||||
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||
|
||||
return (bool)$configVar?->data;
|
||||
}
|
||||
|
||||
private function markAsExecuted(): void
|
||||
{
|
||||
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||
}
|
||||
}
|
@@ -65,6 +65,7 @@ class UpgradeDatabase extends Command
|
||||
'firefly-iii:budget-limit-periods',
|
||||
'firefly-iii:migrate-rule-actions',
|
||||
'firefly-iii:restore-oauth-keys',
|
||||
'firefly-iii:correct-account-balance',
|
||||
// also just in case, some integrity commands:
|
||||
'firefly-iii:create-group-memberships',
|
||||
'firefly-iii:upgrade-group-information',
|
||||
|
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();
|
||||
}
|
||||
}
|
@@ -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.');
|
||||
|
@@ -43,7 +43,7 @@ class AttachmentFactory
|
||||
public function create(array $data): ?Attachment
|
||||
{
|
||||
// append if necessary.
|
||||
$model = !str_contains($data['attachable_type'], 'FireflyIII') ? sprintf('FireflyIII\\Models\\%s', $data['attachable_type'])
|
||||
$model = !str_contains($data['attachable_type'], 'FireflyIII') ? sprintf('FireflyIII\Models\%s', $data['attachable_type'])
|
||||
: $data['attachable_type'];
|
||||
|
||||
// get journal instead of transaction.
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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');
|
||||
}
|
||||
}
|
||||
|
@@ -121,7 +121,7 @@ class LoginController extends Controller
|
||||
|
||||
// Copied directly from AuthenticatesUsers, but with logging added:
|
||||
// If the login attempt was unsuccessful we will increment the number of attempts
|
||||
// to login and redirect the user back to the login form. Of course, when this
|
||||
// to log in and redirect the user back to the login form. Of course, when this
|
||||
// user surpasses their maximum number of attempts they will get locked out.
|
||||
$this->incrementLoginAttempts($request);
|
||||
Log::channel('audit')->warning(sprintf('Login failed. Attempt for user "%s" failed.', $request->get($this->username())));
|
||||
@@ -233,7 +233,7 @@ class LoginController extends Controller
|
||||
$storeInCookie = config('google2fa.store_in_cookie', false);
|
||||
if (false !== $storeInCookie) {
|
||||
$cookieName = config('google2fa.cookie_name', 'google2fa_token');
|
||||
request()->cookies->set($cookieName, 'invalid');
|
||||
\Cookie::queue(\Cookie::make($cookieName, 'invalid-'.time()));
|
||||
}
|
||||
$usernameField = $this->username();
|
||||
|
||||
|
@@ -185,7 +185,7 @@ class BudgetLimitController extends Controller
|
||||
$array['amount_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, $limit['amount']);
|
||||
$array['days_left'] = (string)$this->activeDaysLeft($start, $end);
|
||||
// left per day:
|
||||
$array['left_per_day'] = bcdiv(bcadd($array['spent'], $array['amount']), $array['days_left']);
|
||||
$array['left_per_day'] = 0 === bccomp('0', $array['days_left']) ? bcadd($array['spent'], $array['amount']) : bcdiv(bcadd($array['spent'], $array['amount']), $array['days_left']);
|
||||
|
||||
// left per day formatted.
|
||||
$array['left_per_day_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, $array['left_per_day']);
|
||||
|
@@ -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,14 @@ 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:trigger-credit-recalculation');
|
||||
|
||||
try {
|
||||
Artisan::call('twig:clean');
|
||||
@@ -101,7 +101,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'));
|
||||
|
@@ -65,7 +65,7 @@ class HomeController extends Controller
|
||||
$stringEnd = '';
|
||||
|
||||
try {
|
||||
$stringStart = e((string)$request->get('start'));
|
||||
$stringStart = e((string) $request->get('start'));
|
||||
$start = Carbon::createFromFormat('Y-m-d', $stringStart);
|
||||
} catch (InvalidFormatException $e) {
|
||||
app('log')->error(sprintf('Start: could not parse date string "%s" so ignore it.', $stringStart));
|
||||
@@ -73,7 +73,7 @@ class HomeController extends Controller
|
||||
}
|
||||
|
||||
try {
|
||||
$stringEnd = e((string)$request->get('end'));
|
||||
$stringEnd = e((string) $request->get('end'));
|
||||
$end = Carbon::createFromFormat('Y-m-d', $stringEnd);
|
||||
} catch (InvalidFormatException $e) {
|
||||
app('log')->error(sprintf('End could not parse date string "%s" so ignore it.', $stringEnd));
|
||||
@@ -92,7 +92,7 @@ class HomeController extends Controller
|
||||
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')) {
|
||||
if ($label === (string) trans('firefly.everything') || $label === (string) trans('firefly.customRange')) {
|
||||
$isCustomRange = true;
|
||||
app('log')->debug('Range is now marked as "custom".');
|
||||
}
|
||||
@@ -100,7 +100,7 @@ class HomeController extends Controller
|
||||
$diff = $start->diffInDays($end, true) + 1;
|
||||
|
||||
if ($diff > 366) {
|
||||
$request->session()->flash('warning', (string)trans('firefly.warning_much_data', ['days' => (int)$diff]));
|
||||
$request->session()->flash('warning', (string) trans('firefly.warning_much_data', ['days' => (int) $diff]));
|
||||
}
|
||||
|
||||
$request->session()->put('is_custom_range', $isCustomRange);
|
||||
@@ -120,14 +120,30 @@ 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'));
|
||||
}
|
||||
$subTitle = (string)trans('firefly.welcome_back');
|
||||
|
||||
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');
|
||||
$pageTitle = (string) trans('firefly.main_dashboard_page_title');
|
||||
$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;
|
||||
@@ -142,9 +158,7 @@ class HomeController extends Controller
|
||||
$end = session('end', today(config('app.timezone'))->endOfMonth());
|
||||
$accounts = $repository->getAccountsById($frontpageArray);
|
||||
$today = today(config('app.timezone'));
|
||||
|
||||
// sort frontpage accounts by order
|
||||
$accounts = $accounts->sortBy('order');
|
||||
$accounts = $accounts->sortBy('order'); // sort frontpage accounts by order
|
||||
|
||||
app('log')->debug('Frontpage accounts are ', $frontpageArray);
|
||||
|
||||
@@ -164,6 +178,21 @@ class HomeController extends Controller
|
||||
$user = auth()->user();
|
||||
event(new RequestedVersionCheckStatus($user));
|
||||
|
||||
return view('index', compact('count', 'subTitle', 'transactions', 'billCount', 'start', 'end', 'today'));
|
||||
return view('index', compact('count', 'subTitle', 'transactions', 'billCount', 'start', 'end', 'today', 'pageTitle'));
|
||||
}
|
||||
|
||||
private function indexV2(): mixed
|
||||
{
|
||||
$subTitle = (string) trans('firefly.welcome_back');
|
||||
$pageTitle = (string) trans('firefly.main_dashboard_page_title');
|
||||
|
||||
$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', 'pageTitle'));
|
||||
}
|
||||
}
|
||||
|
@@ -120,7 +120,7 @@ class PreferencesController extends Controller
|
||||
// list of locales also has "equal" which makes it equal to whatever the language is.
|
||||
|
||||
try {
|
||||
$locales = json_decode((string)file_get_contents(resource_path(sprintf('lang/%s/locales.json', $language))), true, 512, JSON_THROW_ON_ERROR);
|
||||
$locales = json_decode((string)file_get_contents(resource_path(sprintf('locales/%s/locales.json', $language))), true, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (\JsonException $e) {
|
||||
app('log')->error($e->getMessage());
|
||||
$locales = [];
|
||||
|
@@ -23,7 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Transaction;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
@@ -65,7 +64,7 @@ class ConvertController extends Controller
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||
app('view')->share('title', (string)trans('firefly.transactions'));
|
||||
app('view')->share('title', (string) trans('firefly.transactions'));
|
||||
app('view')->share('mainTitleIcon', 'fa-exchange');
|
||||
|
||||
return $next($request);
|
||||
@@ -95,7 +94,7 @@ class ConvertController extends Controller
|
||||
|
||||
$groupTitle = $group->title ?? $first->description;
|
||||
$groupArray = $transformer->transformObject($group);
|
||||
$subTitle = (string)trans('firefly.convert_to_'.$destinationType->type, ['description' => $groupTitle]);
|
||||
$subTitle = (string) trans('firefly.convert_to_'.$destinationType->type, ['description' => $groupTitle]);
|
||||
$subTitleIcon = 'fa-exchange';
|
||||
|
||||
// get a list of asset accounts and liabilities and stuff, in various combinations:
|
||||
@@ -111,7 +110,7 @@ class ConvertController extends Controller
|
||||
|
||||
if ($sourceType->type === $destinationType->type) { // cannot convert to its own type.
|
||||
app('log')->debug('This is already a transaction of the expected type..');
|
||||
session()->flash('info', (string)trans('firefly.convert_is_already_type_'.$destinationType->type));
|
||||
session()->flash('info', (string) trans('firefly.convert_is_already_type_'.$destinationType->type));
|
||||
|
||||
return redirect(route('transactions.show', [$group->id]));
|
||||
}
|
||||
@@ -147,7 +146,7 @@ class ConvertController extends Controller
|
||||
// group accounts:
|
||||
/** @var Account $account */
|
||||
foreach ($accountList as $account) {
|
||||
$role = (string)$this->accountRepository->getMetaValue($account, 'account_role');
|
||||
$role = (string) $this->accountRepository->getMetaValue($account, 'account_role');
|
||||
$name = $account->name;
|
||||
if ('' === $role) {
|
||||
$role = 'no_account_type';
|
||||
@@ -165,7 +164,7 @@ class ConvertController extends Controller
|
||||
$role = 'revenue_account';
|
||||
}
|
||||
|
||||
$key = (string)trans('firefly.opt_group_'.$role);
|
||||
$key = (string) trans('firefly.opt_group_'.$role);
|
||||
$grouped[$key][$account->id] = $name;
|
||||
}
|
||||
|
||||
@@ -184,7 +183,7 @@ class ConvertController extends Controller
|
||||
// group accounts:
|
||||
/** @var Account $account */
|
||||
foreach ($accountList as $account) {
|
||||
$role = (string)$this->accountRepository->getMetaValue($account, 'account_role');
|
||||
$role = (string) $this->accountRepository->getMetaValue($account, 'account_role');
|
||||
$name = $account->name;
|
||||
if ('' === $role) {
|
||||
$role = 'no_account_type';
|
||||
@@ -202,7 +201,7 @@ class ConvertController extends Controller
|
||||
$role = 'expense_account';
|
||||
}
|
||||
|
||||
$key = (string)trans('firefly.opt_group_'.$role);
|
||||
$key = (string) trans('firefly.opt_group_'.$role);
|
||||
$grouped[$key][$account->id] = $name;
|
||||
}
|
||||
|
||||
@@ -225,7 +224,7 @@ class ConvertController extends Controller
|
||||
$balance = app('steam')->balance($account, today());
|
||||
$currency = $this->accountRepository->getAccountCurrency($account) ?? $defaultCurrency;
|
||||
$role = 'l_'.$account->accountType->type;
|
||||
$key = (string)trans('firefly.opt_group_'.$role);
|
||||
$key = (string) trans('firefly.opt_group_'.$role);
|
||||
$grouped[$key][$account->id] = $account->name.' ('.app('amount')->formatAnything($currency, $balance, false).')';
|
||||
}
|
||||
|
||||
@@ -247,12 +246,12 @@ class ConvertController extends Controller
|
||||
foreach ($accountList as $account) {
|
||||
$balance = app('steam')->balance($account, today());
|
||||
$currency = $this->accountRepository->getAccountCurrency($account) ?? $defaultCurrency;
|
||||
$role = (string)$this->accountRepository->getMetaValue($account, 'account_role');
|
||||
$role = (string) $this->accountRepository->getMetaValue($account, 'account_role');
|
||||
if ('' === $role) {
|
||||
$role = 'no_account_type';
|
||||
}
|
||||
|
||||
$key = (string)trans('firefly.opt_group_'.$role);
|
||||
$key = (string) trans('firefly.opt_group_'.$role);
|
||||
$grouped[$key][$account->id] = $account->name.' ('.app('amount')->formatAnything($currency, $balance, false).')';
|
||||
}
|
||||
|
||||
@@ -285,7 +284,7 @@ class ConvertController extends Controller
|
||||
// correct transfers:
|
||||
$group->refresh();
|
||||
|
||||
session()->flash('success', (string)trans('firefly.converted_to_'.$destinationType->type));
|
||||
session()->flash('success', (string) trans('firefly.converted_to_'.$destinationType->type));
|
||||
event(new UpdatedTransactionGroup($group, true, true));
|
||||
|
||||
return redirect(route('transactions.show', [$group->id]));
|
||||
@@ -306,11 +305,11 @@ class ConvertController extends Controller
|
||||
$destinationId = $data['destination_id'][$journal->id] ?? null;
|
||||
$destinationName = $data['destination_name'][$journal->id] ?? null;
|
||||
|
||||
// double check its not an empty string.
|
||||
$sourceId = '' === $sourceId || null === $sourceId ? null : (int)$sourceId;
|
||||
$sourceName = '' === $sourceName ? null : (string)$sourceName;
|
||||
$destinationId = '' === $destinationId || null === $destinationId ? null : (int)$destinationId;
|
||||
$destinationName = '' === $destinationName ? null : (string)$destinationName;
|
||||
// double check it's not an empty string.
|
||||
$sourceId = '' === $sourceId || null === $sourceId ? null : (int) $sourceId;
|
||||
$sourceName = '' === $sourceName ? null : (string) $sourceName;
|
||||
$destinationId = '' === $destinationId || null === $destinationId ? null : (int) $destinationId;
|
||||
$destinationName = '' === $destinationName ? null : (string) $destinationName;
|
||||
$validSource = $validator->validateSource(['id' => $sourceId, 'name' => $sourceName]);
|
||||
$validDestination = $validator->validateDestination(['id' => $destinationId, 'name' => $destinationName]);
|
||||
|
||||
@@ -331,6 +330,19 @@ class ConvertController extends Controller
|
||||
'type' => $transactionType->type,
|
||||
];
|
||||
|
||||
// also set the currency to the currency of the source account, in case you're converting a deposit into a transfer.
|
||||
if (TransactionType::TRANSFER === $transactionType->type && TransactionType::DEPOSIT === $journal->transactionType->type) {
|
||||
$source = $this->accountRepository->find((int) $sourceId);
|
||||
$sourceCurrency = $this->accountRepository->getAccountCurrency($source);
|
||||
$dest = $this->accountRepository->find((int) $destinationId);
|
||||
$destCurrency = $this->accountRepository->getAccountCurrency($dest);
|
||||
if (null !== $sourceCurrency && null !== $destCurrency && $sourceCurrency->code !== $destCurrency->code) {
|
||||
$update['currency_id'] = $sourceCurrency->id;
|
||||
$update['foreign_currency_id'] = $destCurrency->id;
|
||||
$update['foreign_amount'] = '1'; // not the best solution but at this point the amount is hard to get.
|
||||
}
|
||||
}
|
||||
|
||||
/** @var JournalUpdateService $service */
|
||||
$service = app(JournalUpdateService::class);
|
||||
$service->setTransactionJournal($journal);
|
||||
|
@@ -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 '';
|
||||
|
@@ -28,4 +28,9 @@ use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
||||
/**
|
||||
* Class VerifyCsrfToken.
|
||||
*/
|
||||
class VerifyCsrfToken extends Middleware {}
|
||||
class VerifyCsrfToken extends Middleware
|
||||
{
|
||||
protected $except = [
|
||||
'oauth/token',
|
||||
];
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
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;
|
||||
}
|
||||
}
|
53
app/JsonApi/V3/Accounts/AccountRepository.php
Normal file
53
app/JsonApi/V3/Accounts/AccountRepository.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/*
|
||||
* AccountRepository.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\Accounts;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Support\JsonApi\Concerns\UsergroupAware;
|
||||
use LaravelJsonApi\Contracts\Store\QueriesAll;
|
||||
use LaravelJsonApi\NonEloquent\AbstractRepository;
|
||||
|
||||
class AccountRepository extends AbstractRepository implements QueriesAll
|
||||
{
|
||||
use UsergroupAware;
|
||||
|
||||
/**
|
||||
* SiteRepository constructor.
|
||||
*/
|
||||
public function __construct() {}
|
||||
|
||||
public function find(string $resourceId): ?object
|
||||
{
|
||||
return Account::find((int) $resourceId);
|
||||
}
|
||||
|
||||
public function queryAll(): Capabilities\AccountQuery
|
||||
{
|
||||
return Capabilities\AccountQuery::make()
|
||||
->withUserGroup($this->userGroup)
|
||||
->withServer($this->server)
|
||||
->withSchema($this->schema)
|
||||
;
|
||||
}
|
||||
}
|
129
app/JsonApi/V3/Accounts/AccountResource.php
Normal file
129
app/JsonApi/V3/Accounts/AccountResource.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\JsonApi\V3\Accounts;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use Illuminate\Http\Request;
|
||||
use LaravelJsonApi\Core\Resources\JsonApiResource;
|
||||
|
||||
/**
|
||||
* @property Account $resource
|
||||
*/
|
||||
class AccountResource extends JsonApiResource
|
||||
{
|
||||
/**
|
||||
* Get the resource's attributes.
|
||||
*
|
||||
* @param null|Request $request
|
||||
*/
|
||||
public function attributes($request): iterable
|
||||
{
|
||||
return [
|
||||
'created_at' => $this->resource->created_at,
|
||||
'updated_at' => $this->resource->updated_at,
|
||||
'name' => $this->resource->name,
|
||||
'iban' => '' === $this->resource->iban ? null : $this->resource->iban,
|
||||
'active' => $this->resource->active,
|
||||
'last_activity' => $this->resource->last_activity,
|
||||
'type' => $this->resource->type,
|
||||
'account_role' => $this->resource->account_role,
|
||||
|
||||
// 'virtual_balance' => $this->resource->virtual_balance,
|
||||
// 'native_balance' => $this->resource->native_balance,
|
||||
// 'user' => $this->resource->user_array,
|
||||
// 'balances' => []
|
||||
//
|
||||
// currency
|
||||
// 'currency_id' => $this->resource->currency_id,
|
||||
// 'currency_code' => $this->resource->currency_code,
|
||||
// 'currency_symbol' => $this->resource->currency_symbol,
|
||||
// 'currency_decimal_places' => $this->resource->currency_decimal_places,
|
||||
|
||||
// balance (in currency, on date)
|
||||
// 'current_balance' => $this->resource->current_balance,
|
||||
|
||||
// 'current_balance' => app('steam')->bcround(app('steam')->balance($account, $date), $decimalPlaces),
|
||||
// 'current_balance_date' => $date->toAtomString(),
|
||||
// 'notes' => $this->repository->getNoteText($account),
|
||||
// 'monthly_payment_date' => $monthlyPaymentDate,
|
||||
// 'credit_card_type' => $creditCardType,
|
||||
// 'account_number' => $this->repository->getMetaValue($account, 'account_number'),
|
||||
// 'bic' => $this->repository->getMetaValue($account, 'BIC'),
|
||||
// 'opening_balance' => $openingBalance,
|
||||
// 'opening_balance_date' => $openingBalanceDate,
|
||||
// 'liability_type' => $liabilityType,
|
||||
// 'liability_direction' => $liabilityDirection,
|
||||
// 'interest' => $interest,
|
||||
// 'interest_period' => $interestPeriod,
|
||||
// 'current_debt' => $this->repository->getMetaValue($account, 'current_debt'),
|
||||
// 'include_net_worth' => $includeNetWorth,
|
||||
// 'longitude' => $longitude,
|
||||
// 'latitude' => $latitude,
|
||||
// 'zoom_level' => $zoomLevel,
|
||||
|
||||
// 'order' => $order,
|
||||
|
||||
// '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,
|
||||
//
|
||||
// // balance:
|
||||
// 'current_balance' => $balance,
|
||||
// 'native_current_balance' => $nativeBalance,
|
||||
// 'current_balance_date' => $this->getDate()->endOfDay()->toAtomString(),
|
||||
//
|
||||
// // balance difference
|
||||
// 'balance_difference' => $balanceDiff,
|
||||
// 'native_balance_difference' => $nativeBalanceDiff,
|
||||
// 'balance_difference_start' => $diffStart,
|
||||
// 'balance_difference_end' => $diffEnd,
|
||||
//
|
||||
// // more meta
|
||||
// 'last_activity' => array_key_exists($id, $this->lastActivity) ? $this->lastActivity[$id]->toAtomString() : null,
|
||||
//
|
||||
// // liability stuff
|
||||
// 'liability_type' => $liabilityType,
|
||||
// 'liability_direction' => $liabilityDirection,
|
||||
// 'interest' => $interest,
|
||||
// 'interest_period' => $interestPeriod,
|
||||
// 'current_debt' => $currentDebt,
|
||||
//
|
||||
// // object group
|
||||
// 'object_group_id' => null !== $objectGroupId ? (string) $objectGroupId : null,
|
||||
// 'object_group_order' => $objectGroupOrder,
|
||||
// 'object_group_title' => $objectGroupTitle,
|
||||
// 'notes' => $this->repository->getNoteText($account),
|
||||
// 'monthly_payment_date' => $monthlyPaymentDate,
|
||||
// 'credit_card_type' => $creditCardType,
|
||||
// 'bic' => $this->repository->getMetaValue($account, 'BIC'),
|
||||
// 'virtual_balance' => number_format((float) $account->virtual_balance, $decimalPlaces, '.', ''),
|
||||
// 'opening_balance' => $openingBalance,
|
||||
// 'opening_balance_date' => $openingBalanceDate,
|
||||
// 'include_net_worth' => $includeNetWorth,
|
||||
// 'longitude' => $longitude,
|
||||
// 'latitude' => $latitude,
|
||||
// 'zoom_level' => $zoomLevel,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource's relationships.
|
||||
*
|
||||
* @param null|Request $request
|
||||
*/
|
||||
public function relationships($request): iterable
|
||||
{
|
||||
return [
|
||||
$this->relation('user')->withData($this->resource->user),
|
||||
$this->relation('account_balances')->withData($this->resource->balances),
|
||||
];
|
||||
}
|
||||
}
|
64
app/JsonApi/V3/Accounts/AccountSchema.php
Normal file
64
app/JsonApi/V3/Accounts/AccountSchema.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\JsonApi\V3\Accounts;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use LaravelJsonApi\Eloquent\Contracts\Paginator;
|
||||
use LaravelJsonApi\Eloquent\Fields\Boolean;
|
||||
use LaravelJsonApi\Eloquent\Fields\DateTime;
|
||||
use LaravelJsonApi\Eloquent\Fields\ID;
|
||||
use LaravelJsonApi\Eloquent\Fields\Number;
|
||||
use LaravelJsonApi\Eloquent\Fields\Relations\HasMany;
|
||||
use LaravelJsonApi\Eloquent\Fields\Relations\HasOne;
|
||||
use LaravelJsonApi\Eloquent\Fields\Str;
|
||||
use LaravelJsonApi\Eloquent\Filters\WhereIdIn;
|
||||
use LaravelJsonApi\Eloquent\Pagination\PagePagination;
|
||||
use LaravelJsonApi\Eloquent\Schema;
|
||||
|
||||
class AccountSchema extends Schema
|
||||
{
|
||||
/**
|
||||
* The model the schema corresponds to.
|
||||
*/
|
||||
public static string $model = Account::class;
|
||||
|
||||
/**
|
||||
* Get the resource fields.
|
||||
*/
|
||||
public function fields(): array
|
||||
{
|
||||
return [
|
||||
ID::make(),
|
||||
DateTime::make('created_at')->sortable()->readOnly(),
|
||||
DateTime::make('updated_at')->sortable()->readOnly(),
|
||||
Str::make('name')->sortable(),
|
||||
Str::make('account_type'),
|
||||
Str::make('virtual_balance'),
|
||||
Str::make('iban'),
|
||||
Boolean::make('active'),
|
||||
Number::make('order'),
|
||||
HasOne::make('user'),
|
||||
HasMany::make('account_balances'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource filters.
|
||||
*/
|
||||
public function filters(): array
|
||||
{
|
||||
return [
|
||||
WhereIdIn::make($this),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource paginator.
|
||||
*/
|
||||
public function pagination(): ?Paginator
|
||||
{
|
||||
return PagePagination::make();
|
||||
}
|
||||
}
|
73
app/JsonApi/V3/Accounts/Capabilities/AccountQuery.php
Normal file
73
app/JsonApi/V3/Accounts/Capabilities/AccountQuery.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/*
|
||||
* AccountQuery.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\Accounts\Capabilities;
|
||||
|
||||
use FireflyIII\Support\JsonApi\Concerns\UsergroupAware;
|
||||
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
|
||||
use FireflyIII\Support\JsonApi\ExpandsQuery;
|
||||
use FireflyIII\Support\JsonApi\FiltersPagination;
|
||||
use FireflyIII\Support\JsonApi\SortsCollection;
|
||||
use FireflyIII\Support\JsonApi\ValidateSortParameters;
|
||||
use LaravelJsonApi\Contracts\Store\HasPagination;
|
||||
use LaravelJsonApi\NonEloquent\Capabilities\QueryAll;
|
||||
use LaravelJsonApi\NonEloquent\Concerns\PaginatesEnumerables;
|
||||
|
||||
class AccountQuery extends QueryAll implements HasPagination
|
||||
{
|
||||
use ExpandsQuery;
|
||||
use FiltersPagination;
|
||||
use PaginatesEnumerables;
|
||||
use SortsCollection;
|
||||
use UsergroupAware;
|
||||
use ValidateSortParameters;
|
||||
|
||||
#[\Override]
|
||||
public function get(): iterable
|
||||
{
|
||||
$filters = $this->queryParameters->filter();
|
||||
$sort = $this->queryParameters->sortFields();
|
||||
$pagination = $this->filtersPagination($this->queryParameters->page());
|
||||
$needsAll = $this->validateParams('account', $sort);
|
||||
$query = $this->userGroup->accounts();
|
||||
|
||||
if (!$needsAll) {
|
||||
$query = $this->addPagination($query, $pagination);
|
||||
}
|
||||
$query = $this->addSortParams($query, $sort);
|
||||
$query = $this->addFilterParams('account', $query, $filters);
|
||||
|
||||
$collection = $query->get(['accounts.*']);
|
||||
|
||||
// enrich data
|
||||
$enrichment = new AccountEnrichment();
|
||||
$collection = $enrichment->enrich($collection);
|
||||
|
||||
// add filters after the query
|
||||
|
||||
// add sort after the query
|
||||
return $this->sortCollection($collection, $sort);
|
||||
// var_dump($filters->value('name'));
|
||||
// exit;
|
||||
}
|
||||
}
|
38
app/JsonApi/V3/Server.php
Normal file
38
app/JsonApi/V3/Server.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\JsonApi\V3;
|
||||
|
||||
use FireflyIII\JsonApi\V3\Accounts\AccountSchema;
|
||||
use FireflyIII\JsonApi\V3\AccountBalances\AccountBalanceSchema;
|
||||
use FireflyIII\JsonApi\V3\Users\UserSchema;
|
||||
use LaravelJsonApi\Core\Server\Server as BaseServer;
|
||||
|
||||
class Server extends BaseServer
|
||||
{
|
||||
/**
|
||||
* The base URI namespace for this server.
|
||||
*/
|
||||
protected string $baseUri = '/api/v3';
|
||||
|
||||
/**
|
||||
* Bootstrap the server when it is handling an HTTP request.
|
||||
*/
|
||||
public function serving(): void
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server's list of schemas.
|
||||
*/
|
||||
protected function allSchemas(): array
|
||||
{
|
||||
return [
|
||||
AccountSchema::class,
|
||||
UserSchema::class,
|
||||
AccountBalanceSchema::class,
|
||||
];
|
||||
}
|
||||
}
|
41
app/JsonApi/V3/Users/UserResource.php
Normal file
41
app/JsonApi/V3/Users/UserResource.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\JsonApi\V3\Users;
|
||||
|
||||
use FireflyIII\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use LaravelJsonApi\Core\Resources\JsonApiResource;
|
||||
|
||||
/**
|
||||
* @property User $resource
|
||||
*/
|
||||
class UserResource extends JsonApiResource
|
||||
{
|
||||
/**
|
||||
* Get the resource's attributes.
|
||||
*
|
||||
* @param null|Request $request
|
||||
*/
|
||||
public function attributes($request): iterable
|
||||
{
|
||||
return [
|
||||
'created_at' => $this->resource->created_at,
|
||||
'updated_at' => $this->resource->updated_at,
|
||||
'email' => $this->resource->email,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource's relationships.
|
||||
*
|
||||
* @param null|Request $request
|
||||
*/
|
||||
public function relationships($request): iterable
|
||||
{
|
||||
return [
|
||||
// @TODO
|
||||
];
|
||||
}
|
||||
}
|
55
app/JsonApi/V3/Users/UserSchema.php
Normal file
55
app/JsonApi/V3/Users/UserSchema.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\JsonApi\V3\Users;
|
||||
|
||||
use FireflyIII\User;
|
||||
use LaravelJsonApi\Eloquent\Contracts\Paginator;
|
||||
use LaravelJsonApi\Eloquent\Fields\DateTime;
|
||||
use LaravelJsonApi\Eloquent\Fields\ID;
|
||||
use LaravelJsonApi\Eloquent\Fields\Relations\HasMany;
|
||||
use LaravelJsonApi\Eloquent\Fields\Str;
|
||||
use LaravelJsonApi\Eloquent\Filters\WhereIdIn;
|
||||
use LaravelJsonApi\Eloquent\Pagination\PagePagination;
|
||||
use LaravelJsonApi\Eloquent\Schema;
|
||||
|
||||
class UserSchema extends Schema
|
||||
{
|
||||
/**
|
||||
* The model the schema corresponds to.
|
||||
*/
|
||||
public static string $model = User::class;
|
||||
|
||||
/**
|
||||
* Get the resource fields.
|
||||
*/
|
||||
public function fields(): array
|
||||
{
|
||||
return [
|
||||
ID::make(),
|
||||
DateTime::make('created_at')->sortable()->readOnly(),
|
||||
DateTime::make('updated_at')->sortable()->readOnly(),
|
||||
Str::make('email'),
|
||||
HasMany::make('accounts'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource filters.
|
||||
*/
|
||||
public function filters(): array
|
||||
{
|
||||
return [
|
||||
WhereIdIn::make($this),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource paginator.
|
||||
*/
|
||||
public function pagination(): ?Paginator
|
||||
{
|
||||
return PagePagination::make();
|
||||
}
|
||||
}
|
@@ -193,6 +193,11 @@ class Account extends Model
|
||||
return $this->hasMany(AccountMeta::class);
|
||||
}
|
||||
|
||||
public function accountBalances(): HasMany
|
||||
{
|
||||
return $this->hasMany(AccountBalance::class);
|
||||
}
|
||||
|
||||
public function getEditNameAttribute(): string
|
||||
{
|
||||
$name = $this->name;
|
||||
|
25
app/Models/AccountBalance.php
Normal file
25
app/Models/AccountBalance.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class AccountBalance extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
protected $fillable = ['account_id', 'title', 'transaction_currency_id', 'balance'];
|
||||
|
||||
public function account(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
public function transactionCurrency(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(TransactionCurrency::class);
|
||||
}
|
||||
}
|
@@ -98,7 +98,7 @@ class UserGroup extends Model
|
||||
{
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
protected $fillable = ['title', 'default_administration'];
|
||||
protected $fillable = ['title'];
|
||||
|
||||
/**
|
||||
* Route binder. Converts the key in the URL to the specified object (or throw 404).
|
||||
|
@@ -1,8 +1,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* auth.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
/*
|
||||
* AccountPolicy.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -17,23 +16,33 @@
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* PLEASE DO NOT EDIT THIS FILE DIRECTLY.
|
||||
* YOUR CHANGES WILL BE OVERWRITTEN!
|
||||
* YOUR PR WITH CHANGES TO THIS FILE WILL BE REJECTED!
|
||||
*
|
||||
* GO TO CROWDIN TO FIX OR CHANGE TRANSLATIONS!
|
||||
*
|
||||
* https://crowdin.com/project/firefly-iii
|
||||
*
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'failed' => 'These credentials do not match our records.',
|
||||
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
|
||||
];
|
||||
namespace FireflyIII\Policies;
|
||||
|
||||
use FireflyIII\Entities\AccountBalance;
|
||||
use FireflyIII\User;
|
||||
|
||||
class AccountBalancePolicy
|
||||
{
|
||||
/**
|
||||
* TODO needs better authentication.
|
||||
*/
|
||||
public function view(User $user, AccountBalance $accountBalance): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Everybody can do this, but selection should limit to user.
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
public function viewAny(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
67
app/Policies/AccountPolicy.php
Normal file
67
app/Policies/AccountPolicy.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/*
|
||||
* AccountPolicy.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\Policies;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\User;
|
||||
|
||||
class AccountPolicy
|
||||
{
|
||||
/**
|
||||
* TODO needs better authentication.
|
||||
*/
|
||||
public function view(User $user, Account $account): bool
|
||||
{
|
||||
return true;
|
||||
|
||||
return auth()->check() && $user->id === $account->user_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Everybody can do this, but selection should limit to user.
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
public function viewAny(): bool
|
||||
{
|
||||
return true;
|
||||
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Everybody can do this, but selection should limit to user.
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
public function viewUser(User $user, Account $account): bool
|
||||
{
|
||||
return $this->view($user, $account);
|
||||
}
|
||||
|
||||
public function viewAccountBalances(User $user, Account $account): bool
|
||||
{
|
||||
return $this->view($user, $account);
|
||||
}
|
||||
}
|
@@ -1,8 +1,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* pagination.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
/*
|
||||
* BalancePolicy.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -17,23 +16,33 @@
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* PLEASE DO NOT EDIT THIS FILE DIRECTLY.
|
||||
* YOUR CHANGES WILL BE OVERWRITTEN!
|
||||
* YOUR PR WITH CHANGES TO THIS FILE WILL BE REJECTED!
|
||||
*
|
||||
* GO TO CROWDIN TO FIX OR CHANGE TRANSLATIONS!
|
||||
*
|
||||
* https://crowdin.com/project/firefly-iii
|
||||
*
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'previous' => '« Предишна',
|
||||
'next' => 'Следваща »',
|
||||
];
|
||||
namespace FireflyIII\Policies;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\User;
|
||||
|
||||
class BalancePolicy
|
||||
{
|
||||
/**
|
||||
* TODO needs better authentication.
|
||||
*/
|
||||
public function view(User $user, Account $account): bool
|
||||
{
|
||||
return auth()->check() && $user->id === $account->user_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Everybody can do this, but selection should limit to user.
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
public function viewAny(): bool
|
||||
{
|
||||
return auth()->check();
|
||||
}
|
||||
}
|
58
app/Policies/UserPolicy.php
Normal file
58
app/Policies/UserPolicy.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/*
|
||||
* UserPolicy.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\Policies;
|
||||
|
||||
use FireflyIII\User;
|
||||
|
||||
class UserPolicy
|
||||
{
|
||||
/**
|
||||
* TODO needs better authentication.
|
||||
*/
|
||||
public function view(User $user, User $user1): bool
|
||||
{
|
||||
return true;
|
||||
|
||||
return auth()->check() && $user->id === $account->user_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Everybody can do this, but selection should limit to user.
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
public function viewAny(): bool
|
||||
{
|
||||
return true;
|
||||
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
public function viewAccounts(User $user): bool
|
||||
{
|
||||
return true;
|
||||
|
||||
return auth()->check();
|
||||
}
|
||||
}
|
@@ -28,6 +28,7 @@ use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Laravel\Passport\Passport;
|
||||
|
||||
/**
|
||||
* Class AppServiceProvider
|
||||
@@ -87,6 +88,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
Passport::ignoreRoutes();
|
||||
// Passport::ignoreMigrations();
|
||||
// Sanctum::ignoreMigrations();
|
||||
}
|
||||
|
@@ -37,6 +37,7 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Services\Internal\Destroy\AccountDestroyService;
|
||||
use FireflyIII\Services\Internal\Update\AccountUpdateService;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
@@ -123,6 +124,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
public function findByIbanNull(string $iban, array $types): ?Account
|
||||
{
|
||||
$iban = Steam::filterSpaces($iban);
|
||||
$query = $this->user->accounts()->where('iban', '!=', '')->whereNotNull('iban');
|
||||
|
||||
if (0 !== count($types)) {
|
||||
|
@@ -146,7 +146,7 @@ class AttachmentRepository implements AttachmentRepositoryInterface
|
||||
// should be validated already:
|
||||
if (array_key_exists('attachable_type', $data) && array_key_exists('attachable_id', $data)) {
|
||||
$attachment->attachable_id = (int)$data['attachable_id'];
|
||||
$attachment->attachable_type = sprintf('FireflyIII\\Models\\%s', $data['attachable_type']);
|
||||
$attachment->attachable_type = sprintf('FireflyIII\Models\%s', $data['attachable_type']);
|
||||
}
|
||||
|
||||
$attachment->save();
|
||||
|
@@ -150,7 +150,6 @@ class RuleRepository implements RuleRepositoryInterface
|
||||
$params[] = sprintf('%s:true', OperatorQuerySearch::getRootOperator($trigger->trigger_type));
|
||||
}
|
||||
if (true === $needsContext) {
|
||||
var_dump('x');
|
||||
$params[] = sprintf('%s:"%s"', OperatorQuerySearch::getRootOperator($trigger->trigger_type), $trigger->trigger_value);
|
||||
}
|
||||
}
|
||||
|
@@ -295,4 +295,16 @@ class UserGroupRepository implements UserGroupRepositoryInterface
|
||||
$this->user->user_group_id = $userGroup->id;
|
||||
$this->user->save();
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getMembershipsFromGroupId(int $groupId): Collection
|
||||
{
|
||||
return $this->user->groupMemberships()->where('user_group_id', $groupId)->get();
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getById(int $id): ?UserGroup
|
||||
{
|
||||
return UserGroup::find($id);
|
||||
}
|
||||
}
|
||||
|
@@ -36,8 +36,12 @@ interface UserGroupRepositoryInterface
|
||||
{
|
||||
public function destroy(UserGroup $userGroup): void;
|
||||
|
||||
public function getMembershipsFromGroupId(int $groupId): Collection;
|
||||
|
||||
public function get(): Collection;
|
||||
|
||||
public function getById(int $id): ?UserGroup;
|
||||
|
||||
public function getAll(): Collection;
|
||||
|
||||
public function setUser(null|Authenticatable|User $user): void;
|
||||
|
@@ -27,11 +27,15 @@ namespace FireflyIII\Repositories\UserGroups\Account;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountMeta;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\ObjectGroup;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Services\Internal\Update\AccountUpdateService;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* Class AccountRepository
|
||||
@@ -77,6 +81,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
public function findByIbanNull(string $iban, array $types): ?Account
|
||||
{
|
||||
$iban = Steam::filterSpaces($iban);
|
||||
$query = $this->userGroup->accounts()->where('iban', '!=', '')->whereNotNull('iban');
|
||||
|
||||
if (0 !== count($types)) {
|
||||
@@ -121,7 +126,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
if (!in_array($type, $list, true)) {
|
||||
return null;
|
||||
}
|
||||
$currencyId = (int)$this->getMetaValue($account, 'currency_id');
|
||||
$currencyId = (int) $this->getMetaValue($account, 'currency_id');
|
||||
if ($currencyId > 0) {
|
||||
return TransactionCurrency::find($currencyId);
|
||||
}
|
||||
@@ -143,7 +148,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
return null;
|
||||
}
|
||||
if (1 === $result->count()) {
|
||||
return (string)$result->first()->data;
|
||||
return (string) $result->first()->data;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -228,7 +233,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
continue;
|
||||
}
|
||||
if ($index !== (int)$account->order) {
|
||||
if ($index !== (int) $account->order) {
|
||||
app('log')->debug(sprintf('Account #%d ("%s"): order should %d be but is %d.', $account->id, $account->name, $index, $account->order));
|
||||
$account->order = $index;
|
||||
$account->save();
|
||||
@@ -238,7 +243,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
}
|
||||
}
|
||||
|
||||
public function getAccountsByType(array $types, ?array $sort = []): Collection
|
||||
public function getAccountsByType(array $types, ?array $sort = [], ?array $filters = []): Collection
|
||||
{
|
||||
$sortable = ['name', 'active']; // TODO yes this is a duplicate array.
|
||||
$res = array_intersect([AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT], $types);
|
||||
@@ -247,6 +252,22 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
$query->accountTypeIn($types);
|
||||
}
|
||||
|
||||
// process filters
|
||||
// TODO this should be repeatable, it feels like a hack when you do it here.
|
||||
// TODO some fields cannot be filtered using the query, and a second filter must be applied on the collection.
|
||||
foreach ($filters as $column => $value) {
|
||||
// filter on NULL values
|
||||
if (null === $value) {
|
||||
continue;
|
||||
}
|
||||
if ('active' === $column) {
|
||||
$query->where('accounts.active', $value);
|
||||
}
|
||||
if ('name' === $column) {
|
||||
$query->where('accounts.name', 'LIKE', sprintf('%%%s%%', $value));
|
||||
}
|
||||
}
|
||||
|
||||
// add sort parameters. At this point they're filtered to allowed fields to sort by:
|
||||
$hasActiveColumn = array_key_exists('active', $sort);
|
||||
if (count($sort) > 0) {
|
||||
@@ -271,7 +292,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
return $query->get(['accounts.*']);
|
||||
}
|
||||
|
||||
public function searchAccount(string $query, array $types, int $limit): Collection
|
||||
public function searchAccount(array $query, array $types, int $limit): Collection
|
||||
{
|
||||
// search by group, not by user
|
||||
$dbQuery = $this->userGroup->accounts()
|
||||
@@ -281,13 +302,17 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
->orderBy('accounts.name', 'ASC')
|
||||
->with(['accountType'])
|
||||
;
|
||||
if ('' !== $query) {
|
||||
if (count($query) > 0) {
|
||||
// split query on spaces just in case:
|
||||
$parts = explode(' ', $query);
|
||||
foreach ($parts as $part) {
|
||||
$search = sprintf('%%%s%%', $part);
|
||||
$dbQuery->where('name', 'LIKE', $search);
|
||||
}
|
||||
$dbQuery->where(function (EloquentBuilder $q) use ($query): void {
|
||||
foreach ($query as $line) {
|
||||
$parts = explode(' ', $line);
|
||||
foreach ($parts as $part) {
|
||||
$search = sprintf('%%%s%%', $part);
|
||||
$q->orWhere('name', 'LIKE', $search);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (0 !== count($types)) {
|
||||
$dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
|
||||
@@ -305,4 +330,70 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
return $service->update($account, $data);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getMetaValues(Collection $accounts, array $fields): Collection
|
||||
{
|
||||
$query = AccountMeta::whereIn('account_id', $accounts->pluck('id')->toArray());
|
||||
if (count($fields) > 0) {
|
||||
$query->whereIn('name', $fields);
|
||||
}
|
||||
|
||||
return $query->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data']);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getAccountTypes(Collection $accounts): Collection
|
||||
{
|
||||
return AccountType::leftJoin('accounts', 'accounts.account_type_id', '=', 'account_types.id')
|
||||
->whereIn('accounts.id', $accounts->pluck('id')->toArray())
|
||||
->get(['accounts.id', 'account_types.type'])
|
||||
;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getLastActivity(Collection $accounts): array
|
||||
{
|
||||
return Transaction::whereIn('account_id', $accounts->pluck('id')->toArray())
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id')
|
||||
->groupBy('transactions.account_id')
|
||||
->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) as date_max')])->toArray() // @phpstan-ignore-line
|
||||
;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getObjectGroups(Collection $accounts): array
|
||||
{
|
||||
$groupIds = [];
|
||||
$return = [];
|
||||
$set = DB::table('object_groupables')->where('object_groupable_type', Account::class)
|
||||
->whereIn('object_groupable_id', $accounts->pluck('id')->toArray())->get()
|
||||
;
|
||||
|
||||
/** @var \stdClass $row */
|
||||
foreach ($set as $row) {
|
||||
$groupIds[] = $row->object_group_id;
|
||||
}
|
||||
$groupIds = array_unique($groupIds);
|
||||
$groups = ObjectGroup::whereIn('id', $groupIds)->get();
|
||||
|
||||
/** @var \stdClass $row */
|
||||
foreach ($set as $row) {
|
||||
if (!array_key_exists($row->object_groupable_id, $return)) {
|
||||
/** @var null|ObjectGroup $group */
|
||||
$group = $groups->firstWhere('id', '=', $row->object_group_id);
|
||||
if (null !== $group) {
|
||||
$return[$row->object_groupable_id] = ['title' => $group->title, 'order' => $group->order, 'id' => $group->id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getAccountBalances(Account $account): Collection
|
||||
{
|
||||
return $account->accountBalances;
|
||||
}
|
||||
}
|
||||
|
@@ -37,6 +37,12 @@ interface AccountRepositoryInterface
|
||||
{
|
||||
public function countAccounts(array $types): int;
|
||||
|
||||
public function getAccountTypes(Collection $accounts): Collection;
|
||||
|
||||
public function getLastActivity(Collection $accounts): array;
|
||||
|
||||
public function getMetaValues(Collection $accounts, array $fields): Collection;
|
||||
|
||||
public function find(int $accountId): ?Account;
|
||||
|
||||
public function findByAccountNumber(string $number, array $types): ?Account;
|
||||
@@ -47,9 +53,11 @@ interface AccountRepositoryInterface
|
||||
|
||||
public function getAccountCurrency(Account $account): ?TransactionCurrency;
|
||||
|
||||
public function getAccountBalances(Account $account): Collection;
|
||||
|
||||
public function getAccountsById(array $accountIds): Collection;
|
||||
|
||||
public function getAccountsByType(array $types, ?array $sort = []): Collection;
|
||||
public function getAccountsByType(array $types, ?array $sort = [], ?array $filters = []): Collection;
|
||||
|
||||
/**
|
||||
* Used in the infinite accounts list.
|
||||
@@ -63,6 +71,8 @@ interface AccountRepositoryInterface
|
||||
*/
|
||||
public function getMetaValue(Account $account, string $field): ?string;
|
||||
|
||||
public function getObjectGroups(Collection $accounts): array;
|
||||
|
||||
public function getUserGroup(): UserGroup;
|
||||
|
||||
/**
|
||||
@@ -70,7 +80,7 @@ interface AccountRepositoryInterface
|
||||
*/
|
||||
public function resetAccountOrder(): void;
|
||||
|
||||
public function searchAccount(string $query, array $types, int $limit): Collection;
|
||||
public function searchAccount(array $query, array $types, int $limit): Collection;
|
||||
|
||||
public function setUser(User $user): void;
|
||||
|
||||
|
@@ -24,17 +24,27 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Repositories\UserGroups\Category;
|
||||
|
||||
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class CategoryRepository implements CategoryRepositoryInterface
|
||||
{
|
||||
use UserGroupTrait;
|
||||
|
||||
public function searchCategory(string $query, int $limit): Collection
|
||||
public function searchCategory(array $query, int $limit): Collection
|
||||
{
|
||||
$search = $this->userGroup->categories();
|
||||
if ('' !== $query) {
|
||||
$search->where('name', 'LIKE', sprintf('%%%s%%', $query));
|
||||
if (count($query) > 0) {
|
||||
// split query on spaces just in case:
|
||||
$search->where(function (EloquentBuilder $q) use ($query): void {
|
||||
foreach ($query as $line) {
|
||||
$parts = explode(' ', $line);
|
||||
foreach ($parts as $part) {
|
||||
$search = sprintf('%%%s%%', $part);
|
||||
$q->orWhere('name', 'LIKE', $search);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return $search->take($limit)->get();
|
||||
|
@@ -30,5 +30,5 @@ interface CategoryRepositoryInterface
|
||||
/**
|
||||
* Search for a category using wild cards. Uses the database, so case sensitive.
|
||||
*/
|
||||
public function searchCategory(string $query, int $limit): Collection;
|
||||
public function searchCategory(array $query, int $limit): Collection;
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Repositories\UserGroups\Journal;
|
||||
|
||||
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@@ -33,15 +34,24 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
{
|
||||
use UserGroupTrait;
|
||||
|
||||
public function searchJournalDescriptions(string $search, int $limit): Collection
|
||||
public function searchJournalDescriptions(array $query, int $limit): Collection
|
||||
{
|
||||
$query = $this->userGroup->transactionJournals()
|
||||
$search = $this->userGroup->transactionJournals()
|
||||
->orderBy('date', 'DESC')
|
||||
;
|
||||
if ('' !== $search) {
|
||||
$query->where('description', 'LIKE', sprintf('%%%s%%', $search));
|
||||
if (count($query) > 0) {
|
||||
// split query on spaces just in case:
|
||||
$search->where(function (EloquentBuilder $q) use ($query): void {
|
||||
foreach ($query as $line) {
|
||||
$parts = explode(' ', $line);
|
||||
foreach ($parts as $part) {
|
||||
$search = sprintf('%%%s%%', $part);
|
||||
$q->orWhere('description', 'LIKE', $search);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return $query->take($limit)->get();
|
||||
return $search->take($limit)->get();
|
||||
}
|
||||
}
|
||||
|
@@ -34,7 +34,7 @@ interface JournalRepositoryInterface
|
||||
/**
|
||||
* Search in journal descriptions.
|
||||
*/
|
||||
public function searchJournalDescriptions(string $search, int $limit): Collection;
|
||||
public function searchJournalDescriptions(array $query, int $limit): Collection;
|
||||
|
||||
public function setUser(User $user): void;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user