mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-11-21 09:00:07 +00:00
Merge branch 'release/4.7.10'
This commit is contained in:
@@ -1,73 +1,3 @@
|
|||||||
# This is the main Apache server configuration file. It contains the
|
|
||||||
# configuration directives that give the server its instructions.
|
|
||||||
# See http://httpd.apache.org/docs/2.4/ for detailed information about
|
|
||||||
# the directives and /usr/share/doc/apache2/README.Debian about Debian specific
|
|
||||||
# hints.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# Summary of how the Apache 2 configuration works in Debian:
|
|
||||||
# The Apache 2 web server configuration in Debian is quite different to
|
|
||||||
# upstream's suggested way to configure the web server. This is because Debian's
|
|
||||||
# default Apache2 installation attempts to make adding and removing modules,
|
|
||||||
# virtual hosts, and extra configuration directives as flexible as possible, in
|
|
||||||
# order to make automating the changes and administering the server as easy as
|
|
||||||
# possible.
|
|
||||||
|
|
||||||
# It is split into several files forming the configuration hierarchy outlined
|
|
||||||
# below, all located in the /etc/apache2/ directory:
|
|
||||||
#
|
|
||||||
# /etc/apache2/
|
|
||||||
# |-- apache2.conf
|
|
||||||
# | `-- ports.conf
|
|
||||||
# |-- mods-enabled
|
|
||||||
# | |-- *.load
|
|
||||||
# | `-- *.conf
|
|
||||||
# |-- conf-enabled
|
|
||||||
# | `-- *.conf
|
|
||||||
# `-- sites-enabled
|
|
||||||
# `-- *.conf
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# * apache2.conf is the main configuration file (this file). It puts the pieces
|
|
||||||
# together by including all remaining configuration files when starting up the
|
|
||||||
# web server.
|
|
||||||
#
|
|
||||||
# * ports.conf is always included from the main configuration file. It is
|
|
||||||
# supposed to determine listening ports for incoming connections which can be
|
|
||||||
# customized anytime.
|
|
||||||
#
|
|
||||||
# * Configuration files in the mods-enabled/, conf-enabled/ and sites-enabled/
|
|
||||||
# directories contain particular configuration snippets which manage modules,
|
|
||||||
# global configuration fragments, or virtual host configurations,
|
|
||||||
# respectively.
|
|
||||||
#
|
|
||||||
# They are activated by symlinking available configuration files from their
|
|
||||||
# respective *-available/ counterparts. These should be managed by using our
|
|
||||||
# helpers a2enmod/a2dismod, a2ensite/a2dissite and a2enconf/a2disconf. See
|
|
||||||
# their respective man pages for detailed information.
|
|
||||||
#
|
|
||||||
# * The binary is called apache2. Due to the use of environment variables, in
|
|
||||||
# the default configuration, apache2 needs to be started/stopped with
|
|
||||||
# /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not
|
|
||||||
# work with the default configuration.
|
|
||||||
|
|
||||||
|
|
||||||
# Global configuration
|
|
||||||
#
|
|
||||||
|
|
||||||
#
|
|
||||||
# ServerRoot: The top of the directory tree under which the server's
|
|
||||||
# configuration, error, and log files are kept.
|
|
||||||
#
|
|
||||||
# NOTE! If you intend to place this on an NFS (or otherwise network)
|
|
||||||
# mounted filesystem then please read the Mutex documentation (available
|
|
||||||
# at <URL:http://httpd.apache.org/docs/2.4/mod/core.html#mutex>);
|
|
||||||
# you will save yourself a lot of trouble.
|
|
||||||
#
|
|
||||||
# Do NOT add a slash at the end of the directory path.
|
|
||||||
#
|
|
||||||
#ServerRoot "/etc/apache2"
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# The accept serialization lock file MUST BE STORED ON A LOCAL DISK.
|
# The accept serialization lock file MUST BE STORED ON A LOCAL DISK.
|
||||||
#
|
#
|
||||||
|
|||||||
18
.deploy/docker/build-amd64.sh
Executable file
18
.deploy/docker/build-amd64.sh
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# build image
|
||||||
|
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
||||||
|
|
||||||
|
if [ "$TRAVIS_BRANCH" == "develop" ]; then
|
||||||
|
echo "Build develop amd64"
|
||||||
|
docker build -t jc5x/firefly-iii:develop-amd -f Dockerfile .
|
||||||
|
docker push jc5x/firefly-iii:develop-amd
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$TRAVIS_BRANCH" == "master" ]; then
|
||||||
|
echo "Build master amd64"
|
||||||
|
docker build -t jc5x/firefly-iii:latest-amd -f Dockerfile .
|
||||||
|
docker tag jc5x/firefly-iii:latest-amd jc5x/firefly-iii:release-$VERSION-amd
|
||||||
|
docker push jc5x/firefly-iii:latest-amd
|
||||||
|
docker push jc5x/firefly-iii:release-$VERSION-amd
|
||||||
|
fi
|
||||||
28
.deploy/docker/build-arm.sh
Executable file
28
.deploy/docker/build-arm.sh
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
docker run --rm --privileged multiarch/qemu-user-static:register --reset
|
||||||
|
|
||||||
|
|
||||||
|
# get qemu-arm-static binary
|
||||||
|
mkdir tmp
|
||||||
|
pushd tmp && \
|
||||||
|
curl -L -o qemu-arm-static.tar.gz https://github.com/multiarch/qemu-user-static/releases/download/v2.6.0/qemu-arm-static.tar.gz && \
|
||||||
|
tar xzf qemu-arm-static.tar.gz && \
|
||||||
|
popd
|
||||||
|
|
||||||
|
# build image
|
||||||
|
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
||||||
|
|
||||||
|
if [ "$TRAVIS_BRANCH" == "develop" ]; then
|
||||||
|
echo "Build develop arm"
|
||||||
|
docker build --tag jc5x/firefly-iii:develop-arm --file Dockerfile-ARM .
|
||||||
|
docker push jc5x/firefly-iii:develop-arm
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$TRAVIS_BRANCH" == "master" ]; then
|
||||||
|
echo "Build master arm"
|
||||||
|
docker build --tag jc5x/firefly-iii:latest-arm --file Dockerfile-ARM .
|
||||||
|
docker tag jc5x/firefly-iii:latest-arm jc5x/firefly-iii:release-$VERSION-arm
|
||||||
|
docker push jc5x/firefly-iii:latest-arm
|
||||||
|
docker push jc5x/firefly-iii:release-$VERSION-arm
|
||||||
|
fi
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
[program:cron]
|
|
||||||
command=/usr/sbin/cron -f -L 15
|
|
||||||
user=root
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
stdout_events_enabled=true
|
|
||||||
stderr_events_enabled=true
|
|
||||||
stdout_logfile=/dev/stdout
|
|
||||||
stdout_logfile_maxbytes=0
|
|
||||||
startsecs=10
|
|
||||||
startretries=3
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
FF_APP_ENV=local
|
|
||||||
FF_APP_KEY=S0m3R@nd0mString0f32Ch@rsEx@ct1y
|
|
||||||
FF_DB_HOST=
|
|
||||||
FF_DB_NAME=
|
|
||||||
FF_DB_USER=
|
|
||||||
FF_DB_PASSWORD=
|
|
||||||
@@ -1,42 +1,62 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "Now in entrypoint.sh for Firefly III"
|
||||||
|
|
||||||
# make sure the correct directories exists (suggested by @chrif):
|
# make sure the correct directories exists (suggested by @chrif):
|
||||||
mkdir -p $FIREFLY_PATH/storage/app
|
echo "Making directories..."
|
||||||
mkdir -p $FIREFLY_PATH/storage/app/public
|
mkdir -p $FIREFLY_PATH/storage/app/public
|
||||||
mkdir -p $FIREFLY_PATH/storage/build
|
mkdir -p $FIREFLY_PATH/storage/build
|
||||||
mkdir -p $FIREFLY_PATH/storage/database
|
mkdir -p $FIREFLY_PATH/storage/database
|
||||||
mkdir -p $FIREFLY_PATH/storage/debugbar
|
mkdir -p $FIREFLY_PATH/storage/debugbar
|
||||||
mkdir -p $FIREFLY_PATH/storage/export
|
mkdir -p $FIREFLY_PATH/storage/export
|
||||||
mkdir -p $FIREFLY_PATH/storage/framework/cache
|
|
||||||
mkdir -p $FIREFLY_PATH/storage/framework/cache/data
|
mkdir -p $FIREFLY_PATH/storage/framework/cache/data
|
||||||
mkdir -p $FIREFLY_PATH/storage/framework/sessions
|
mkdir -p $FIREFLY_PATH/storage/framework/sessions
|
||||||
mkdir -p $FIREFLY_PATH/storage/framework/testing
|
mkdir -p $FIREFLY_PATH/storage/framework/testing
|
||||||
mkdir -p $FIREFLY_PATH/storage/framework/views
|
mkdir -p $FIREFLY_PATH/storage/framework/views/v1
|
||||||
|
mkdir -p $FIREFLY_PATH/storage/framework/views/v2
|
||||||
mkdir -p $FIREFLY_PATH/storage/logs
|
mkdir -p $FIREFLY_PATH/storage/logs
|
||||||
mkdir -p $FIREFLY_PATH/storage/upload
|
mkdir -p $FIREFLY_PATH/storage/upload
|
||||||
|
|
||||||
|
|
||||||
|
echo "Touch DB file (if SQLlite)..."
|
||||||
if [[ $DB_CONNECTION == "sqlite" ]]
|
if [[ $DB_CONNECTION == "sqlite" ]]
|
||||||
then
|
then
|
||||||
touch $FIREFLY_PATH/storage/database/database.sqlite
|
touch $FIREFLY_PATH/storage/database/database.sqlite
|
||||||
|
echo "Touched!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $FF_DB_CONNECTION == "sqlite" ]]
|
||||||
|
then
|
||||||
|
touch $FIREFLY_PATH/storage/database/database.sqlite
|
||||||
|
echo "Touched!"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# make sure we own the volumes:
|
# make sure we own the volumes:
|
||||||
|
echo "Run chown on ${FIREFLY_PATH}/storage..."
|
||||||
chown -R www-data:www-data -R $FIREFLY_PATH/storage
|
chown -R www-data:www-data -R $FIREFLY_PATH/storage
|
||||||
|
echo "Run chmod on ${FIREFLY_PATH}/storage..."
|
||||||
chmod -R 775 $FIREFLY_PATH/storage
|
chmod -R 775 $FIREFLY_PATH/storage
|
||||||
|
|
||||||
# remove any lingering files that may break upgrades:
|
# remove any lingering files that may break upgrades:
|
||||||
|
echo "Remove log file..."
|
||||||
rm -f $FIREFLY_PATH/storage/logs/laravel.log
|
rm -f $FIREFLY_PATH/storage/logs/laravel.log
|
||||||
|
|
||||||
|
echo "Map environment variables on .env file..."
|
||||||
cat .env.docker | envsubst > .env
|
cat .env.docker | envsubst > .env
|
||||||
|
echo "Dump auto load..."
|
||||||
composer dump-autoload
|
composer dump-autoload
|
||||||
|
echo "Discover packages..."
|
||||||
php artisan package:discover
|
php artisan package:discover
|
||||||
|
|
||||||
|
echo "Run various artisan commands..."
|
||||||
php artisan migrate --seed
|
php artisan migrate --seed
|
||||||
|
php artisan firefly:decrypt-all
|
||||||
php artisan firefly:upgrade-database
|
php artisan firefly:upgrade-database
|
||||||
php artisan firefly:verify
|
php artisan firefly:verify
|
||||||
php artisan passport:install
|
php artisan passport:install
|
||||||
php artisan cache:clear
|
php artisan cache:clear
|
||||||
|
|
||||||
php artisan firefly:instructions install
|
php artisan firefly:instructions install
|
||||||
exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf --nodaemon
|
|
||||||
|
echo "Go!"
|
||||||
|
exec apache2-foreground
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
[program:apache2]
|
|
||||||
command=/bin/bash -c "source /etc/apache2/envvars && exec /usr/sbin/apache2 -DFOREGROUND"
|
|
||||||
stdout_events_enabled=true
|
|
||||||
stderr_events_enabled=true
|
|
||||||
stdout_logfile=/dev/stdout
|
|
||||||
stdout_logfile_maxbytes=0
|
|
||||||
34
.deploy/docker/manifest.sh
Executable file
34
.deploy/docker/manifest.sh
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
if [ "$TRAVIS_BRANCH" == "develop" ]; then
|
||||||
|
TARGET=jc5x/firefly-iii:develop
|
||||||
|
ARM=jc5x/firefly-iii:develop-arm
|
||||||
|
AMD=jc5x/firefly-iii:develop-amd
|
||||||
|
|
||||||
|
docker manifest create $TARGET $AMD $ARM
|
||||||
|
docker manifest annotate $TARGET $ARM --arch arm --os linux
|
||||||
|
docker manifest annotate $TARGET $AMD --arch amd64 --os linux
|
||||||
|
docker manifest push $TARGET
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "The version is $VERSION"
|
||||||
|
|
||||||
|
if [ "$TRAVIS_BRANCH" == "master" ]; then
|
||||||
|
TARGET=jc5x/firefly-iii:latest
|
||||||
|
ARM=jc5x/firefly-iii:latest-arm
|
||||||
|
AMD=jc5x/firefly-iii:latest-amd
|
||||||
|
|
||||||
|
docker manifest create $TARGET $AMD $ARM
|
||||||
|
docker manifest annotate $TARGET $ARM --arch arm --os linux
|
||||||
|
docker manifest annotate $TARGET $AMD --arch amd64 --os linux
|
||||||
|
docker manifest push $TARGET
|
||||||
|
|
||||||
|
# and another one for version specific:
|
||||||
|
TARGET=jc5x/firefly-iii:release-$VERSION
|
||||||
|
ARM=jc5x/firefly-iii:release-$VERSION-arm
|
||||||
|
AMD=jc5x/firefly-iii:release-$VERSION-amd
|
||||||
|
|
||||||
|
docker manifest create $TARGET $AMD $ARM
|
||||||
|
docker manifest annotate $TARGET $ARM --arch arm --os linux
|
||||||
|
docker manifest annotate $TARGET $AMD --arch amd64 --os linux
|
||||||
|
docker manifest push $TARGET
|
||||||
|
fi
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
# supervisor config file
|
|
||||||
# Adapted from the config file distributed with the supervisor package on Debian
|
|
||||||
# Jessie
|
|
||||||
|
|
||||||
# Enable supervisord in non-daemon mode. Disable the logfile as we receive
|
|
||||||
# log messages via stdout/err. Set up the child process log directory in case
|
|
||||||
# the user doesn't set logging to stdout/err.
|
|
||||||
[supervisord]
|
|
||||||
nodaemon = true
|
|
||||||
logfile = NONE
|
|
||||||
pidfile = /var/run/supervisord.pid
|
|
||||||
childlogdir = /var/log/supervisor
|
|
||||||
loglevel=debug
|
|
||||||
|
|
||||||
# Enable supervisorctl via RPC interface over Unix socket
|
|
||||||
[unix_http_server]
|
|
||||||
file = /var/run/supervisor.sock
|
|
||||||
chmod = 0700
|
|
||||||
|
|
||||||
[rpcinterface:supervisor]
|
|
||||||
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
|
||||||
|
|
||||||
[supervisorctl]
|
|
||||||
serverurl = unix:///var/run/supervisor.sock
|
|
||||||
|
|
||||||
|
|
||||||
# Include conf files for child processes
|
|
||||||
# Debian/Ubuntu packages use /etc/supervisor/conf.d
|
|
||||||
[include]
|
|
||||||
files = /etc/supervisor/conf.d/*.conf
|
|
||||||
10
.env.docker
10
.env.docker
@@ -93,8 +93,14 @@ SEND_REPORT_JOURNALS=${SEND_REPORT_JOURNALS}
|
|||||||
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
|
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
|
||||||
MAPBOX_API_KEY=${MAPBOX_API_KEY}
|
MAPBOX_API_KEY=${MAPBOX_API_KEY}
|
||||||
|
|
||||||
# Set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
|
# Firefly III currently supports two provider for live Currency Exchange Rates:
|
||||||
# Please note that this will only work for paid fixer.io accounts because they severly limited
|
# "fixer" is the default (for backward compatibility), and "ratesapi" is the new one.
|
||||||
|
# RatesApi.IO (see https://ratesapi.io) is a FREE and OPEN SOURCE live currency exchange rates,
|
||||||
|
# built compatible with Fixer.IO, based on data published by European Central Bank, and don't require API key.
|
||||||
|
CER_PROVIDER=${CER_PROVIDER}
|
||||||
|
# If you have select "fixer" as default currency exchange rates,
|
||||||
|
# set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
|
||||||
|
# Please note that this WILL ONLY WORK FOR PAID fixer.io accounts because they severely limited
|
||||||
# the free API up to the point where you might as well offer nothing.
|
# the free API up to the point where you might as well offer nothing.
|
||||||
FIXER_API_KEY=${FIXER_API_KEY}
|
FIXER_API_KEY=${FIXER_API_KEY}
|
||||||
|
|
||||||
|
|||||||
10
.env.example
10
.env.example
@@ -93,8 +93,14 @@ SEND_REPORT_JOURNALS=true
|
|||||||
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
|
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
|
||||||
MAPBOX_API_KEY=
|
MAPBOX_API_KEY=
|
||||||
|
|
||||||
# Set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
|
# Firefly III currently supports two provider for live Currency Exchange Rates:
|
||||||
# Please note that this will only work for paid fixer.io accounts because they severly limited
|
# "fixer" is the default (for backward compatibility), and "ratesapi" is the new one.
|
||||||
|
# RatesApi.IO (see https://ratesapi.io) is a FREE and OPEN SOURCE live currency exchange rates,
|
||||||
|
# built compatible with Fixer.IO, based on data published by European Central Bank, and don't require API key.
|
||||||
|
CER_PROVIDER=fixer
|
||||||
|
# If you have select "fixer" as default currency exchange rates,
|
||||||
|
# set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
|
||||||
|
# Please note that this WILL ONLY WORK FOR PAID fixer.io accounts because they severely limited
|
||||||
# the free API up to the point where you might as well offer nothing.
|
# the free API up to the point where you might as well offer nothing.
|
||||||
FIXER_API_KEY=
|
FIXER_API_KEY=
|
||||||
|
|
||||||
|
|||||||
10
.env.heroku
10
.env.heroku
@@ -93,8 +93,14 @@ SEND_REPORT_JOURNALS=true
|
|||||||
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
|
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
|
||||||
MAPBOX_API_KEY=
|
MAPBOX_API_KEY=
|
||||||
|
|
||||||
# Set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
|
# Firefly III currently supports two provider for live Currency Exchange Rates:
|
||||||
# Please note that this will only work for paid fixer.io accounts because they severly limited
|
# "fixer" is the default (for backward compatibility), and "ratesapi" is the new one.
|
||||||
|
# RatesApi.IO (see https://ratesapi.io) is a FREE and OPEN SOURCE live currency exchange rates,
|
||||||
|
# built compatible with Fixer.IO, based on data published by European Central Bank, and don't require API key.
|
||||||
|
CER_PROVIDER=fixer
|
||||||
|
# If you have select "fixer" as default currency exchange rates,
|
||||||
|
# set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
|
||||||
|
# Please note that this WILL ONLY WORK FOR PAID fixer.io accounts because they severely limited
|
||||||
# the free API up to the point where you might as well offer nothing.
|
# the free API up to the point where you might as well offer nothing.
|
||||||
FIXER_API_KEY=
|
FIXER_API_KEY=
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
APP_ENV=local
|
APP_ENV=local
|
||||||
|
|
||||||
# Set to true if you want to see debug information in error screens.
|
# Set to true if you want to see debug information in error screens.
|
||||||
APP_DEBUG=false
|
APP_DEBUG=true
|
||||||
|
|
||||||
# This should be your email address
|
# This should be your email address
|
||||||
SITE_OWNER=sandstorm@example.com
|
SITE_OWNER=sandstorm@example.com
|
||||||
@@ -93,8 +93,14 @@ SEND_REPORT_JOURNALS=true
|
|||||||
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
|
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
|
||||||
MAPBOX_API_KEY=
|
MAPBOX_API_KEY=
|
||||||
|
|
||||||
# Set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
|
# Firefly III currently supports two provider for live Currency Exchange Rates:
|
||||||
# Please note that this will only work for paid fixer.io accounts because they severly limited
|
# "fixer" is the default (for backward compatibility), and "ratesapi" is the new one.
|
||||||
|
# RatesApi.IO (see https://ratesapi.io) is a FREE and OPEN SOURCE live currency exchange rates,
|
||||||
|
# built compatible with Fixer.IO, based on data published by European Central Bank, and don't require API key.
|
||||||
|
CER_PROVIDER=fixer
|
||||||
|
# If you have select "fixer" as default currency exchange rates,
|
||||||
|
# set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
|
||||||
|
# Please note that this WILL ONLY WORK FOR PAID fixer.io accounts because they severely limited
|
||||||
# the free API up to the point where you might as well offer nothing.
|
# the free API up to the point where you might as well offer nothing.
|
||||||
FIXER_API_KEY=
|
FIXER_API_KEY=
|
||||||
|
|
||||||
|
|||||||
10
.env.testing
10
.env.testing
@@ -93,8 +93,14 @@ SEND_REPORT_JOURNALS=true
|
|||||||
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
|
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
|
||||||
MAPBOX_API_KEY=
|
MAPBOX_API_KEY=
|
||||||
|
|
||||||
# Set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
|
# Firefly III currently supports two provider for live Currency Exchange Rates:
|
||||||
# Please note that this will only work for paid fixer.io accounts because they severly limited
|
# "fixer" is the default (for backward compatibility), and "ratesapi" is the new one.
|
||||||
|
# RatesApi.IO (see https://ratesapi.io) is a FREE and OPEN SOURCE live currency exchange rates,
|
||||||
|
# built compatible with Fixer.IO, based on data published by European Central Bank, and don't require API key.
|
||||||
|
CER_PROVIDER=fixer
|
||||||
|
# If you have select "fixer" as default currency exchange rates,
|
||||||
|
# set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
|
||||||
|
# Please note that this WILL ONLY WORK FOR PAID fixer.io accounts because they severely limited
|
||||||
# the free API up to the point where you might as well offer nothing.
|
# the free API up to the point where you might as well offer nothing.
|
||||||
FIXER_API_KEY=
|
FIXER_API_KEY=
|
||||||
|
|
||||||
|
|||||||
14
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
14
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
@@ -5,21 +5,23 @@ about: Create a report to help Firefly III improve
|
|||||||
---
|
---
|
||||||
|
|
||||||
**Bug description**
|
**Bug description**
|
||||||
I am running Firefly III version x.x.x
|
I am running Firefly III version x.x.x, and my problem is:
|
||||||
|
|
||||||
(please give a clear and concise description of what the bug is)
|
<!-- Replace the version and describe your problem or your issue will be closed. -->
|
||||||
|
|
||||||
**Steps to reproduce**
|
**Steps to reproduce**
|
||||||
What do you need to do to trigger this bug?
|
<!-- What do you need to do to trigger this bug? -->
|
||||||
|
|
||||||
**Expected behavior**
|
**Expected behavior**
|
||||||
What do you expect to see after those steps?
|
<!-- What do you expect to see after those steps? -->
|
||||||
|
|
||||||
**Extra info**
|
**Extra info**
|
||||||
Please add extra info here, such as OS, browser, and the output from the /debug page of your Firefly III installation (click the version at the bottom).
|
<!-- Please add extra info here, such as OS, browser, and the output from the /debug page of your Firefly III installation (click the version at the bottom). -->
|
||||||
|
|
||||||
**Bonus points**
|
**Bonus points**
|
||||||
Earn bonus points by:
|
<!-- Earn bonus points by:
|
||||||
|
|
||||||
- Post a stacktrace from your log files
|
- Post a stacktrace from your log files
|
||||||
- Add a screenshot
|
- Add a screenshot
|
||||||
|
- Remember the human
|
||||||
|
-->
|
||||||
16
.github/ISSUE_TEMPLATE/Custom.md
vendored
16
.github/ISSUE_TEMPLATE/Custom.md
vendored
@@ -7,21 +7,19 @@ about: Ask away!
|
|||||||
I am running Firefly III version x.x.x
|
I am running Firefly III version x.x.x
|
||||||
|
|
||||||
**Description**
|
**Description**
|
||||||
|
<!-- (if relevant of course) -->
|
||||||
|
|
||||||
|
|
||||||
**Steps to reproduce**
|
|
||||||
(if relevant of course)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**Extra info**
|
**Extra info**
|
||||||
Please add extra info here, such as OS, browser, and the output from the `/debug`-page of your Firefly III installation (click the version at the bottom).
|
<!-- Please add extra info here, such as OS, browser, and the output from the `/debug`-page of your Firefly III installation (click the version at the bottom). -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**Bonus points**
|
**Bonus points**
|
||||||
Earn bonus points by:
|
<!-- Earn bonus points by:
|
||||||
|
|
||||||
- Add a screenshot
|
- Add a screenshot
|
||||||
|
- Make a drawing
|
||||||
|
- Donate money (just kidding ;)
|
||||||
- Replicate the problem on the demo site https://demo.firefly-iii.org/
|
- Replicate the problem on the demo site https://demo.firefly-iii.org/
|
||||||
|
- Remember the human
|
||||||
|
-->
|
||||||
23
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
23
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
@@ -5,17 +5,28 @@ about: Suggest an idea or feature for Firefly III
|
|||||||
---
|
---
|
||||||
|
|
||||||
**Description**
|
**Description**
|
||||||
|
<!--
|
||||||
Please describe your feature request:
|
Please describe your feature request:
|
||||||
|
|
||||||
- I would like Firefly III to do X.
|
- I would like Firefly III to do ABC.
|
||||||
- What if you would add feature Y?
|
- What if you would add feature XYZ?
|
||||||
- Firefly III doesn't do Z.
|
- Firefly III doesn't do DEF.
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
**Solution**
|
**Solution**
|
||||||
Describe what your feature would add to Firefly III.
|
<!-- Describe what your feature would add to Firefly III. -->
|
||||||
|
|
||||||
**What are alternatives?**
|
**What are alternatives?**
|
||||||
Please describe what alternatives currently exist.
|
<!-- Please describe what alternatives currently exist. -->
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context or screenshots about the feature request here.
|
<!-- Add any other context or screenshots about the feature request here. -->
|
||||||
|
|
||||||
|
**Bonus points**
|
||||||
|
<!-- Earn bonus points by:
|
||||||
|
|
||||||
|
- Make a drawing
|
||||||
|
- Donate money (just kidding ;)
|
||||||
|
- Remember the human
|
||||||
|
-->
|
||||||
9
.github/pull_request_template.md
vendored
9
.github/pull_request_template.md
vendored
@@ -1,4 +1,11 @@
|
|||||||
Fixes # (if relevant)
|
<!--
|
||||||
|
Please read me:
|
||||||
|
|
||||||
|
1) DO NOT create a pull request for the MASTER branch.
|
||||||
|
2) DO NOT create pull requests to add new CURRENCIES.
|
||||||
|
-->
|
||||||
|
|
||||||
|
Fixes issue # (if relevant)
|
||||||
|
|
||||||
Changes in this pull request:
|
Changes in this pull request:
|
||||||
|
|
||||||
|
|||||||
4
.github/stale.yml
vendored
4
.github/stale.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
# Configuration for probot-stale - https://github.com/probot/stale
|
# Configuration for probot-stale - https://github.com/probot/stale
|
||||||
|
|
||||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||||
daysUntilStale: 14
|
daysUntilStale: 7
|
||||||
|
|
||||||
# Number of days of inactivity before a stale Issue or Pull Request is closed.
|
# 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.
|
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||||
@@ -13,6 +13,8 @@ exemptLabels:
|
|||||||
- enhancement
|
- enhancement
|
||||||
- feature
|
- feature
|
||||||
- bug
|
- bug
|
||||||
|
- possible-bug
|
||||||
|
- announcement
|
||||||
|
|
||||||
# Set to true to ignore issues in a project (defaults to false)
|
# Set to true to ignore issues in a project (defaults to false)
|
||||||
exemptProjects: false
|
exemptProjects: false
|
||||||
|
|||||||
@@ -1,3 +1,53 @@
|
|||||||
|
# 4.7.10
|
||||||
|
- [Issue 2037](https://github.com/firefly-iii/firefly-iii/issues/2037) Added some new magic keywords to reports.
|
||||||
|
- Added a new currency exchange rate service, [ratesapi.io](https://ratesapi.io/), that does not require expensive API keys. Built by [@BoGnY](https://github.com/BoGnY).
|
||||||
|
- Added Chinese Traditional translations. Thanks!
|
||||||
|
- [Issue 1977](https://github.com/firefly-iii/firefly-iii/issues/1977) Docker image now includes memcached support
|
||||||
|
- [Issue 2031](https://github.com/firefly-iii/firefly-iii/issues/2031) A new generic debit/credit indicator for imports.
|
||||||
|
- The new Docker image no longer has the capability to run cron jobs, and will no longer generate your recurring transactions for you. This has been done to simplify the build and make sure your Docker container runs one service, as it should. To set up a cron job for your new Docker container, [check out the documentation](https://docs.firefly-iii.org/en/latest/installation/cronjob.html).
|
||||||
|
- Due to a change in the database structure, this upgrade will reset your preferences. Sorry about that.
|
||||||
|
- I will no longer accept PR's that introduce new currencies.
|
||||||
|
- Firefly III no longer encrypts the database and will [decrypt the database]() on its first run.
|
||||||
|
- [Issue 1923](https://github.com/firefly-iii/firefly-iii/issues/1923) Broken window position for date picker.
|
||||||
|
- [Issue 1967](https://github.com/firefly-iii/firefly-iii/issues/1967) Attachments were hidden in bill view.
|
||||||
|
- [Issue 1927](https://github.com/firefly-iii/firefly-iii/issues/1927) It was impossible to make recurring transactions skip.
|
||||||
|
- [Issue 1929](https://github.com/firefly-iii/firefly-iii/issues/1929) Fix the recurring transactions calendar overview.
|
||||||
|
- [Issue 1933](https://github.com/firefly-iii/firefly-iii/issues/1933) Fixed a bug that made it impossible to authenticate to FreeIPA servers.
|
||||||
|
- [Issue 1938](https://github.com/firefly-iii/firefly-iii/issues/1938) The importer can now handle the insane way Postbank (DE) formats its numbers.
|
||||||
|
- [Issue 1942](https://github.com/firefly-iii/firefly-iii/issues/1942) Favicons are relative so Scriptaculous installations work better.
|
||||||
|
- [Issue 1944](https://github.com/firefly-iii/firefly-iii/issues/1944) Make sure that the search allows you to mass-select transactions.
|
||||||
|
- [Issue 1945](https://github.com/firefly-iii/firefly-iii/issues/1945) Slight UI change so the drop-down menu renders better.
|
||||||
|
- [Issue 1955](https://github.com/firefly-iii/firefly-iii/issues/1955) Fixed a bug in the category report.
|
||||||
|
- [Issue 1968](https://github.com/firefly-iii/firefly-iii/issues/1968) The yearly range would jump to 1-Jan / 1-Jan instead of 1-Jan / 31-Dec
|
||||||
|
- [Issue 1975](https://github.com/firefly-iii/firefly-iii/issues/1975) Fixed explanation for missing credit card liabilities.
|
||||||
|
- [Issue 1979](https://github.com/firefly-iii/firefly-iii/issues/1979) Make sure tags are trimmed.
|
||||||
|
- [Issue 1983](https://github.com/firefly-iii/firefly-iii/issues/1983) Could not use your favorite decimal separator.
|
||||||
|
- [Issue 1989](https://github.com/firefly-iii/firefly-iii/issues/1989) Bug in YNAB importer forced you to select all accounts.
|
||||||
|
- [Issue 1990](https://github.com/firefly-iii/firefly-iii/issues/1990) Rule description was invisible in edit screen.
|
||||||
|
- [Issue 1996](https://github.com/firefly-iii/firefly-iii/issues/1996) Deleted budget would inadvertently also hide transactions.
|
||||||
|
- [Issue 2001](https://github.com/firefly-iii/firefly-iii/issues/2001) Various issues with tag chart view.
|
||||||
|
- [Issue 2009](https://github.com/firefly-iii/firefly-iii/issues/2009) Could not change recurrence back to "forever".
|
||||||
|
- [Issue 2033](https://github.com/firefly-iii/firefly-iii/issues/2033) Longitude can go from -180 to 180.
|
||||||
|
- [Issue 2034](https://github.com/firefly-iii/firefly-iii/issues/2034) Rules were not being triggered in mass-edit.
|
||||||
|
- #2043 In rare instances the repetition of a recurring transaction was displayed incorrectly.
|
||||||
|
- Fixed broken translations in the recurring transactions overview.
|
||||||
|
- When you create a recurring transfer you make make it fill (or empty) a piggy bank. This was not working, despite a fix in 4.7.8.
|
||||||
|
- Fixed a bug where the importer would not be capable of creating new currencies.
|
||||||
|
- Rule trigger tester would skip the amount.
|
||||||
|
- OAuth2 form can now submit back to original requester.
|
||||||
|
- Submitting transactions with a disabled currency will auto-enable the currency.
|
||||||
|
- The documentation now states that "Deposit" is a possible return when you get a transaction.
|
||||||
|
- "savingAsset" was incorrectly documented as "savingsAsset".
|
||||||
|
- Account endpoint can now return type "reconciliation" and "initial-balance" correctly.
|
||||||
|
- New API endpoint under `/summary/basic` that gives you a basic overview of the user's finances.
|
||||||
|
- New API endpoints under `/chart/*` to allow you to render charts.
|
||||||
|
- `/accounts/x/transactions` now supports the limit query parameter.
|
||||||
|
- `/budgets/x/transactions` now supports the limit query parameter.
|
||||||
|
- `/available_budgets` now supports custom start and end date parameters.
|
||||||
|
- New endpoint `/preferences/prefName` to retrieve a single preference.
|
||||||
|
- Added field `account_name` to all piggy banks.
|
||||||
|
- New tag cloud in API.
|
||||||
|
|
||||||
# 4.7.9
|
# 4.7.9
|
||||||
- [Issue 1622](https://github.com/firefly-iii/firefly-iii/issues/1622) Can now unlink a transaction from a bill.
|
- [Issue 1622](https://github.com/firefly-iii/firefly-iii/issues/1622) Can now unlink a transaction from a bill.
|
||||||
- [Issue 1848](https://github.com/firefly-iii/firefly-iii/issues/1848) Added support for the Swiss Franc.
|
- [Issue 1848](https://github.com/firefly-iii/firefly-iii/issues/1848) Added support for the Swiss Franc.
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ mkdir -p /var/storage/build
|
|||||||
mkdir -p /var/storage/database
|
mkdir -p /var/storage/database
|
||||||
mkdir -p /var/storage/debugbar
|
mkdir -p /var/storage/debugbar
|
||||||
mkdir -p /var/storage/export
|
mkdir -p /var/storage/export
|
||||||
mkdir -p /var/storage/framework/cache
|
mkdir -p /var/storage/framework/cache/v1
|
||||||
mkdir -p /var/storage/framework/sessions
|
mkdir -p /var/storage/framework/sessions
|
||||||
mkdir -p /var/storage/framework/views
|
mkdir -p /var/storage/framework/views/v1
|
||||||
mkdir -p /var/storage/logs
|
mkdir -p /var/storage/logs
|
||||||
mkdir -p /var/storage/upload
|
mkdir -p /var/storage/upload
|
||||||
|
|
||||||
@@ -58,9 +58,5 @@ echo "Migrating..."
|
|||||||
php /opt/app/artisan migrate --seed --force
|
php /opt/app/artisan migrate --seed --force
|
||||||
echo "Done!"
|
echo "Done!"
|
||||||
|
|
||||||
echo "Clear cache.."
|
|
||||||
php /opt/app/artisan cache:clear
|
|
||||||
echo "Done"
|
|
||||||
|
|
||||||
# Start nginx.
|
# Start nginx.
|
||||||
/usr/sbin/nginx -c /opt/app/.sandstorm/service-config/nginx.conf -g "daemon off;"
|
/usr/sbin/nginx -c /opt/app/.sandstorm/service-config/nginx.conf -g "daemon off;"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -15,8 +15,8 @@ const pkgdef :Spk.PackageDefinition = (
|
|||||||
|
|
||||||
manifest = (
|
manifest = (
|
||||||
appTitle = (defaultText = "Firefly III"),
|
appTitle = (defaultText = "Firefly III"),
|
||||||
appVersion = 19,
|
appVersion = 20,
|
||||||
appMarketingVersion = (defaultText = "4.7.9"),
|
appMarketingVersion = (defaultText = "4.7.10"),
|
||||||
|
|
||||||
actions = [
|
actions = [
|
||||||
# Define your "new document" handlers here.
|
# Define your "new document" handlers here.
|
||||||
@@ -103,7 +103,7 @@ const pkgdef :Spk.PackageDefinition = (
|
|||||||
# not have been detected as a dependency during `spk dev`. If you list
|
# not have been detected as a dependency during `spk dev`. If you list
|
||||||
# a directory here, its entire contents will be included recursively.
|
# a directory here, its entire contents will be included recursively.
|
||||||
|
|
||||||
#bridgeConfig = (
|
bridgeConfig = (
|
||||||
# # Used for integrating permissions and roles into the Sandstorm shell
|
# # Used for integrating permissions and roles into the Sandstorm shell
|
||||||
# # and for sandstorm-http-bridge to pass to your app.
|
# # and for sandstorm-http-bridge to pass to your app.
|
||||||
# # Uncomment this block and adjust the permissions and roles to make
|
# # Uncomment this block and adjust the permissions and roles to make
|
||||||
@@ -165,12 +165,12 @@ const pkgdef :Spk.PackageDefinition = (
|
|||||||
# ),
|
# ),
|
||||||
# ],
|
# ],
|
||||||
# ),
|
# ),
|
||||||
# #apiPath = "/api",
|
apiPath = "/api/v1/",
|
||||||
# # Apps can export an API to the world. The API is to be used primarily by Javascript
|
# # Apps can export an API to the world. The API is to be used primarily by Javascript
|
||||||
# # code and native apps, so it can't serve out regular HTML to browsers. If a request
|
# # code and native apps, so it can't serve out regular HTML to browsers. If a request
|
||||||
# # comes in to your app's API, sandstorm-http-bridge will prefix the request's path with
|
# # comes in to your app's API, sandstorm-http-bridge will prefix the request's path with
|
||||||
# # this string, if specified.
|
# # this string, if specified.
|
||||||
#),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const myCommand :Spk.Manifest.Command = (
|
const myCommand :Spk.Manifest.Command = (
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ apt-get update
|
|||||||
apt-get install -y python-software-properties software-properties-common
|
apt-get install -y python-software-properties software-properties-common
|
||||||
|
|
||||||
# install all languages
|
# install all languages
|
||||||
|
sed -i 's/# es_ES.UTF-8 UTF-8/es_ES.UTF-8 UTF-8/g' /etc/locale.gen
|
||||||
sed -i 's/# de_DE.UTF-8 UTF-8/de_DE.UTF-8 UTF-8/g' /etc/locale.gen
|
sed -i 's/# de_DE.UTF-8 UTF-8/de_DE.UTF-8 UTF-8/g' /etc/locale.gen
|
||||||
sed -i 's/# fr_FR.UTF-8 UTF-8/fr_FR.UTF-8 UTF-8/g' /etc/locale.gen
|
sed -i 's/# fr_FR.UTF-8 UTF-8/fr_FR.UTF-8 UTF-8/g' /etc/locale.gen
|
||||||
sed -i 's/# it_IT.UTF-8 UTF-8/it_IT.UTF-8 UTF-8/g' /etc/locale.gen
|
sed -i 's/# it_IT.UTF-8 UTF-8/it_IT.UTF-8 UTF-8/g' /etc/locale.gen
|
||||||
@@ -20,7 +21,7 @@ sed -i 's/# nl_NL.UTF-8 UTF-8/nl_NL.UTF-8 UTF-8/g' /etc/locale.gen
|
|||||||
sed -i 's/# pl_PL.UTF-8 UTF-8/pl_PL.UTF-8 UTF-8/g' /etc/locale.gen
|
sed -i 's/# pl_PL.UTF-8 UTF-8/pl_PL.UTF-8 UTF-8/g' /etc/locale.gen
|
||||||
sed -i 's/# pt_BR.UTF-8 UTF-8/pt_BR.UTF-8 UTF-8/g' /etc/locale.gen
|
sed -i 's/# pt_BR.UTF-8 UTF-8/pt_BR.UTF-8 UTF-8/g' /etc/locale.gen
|
||||||
sed -i 's/# ru_RU.UTF-8 UTF-8/ru_RU.UTF-8 UTF-8/g' /etc/locale.gen
|
sed -i 's/# ru_RU.UTF-8 UTF-8/ru_RU.UTF-8 UTF-8/g' /etc/locale.gen
|
||||||
sed -i 's/# tr_TR.UTF-8 UTF-8/tr_TR.UTF-8 UTF-8/g' /etc/locale.gen
|
sed -i 's/# zh_TW.UTF-8 UTF-8/zh_TW.UTF-8 UTF-8/g' /etc/locale.gen
|
||||||
|
|
||||||
dpkg-reconfigure --frontend=noninteractive locales
|
dpkg-reconfigure --frontend=noninteractive locales
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sou
|
|||||||
|
|
||||||
# install packages.
|
# install packages.
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install -y nginx php7.2-fpm php7.2-mysql php7.2-gd php7.2-cli php7.2-curl git php7.2-dev php7.2-zip php7.2-intl php7.2-dom php7.2-mbstring php7.2-bcmath mysql-server
|
apt-get install -y nginx php7.2-fpm php7.2-mysql php7.2-gd php7.2-cli php7.2-curl php7.2-ldap git php7.2-dev php7.2-zip php7.2-intl php7.2-dom php7.2-mbstring php7.2-bcmath mysql-server
|
||||||
service nginx stop
|
service nginx stop
|
||||||
service php7.2-fpm stop
|
service php7.2-fpm stop
|
||||||
service mysql stop
|
service mysql stop
|
||||||
|
|||||||
42
.travis.yml
42
.travis.yml
@@ -1,29 +1,23 @@
|
|||||||
language: php
|
sudo: required
|
||||||
php:
|
language: bash
|
||||||
- 7.2
|
|
||||||
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- vendor
|
|
||||||
- $HOME/.composer/cache
|
|
||||||
|
|
||||||
install:
|
|
||||||
- rm composer.lock
|
|
||||||
- composer update --no-scripts
|
|
||||||
- cp .env.testing .env
|
|
||||||
- php artisan clear-compiled
|
|
||||||
- php artisan env
|
|
||||||
- wget -q https://github.com/firefly-iii/test-data/raw/master/storage/database.sqlite -O storage/database/database.sqlite
|
|
||||||
- mkdir -p build/logs
|
|
||||||
|
|
||||||
script:
|
|
||||||
- ./vendor/bin/phpunit -c phpunit.coverage.xml
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
- travis_retry php vendor/bin/php-coveralls -x storage/build/clover-all.xml
|
|
||||||
|
|
||||||
# safelist
|
# safelist
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- develop
|
- develop
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
env:
|
||||||
|
- VERSION=4.7.10
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
|
||||||
|
script:
|
||||||
|
# enable experimental features.
|
||||||
|
- echo '{"experimental":true}' | sudo tee /etc/docker/daemon.json
|
||||||
|
- sudo service docker restart
|
||||||
|
- docker version -f '{{.Server.Experimental}}'
|
||||||
|
- docker version
|
||||||
|
- .deploy/docker/build-amd64.sh
|
||||||
|
- .deploy/docker/build-arm.sh
|
||||||
|
- .deploy/docker/manifest.sh
|
||||||
112
Dockerfile
112
Dockerfile
@@ -1,92 +1,48 @@
|
|||||||
FROM php:7.2-apache
|
FROM php:7.2-apache
|
||||||
|
|
||||||
# If building on a RPi, use --build-arg cores=3 to use all cores when compiling
|
ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1
|
||||||
# to speed up the image build
|
LABEL version="1.3" maintainer="thegrumpydictator@gmail.com"
|
||||||
ARG CORES
|
|
||||||
ENV CORES ${CORES:-1}
|
|
||||||
|
|
||||||
ENV FIREFLY_PATH=/var/www/firefly-iii/ CURL_VERSION=7.60.0 OPENSSL_VERSION=1.1.1-pre6 COMPOSER_ALLOW_SUPERUSER=1
|
|
||||||
LABEL version="1.2" maintainer="thegrumpydictator@gmail.com"
|
|
||||||
|
|
||||||
# install packages
|
|
||||||
RUN apt-get update -y && \
|
|
||||||
apt-get install -y --no-install-recommends libcurl4-openssl-dev \
|
|
||||||
zlib1g-dev \
|
|
||||||
libjpeg62-turbo-dev \
|
|
||||||
wget \
|
|
||||||
libpng-dev \
|
|
||||||
libicu-dev \
|
|
||||||
libldap2-dev \
|
|
||||||
libedit-dev \
|
|
||||||
libtidy-dev \
|
|
||||||
libxml2-dev \
|
|
||||||
unzip \
|
|
||||||
libsqlite3-dev \
|
|
||||||
nano \
|
|
||||||
curl \
|
|
||||||
openssl \
|
|
||||||
libpq-dev \
|
|
||||||
libbz2-dev \
|
|
||||||
gettext-base \
|
|
||||||
cron \
|
|
||||||
rsyslog \
|
|
||||||
supervisor \
|
|
||||||
locales && \
|
|
||||||
apt-get clean && \
|
|
||||||
rm -rf /var/lib/apt/lists/* && \
|
|
||||||
docker-php-ext-configure ldap --with-libdir=lib/x86_64-linux-gnu/ && \
|
|
||||||
docker-php-ext-install ldap
|
|
||||||
|
|
||||||
|
|
||||||
# Make sure that libcurl is using the newer curl libaries
|
|
||||||
#RUN echo "/usr/local/lib" >> /etc/ld.so.conf.d/00-curl.conf && ldconfig
|
|
||||||
|
|
||||||
# Mimic the Debian/Ubuntu config file structure for supervisor
|
|
||||||
COPY .deploy/docker/supervisord.conf /etc/supervisor/supervisord.conf
|
|
||||||
RUN mkdir -p /etc/supervisor/conf.d /var/log/supervisor
|
|
||||||
|
|
||||||
# copy Firefly III supervisor conf file.
|
|
||||||
COPY ./.deploy/docker/firefly-iii.conf /etc/supervisor/conf.d/firefly-iii.conf
|
|
||||||
|
|
||||||
# copy cron job supervisor conf file.
|
|
||||||
COPY ./.deploy/docker/cronjob.conf /etc/supervisor/conf.d/cronjob.conf
|
|
||||||
|
|
||||||
# copy ca certs to correct location
|
|
||||||
COPY ./.deploy/docker/cacert.pem /usr/local/ssl/cert.pem
|
|
||||||
|
|
||||||
# test crons added via crontab
|
|
||||||
RUN echo "0 3 * * * /usr/local/bin/php /var/www/firefly-iii/artisan firefly:cron" | crontab -
|
|
||||||
#RUN (crontab -l ; echo "*/1 * * * * free >> /var/www/firefly-iii/public/cron.html") 2>&1 | crontab -
|
|
||||||
|
|
||||||
# Install PHP exentions, install composer, update languages.
|
|
||||||
RUN docker-php-ext-install -j$(nproc) gd intl tidy zip curl bcmath pdo_mysql bz2 pdo_pgsql && \
|
|
||||||
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \
|
|
||||||
echo "en_US.UTF-8 UTF-8\nde_DE.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nit_IT.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8\ntr_TR.UTF-8 UTF-8\n\n" > /etc/locale.gen && locale-gen
|
|
||||||
|
|
||||||
# copy Apache config to correct spot.
|
|
||||||
COPY ./.deploy/docker/apache2.conf /etc/apache2/apache2.conf
|
|
||||||
|
|
||||||
# Enable apache mod rewrite and mod ssl..
|
|
||||||
RUN a2enmod rewrite && a2enmod ssl
|
|
||||||
|
|
||||||
# Create volumes
|
# Create volumes
|
||||||
VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload
|
VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload
|
||||||
|
|
||||||
# Enable default site (Firefly III)
|
# Install some stuff
|
||||||
COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
|
RUN apt-get update && apt-get install -y libpng-dev \
|
||||||
|
libicu-dev \
|
||||||
|
unzip \
|
||||||
|
gettext-base \
|
||||||
|
libldap2-dev \
|
||||||
|
libpq-dev \
|
||||||
|
locales \
|
||||||
|
libmemcached-dev && \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Make sure we own Firefly III directory
|
# Copy in Firefly III source
|
||||||
RUN chown -R www-data:www-data /var/www && chmod -R 775 $FIREFLY_PATH/storage
|
|
||||||
|
|
||||||
# Copy in Firefly Source
|
|
||||||
WORKDIR $FIREFLY_PATH
|
WORKDIR $FIREFLY_PATH
|
||||||
ADD . $FIREFLY_PATH
|
ADD . $FIREFLY_PATH
|
||||||
|
|
||||||
# Fix the link to curl:
|
# copy ca certs to correct location
|
||||||
#RUN rm -rf /usr/local/lib/libcurl.so.4 && ln -s /usr/lib/x86_64-linux-gnu/libcurl.so.4.4.0 /usr/local/lib/libcurl.so.4
|
COPY ./.deploy/docker/cacert.pem /usr/local/ssl/cert.pem
|
||||||
|
|
||||||
# Run composer
|
# copy Apache config to correct spot.
|
||||||
RUN composer install --prefer-dist --no-dev --no-scripts --no-suggest
|
COPY ./.deploy/docker/apache2.conf /etc/apache2/apache2.conf
|
||||||
|
|
||||||
|
# Enable default site (Firefly III)
|
||||||
|
COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
|
||||||
|
|
||||||
|
# Run a lot of installation commands:
|
||||||
|
RUN chown -R www-data:www-data /var/www && \
|
||||||
|
chmod -R 775 $FIREFLY_PATH/storage && \
|
||||||
|
a2enmod rewrite && a2enmod ssl && \
|
||||||
|
docker-php-ext-configure ldap --with-libdir=lib/$(gcc -dumpmachine)/ && \
|
||||||
|
docker-php-ext-install -j$(nproc) zip bcmath ldap gd pdo_pgsql pdo_mysql intl opcache && \
|
||||||
|
pecl install memcached-3.1.3 && \
|
||||||
|
docker-php-ext-enable memcached && \
|
||||||
|
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \
|
||||||
|
echo "de_DE.UTF-8 UTF-8\nen_US.UTF-8 UTF-8\nes_ES.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nid_ID.UTF-8 UTF-8\nit_IT.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8\ntr_TR.UTF-8 UTF-8\nzh_TW.UTF-8 UTF-8\nzh_CN.UTF-8 UTF-8\n\n" > /etc/locale.gen && \
|
||||||
|
locale-gen && \
|
||||||
|
composer install --prefer-dist --no-dev --no-scripts --no-suggest
|
||||||
|
|
||||||
# Expose port 80
|
# Expose port 80
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|||||||
50
Dockerfile-ARM
Normal file
50
Dockerfile-ARM
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
FROM arm32v7/php:7.2.8-apache-stretch
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
COPY tmp/qemu-arm-static /usr/bin/qemu-arm-static
|
||||||
|
ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1
|
||||||
|
LABEL version="1.3" maintainer="thegrumpydictator@gmail.com"
|
||||||
|
|
||||||
|
# Create volumes
|
||||||
|
VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload
|
||||||
|
|
||||||
|
# Install some stuff
|
||||||
|
RUN apt-get update && apt-get install -y libpng-dev \
|
||||||
|
libicu-dev \
|
||||||
|
unzip \
|
||||||
|
gettext-base \
|
||||||
|
libldap2-dev \
|
||||||
|
libpq-dev \
|
||||||
|
locales \
|
||||||
|
libmemcached-dev
|
||||||
|
|
||||||
|
# Copy in Firefly III source
|
||||||
|
WORKDIR $FIREFLY_PATH
|
||||||
|
ADD . $FIREFLY_PATH
|
||||||
|
|
||||||
|
# copy ca certs to correct location
|
||||||
|
COPY ./.deploy/docker/cacert.pem /usr/local/ssl/cert.pem
|
||||||
|
|
||||||
|
# copy Apache config to correct spot.
|
||||||
|
COPY ./.deploy/docker/apache2.conf /etc/apache2/apache2.conf
|
||||||
|
|
||||||
|
# Enable default site (Firefly III)
|
||||||
|
COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
|
||||||
|
|
||||||
|
# Run a lot of installation commands:
|
||||||
|
RUN chown -R www-data:www-data /var/www && \
|
||||||
|
chmod -R 775 $FIREFLY_PATH/storage && \
|
||||||
|
a2enmod rewrite && a2enmod ssl && \
|
||||||
|
docker-php-ext-configure ldap --with-libdir=lib/$(gcc -dumpmachine)/ && \
|
||||||
|
docker-php-ext-install -j$(nproc) zip bcmath ldap gd pdo_pgsql pdo_mysql intl opcache && \
|
||||||
|
pecl install memcached-3.1.3 && \
|
||||||
|
docker-php-ext-enable memcached && \
|
||||||
|
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \
|
||||||
|
echo "de_DE.UTF-8 UTF-8\nen_US.UTF-8 UTF-8\nes_ES.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nid_ID.UTF-8 UTF-8\nit_IT.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8\ntr_TR.UTF-8 UTF-8\nzh_TW.UTF-8 UTF-8\nzh_CN.UTF-8 UTF-8\n\n" > /etc/locale.gen && \
|
||||||
|
locale-gen && \
|
||||||
|
composer install --prefer-dist --no-dev --no-scripts --no-suggest
|
||||||
|
|
||||||
|
# Expose port 80
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
# Run entrypoint thing
|
||||||
|
ENTRYPOINT [".deploy/docker/entrypoint.sh"]
|
||||||
@@ -233,6 +233,12 @@ class AccountController extends Controller
|
|||||||
$type = $request->get('type') ?? 'default';
|
$type = $request->get('type') ?? 'default';
|
||||||
$this->parameters->set('type', $type);
|
$this->parameters->set('type', $type);
|
||||||
|
|
||||||
|
// user can overrule page size with limit parameter.
|
||||||
|
$limit = $this->parameters->get('limit');
|
||||||
|
if (null !== $limit && $limit > 0) {
|
||||||
|
$pageSize = $limit;
|
||||||
|
}
|
||||||
|
|
||||||
$types = $this->mapTransactionTypes($this->parameters->get('type'));
|
$types = $this->mapTransactionTypes($this->parameters->get('type'));
|
||||||
$manager = new Manager();
|
$manager = new Manager();
|
||||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||||
|
|||||||
@@ -104,6 +104,17 @@ class AvailableBudgetController extends Controller
|
|||||||
|
|
||||||
// get list of available budgets. Count it and split it.
|
// get list of available budgets. Count it and split it.
|
||||||
$collection = $this->repository->getAvailableBudgets();
|
$collection = $this->repository->getAvailableBudgets();
|
||||||
|
|
||||||
|
// filter list on start and end date, if present.
|
||||||
|
// TODO: put this in the query.
|
||||||
|
$start = $this->parameters->get('start');
|
||||||
|
$end = $this->parameters->get('end');
|
||||||
|
if(null !== $start && null !== $end) {
|
||||||
|
$collection = $collection->filter(function(AvailableBudget $availableBudget) use ($start, $end) {
|
||||||
|
return $availableBudget->start_date->gte($start) && $availableBudget->end_date->lte($end);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$count = $collection->count();
|
$count = $collection->count();
|
||||||
$availableBudgets = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
$availableBudgets = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||||
|
|
||||||
|
|||||||
@@ -247,6 +247,13 @@ class BudgetController extends Controller
|
|||||||
public function transactions(Request $request, Budget $budget): JsonResponse
|
public function transactions(Request $request, Budget $budget): JsonResponse
|
||||||
{
|
{
|
||||||
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
||||||
|
|
||||||
|
// user can overrule page size with limit parameter.
|
||||||
|
$limit = $this->parameters->get('limit');
|
||||||
|
if (null !== $limit && $limit > 0) {
|
||||||
|
$pageSize = $limit;
|
||||||
|
}
|
||||||
|
|
||||||
$type = $request->get('type') ?? 'default';
|
$type = $request->get('type') ?? 'default';
|
||||||
$this->parameters->set('type', $type);
|
$this->parameters->set('type', $type);
|
||||||
|
|
||||||
|
|||||||
327
app/Api/V1/Controllers/Chart/AccountController.php
Normal file
327
app/Api/V1/Controllers/Chart/AccountController.php
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Api\V1\Controllers\Chart;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Api\V1\Controllers\Controller;
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Models\Account;
|
||||||
|
use FireflyIII\Models\AccountType;
|
||||||
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||||
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AccountController
|
||||||
|
*/
|
||||||
|
class AccountController extends Controller
|
||||||
|
{
|
||||||
|
/** @var CurrencyRepositoryInterface */
|
||||||
|
private $currencyRepository;
|
||||||
|
/** @var AccountRepositoryInterface */
|
||||||
|
private $repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AccountController constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->middleware(
|
||||||
|
function ($request, $next) {
|
||||||
|
/** @var User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
$this->repository = app(AccountRepositoryInterface::class);
|
||||||
|
$this->repository->setUser($user);
|
||||||
|
|
||||||
|
$this->currencyRepository = app(CurrencyRepositoryInterface::class);
|
||||||
|
$this->currencyRepository->setUser($user);
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function expenseOverview(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
// parameters for chart:
|
||||||
|
$start = (string)$request->get('start');
|
||||||
|
$end = (string)$request->get('end');
|
||||||
|
if ('' === $start || '' === $end) {
|
||||||
|
throw new FireflyException('Start and end are mandatory parameters.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$start = Carbon::createFromFormat('Y-m-d', $start);
|
||||||
|
$end = Carbon::createFromFormat('Y-m-d', $end);
|
||||||
|
$start->subDay();
|
||||||
|
|
||||||
|
// prep some vars:
|
||||||
|
$currencies = [];
|
||||||
|
$chartData = [];
|
||||||
|
$tempData = [];
|
||||||
|
|
||||||
|
// grab all accounts and names
|
||||||
|
$accounts = $this->repository->getAccountsByType([AccountType::EXPENSE]);
|
||||||
|
$accountNames = $this->extractNames($accounts);
|
||||||
|
$startBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $start);
|
||||||
|
$endBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $end);
|
||||||
|
|
||||||
|
// loop the end balances. This is an array for each account ($expenses)
|
||||||
|
foreach ($endBalances as $accountId => $expenses) {
|
||||||
|
$accountId = (int)$accountId;
|
||||||
|
// loop each expense entry (each entry can be a different currency).
|
||||||
|
foreach ($expenses as $currencyId => $endAmount) {
|
||||||
|
$currencyId = (int)$currencyId;
|
||||||
|
|
||||||
|
// see if there is an accompanying start amount.
|
||||||
|
// grab the difference and find the currency.
|
||||||
|
$startAmount = $startBalances[$accountId][$currencyId] ?? '0';
|
||||||
|
$diff = bcsub($endAmount, $startAmount);
|
||||||
|
$currencies[$currencyId] = $currencies[$currencyId] ?? $this->currencyRepository->findNull($currencyId);
|
||||||
|
if (0 !== bccomp($diff, '0')) {
|
||||||
|
// store the values in a temporary array.
|
||||||
|
$tempData[] = [
|
||||||
|
'name' => $accountNames[$accountId],
|
||||||
|
'difference' => $diff,
|
||||||
|
'diff_float' => (float)$diff,
|
||||||
|
'currency_id' => $currencyId,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort temp array by amount.
|
||||||
|
$amounts = array_column($tempData, 'diff_float');
|
||||||
|
array_multisort($amounts, SORT_DESC, $tempData);
|
||||||
|
|
||||||
|
// loop all found currencies and build the data array for the chart.
|
||||||
|
/**
|
||||||
|
* @var int $currencyId
|
||||||
|
* @var TransactionCurrency $currency
|
||||||
|
*/
|
||||||
|
foreach ($currencies as $currencyId => $currency) {
|
||||||
|
$currentSet = [
|
||||||
|
'label' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
|
||||||
|
'currency_id' => $currency->id,
|
||||||
|
'currency_code' => $currency->code,
|
||||||
|
'currency_symbol' => $currency->symbol,
|
||||||
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
|
'type' => 'bar', // line, area or bar
|
||||||
|
'yAxisID' => 0, // 0, 1, 2
|
||||||
|
'entries' => $this->expandNames($tempData),
|
||||||
|
];
|
||||||
|
$chartData[$currencyId] = $currentSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// loop temp data and place data in correct array:
|
||||||
|
foreach ($tempData as $entry) {
|
||||||
|
$currencyId = $entry['currency_id'];
|
||||||
|
$name = $entry['name'];
|
||||||
|
$chartData[$currencyId]['entries'][$name] = round($entry['difference'], $chartData[$currencyId]['currency_decimal_places']);
|
||||||
|
}
|
||||||
|
$chartData = array_values($chartData);
|
||||||
|
|
||||||
|
return response()->json($chartData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function revenueOverview(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
// parameters for chart:
|
||||||
|
$start = (string)$request->get('start');
|
||||||
|
$end = (string)$request->get('end');
|
||||||
|
if ('' === $start || '' === $end) {
|
||||||
|
throw new FireflyException('Start and end are mandatory parameters.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$start = Carbon::createFromFormat('Y-m-d', $start);
|
||||||
|
$end = Carbon::createFromFormat('Y-m-d', $end);
|
||||||
|
$start->subDay();
|
||||||
|
|
||||||
|
// prep some vars:
|
||||||
|
$currencies = [];
|
||||||
|
$chartData = [];
|
||||||
|
$tempData = [];
|
||||||
|
|
||||||
|
// grab all accounts and names
|
||||||
|
$accounts = $this->repository->getAccountsByType([AccountType::REVENUE]);
|
||||||
|
$accountNames = $this->extractNames($accounts);
|
||||||
|
$startBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $start);
|
||||||
|
$endBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $end);
|
||||||
|
|
||||||
|
// loop the end balances. This is an array for each account ($expenses)
|
||||||
|
foreach ($endBalances as $accountId => $expenses) {
|
||||||
|
$accountId = (int)$accountId;
|
||||||
|
// loop each expense entry (each entry can be a different currency).
|
||||||
|
foreach ($expenses as $currencyId => $endAmount) {
|
||||||
|
$currencyId = (int)$currencyId;
|
||||||
|
|
||||||
|
// see if there is an accompanying start amount.
|
||||||
|
// grab the difference and find the currency.
|
||||||
|
$startAmount = $startBalances[$accountId][$currencyId] ?? '0';
|
||||||
|
$diff = bcsub($endAmount, $startAmount);
|
||||||
|
$currencies[$currencyId] = $currencies[$currencyId] ?? $this->currencyRepository->findNull($currencyId);
|
||||||
|
if (0 !== bccomp($diff, '0')) {
|
||||||
|
// store the values in a temporary array.
|
||||||
|
$tempData[] = [
|
||||||
|
'name' => $accountNames[$accountId],
|
||||||
|
'difference' => bcmul($diff,'-1'),
|
||||||
|
'diff_float' => (float)$diff * -1,
|
||||||
|
'currency_id' => $currencyId,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort temp array by amount.
|
||||||
|
$amounts = array_column($tempData, 'diff_float');
|
||||||
|
array_multisort($amounts, SORT_DESC, $tempData);
|
||||||
|
|
||||||
|
// loop all found currencies and build the data array for the chart.
|
||||||
|
/**
|
||||||
|
* @var int $currencyId
|
||||||
|
* @var TransactionCurrency $currency
|
||||||
|
*/
|
||||||
|
foreach ($currencies as $currencyId => $currency) {
|
||||||
|
$currentSet = [
|
||||||
|
'label' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
|
||||||
|
'currency_id' => $currency->id,
|
||||||
|
'currency_code' => $currency->code,
|
||||||
|
'currency_symbol' => $currency->symbol,
|
||||||
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
|
'type' => 'bar', // line, area or bar
|
||||||
|
'yAxisID' => 0, // 0, 1, 2
|
||||||
|
'entries' => $this->expandNames($tempData),
|
||||||
|
];
|
||||||
|
$chartData[$currencyId] = $currentSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// loop temp data and place data in correct array:
|
||||||
|
foreach ($tempData as $entry) {
|
||||||
|
$currencyId = $entry['currency_id'];
|
||||||
|
$name = $entry['name'];
|
||||||
|
$chartData[$currencyId]['entries'][$name] = round($entry['difference'], $chartData[$currencyId]['currency_decimal_places']);
|
||||||
|
}
|
||||||
|
$chartData = array_values($chartData);
|
||||||
|
|
||||||
|
return response()->json($chartData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function overview(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
// parameters for chart:
|
||||||
|
$start = (string)$request->get('start');
|
||||||
|
$end = (string)$request->get('end');
|
||||||
|
if ('' === $start || '' === $end) {
|
||||||
|
throw new FireflyException('Start and end are mandatory parameters.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$start = Carbon::createFromFormat('Y-m-d', $start);
|
||||||
|
$end = Carbon::createFromFormat('Y-m-d', $end);
|
||||||
|
|
||||||
|
// user's preferences
|
||||||
|
$defaultSet = $this->repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray();
|
||||||
|
$frontPage = app('preferences')->get('frontPageAccounts', $defaultSet);
|
||||||
|
$default = app('amount')->getDefaultCurrency();
|
||||||
|
if (0 === \count($frontPage->data)) {
|
||||||
|
$frontPage->data = $defaultSet;
|
||||||
|
$frontPage->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// get accounts:
|
||||||
|
$accounts = $this->repository->getAccountsById($frontPage->data);
|
||||||
|
$chartData = [];
|
||||||
|
/** @var Account $account */
|
||||||
|
foreach ($accounts as $account) {
|
||||||
|
$currency = $this->repository->getAccountCurrency($account);
|
||||||
|
if (null === $currency) {
|
||||||
|
$currency = $default;
|
||||||
|
}
|
||||||
|
$currentSet = [
|
||||||
|
'label' => $account->name,
|
||||||
|
'currency_id' => $currency->id,
|
||||||
|
'currency_code' => $currency->code,
|
||||||
|
'currency_symbol' => $currency->symbol,
|
||||||
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
|
'type' => 'line', // line, area or bar
|
||||||
|
'yAxisID' => 0, // 0, 1, 2
|
||||||
|
'entries' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
$currentStart = clone $start;
|
||||||
|
$range = app('steam')->balanceInRange($account, $start, clone $end);
|
||||||
|
$previous = round(array_values($range)[0], 12);
|
||||||
|
while ($currentStart <= $end) {
|
||||||
|
$format = $currentStart->format('Y-m-d');
|
||||||
|
$label = $currentStart->format('Y-m-d');
|
||||||
|
$balance = isset($range[$format]) ? round($range[$format], 12) : $previous;
|
||||||
|
$previous = $balance;
|
||||||
|
$currentStart->addDay();
|
||||||
|
$currentSet['entries'][$label] = $balance;
|
||||||
|
}
|
||||||
|
$chartData[] = $currentSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json($chartData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Small helper function for the revenue and expense account charts.
|
||||||
|
* TODO should include Trait instead of doing this.
|
||||||
|
*
|
||||||
|
* @param array $names
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function expandNames(array $names): array
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
foreach ($names as $entry) {
|
||||||
|
$result[$entry['name']] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Small helper function for the revenue and expense account charts.
|
||||||
|
* TODO should include Trait instead of doing this.
|
||||||
|
*
|
||||||
|
* @param Collection $accounts
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function extractNames(Collection $accounts): array
|
||||||
|
{
|
||||||
|
$return = [];
|
||||||
|
/** @var Account $account */
|
||||||
|
foreach ($accounts as $account) {
|
||||||
|
$return[$account->id] = $account->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
93
app/Api/V1/Controllers/Chart/AvailableBudgetController.php
Normal file
93
app/Api/V1/Controllers/Chart/AvailableBudgetController.php
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Api\V1\Controllers\Chart;
|
||||||
|
|
||||||
|
|
||||||
|
use FireflyIII\Api\V1\Controllers\Controller;
|
||||||
|
use FireflyIII\Models\AvailableBudget;
|
||||||
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AvailableBudgetController
|
||||||
|
*/
|
||||||
|
class AvailableBudgetController extends Controller
|
||||||
|
{
|
||||||
|
/** @var BudgetRepositoryInterface */
|
||||||
|
private $repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AvailableBudgetController constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->middleware(
|
||||||
|
function ($request, $next) {
|
||||||
|
/** @var User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
$this->repository = app(BudgetRepositoryInterface::class);
|
||||||
|
$this->repository->setUser($user);
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @param AvailableBudget $availableBudget
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
*/
|
||||||
|
public function overview(Request $request, AvailableBudget $availableBudget): JsonResponse
|
||||||
|
{
|
||||||
|
$currency = $availableBudget->transactionCurrency;
|
||||||
|
$budgets = $this->repository->getActiveBudgets();
|
||||||
|
$budgetInformation = $this->repository->spentInPeriodMc($budgets, new Collection, $availableBudget->start_date, $availableBudget->end_date);
|
||||||
|
$spent = 0.0;
|
||||||
|
|
||||||
|
// get for current currency
|
||||||
|
foreach ($budgetInformation as $spentInfo) {
|
||||||
|
if ($spentInfo['currency_id'] === $availableBudget->transaction_currency_id) {
|
||||||
|
$spent = $spentInfo['amount'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$left = bcadd($availableBudget->amount, (string)$spent);
|
||||||
|
// left less than zero? Set to zero.
|
||||||
|
if (bccomp($left, '0') === -1) {
|
||||||
|
$left = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
$chartData = [
|
||||||
|
[
|
||||||
|
'label' => trans('firefly.spent'),
|
||||||
|
'currency_id' => $currency->id,
|
||||||
|
'currency_code' => $currency->code,
|
||||||
|
'currency_symbol' => $currency->symbol,
|
||||||
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
|
'type' => 'pie',
|
||||||
|
'yAxisID' => 0, // 0, 1, 2
|
||||||
|
'entries' => [$spent * -1],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'label' => trans('firefly.left'),
|
||||||
|
'currency_id' => $currency->id,
|
||||||
|
'currency_code' => $currency->code,
|
||||||
|
'currency_symbol' => $currency->symbol,
|
||||||
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
|
'type' => 'line', // line, area or bar
|
||||||
|
'yAxisID' => 0, // 0, 1, 2
|
||||||
|
'entries' => [round($left, $currency->decimal_places)],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
return response()->json($chartData);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
188
app/Api/V1/Controllers/Chart/CategoryController.php
Normal file
188
app/Api/V1/Controllers/Chart/CategoryController.php
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Api\V1\Controllers\Chart;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Api\V1\Controllers\Controller;
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||||
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CategoryController
|
||||||
|
*/
|
||||||
|
class CategoryController extends Controller
|
||||||
|
{
|
||||||
|
/** @var CategoryRepositoryInterface */
|
||||||
|
private $categoryRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AccountController constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->middleware(
|
||||||
|
function ($request, $next) {
|
||||||
|
/** @var User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
$this->categoryRepository = app(CategoryRepositoryInterface::class);
|
||||||
|
$this->categoryRepository->setUser($user);
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function overview(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
// parameters for chart:
|
||||||
|
$start = (string)$request->get('start');
|
||||||
|
$end = (string)$request->get('end');
|
||||||
|
if ('' === $start || '' === $end) {
|
||||||
|
throw new FireflyException('Start and end are mandatory parameters.');
|
||||||
|
}
|
||||||
|
$start = Carbon::createFromFormat('Y-m-d', $start);
|
||||||
|
$end = Carbon::createFromFormat('Y-m-d', $end);
|
||||||
|
$tempData = [];
|
||||||
|
$spent = $this->categoryRepository->spentInPeriodPerCurrency(new Collection, new Collection, $start, $end);
|
||||||
|
$earned = $this->categoryRepository->earnedInPeriodPerCurrency(new Collection, new Collection, $start, $end);
|
||||||
|
$categories = [];
|
||||||
|
|
||||||
|
// earned:
|
||||||
|
foreach ($earned as $categoryId => $row) {
|
||||||
|
$categoryName = $row['name'];
|
||||||
|
foreach ($row['earned'] as $currencyId => $income) {
|
||||||
|
// find or make set for currency:
|
||||||
|
$key = sprintf('%s-e', $currencyId);
|
||||||
|
$decimalPlaces = $income['currency_decimal_places'];
|
||||||
|
if (!isset($tempData[$key])) {
|
||||||
|
$tempData[$key] = [
|
||||||
|
'label' => (string)trans('firefly.box_earned_in_currency', ['currency' => $income['currency_symbol']]),
|
||||||
|
'currency_id' => $income['currency_id'],
|
||||||
|
'currency_code' => $income['currency_code'],
|
||||||
|
'currency_symbol' => $income['currency_symbol'],
|
||||||
|
'currency_decimal_places' => $decimalPlaces,
|
||||||
|
'type' => 'bar', // line, area or bar
|
||||||
|
'yAxisID' => 0, // 0, 1, 2
|
||||||
|
'entries' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$amount = round($income['earned'], $decimalPlaces);
|
||||||
|
$categories[$categoryName] = isset($categories[$categoryName]) ? $categories[$categoryName] + $amount : $amount;
|
||||||
|
$tempData[$key]['entries'][$categoryName]
|
||||||
|
= $amount;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// earned with no category:
|
||||||
|
$noCategory = $this->categoryRepository->earnedInPeriodPcWoCategory(new Collection, $start, $end);
|
||||||
|
foreach ($noCategory as $currencyId => $income) {
|
||||||
|
$categoryName = (string)trans('firefly.no_category');
|
||||||
|
// find or make set for currency:
|
||||||
|
$key = sprintf('%s-e', $currencyId);
|
||||||
|
$decimalPlaces = $income['currency_decimal_places'];
|
||||||
|
if (!isset($tempData[$key])) {
|
||||||
|
$tempData[$key] = [
|
||||||
|
'label' => (string)trans('firefly.box_earned_in_currency', ['currency' => $income['currency_symbol']]),
|
||||||
|
'currency_id' => $income['currency_id'],
|
||||||
|
'currency_code' => $income['currency_code'],
|
||||||
|
'currency_symbol' => $income['currency_symbol'],
|
||||||
|
'currency_decimal_places' => $decimalPlaces,
|
||||||
|
'type' => 'bar', // line, area or bar
|
||||||
|
'yAxisID' => 0, // 0, 1, 2
|
||||||
|
'entries' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$amount = round($income['spent'], $decimalPlaces);
|
||||||
|
$categories[$categoryName] = isset($categories[$categoryName]) ? $categories[$categoryName] + $amount : $amount;
|
||||||
|
$tempData[$key]['entries'][$categoryName]
|
||||||
|
= $amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// spent
|
||||||
|
foreach ($spent as $categoryId => $row) {
|
||||||
|
$categoryName = $row['name'];
|
||||||
|
// create a new set if necessary, "spent (EUR)":
|
||||||
|
foreach ($row['spent'] as $currencyId => $expense) {
|
||||||
|
// find or make set for currency:
|
||||||
|
$key = sprintf('%s-s', $currencyId);
|
||||||
|
$decimalPlaces = $expense['currency_decimal_places'];
|
||||||
|
if (!isset($tempData[$key])) {
|
||||||
|
$tempData[$key] = [
|
||||||
|
'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $expense['currency_symbol']]),
|
||||||
|
'currency_id' => $expense['currency_id'],
|
||||||
|
'currency_code' => $expense['currency_code'],
|
||||||
|
'currency_symbol' => $expense['currency_symbol'],
|
||||||
|
'currency_decimal_places' => $decimalPlaces,
|
||||||
|
'type' => 'bar', // line, area or bar
|
||||||
|
'yAxisID' => 0, // 0, 1, 2
|
||||||
|
'entries' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$amount = round($expense['spent'], $decimalPlaces);
|
||||||
|
$categories[$categoryName] = isset($categories[$categoryName]) ? $categories[$categoryName] + $amount : $amount;
|
||||||
|
$tempData[$key]['entries'][$categoryName]
|
||||||
|
= $amount;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// spent with no category
|
||||||
|
$noCategory = $this->categoryRepository->spentInPeriodPcWoCategory(new Collection, $start, $end);
|
||||||
|
foreach ($noCategory as $currencyId => $expense) {
|
||||||
|
$categoryName = (string)trans('firefly.no_category');
|
||||||
|
// find or make set for currency:
|
||||||
|
$key = sprintf('%s-s', $currencyId);
|
||||||
|
$decimalPlaces = $expense['currency_decimal_places'];
|
||||||
|
if (!isset($tempData[$key])) {
|
||||||
|
$tempData[$key] = [
|
||||||
|
'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $expense['currency_symbol']]),
|
||||||
|
'currency_id' => $expense['currency_id'],
|
||||||
|
'currency_code' => $expense['currency_code'],
|
||||||
|
'currency_symbol' => $expense['currency_symbol'],
|
||||||
|
'currency_decimal_places' => $decimalPlaces,
|
||||||
|
'type' => 'bar', // line, area or bar
|
||||||
|
'yAxisID' => 0, // 0, 1, 2
|
||||||
|
'entries' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$amount = round($expense['spent'], $decimalPlaces);
|
||||||
|
$categories[$categoryName] = isset($categories[$categoryName]) ? $categories[$categoryName] + $amount : $amount;
|
||||||
|
$tempData[$key]['entries'][$categoryName]
|
||||||
|
= $amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
asort($categories);
|
||||||
|
$keys = array_keys($categories);
|
||||||
|
|
||||||
|
// re-sort every spent array and add 0 for missing entries.
|
||||||
|
foreach ($tempData as $index => $set) {
|
||||||
|
$oldSet = $set['entries'];
|
||||||
|
$newSet = [];
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$value = $oldSet[$key] ?? 0;
|
||||||
|
$value = $value < 0 ? $value * -1 : $value;
|
||||||
|
$newSet[$key] = $value;
|
||||||
|
}
|
||||||
|
$tempData[$index]['entries'] = $newSet;
|
||||||
|
}
|
||||||
|
$chartData = array_values($tempData);
|
||||||
|
|
||||||
|
return response()->json($chartData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -113,6 +113,15 @@ class Controller extends BaseController
|
|||||||
$bag->set($field, $obj);
|
$bag->set($field, $obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// integer fields:
|
||||||
|
$integers = ['limit'];
|
||||||
|
foreach ($integers as $integer) {
|
||||||
|
$value = request()->query->get($integer);
|
||||||
|
if (null !== $value) {
|
||||||
|
$bag->set($integer, (int)$value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $bag;
|
return $bag;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Api\V1\Controllers;
|
namespace FireflyIII\Api\V1\Controllers;
|
||||||
|
|
||||||
use FireflyIII\Api\V1\Requests\PreferenceRequest;
|
use FireflyIII\Api\V1\Requests\PreferenceRequest;
|
||||||
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\Preference;
|
use FireflyIII\Models\Preference;
|
||||||
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Transformers\PreferenceTransformer;
|
use FireflyIII\Transformers\PreferenceTransformer;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
@@ -41,6 +43,34 @@ use League\Fractal\Serializer\JsonApiSerializer;
|
|||||||
*/
|
*/
|
||||||
class PreferenceController extends Controller
|
class PreferenceController extends Controller
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* LinkTypeController constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->middleware(
|
||||||
|
function ($request, $next) {
|
||||||
|
/** @var User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
$repository = app(AccountRepositoryInterface::class);
|
||||||
|
$repository->setUser($user);
|
||||||
|
|
||||||
|
// an important fallback is that the frontPageAccount array gets refilled automatically
|
||||||
|
// when it turns up empty.
|
||||||
|
$frontPageAccounts = app('preferences')->getForUser($user, 'frontPageAccounts', [])->data;
|
||||||
|
if (\count($frontPageAccounts) === 0) {
|
||||||
|
/** @var Collection $accounts */
|
||||||
|
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||||
|
$accountIds = $accounts->pluck('id')->toArray();
|
||||||
|
app('preferences')->setForUser($user, 'frontPageAccounts', $accountIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List all of them.
|
* List all of them.
|
||||||
*
|
*
|
||||||
@@ -57,6 +87,7 @@ class PreferenceController extends Controller
|
|||||||
'transaction_journal_optional_fields', 'frontPageAccounts', 'viewRange',
|
'transaction_journal_optional_fields', 'frontPageAccounts', 'viewRange',
|
||||||
'listPageSize, twoFactorAuthEnabled',
|
'listPageSize, twoFactorAuthEnabled',
|
||||||
];
|
];
|
||||||
|
|
||||||
$preferences = new Collection;
|
$preferences = new Collection;
|
||||||
foreach ($available as $name) {
|
foreach ($available as $name) {
|
||||||
$pref = app('preferences')->getForUser($user, $name);
|
$pref = app('preferences')->getForUser($user, $name);
|
||||||
@@ -83,6 +114,32 @@ class PreferenceController extends Controller
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a single preference by name.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Preference $preference
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||||
|
*/
|
||||||
|
public function show(Request $request, Preference $preference): JsonResponse
|
||||||
|
{
|
||||||
|
// create some objects:
|
||||||
|
$manager = new Manager;
|
||||||
|
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||||
|
|
||||||
|
// present to user.
|
||||||
|
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||||
|
/** @var PreferenceTransformer $transformer */
|
||||||
|
$transformer = app(PreferenceTransformer::class);
|
||||||
|
$transformer->setParameters($this->parameters);
|
||||||
|
|
||||||
|
$resource = new Item($preference, $transformer, 'preferences');
|
||||||
|
|
||||||
|
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a preference.
|
* Update a preference.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -234,28 +234,10 @@ class RuleGroupController extends Controller
|
|||||||
if (0 === $rules->count()) {
|
if (0 === $rules->count()) {
|
||||||
throw new FireflyException('No rules in this rule group.');
|
throw new FireflyException('No rules in this rule group.');
|
||||||
}
|
}
|
||||||
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
$parameters = $this->getTestParameters($request);
|
||||||
$page = 0 === (int)$request->query('page') ? 1 : (int)$request->query('page');
|
$accounts = $this->getAccountParameter($parameters['account_list']);
|
||||||
$startDate = null === $request->query('start_date') ? null : Carbon::createFromFormat('Y-m-d', $request->query('start_date'));
|
|
||||||
$endDate = null === $request->query('end_date') ? null : Carbon::createFromFormat('Y-m-d', $request->query('end_date'));
|
|
||||||
$searchLimit = 0 === (int)$request->query('search_limit') ? (int)config('firefly.test-triggers.limit') : (int)$request->query('search_limit');
|
|
||||||
$triggerLimit = 0 === (int)$request->query('triggered_limit') ? (int)config('firefly.test-triggers.range') : (int)$request->query('triggered_limit');
|
|
||||||
$accountList = '' === (string)$request->query('accounts') ? [] : explode(',', $request->query('accounts'));
|
|
||||||
$accounts = new Collection;
|
|
||||||
|
|
||||||
foreach ($accountList as $accountId) {
|
|
||||||
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
|
|
||||||
$account = $this->accountRepository->findNull((int)$accountId);
|
|
||||||
if (null !== $account && AccountType::ASSET === $account->accountType->type) {
|
|
||||||
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
|
|
||||||
$accounts->push($account);
|
|
||||||
}
|
|
||||||
if (null === $account) {
|
|
||||||
Log::debug(sprintf('No asset account with id "%s"', $accountId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$matchingTransactions = new Collection;
|
$matchingTransactions = new Collection;
|
||||||
|
|
||||||
Log::debug(sprintf('Going to test %d rules', $rules->count()));
|
Log::debug(sprintf('Going to test %d rules', $rules->count()));
|
||||||
/** @var Rule $rule */
|
/** @var Rule $rule */
|
||||||
foreach ($rules as $rule) {
|
foreach ($rules as $rule) {
|
||||||
@@ -264,10 +246,10 @@ class RuleGroupController extends Controller
|
|||||||
$matcher = app(TransactionMatcher::class);
|
$matcher = app(TransactionMatcher::class);
|
||||||
// set all parameters:
|
// set all parameters:
|
||||||
$matcher->setRule($rule);
|
$matcher->setRule($rule);
|
||||||
$matcher->setStartDate($startDate);
|
$matcher->setStartDate($parameters['start_date']);
|
||||||
$matcher->setEndDate($endDate);
|
$matcher->setEndDate($parameters['end_date']);
|
||||||
$matcher->setSearchLimit($searchLimit);
|
$matcher->setSearchLimit($parameters['search_limit']);
|
||||||
$matcher->setTriggeredLimit($triggerLimit);
|
$matcher->setTriggeredLimit($parameters['trigger_limit']);
|
||||||
$matcher->setAccounts($accounts);
|
$matcher->setAccounts($accounts);
|
||||||
|
|
||||||
$result = $matcher->findTransactionsByRule();
|
$result = $matcher->findTransactionsByRule();
|
||||||
@@ -277,9 +259,9 @@ class RuleGroupController extends Controller
|
|||||||
|
|
||||||
// make paginator out of results.
|
// make paginator out of results.
|
||||||
$count = $matchingTransactions->count();
|
$count = $matchingTransactions->count();
|
||||||
$transactions = $matchingTransactions->slice(($page - 1) * $pageSize, $pageSize);
|
$transactions = $matchingTransactions->slice(($parameters['page'] - 1) * $parameters['page_size'], $parameters['page_size']);
|
||||||
// make paginator:
|
// make paginator:
|
||||||
$paginator = new LengthAwarePaginator($transactions, $count, $pageSize, $this->parameters->get('page'));
|
$paginator = new LengthAwarePaginator($transactions, $count, $parameters['page_size'], $parameters['page']);
|
||||||
$paginator->setPath(route('api.v1.rule_groups.test', [$group->id]) . $this->buildParams());
|
$paginator->setPath(route('api.v1.rule_groups.test', [$group->id]) . $this->buildParams());
|
||||||
|
|
||||||
// resulting list is presented as JSON thing.
|
// resulting list is presented as JSON thing.
|
||||||
@@ -371,4 +353,49 @@ class RuleGroupController extends Controller
|
|||||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $accounts
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
private function getAccountParameter(array $accounts): Collection
|
||||||
|
{
|
||||||
|
$return = new Collection;
|
||||||
|
foreach ($accounts as $accountId) {
|
||||||
|
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
|
||||||
|
$account = $this->accountRepository->findNull((int)$accountId);
|
||||||
|
if (null !== $account && AccountType::ASSET === $account->accountType->type) {
|
||||||
|
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
|
||||||
|
$return->push($account);
|
||||||
|
}
|
||||||
|
if (null === $account) {
|
||||||
|
Log::debug(sprintf('No asset account with id "%s"', $accountId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getTestParameters(Request $request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'page_size' => (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data,
|
||||||
|
'page' => 0 === (int)$request->query('page') ? 1 : (int)$request->query('page'),
|
||||||
|
'start_date' => null === $request->query('start_date') ? null : Carbon::createFromFormat('Y-m-d', $request->query('start_date')),
|
||||||
|
'end_date' => null === $request->query('end_date') ? null : Carbon::createFromFormat('Y-m-d', $request->query('end_date')),
|
||||||
|
'search_limit' => 0 === (int)$request->query('search_limit') ? (int)config('firefly.test-triggers.limit') : (int)$request->query('search_limit'),
|
||||||
|
'trigger_limit' => 0 === (int)$request->query('triggered_limit')
|
||||||
|
? (int)config('firefly.test-triggers.range')
|
||||||
|
: (int)$request->query(
|
||||||
|
'triggered_limit'
|
||||||
|
),
|
||||||
|
'account_list' => '' === (string)$request->query('accounts') ? [] : explode(',', $request->query('accounts')),
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
392
app/Api/V1/Controllers/SummaryController.php
Normal file
392
app/Api/V1/Controllers/SummaryController.php
Normal file
@@ -0,0 +1,392 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Api\V1\Controllers;
|
||||||
|
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
||||||
|
use FireflyIII\Helpers\Report\NetWorthInterface;
|
||||||
|
use FireflyIII\Models\Account;
|
||||||
|
use FireflyIII\Models\AccountType;
|
||||||
|
use FireflyIII\Models\Transaction;
|
||||||
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
|
use FireflyIII\Models\TransactionType;
|
||||||
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||||
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class SummaryController
|
||||||
|
*/
|
||||||
|
class SummaryController extends Controller
|
||||||
|
{
|
||||||
|
/** @var AccountRepositoryInterface */
|
||||||
|
private $accountRepository;
|
||||||
|
/** @var BillRepositoryInterface */
|
||||||
|
private $billRepository;
|
||||||
|
/** @var BudgetRepositoryInterface */
|
||||||
|
private $budgetRepository;
|
||||||
|
/** @var CurrencyRepositoryInterface */
|
||||||
|
private $currencyRepos;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AccountController constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->middleware(
|
||||||
|
function ($request, $next) {
|
||||||
|
/** @var User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||||
|
$this->billRepository = app(BillRepositoryInterface::class);
|
||||||
|
$this->budgetRepository = app(BudgetRepositoryInterface::class);
|
||||||
|
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||||
|
|
||||||
|
$this->billRepository->setUser($user);
|
||||||
|
$this->currencyRepos->setUser($user);
|
||||||
|
$this->budgetRepository->setUser($user);
|
||||||
|
$this->accountRepository->setUser($user);
|
||||||
|
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function basic(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
// parameters for boxes:
|
||||||
|
$start = (string)$request->get('start');
|
||||||
|
$end = (string)$request->get('end');
|
||||||
|
if ('' === $start || '' === $end) {
|
||||||
|
throw new FireflyException('Start and end are mandatory parameters.');
|
||||||
|
}
|
||||||
|
$start = Carbon::createFromFormat('Y-m-d', $start);
|
||||||
|
$end = Carbon::createFromFormat('Y-m-d', $end);
|
||||||
|
// balance information:
|
||||||
|
$balanceData = $this->getBalanceInformation($start, $end);
|
||||||
|
$billData = $this->getBillInformation($start, $end);
|
||||||
|
$spentData = $this->getLeftToSpendInfo($start, $end);
|
||||||
|
$networthData = $this->getNetWorthInfo($start, $end);
|
||||||
|
$total = array_merge($balanceData, $billData, $spentData, $networthData);
|
||||||
|
// TODO: liabilities with icon line-chart
|
||||||
|
|
||||||
|
return response()->json($total);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if date is outside session range.
|
||||||
|
*
|
||||||
|
* @param Carbon $date
|
||||||
|
*
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||||
|
*/
|
||||||
|
protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference
|
||||||
|
{
|
||||||
|
$result = false;
|
||||||
|
if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
|
||||||
|
$result = true;
|
||||||
|
}
|
||||||
|
// start and end in the past? use $end
|
||||||
|
if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) {
|
||||||
|
$result = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will scroll through the results of the spentInPeriodMc() array and return the correct info.
|
||||||
|
*
|
||||||
|
* @param array $spentInfo
|
||||||
|
* @param TransactionCurrency $currency
|
||||||
|
*
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
private function findInSpentArray(array $spentInfo, TransactionCurrency $currency): float
|
||||||
|
{
|
||||||
|
foreach ($spentInfo as $array) {
|
||||||
|
if ($array['currency_id'] === $currency->id) {
|
||||||
|
return $array['amount'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getBalanceInformation(Carbon $start, Carbon $end): array
|
||||||
|
{
|
||||||
|
// prep some arrays:
|
||||||
|
$incomes = [];
|
||||||
|
$expenses = [];
|
||||||
|
$sums = [];
|
||||||
|
$return = [];
|
||||||
|
|
||||||
|
// collect income of user:
|
||||||
|
/** @var TransactionCollectorInterface $collector */
|
||||||
|
$collector = app(TransactionCollectorInterface::class);
|
||||||
|
$collector->setAllAssetAccounts()->setRange($start, $end)
|
||||||
|
->setTypes([TransactionType::DEPOSIT])
|
||||||
|
->withOpposingAccount();
|
||||||
|
$set = $collector->getTransactions();
|
||||||
|
/** @var Transaction $transaction */
|
||||||
|
foreach ($set as $transaction) {
|
||||||
|
$currencyId = (int)$transaction->transaction_currency_id;
|
||||||
|
$incomes[$currencyId] = $incomes[$currencyId] ?? '0';
|
||||||
|
$incomes[$currencyId] = bcadd($incomes[$currencyId], $transaction->transaction_amount);
|
||||||
|
$sums[$currencyId] = $sums[$currencyId] ?? '0';
|
||||||
|
$sums[$currencyId] = bcadd($sums[$currencyId], $transaction->transaction_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect expenses:
|
||||||
|
/** @var TransactionCollectorInterface $collector */
|
||||||
|
$collector = app(TransactionCollectorInterface::class);
|
||||||
|
$collector->setAllAssetAccounts()->setRange($start, $end)
|
||||||
|
->setTypes([TransactionType::WITHDRAWAL])
|
||||||
|
->withOpposingAccount();
|
||||||
|
$set = $collector->getTransactions();
|
||||||
|
/** @var Transaction $transaction */
|
||||||
|
foreach ($set as $transaction) {
|
||||||
|
$currencyId = (int)$transaction->transaction_currency_id;
|
||||||
|
$expenses[$currencyId] = $expenses[$currencyId] ?? '0';
|
||||||
|
$expenses[$currencyId] = bcadd($expenses[$currencyId], $transaction->transaction_amount);
|
||||||
|
$sums[$currencyId] = $sums[$currencyId] ?? '0';
|
||||||
|
$sums[$currencyId] = bcadd($sums[$currencyId], $transaction->transaction_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// format amounts:
|
||||||
|
$keys = array_keys($sums);
|
||||||
|
foreach ($keys as $currencyId) {
|
||||||
|
$currency = $this->currencyRepos->findNull($currencyId);
|
||||||
|
if (null === $currency) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// create objects for big array.
|
||||||
|
$return[] = [
|
||||||
|
'key' => sprintf('balance-in-%s', $currency->code),
|
||||||
|
'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]),
|
||||||
|
'monetary_value' => round($sums[$currencyId] ?? 0, $currency->decimal_places),
|
||||||
|
'currency_id' => $currency->id,
|
||||||
|
'currency_code' => $currency->code,
|
||||||
|
'currency_symbol' => $currency->symbol,
|
||||||
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
|
'value_parsed' => app('amount')->formatAnything($currency, $sums[$currencyId] ?? '0', false),
|
||||||
|
'local_icon' => 'balance-scale',
|
||||||
|
'sub_title' => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false) .
|
||||||
|
' + ' . app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false),
|
||||||
|
];
|
||||||
|
$return[] = [
|
||||||
|
'key' => sprintf('spent-in-%s', $currency->code),
|
||||||
|
'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
|
||||||
|
'monetary_value' => round($expenses[$currencyId] ?? 0, $currency->decimal_places),
|
||||||
|
'currency_id' => $currency->id,
|
||||||
|
'currency_code' => $currency->code,
|
||||||
|
'currency_symbol' => $currency->symbol,
|
||||||
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
|
'value_parsed' => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false),
|
||||||
|
'local_icon' => 'balance-scale',
|
||||||
|
'sub_title' => '',
|
||||||
|
];
|
||||||
|
$return[] = [
|
||||||
|
'key' => sprintf('earned-in-%s', $currency->code),
|
||||||
|
'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
|
||||||
|
'monetary_value' => round($incomes[$currencyId] ?? 0, $currency->decimal_places),
|
||||||
|
'currency_id' => $currency->id,
|
||||||
|
'currency_code' => $currency->code,
|
||||||
|
'currency_symbol' => $currency->symbol,
|
||||||
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
|
'value_parsed' => app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false),
|
||||||
|
'local_icon' => 'balance-scale',
|
||||||
|
'sub_title' => '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getBillInformation(Carbon $start, Carbon $end): array
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Since both this method and the chart use the exact same data, we can suffice
|
||||||
|
* with calling the one method in the bill repository that will get this amount.
|
||||||
|
*/
|
||||||
|
$paidAmount = $this->billRepository->getBillsPaidInRangePerCurrency($start, $end);
|
||||||
|
$unpaidAmount = $this->billRepository->getBillsUnpaidInRangePerCurrency($start, $end);
|
||||||
|
$return = [];
|
||||||
|
foreach ($paidAmount as $currencyId => $amount) {
|
||||||
|
$amount = bcmul($amount, '-1');
|
||||||
|
$currency = $this->currencyRepos->findNull((int)$currencyId);
|
||||||
|
if (null === $currency) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$return[] = [
|
||||||
|
'key' => sprintf('bills-paid-in-%s', $currency->code),
|
||||||
|
'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $currency->symbol]),
|
||||||
|
'monetary_value' => round($amount, $currency->decimal_places),
|
||||||
|
'currency_id' => $currency->id,
|
||||||
|
'currency_code' => $currency->code,
|
||||||
|
'currency_symbol' => $currency->symbol,
|
||||||
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
|
'value_parsed' => app('amount')->formatAnything($currency, $amount, false),
|
||||||
|
'local_icon' => 'check',
|
||||||
|
'sub_title' => '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($unpaidAmount as $currencyId => $amount) {
|
||||||
|
$amount = bcmul($amount, '-1');
|
||||||
|
$currency = $this->currencyRepos->findNull((int)$currencyId);
|
||||||
|
if (null === $currency) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$return[] = [
|
||||||
|
'key' => sprintf('bills-unpaid-in-%s', $currency->code),
|
||||||
|
'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $currency->symbol]),
|
||||||
|
'monetary_value' => round($amount, $currency->decimal_places),
|
||||||
|
'currency_id' => $currency->id,
|
||||||
|
'currency_code' => $currency->code,
|
||||||
|
'currency_symbol' => $currency->symbol,
|
||||||
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
|
'value_parsed' => app('amount')->formatAnything($currency, $amount, false),
|
||||||
|
'local_icon' => 'calendar-o',
|
||||||
|
'sub_title' => '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getLeftToSpendInfo(Carbon $start, Carbon $end): array
|
||||||
|
{
|
||||||
|
$return = [];
|
||||||
|
$today = new Carbon;
|
||||||
|
$available = $this->budgetRepository->getAvailableBudgetWithCurrency($start, $end);
|
||||||
|
$budgets = $this->budgetRepository->getActiveBudgets();
|
||||||
|
$spentInfo = $this->budgetRepository->spentInPeriodMc($budgets, new Collection, $start, $end);
|
||||||
|
foreach ($available as $currencyId => $amount) {
|
||||||
|
$currency = $this->currencyRepos->findNull($currencyId);
|
||||||
|
if (null === $currency) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$spentInCurrency = (string)$this->findInSpentArray($spentInfo, $currency);
|
||||||
|
$leftToSpend = bcadd($amount, $spentInCurrency);
|
||||||
|
|
||||||
|
$days = $today->diffInDays($end) + 1;
|
||||||
|
$perDay = '0';
|
||||||
|
if (0 !== $days && bccomp($leftToSpend, '0') > -1) {
|
||||||
|
$perDay = bcdiv($leftToSpend, (string)$days);
|
||||||
|
}
|
||||||
|
|
||||||
|
$return[] = [
|
||||||
|
'key' => sprintf('left-to-spend-in-%s', $currency->code),
|
||||||
|
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $currency->symbol]),
|
||||||
|
'monetary_value' => round($leftToSpend, $currency->decimal_places),
|
||||||
|
'currency_id' => $currency->id,
|
||||||
|
'currency_code' => $currency->code,
|
||||||
|
'currency_symbol' => $currency->symbol,
|
||||||
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
|
'value_parsed' => app('amount')->formatAnything($currency, $leftToSpend, false),
|
||||||
|
'local_icon' => 'money',
|
||||||
|
'sub_title' => (string)trans('firefly.box_spend_per_day', ['amount' => app('amount')->formatAnything($currency, $perDay, false)]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getNetWorthInfo(Carbon $start, Carbon $end): array
|
||||||
|
{
|
||||||
|
$date = Carbon::create()->startOfDay();
|
||||||
|
|
||||||
|
// start and end in the future? use $end
|
||||||
|
if ($this->notInDateRange($date, $start, $end)) {
|
||||||
|
/** @var Carbon $date */
|
||||||
|
$date = session('end', Carbon::now()->endOfMonth());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var NetWorthInterface $netWorthHelper */
|
||||||
|
$netWorthHelper = app(NetWorthInterface::class);
|
||||||
|
$netWorthHelper->setUser(auth()->user());
|
||||||
|
$allAccounts = $this->accountRepository->getActiveAccountsByType([AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE]);
|
||||||
|
|
||||||
|
// filter list on preference of being included.
|
||||||
|
$filtered = $allAccounts->filter(
|
||||||
|
function (Account $account) {
|
||||||
|
$includeNetWorth = $this->accountRepository->getMetaValue($account, 'include_net_worth');
|
||||||
|
|
||||||
|
return null === $includeNetWorth ? true : '1' === $includeNetWorth;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$netWorthSet = $netWorthHelper->getNetWorthByCurrency($filtered, $date);
|
||||||
|
$return = [];
|
||||||
|
foreach ($netWorthSet as $index => $data) {
|
||||||
|
/** @var TransactionCurrency $currency */
|
||||||
|
$currency = $data['currency'];
|
||||||
|
$amount = round($data['balance'], $currency->decimal_places);
|
||||||
|
if ($amount === 0.0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// return stuff
|
||||||
|
$return[] = [
|
||||||
|
'key' => sprintf('net-worth-in-%s', $currency->code),
|
||||||
|
'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $currency->symbol]),
|
||||||
|
'monetary_value' => $amount,
|
||||||
|
'currency_id' => $currency->id,
|
||||||
|
'currency_code' => $currency->code,
|
||||||
|
'currency_symbol' => $currency->symbol,
|
||||||
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
|
'value_parsed' => app('amount')->formatAnything($currency, $data['balance'], false),
|
||||||
|
'local_icon' => 'line-chart',
|
||||||
|
'sub_title' => '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -23,7 +23,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Api\V1\Controllers;
|
namespace FireflyIII\Api\V1\Controllers;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Api\V1\Requests\TagRequest;
|
use FireflyIII\Api\V1\Requests\TagRequest;
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
||||||
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
||||||
use FireflyIII\Models\Tag;
|
use FireflyIII\Models\Tag;
|
||||||
@@ -71,6 +73,55 @@ class TagController extends Controller
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function cloud(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
// parameters for cloud:
|
||||||
|
$start = (string)$request->get('start');
|
||||||
|
$end = (string)$request->get('end');
|
||||||
|
if ('' === $start || '' === $end) {
|
||||||
|
throw new FireflyException('Start and end are mandatory parameters.');
|
||||||
|
}
|
||||||
|
$start = Carbon::createFromFormat('Y-m-d', $start);
|
||||||
|
$end = Carbon::createFromFormat('Y-m-d', $end);
|
||||||
|
|
||||||
|
// get all tags:
|
||||||
|
$tags = $this->repository->get();
|
||||||
|
$min = null;
|
||||||
|
$max = 0;
|
||||||
|
$return = [
|
||||||
|
'tags' => [],
|
||||||
|
];
|
||||||
|
/** @var Tag $tag */
|
||||||
|
foreach ($tags as $tag) {
|
||||||
|
$earned = (float)$this->repository->earnedInPeriod($tag, $start, $end);
|
||||||
|
$spent = (float)$this->repository->spentInPeriod($tag, $start, $end);
|
||||||
|
$size = ($spent * -1) + $earned;
|
||||||
|
$min = $min ?? $size;
|
||||||
|
if ($size > 0) {
|
||||||
|
$max = $size > $max ? $size : $max;
|
||||||
|
$return['tags'][] = [
|
||||||
|
'tag' => $tag->tag,
|
||||||
|
'id' => $tag->id,
|
||||||
|
'size' => $size,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($return['tags'] as $index => $info) {
|
||||||
|
$return['tags'][$index]['relative'] = $return['tags'][$index]['size'] / $max;
|
||||||
|
}
|
||||||
|
$return['min'] = $min;
|
||||||
|
$return['max'] = $max;
|
||||||
|
|
||||||
|
|
||||||
|
return response()->json($return);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the resource.
|
* Delete the resource.
|
||||||
*
|
*
|
||||||
@@ -121,7 +172,6 @@ class TagController extends Controller
|
|||||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||||
|
|
||||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ class TransactionController extends Controller
|
|||||||
$data['user'] = auth()->user()->id;
|
$data['user'] = auth()->user()->id;
|
||||||
$journal = $repository->store($data);
|
$journal = $repository->store($data);
|
||||||
|
|
||||||
event(new StoredTransactionJournal($journal, 0));
|
event(new StoredTransactionJournal($journal));
|
||||||
|
|
||||||
$manager = new Manager();
|
$manager = new Manager();
|
||||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ class TagRequest extends Request
|
|||||||
'description' => 'min:1|nullable',
|
'description' => 'min:1|nullable',
|
||||||
'date' => 'date|nullable',
|
'date' => 'date|nullable',
|
||||||
'latitude' => 'numeric|min:-90|max:90|nullable|required_with:longitude',
|
'latitude' => 'numeric|min:-90|max:90|nullable|required_with:longitude',
|
||||||
'longitude' => 'numeric|min:-90|max:90|nullable|required_with:latitude',
|
'longitude' => 'numeric|min:-180|max:180|nullable|required_with:latitude',
|
||||||
'zoom_level' => 'numeric|min:0|max:80|nullable',
|
'zoom_level' => 'numeric|min:0|max:80|nullable',
|
||||||
];
|
];
|
||||||
switch ($this->method()) {
|
switch ($this->method()) {
|
||||||
|
|||||||
@@ -1,5 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ApplyRules.php
|
||||||
|
* Copyright (c) 2018 thegrumpydictator@gmail.com
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III.
|
||||||
|
*
|
||||||
|
* Firefly III is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Firefly III 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Console\Commands;
|
namespace FireflyIII\Console\Commands;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
|||||||
@@ -1,5 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cron.php
|
||||||
|
* Copyright (c) 2018 thegrumpydictator@gmail.com
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III.
|
||||||
|
*
|
||||||
|
* Firefly III is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Firefly III 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Console\Commands;
|
namespace FireflyIII\Console\Commands;
|
||||||
|
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
|||||||
116
app/Console/Commands/DecryptDatabase.php
Normal file
116
app/Console/Commands/DecryptDatabase.php
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace FireflyIII\Console\Commands;
|
||||||
|
|
||||||
|
use Crypt;
|
||||||
|
use DB;
|
||||||
|
use FireflyIII\Support\Facades\FireflyConfig;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Contracts\Encryption\DecryptException;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Class DecryptDatabase
|
||||||
|
*/
|
||||||
|
class DecryptDatabase extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Decrypts the database.';
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'firefly:decrypt-all';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$this->line('Going to decrypt the database.');
|
||||||
|
$tables = [
|
||||||
|
'accounts' => ['name', 'iban'],
|
||||||
|
'attachments' => ['filename', 'mime', 'title', 'description'],
|
||||||
|
'bills' => ['name', 'match'],
|
||||||
|
'budgets' => ['name'],
|
||||||
|
'categories' => ['name'],
|
||||||
|
'piggy_banks' => ['name'],
|
||||||
|
'preferences' => ['data'],
|
||||||
|
'tags' => ['tag', 'description'],
|
||||||
|
'transaction_journals' => ['description'],
|
||||||
|
'transactions' => ['description'],
|
||||||
|
'journal_links' => ['comment'],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($tables as $table => $fields) {
|
||||||
|
if ($this->isDecrypted($table)) {
|
||||||
|
$this->info(sprintf('No decryption required for table "%s".', $table));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
$rows = DB::table($table)->get(['id', $field]);
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$original = $row->$field;
|
||||||
|
if (null === $original) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$id = $row->id;
|
||||||
|
$value = $this->tryDecrypt($original);
|
||||||
|
if ($value !== $original) {
|
||||||
|
Log::debug(sprintf('Decrypted field "%s" "%s" to "%s" in table "%s" (row #%d)', $field, $original, $value, $table, $id));
|
||||||
|
DB::table($table)->where('id', $id)->update([$field => $value]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->line(sprintf('Decrypted the data in table "%s".', $table));
|
||||||
|
// mark as decrypted:
|
||||||
|
$configName = sprintf('is_decrypted_%s', $table);
|
||||||
|
FireflyConfig::set($configName, true);
|
||||||
|
|
||||||
|
}
|
||||||
|
$this->info('Done!');
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $table
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function isDecrypted(string $table): bool
|
||||||
|
{
|
||||||
|
$configName = sprintf('is_decrypted_%s', $table);
|
||||||
|
$configVar = FireflyConfig::get($configName, false);
|
||||||
|
if (null !== $configVar) {
|
||||||
|
return $configVar->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $value
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
private function tryDecrypt($value)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$value = Crypt::decrypt($value);
|
||||||
|
} catch (DecryptException $e) {
|
||||||
|
//Log::debug(sprintf('Could not decrypt. %s', $e->getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* UseEncryption.php
|
|
||||||
* Copyright (c) 2018 thegrumpydictator@gmail.com
|
|
||||||
*
|
|
||||||
* This file is part of Firefly III.
|
|
||||||
*
|
|
||||||
* Firefly III is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Firefly III 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace FireflyIII\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class UseEncryption.
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
class UseEncryption extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The console command description.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'This command will make sure that entries in the database will be encrypted (or not) according to the settings in .env';
|
|
||||||
/**
|
|
||||||
* The name and signature of the console command.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'firefly:use-encryption';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the console command.
|
|
||||||
*/
|
|
||||||
public function handle(): int
|
|
||||||
{
|
|
||||||
if (true === config('firefly.encryption')) {
|
|
||||||
$this->info('Firefly III configuration calls for encrypted data.');
|
|
||||||
}
|
|
||||||
if (false === config('firefly.encryption')) {
|
|
||||||
$this->info('Firefly III configuration calls for unencrypted data.');
|
|
||||||
}
|
|
||||||
$this->handleObjects('Account', 'name', 'encrypted');
|
|
||||||
$this->handleObjects('Bill', 'name', 'name_encrypted');
|
|
||||||
$this->handleObjects('Bill', 'match', 'match_encrypted');
|
|
||||||
$this->handleObjects('Budget', 'name', 'encrypted');
|
|
||||||
$this->handleObjects('Category', 'name', 'encrypted');
|
|
||||||
$this->handleObjects('PiggyBank', 'name', 'encrypted');
|
|
||||||
$this->handleObjects('TransactionJournal', 'description', 'encrypted');
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run each object and encrypt them (or not).
|
|
||||||
*
|
|
||||||
* @param string $class
|
|
||||||
* @param string $field
|
|
||||||
* @param string $indicator
|
|
||||||
*/
|
|
||||||
public function handleObjects(string $class, string $field, string $indicator): void
|
|
||||||
{
|
|
||||||
$fqn = sprintf('FireflyIII\Models\%s', $class);
|
|
||||||
$encrypt = true === config('firefly.encryption') ? 0 : 1;
|
|
||||||
/** @noinspection PhpUndefinedMethodInspection */
|
|
||||||
$set = $fqn::where($indicator, $encrypt)->withTrashed()->get();
|
|
||||||
|
|
||||||
foreach ($set as $entry) {
|
|
||||||
$newName = $entry->$field;
|
|
||||||
$entry->$field = $newName;
|
|
||||||
/** @noinspection PhpUndefinedMethodInspection */
|
|
||||||
$entry->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @noinspection PhpUndefinedMethodInspection */
|
|
||||||
$this->line(sprintf('Updated %d %s.', $set->count(), strtolower(Str::plural($class))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -450,12 +450,6 @@ class VerifyDatabase extends Command
|
|||||||
/** @var stdClass $entry */
|
/** @var stdClass $entry */
|
||||||
foreach ($set as $entry) {
|
foreach ($set as $entry) {
|
||||||
$objName = $entry->name;
|
$objName = $entry->name;
|
||||||
try {
|
|
||||||
$objName = Crypt::decrypt($objName);
|
|
||||||
} catch (DecryptException $e) {
|
|
||||||
// it probably was not encrypted.
|
|
||||||
Log::debug(sprintf('Not a problem: %s', $e->getMessage()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// also count the transactions:
|
// also count the transactions:
|
||||||
$countTransactions = DB::table('budget_transaction')->where('budget_id', $entry->id)->count();
|
$countTransactions = DB::table('budget_transaction')->where('budget_id', $entry->id)->count();
|
||||||
@@ -488,12 +482,6 @@ class VerifyDatabase extends Command
|
|||||||
/** @var stdClass $entry */
|
/** @var stdClass $entry */
|
||||||
foreach ($set as $entry) {
|
foreach ($set as $entry) {
|
||||||
$objName = $entry->name;
|
$objName = $entry->name;
|
||||||
try {
|
|
||||||
$objName = Crypt::decrypt($objName);
|
|
||||||
} catch (DecryptException $e) {
|
|
||||||
// it probably was not encrypted.
|
|
||||||
Log::debug(sprintf('Not a problem: %s', $e->getMessage()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// also count the transactions:
|
// also count the transactions:
|
||||||
$countTransactions = DB::table('category_transaction')->where('category_id', $entry->id)->count();
|
$countTransactions = DB::table('category_transaction')->where('category_id', $entry->id)->count();
|
||||||
@@ -627,12 +615,6 @@ class VerifyDatabase extends Command
|
|||||||
/** @var stdClass $entry */
|
/** @var stdClass $entry */
|
||||||
foreach ($set as $entry) {
|
foreach ($set as $entry) {
|
||||||
$objName = $entry->name;
|
$objName = $entry->name;
|
||||||
try {
|
|
||||||
$objName = Crypt::decrypt($objName);
|
|
||||||
} catch (DecryptException $e) {
|
|
||||||
// it probably was not encrypted.
|
|
||||||
Log::debug(sprintf('Not a problem: %s', $e->getMessage()));
|
|
||||||
}
|
|
||||||
|
|
||||||
$line = sprintf(
|
$line = sprintf(
|
||||||
'User #%d (%s) has %s #%d ("%s") which has no transactions.',
|
'User #%d (%s) has %s #%d ("%s") which has no transactions.',
|
||||||
|
|||||||
@@ -1,4 +1,25 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RequestedReportOnJournals.php
|
||||||
|
* Copyright (c) 2018 thegrumpydictator@gmail.com
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III.
|
||||||
|
*
|
||||||
|
* Firefly III is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Firefly III 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
/**
|
/**
|
||||||
* RequestedReportOnJournals.php
|
* RequestedReportOnJournals.php
|
||||||
|
|||||||
@@ -38,18 +38,14 @@ class StoredTransactionJournal extends Event
|
|||||||
|
|
||||||
/** @var TransactionJournal The journal that was stored. */
|
/** @var TransactionJournal The journal that was stored. */
|
||||||
public $journal;
|
public $journal;
|
||||||
/** @var int The piggy bank ID. */
|
|
||||||
public $piggyBankId;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new event instance.
|
* Create a new event instance.
|
||||||
*
|
*
|
||||||
* @param TransactionJournal $journal
|
* @param TransactionJournal $journal
|
||||||
* @param int $piggyBankId
|
|
||||||
*/
|
*/
|
||||||
public function __construct(TransactionJournal $journal, int $piggyBankId)
|
public function __construct(TransactionJournal $journal)
|
||||||
{
|
{
|
||||||
$this->journal = $journal;
|
$this->journal = $journal;
|
||||||
$this->piggyBankId = $piggyBankId;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,14 +148,14 @@ final class Entry
|
|||||||
|
|
||||||
$entry->transaction_type = $transaction->transaction_type_type;
|
$entry->transaction_type = $transaction->transaction_type_type;
|
||||||
$entry->asset_account_id = (string)$transaction->account_id;
|
$entry->asset_account_id = (string)$transaction->account_id;
|
||||||
$entry->asset_account_name = app('steam')->tryDecrypt($transaction->account_name);
|
$entry->asset_account_name = $transaction->account_name;
|
||||||
$entry->asset_account_iban = $transaction->account_iban;
|
$entry->asset_account_iban = $transaction->account_iban;
|
||||||
$entry->asset_account_number = $transaction->account_number;
|
$entry->asset_account_number = $transaction->account_number;
|
||||||
$entry->asset_account_bic = $transaction->account_bic;
|
$entry->asset_account_bic = $transaction->account_bic;
|
||||||
$entry->asset_currency_code = $transaction->account_currency_code;
|
$entry->asset_currency_code = $transaction->account_currency_code;
|
||||||
|
|
||||||
$entry->opposing_account_id = (string)$transaction->opposing_account_id;
|
$entry->opposing_account_id = (string)$transaction->opposing_account_id;
|
||||||
$entry->opposing_account_name = app('steam')->tryDecrypt($transaction->opposing_account_name);
|
$entry->opposing_account_name = $transaction->opposing_account_name;
|
||||||
$entry->opposing_account_iban = $transaction->opposing_account_iban;
|
$entry->opposing_account_iban = $transaction->opposing_account_iban;
|
||||||
$entry->opposing_account_number = $transaction->opposing_account_number;
|
$entry->opposing_account_number = $transaction->opposing_account_number;
|
||||||
$entry->opposing_account_bic = $transaction->opposing_account_bic;
|
$entry->opposing_account_bic = $transaction->opposing_account_bic;
|
||||||
@@ -163,23 +163,23 @@ final class Entry
|
|||||||
|
|
||||||
// budget
|
// budget
|
||||||
$entry->budget_id = (string)$transaction->transaction_budget_id;
|
$entry->budget_id = (string)$transaction->transaction_budget_id;
|
||||||
$entry->budget_name = app('steam')->tryDecrypt($transaction->transaction_budget_name);
|
$entry->budget_name = $transaction->transaction_budget_name;
|
||||||
if (null === $transaction->transaction_budget_id) {
|
if (null === $transaction->transaction_budget_id) {
|
||||||
$entry->budget_id = $transaction->transaction_journal_budget_id;
|
$entry->budget_id = $transaction->transaction_journal_budget_id;
|
||||||
$entry->budget_name = app('steam')->tryDecrypt($transaction->transaction_journal_budget_name);
|
$entry->budget_name = $transaction->transaction_journal_budget_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// category
|
// category
|
||||||
$entry->category_id = (string)$transaction->transaction_category_id;
|
$entry->category_id = (string)$transaction->transaction_category_id;
|
||||||
$entry->category_name = app('steam')->tryDecrypt($transaction->transaction_category_name);
|
$entry->category_name = $transaction->transaction_category_name;
|
||||||
if (null === $transaction->transaction_category_id) {
|
if (null === $transaction->transaction_category_id) {
|
||||||
$entry->category_id = $transaction->transaction_journal_category_id;
|
$entry->category_id = $transaction->transaction_journal_category_id;
|
||||||
$entry->category_name = app('steam')->tryDecrypt($transaction->transaction_journal_category_name);
|
$entry->category_name = $transaction->transaction_journal_category_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// budget
|
// budget
|
||||||
$entry->bill_id = (string)$transaction->bill_id;
|
$entry->bill_id = (string)$transaction->bill_id;
|
||||||
$entry->bill_name = app('steam')->tryDecrypt($transaction->bill_name);
|
$entry->bill_name = $transaction->bill_name;
|
||||||
|
|
||||||
$entry->tags = $transaction->tags;
|
$entry->tags = $transaction->tags;
|
||||||
$entry->notes = $transaction->notes;
|
$entry->notes = $transaction->notes;
|
||||||
|
|||||||
@@ -368,7 +368,7 @@ class ExpandedProcessor implements ProcessorInterface
|
|||||||
foreach ($set as $entry) {
|
foreach ($set as $entry) {
|
||||||
$id = (int)$entry->transaction_journal_id;
|
$id = (int)$entry->transaction_journal_id;
|
||||||
$result[$id] = $result[$id] ?? [];
|
$result[$id] = $result[$id] ?? [];
|
||||||
$result[$id][] = Crypt::decrypt($entry->tag);
|
$result[$id][] = $entry->tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
|
|||||||
@@ -58,20 +58,19 @@ class TagFactory
|
|||||||
{
|
{
|
||||||
$zoomLevel = 0 === (int)$data['zoom_level'] ? null : (int)$data['zoom_level'];
|
$zoomLevel = 0 === (int)$data['zoom_level'] ? null : (int)$data['zoom_level'];
|
||||||
$latitude = 0.0 === (float)$data['latitude'] ? null : (float)$data['latitude'];
|
$latitude = 0.0 === (float)$data['latitude'] ? null : (float)$data['latitude'];
|
||||||
$longitude = 0.0 === (float)$data['longitude'] ? null : (int)$data['longitude'];
|
$longitude = 0.0 === (float)$data['longitude'] ? null : (float)$data['longitude'];
|
||||||
|
$array = [
|
||||||
return Tag::create(
|
|
||||||
[
|
|
||||||
'user_id' => $this->user->id,
|
'user_id' => $this->user->id,
|
||||||
'tag' => $data['tag'],
|
'tag' => trim($data['tag']),
|
||||||
'tagMode' => 'nothing',
|
'tagMode' => 'nothing',
|
||||||
'date' => $data['date'],
|
'date' => $data['date'],
|
||||||
'description' => $data['description'],
|
'description' => $data['description'],
|
||||||
'latitude' => $latitude,
|
'latitude' => $latitude,
|
||||||
'longitude ' => $longitude,
|
'longitude' => $longitude,
|
||||||
'zoomLevel' => $zoomLevel,
|
'zoomLevel' => $zoomLevel,
|
||||||
]
|
];
|
||||||
);
|
|
||||||
|
return Tag::create($array);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,6 +80,7 @@ class TagFactory
|
|||||||
*/
|
*/
|
||||||
public function findOrCreate(string $tag): ?Tag
|
public function findOrCreate(string $tag): ?Tag
|
||||||
{
|
{
|
||||||
|
$tag = trim($tag);
|
||||||
if (null === $this->tags) {
|
if (null === $this->tags) {
|
||||||
$this->tags = $this->user->tags()->get();
|
$this->tags = $this->user->tags()->get();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,6 +114,13 @@ class TransactionFactory
|
|||||||
$currency = $this->findCurrency($data['currency_id'], $data['currency_code']);
|
$currency = $this->findCurrency($data['currency_id'], $data['currency_code']);
|
||||||
$currency = $currency ?? $defaultCurrency;
|
$currency = $currency ?? $defaultCurrency;
|
||||||
|
|
||||||
|
// enable currency:
|
||||||
|
if(false === $currency->enabled) {
|
||||||
|
$currency->enabled = true;
|
||||||
|
$currency->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// type of source account and destination account depends on journal type:
|
// type of source account and destination account depends on journal type:
|
||||||
$sourceType = $this->accountType($journal, 'source');
|
$sourceType = $this->accountType($journal, 'source');
|
||||||
$destinationType = $this->accountType($journal, 'destination');
|
$destinationType = $this->accountType($journal, 'destination');
|
||||||
|
|||||||
@@ -304,26 +304,10 @@ class TransactionCollector implements TransactionCollectorInterface
|
|||||||
// run all filters:
|
// run all filters:
|
||||||
$set = $this->filter($set);
|
$set = $this->filter($set);
|
||||||
|
|
||||||
// loop for decryption.
|
// loop for date.
|
||||||
$set->each(
|
$set->each(
|
||||||
function (Transaction $transaction) {
|
function (Transaction $transaction) {
|
||||||
$transaction->date = new Carbon($transaction->date);
|
$transaction->date = new Carbon($transaction->date);
|
||||||
$transaction->description = app('steam')->decrypt((int)$transaction->encrypted, $transaction->description);
|
|
||||||
|
|
||||||
if (null !== $transaction->bill_name) {
|
|
||||||
$transaction->bill_name = app('steam')->decrypt((int)$transaction->bill_name_encrypted, $transaction->bill_name);
|
|
||||||
}
|
|
||||||
$transaction->account_name = app('steam')->tryDecrypt($transaction->account_name);
|
|
||||||
$transaction->opposing_account_name = app('steam')->tryDecrypt($transaction->opposing_account_name);
|
|
||||||
$transaction->account_iban = app('steam')->tryDecrypt($transaction->account_iban);
|
|
||||||
$transaction->opposing_account_iban = app('steam')->tryDecrypt($transaction->opposing_account_iban);
|
|
||||||
|
|
||||||
// budget name
|
|
||||||
$transaction->transaction_journal_budget_name = app('steam')->tryDecrypt($transaction->transaction_journal_budget_name);
|
|
||||||
$transaction->transaction_budget_name = app('steam')->tryDecrypt($transaction->transaction_budget_name);
|
|
||||||
// category name:
|
|
||||||
$transaction->transaction_journal_category_name = app('steam')->tryDecrypt($transaction->transaction_journal_category_name);
|
|
||||||
$transaction->transaction_category_name = app('steam')->tryDecrypt($transaction->transaction_category_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class FiscalHelper implements FiscalHelperInterface
|
|||||||
*/
|
*/
|
||||||
public function endOfFiscalYear(Carbon $date): Carbon
|
public function endOfFiscalYear(Carbon $date): Carbon
|
||||||
{
|
{
|
||||||
// get start of fiscal year for passed date
|
Log::debug(sprintf('Now in endOfFiscalYear(%s).', $date->format('Y-m-d')));
|
||||||
$endDate = $this->startOfFiscalYear($date);
|
$endDate = $this->startOfFiscalYear($date);
|
||||||
if (true === $this->useCustomFiscalYear) {
|
if (true === $this->useCustomFiscalYear) {
|
||||||
// add 1 year and sub 1 day
|
// add 1 year and sub 1 day
|
||||||
@@ -62,6 +62,7 @@ class FiscalHelper implements FiscalHelperInterface
|
|||||||
if (false === $this->useCustomFiscalYear) {
|
if (false === $this->useCustomFiscalYear) {
|
||||||
$endDate->endOfYear();
|
$endDate->endOfYear();
|
||||||
}
|
}
|
||||||
|
Log::debug(sprintf('Result of endOfFiscalYear(%s) = %s', $date->format('Y-m-d'), $endDate->format('Y-m-d')));
|
||||||
|
|
||||||
return $endDate;
|
return $endDate;
|
||||||
}
|
}
|
||||||
@@ -78,7 +79,7 @@ class FiscalHelper implements FiscalHelperInterface
|
|||||||
if (true === $this->useCustomFiscalYear) {
|
if (true === $this->useCustomFiscalYear) {
|
||||||
$prefStartStr = app('preferences')->get('fiscalYearStart', '01-01')->data;
|
$prefStartStr = app('preferences')->get('fiscalYearStart', '01-01')->data;
|
||||||
[$mth, $day] = explode('-', $prefStartStr);
|
[$mth, $day] = explode('-', $prefStartStr);
|
||||||
$startDate->month((int)$mth)->day((int)$day);
|
$startDate->day((int)$day)->month((int)$mth);
|
||||||
|
|
||||||
// if start date is after passed date, sub 1 year.
|
// if start date is after passed date, sub 1 year.
|
||||||
if ($startDate > $date) {
|
if ($startDate > $date) {
|
||||||
@@ -89,6 +90,8 @@ class FiscalHelper implements FiscalHelperInterface
|
|||||||
$startDate->startOfYear();
|
$startDate->startOfYear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log::debug(sprintf('Result of startOfFiscalYear(%s) = %s', $date->format('Y-m-d'), $startDate->format('Y-m-d')));
|
||||||
|
|
||||||
return $startDate;
|
return $startDate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,48 @@ class ResetPasswordController extends Controller
|
|||||||
$this->middleware('guest');
|
$this->middleware('guest');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the given user's password.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
|
||||||
|
* @throws \Illuminate\Validation\ValidationException
|
||||||
|
*/
|
||||||
|
public function reset(Request $request)
|
||||||
|
{
|
||||||
|
$loginProvider = config('firefly.login_provider');
|
||||||
|
if ('eloquent' !== $loginProvider) {
|
||||||
|
$message = sprintf('Cannot reset password when authenticating over "%s".', $loginProvider);
|
||||||
|
|
||||||
|
return view('error', compact('message'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$rules = [
|
||||||
|
'token' => 'required',
|
||||||
|
'email' => 'required|email',
|
||||||
|
'password' => 'required|confirmed|min:6|secure_password',
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->validate($request, $rules, $this->validationErrorMessages());
|
||||||
|
|
||||||
|
// Here we will attempt to reset the user's password. If it is successful we
|
||||||
|
// will update the password on an actual user model and persist it to the
|
||||||
|
// database. Otherwise we will parse the error and return the response.
|
||||||
|
$response = $this->broker()->reset(
|
||||||
|
$this->credentials($request), function ($user, $password) {
|
||||||
|
$this->resetPassword($user, $password);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// If the password was successfully reset, we will redirect the user back to
|
||||||
|
// the application's home authenticated view. If there is an error we can
|
||||||
|
// redirect them back to where they came from with their error message.
|
||||||
|
return $response === Password::PASSWORD_RESET
|
||||||
|
? $this->sendResetResponse($request, $response)
|
||||||
|
: $this->sendResetFailedResponse($request, $response);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the password reset view for the given token.
|
* Display the password reset view for the given token.
|
||||||
*
|
*
|
||||||
@@ -89,57 +131,7 @@ class ResetPasswordController extends Controller
|
|||||||
|
|
||||||
/** @noinspection PhpUndefinedFieldInspection */
|
/** @noinspection PhpUndefinedFieldInspection */
|
||||||
return view('auth.passwords.reset')->with(
|
return view('auth.passwords.reset')->with(
|
||||||
['token' => $token, 'email' => $request->email, 'allowRegistration' => $allowRegistration,'pageTitle' => $pageTitle]
|
['token' => $token, 'email' => $request->email, 'allowRegistration' => $allowRegistration, 'pageTitle' => $pageTitle]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the given user's password.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
|
|
||||||
* @throws \Illuminate\Validation\ValidationException
|
|
||||||
*/
|
|
||||||
public function reset(Request $request)
|
|
||||||
{
|
|
||||||
$loginProvider = config('firefly.login_provider');
|
|
||||||
if ('eloquent' !== $loginProvider) {
|
|
||||||
$message = sprintf('Cannot reset password when authenticating over "%s".', $loginProvider);
|
|
||||||
|
|
||||||
return view('error', compact('message'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->validate($request, $this->rules(), $this->validationErrorMessages());
|
|
||||||
|
|
||||||
// Here we will attempt to reset the user's password. If it is successful we
|
|
||||||
// will update the password on an actual user model and persist it to the
|
|
||||||
// database. Otherwise we will parse the error and return the response.
|
|
||||||
$response = $this->broker()->reset(
|
|
||||||
$this->credentials($request), function ($user, $password) {
|
|
||||||
$this->resetPassword($user, $password);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// If the password was successfully reset, we will redirect the user back to
|
|
||||||
// the application's home authenticated view. If there is an error we can
|
|
||||||
// redirect them back to where they came from with their error message.
|
|
||||||
return $response === Password::PASSWORD_RESET
|
|
||||||
? $this->sendResetResponse($request, $response)
|
|
||||||
: $this->sendResetFailedResponse($request, $response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the password reset validation rules.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function rules()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'token' => 'required',
|
|
||||||
'email' => 'required|email',
|
|
||||||
'password' => 'required|confirmed|min:6|secure_password',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,11 @@ use Carbon\Carbon;
|
|||||||
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
|
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
||||||
use FireflyIII\Http\Requests\BillFormRequest;
|
use FireflyIII\Http\Requests\BillFormRequest;
|
||||||
|
use FireflyIII\Models\Attachment;
|
||||||
use FireflyIII\Models\Bill;
|
use FireflyIII\Models\Bill;
|
||||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
use FireflyIII\TransactionRules\TransactionMatcher;
|
use FireflyIII\TransactionRules\TransactionMatcher;
|
||||||
|
use FireflyIII\Transformers\AttachmentTransformer;
|
||||||
use FireflyIII\Transformers\BillTransformer;
|
use FireflyIII\Transformers\BillTransformer;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -315,8 +317,21 @@ class BillController extends Controller
|
|||||||
$transactions = $collector->getPaginatedTransactions();
|
$transactions = $collector->getPaginatedTransactions();
|
||||||
$transactions->setPath(route('bills.show', [$bill->id]));
|
$transactions->setPath(route('bills.show', [$bill->id]));
|
||||||
|
|
||||||
|
// transform any attachments as well.
|
||||||
|
$collection = $this->billRepository->getAttachments($bill);
|
||||||
|
$attachments = new Collection;
|
||||||
|
if ($collection->count() > 0) {
|
||||||
|
/** @var AttachmentTransformer $transformer */
|
||||||
|
$transformer = app(AttachmentTransformer::class);
|
||||||
|
$attachments = $collection->each(
|
||||||
|
function (Attachment $attachment) use ($transformer) {
|
||||||
|
return $transformer->transform($attachment);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return view('bills.show', compact('transactions', 'rules', 'yearAverage', 'overallAverage', 'year', 'object', 'bill', 'subTitle'));
|
|
||||||
|
return view('bills.show', compact('attachments', 'transactions', 'rules', 'yearAverage', 'overallAverage', 'year', 'object', 'bill', 'subTitle'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,12 +31,10 @@ use FireflyIII\Http\Controllers\Controller;
|
|||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\Budget;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
|
||||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\Http\Controllers\AugumentData;
|
||||||
use FireflyIII\Support\Http\Controllers\PeriodOverview;
|
use FireflyIII\Support\Http\Controllers\PeriodOverview;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -44,10 +42,7 @@ use Illuminate\Support\Collection;
|
|||||||
*/
|
*/
|
||||||
class ShowController extends Controller
|
class ShowController extends Controller
|
||||||
{
|
{
|
||||||
use PeriodOverview;
|
use PeriodOverview, AugumentData;
|
||||||
|
|
||||||
/** @var BudgetRepositoryInterface The budget repository */
|
|
||||||
private $repository;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ShowController constructor.
|
* ShowController constructor.
|
||||||
@@ -62,7 +57,6 @@ class ShowController extends Controller
|
|||||||
function ($request, $next) {
|
function ($request, $next) {
|
||||||
app('view')->share('title', (string)trans('firefly.budgets'));
|
app('view')->share('title', (string)trans('firefly.budgets'));
|
||||||
app('view')->share('mainTitleIcon', 'fa-tasks');
|
app('view')->share('mainTitleIcon', 'fa-tasks');
|
||||||
$this->repository = app(BudgetRepositoryInterface::class);
|
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
@@ -203,39 +197,4 @@ class ShowController extends Controller
|
|||||||
|
|
||||||
return view('budgets.show', compact('limits', 'budget', 'budgetLimit', 'transactions', 'subTitle'));
|
return view('budgets.show', compact('limits', 'budget', 'budgetLimit', 'transactions', 'subTitle'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all budget limits for a budget.
|
|
||||||
*
|
|
||||||
* @param Budget $budget
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
*
|
|
||||||
* @return Collection
|
|
||||||
*/
|
|
||||||
protected function getLimits(Budget $budget, Carbon $start, Carbon $end): Collection // get data + augment with info
|
|
||||||
{
|
|
||||||
// properties for cache
|
|
||||||
$cache = new CacheProperties;
|
|
||||||
$cache->addProperty($start);
|
|
||||||
$cache->addProperty($end);
|
|
||||||
$cache->addProperty($budget->id);
|
|
||||||
$cache->addProperty('get-limits');
|
|
||||||
|
|
||||||
if ($cache->has()) {
|
|
||||||
return $cache->get(); // @codeCoverageIgnore
|
|
||||||
}
|
|
||||||
|
|
||||||
$set = $this->repository->getBudgetLimits($budget, $start, $end);
|
|
||||||
$limits = new Collection();
|
|
||||||
|
|
||||||
/** @var BudgetLimit $entry */
|
|
||||||
foreach ($set as $entry) {
|
|
||||||
$entry->spent = $this->repository->spentInPeriod(new Collection([$budget]), new Collection(), $entry->start_date, $entry->end_date);
|
|
||||||
$limits->push($entry);
|
|
||||||
}
|
|
||||||
$cache->store($limits);
|
|
||||||
|
|
||||||
return $set;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
|||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\Http\Controllers\PeriodOverview;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Log;
|
use Log;
|
||||||
@@ -41,7 +41,7 @@ use Log;
|
|||||||
*/
|
*/
|
||||||
class NoCategoryController extends Controller
|
class NoCategoryController extends Controller
|
||||||
{
|
{
|
||||||
|
use PeriodOverview;
|
||||||
/** @var JournalRepositoryInterface Journals and transactions overview */
|
/** @var JournalRepositoryInterface Journals and transactions overview */
|
||||||
private $journalRepos;
|
private $journalRepos;
|
||||||
|
|
||||||
@@ -135,92 +135,4 @@ class NoCategoryController extends Controller
|
|||||||
|
|
||||||
return view('categories.no-category', compact('transactions', 'subTitle', 'periods', 'start', 'end'));
|
return view('categories.no-category', compact('transactions', 'subTitle', 'periods', 'start', 'end'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show period overview for no category view.
|
|
||||||
*
|
|
||||||
* @param Carbon $theDate
|
|
||||||
*
|
|
||||||
* @return Collection
|
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
|
||||||
*/
|
|
||||||
protected function getNoCategoryPeriodOverview(Carbon $theDate): Collection // period overview method.
|
|
||||||
{
|
|
||||||
Log::debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d')));
|
|
||||||
$range = app('preferences')->get('viewRange', '1M')->data;
|
|
||||||
$first = $this->journalRepos->firstNull();
|
|
||||||
$start = null === $first ? new Carbon : $first->date;
|
|
||||||
$end = $theDate ?? new Carbon;
|
|
||||||
|
|
||||||
Log::debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d')));
|
|
||||||
Log::debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d')));
|
|
||||||
|
|
||||||
// properties for cache
|
|
||||||
$cache = new CacheProperties;
|
|
||||||
$cache->addProperty($start);
|
|
||||||
$cache->addProperty($end);
|
|
||||||
$cache->addProperty('no-category-period-entries');
|
|
||||||
|
|
||||||
if ($cache->has()) {
|
|
||||||
return $cache->get(); // @codeCoverageIgnore
|
|
||||||
}
|
|
||||||
|
|
||||||
$dates = app('navigation')->blockPeriods($start, $end, $range);
|
|
||||||
$entries = new Collection;
|
|
||||||
|
|
||||||
foreach ($dates as $date) {
|
|
||||||
|
|
||||||
// count journals without category in this period:
|
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
|
||||||
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutCategory()
|
|
||||||
->withOpposingAccount()->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]);
|
|
||||||
$collector->removeFilter(InternalTransferFilter::class);
|
|
||||||
$count = $collector->getTransactions()->count();
|
|
||||||
|
|
||||||
// amount transferred
|
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
|
||||||
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutCategory()
|
|
||||||
->withOpposingAccount()->setTypes([TransactionType::TRANSFER]);
|
|
||||||
$collector->removeFilter(InternalTransferFilter::class);
|
|
||||||
$transferred = app('steam')->positive((string)$collector->getTransactions()->sum('transaction_amount'));
|
|
||||||
|
|
||||||
// amount spent
|
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
|
||||||
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutCategory()->withOpposingAccount()->setTypes(
|
|
||||||
[TransactionType::WITHDRAWAL]
|
|
||||||
);
|
|
||||||
$spent = $collector->getTransactions()->sum('transaction_amount');
|
|
||||||
|
|
||||||
// amount earned
|
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
|
||||||
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutCategory()->withOpposingAccount()->setTypes(
|
|
||||||
[TransactionType::DEPOSIT]
|
|
||||||
);
|
|
||||||
$earned = $collector->getTransactions()->sum('transaction_amount');
|
|
||||||
/** @noinspection PhpUndefinedMethodInspection */
|
|
||||||
$dateStr = $date['end']->format('Y-m-d');
|
|
||||||
$dateName = app('navigation')->periodShow($date['end'], $date['period']);
|
|
||||||
$entries->push(
|
|
||||||
[
|
|
||||||
'string' => $dateStr,
|
|
||||||
'name' => $dateName,
|
|
||||||
'count' => $count,
|
|
||||||
'spent' => $spent,
|
|
||||||
'earned' => $earned,
|
|
||||||
'transferred' => $transferred,
|
|
||||||
'start' => clone $date['start'],
|
|
||||||
'end' => clone $date['end'],
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Log::debug('End of loops');
|
|
||||||
$cache->store($entries);
|
|
||||||
|
|
||||||
return $entries;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
|||||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use FireflyIII\Support\Http\Controllers\AugumentData;
|
use FireflyIII\Support\Http\Controllers\AugumentData;
|
||||||
|
use FireflyIII\Support\Http\Controllers\ChartGeneration;
|
||||||
use FireflyIII\Support\Http\Controllers\DateCalculation;
|
use FireflyIII\Support\Http\Controllers\DateCalculation;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -49,7 +50,7 @@ use Log;
|
|||||||
*/
|
*/
|
||||||
class AccountController extends Controller
|
class AccountController extends Controller
|
||||||
{
|
{
|
||||||
use DateCalculation, AugumentData;
|
use DateCalculation, AugumentData, ChartGeneration;
|
||||||
|
|
||||||
/** @var GeneratorInterface Chart generation methods. */
|
/** @var GeneratorInterface Chart generation methods. */
|
||||||
protected $generator;
|
protected $generator;
|
||||||
@@ -109,7 +110,7 @@ class AccountController extends Controller
|
|||||||
$tempData = [];
|
$tempData = [];
|
||||||
|
|
||||||
// grab all accounts and names
|
// grab all accounts and names
|
||||||
$accounts = $this->accountRepository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY]);
|
$accounts = $this->accountRepository->getAccountsByType([AccountType::EXPENSE]);
|
||||||
$accountNames = $this->extractNames($accounts);
|
$accountNames = $this->extractNames($accounts);
|
||||||
|
|
||||||
// grab all balances
|
// grab all balances
|
||||||
@@ -604,103 +605,4 @@ class AccountController extends Controller
|
|||||||
|
|
||||||
return response()->json($data);
|
return response()->json($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows an overview of the account balances for a set of accounts.
|
|
||||||
*
|
|
||||||
* @param Collection $accounts
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
|
||||||
*/
|
|
||||||
protected function accountBalanceChart(Collection $accounts, Carbon $start, Carbon $end): array // chart helper method.
|
|
||||||
{
|
|
||||||
// chart properties for cache:
|
|
||||||
$cache = new CacheProperties();
|
|
||||||
$cache->addProperty($start);
|
|
||||||
$cache->addProperty($end);
|
|
||||||
$cache->addProperty('chart.account.account-balance-chart');
|
|
||||||
$cache->addProperty($accounts);
|
|
||||||
if ($cache->has()) {
|
|
||||||
return $cache->get(); // @codeCoverageIgnore
|
|
||||||
}
|
|
||||||
Log::debug('Regenerate chart.account.account-balance-chart from scratch.');
|
|
||||||
|
|
||||||
/** @var CurrencyRepositoryInterface $repository */
|
|
||||||
$repository = app(CurrencyRepositoryInterface::class);
|
|
||||||
/** @var AccountRepositoryInterface $accountRepos */
|
|
||||||
$accountRepos = app(AccountRepositoryInterface::class);
|
|
||||||
|
|
||||||
$default = app('amount')->getDefaultCurrency();
|
|
||||||
$chartData = [];
|
|
||||||
/** @var Account $account */
|
|
||||||
foreach ($accounts as $account) {
|
|
||||||
$currency = $repository->findNull((int)$accountRepos->getMetaValue($account, 'currency_id'));
|
|
||||||
if (null === $currency) {
|
|
||||||
$currency = $default;
|
|
||||||
}
|
|
||||||
$currentSet = [
|
|
||||||
'label' => $account->name,
|
|
||||||
'currency_symbol' => $currency->symbol,
|
|
||||||
'entries' => [],
|
|
||||||
];
|
|
||||||
|
|
||||||
$currentStart = clone $start;
|
|
||||||
$range = app('steam')->balanceInRange($account, $start, clone $end);
|
|
||||||
$previous = array_values($range)[0];
|
|
||||||
while ($currentStart <= $end) {
|
|
||||||
$format = $currentStart->format('Y-m-d');
|
|
||||||
$label = $currentStart->formatLocalized((string)trans('config.month_and_day'));
|
|
||||||
$balance = isset($range[$format]) ? round($range[$format], 12) : $previous;
|
|
||||||
$previous = $balance;
|
|
||||||
$currentStart->addDay();
|
|
||||||
$currentSet['entries'][$label] = $balance;
|
|
||||||
}
|
|
||||||
$chartData[] = $currentSet;
|
|
||||||
}
|
|
||||||
$data = $this->generator->multiSet($chartData);
|
|
||||||
$cache->store($data);
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Small helper function for the revenue and expense account charts.
|
|
||||||
*
|
|
||||||
* @param array $names
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function expandNames(array $names): array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
foreach ($names as $entry) {
|
|
||||||
$result[$entry['name']] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Small helper function for the revenue and expense account charts.
|
|
||||||
*
|
|
||||||
* @param Collection $accounts
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function extractNames(Collection $accounts): array
|
|
||||||
{
|
|
||||||
$return = [];
|
|
||||||
/** @var Account $account */
|
|
||||||
foreach ($accounts as $account) {
|
|
||||||
$return[$account->id] = $account->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -476,154 +476,4 @@ class BudgetController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the amount of money budgeted in a period.
|
|
||||||
*
|
|
||||||
* @param Budget $budget
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function getBudgetedInPeriod(Budget $budget, Carbon $start, Carbon $end): array // get data + augment with info
|
|
||||||
{
|
|
||||||
$key = app('navigation')->preferredCarbonFormat($start, $end);
|
|
||||||
$range = app('navigation')->preferredRangeFormat($start, $end);
|
|
||||||
$current = clone $start;
|
|
||||||
$budgeted = [];
|
|
||||||
while ($current < $end) {
|
|
||||||
/** @var Carbon $currentStart */
|
|
||||||
$currentStart = app('navigation')->startOfPeriod($current, $range);
|
|
||||||
/** @var Carbon $currentEnd */
|
|
||||||
$currentEnd = app('navigation')->endOfPeriod($current, $range);
|
|
||||||
$budgetLimits = $this->repository->getBudgetLimits($budget, $currentStart, $currentEnd);
|
|
||||||
$index = $currentStart->format($key);
|
|
||||||
$budgeted[$index] = $budgetLimits->sum('amount');
|
|
||||||
$currentEnd->addDay();
|
|
||||||
$current = clone $currentEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $budgeted;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the expenses for a budget in a date range.
|
|
||||||
*
|
|
||||||
* @param Collection $limits
|
|
||||||
* @param Budget $budget
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
*/
|
|
||||||
protected function getExpensesForBudget(Collection $limits, Budget $budget, Carbon $start, Carbon $end): array // get data + augment with info
|
|
||||||
{
|
|
||||||
$return = [];
|
|
||||||
if (0 === $limits->count()) {
|
|
||||||
$spent = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end);
|
|
||||||
if (0 !== bccomp($spent, '0')) {
|
|
||||||
$return[$budget->name]['spent'] = bcmul($spent, '-1');
|
|
||||||
$return[$budget->name]['left'] = 0;
|
|
||||||
$return[$budget->name]['overspent'] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$rows = $this->spentInPeriodMulti($budget, $limits);
|
|
||||||
foreach ($rows as $name => $row) {
|
|
||||||
if (0 !== bccomp($row['spent'], '0') || 0 !== bccomp($row['left'], '0')) {
|
|
||||||
$return[$name] = $row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unset($rows);
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Returns an array with the following values:
|
|
||||||
* 0 =>
|
|
||||||
* 'name' => name of budget + repetition
|
|
||||||
* 'left' => left in budget repetition (always zero)
|
|
||||||
* 'overspent' => spent more than budget repetition? (always zero)
|
|
||||||
* 'spent' => actually spent in period for budget
|
|
||||||
* 1 => (etc)
|
|
||||||
*
|
|
||||||
* @param Budget $budget
|
|
||||||
* @param Collection $limits
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
protected function spentInPeriodMulti(Budget $budget, Collection $limits): array // get data + augment with info
|
|
||||||
{
|
|
||||||
$return = [];
|
|
||||||
$format = (string)trans('config.month_and_day');
|
|
||||||
$name = $budget->name;
|
|
||||||
/** @var BudgetLimit $budgetLimit */
|
|
||||||
foreach ($limits as $budgetLimit) {
|
|
||||||
$expenses = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $budgetLimit->start_date, $budgetLimit->end_date);
|
|
||||||
$expenses = app('steam')->positive($expenses);
|
|
||||||
|
|
||||||
if ($limits->count() > 1) {
|
|
||||||
$name = $budget->name . ' ' . trans(
|
|
||||||
'firefly.between_dates',
|
|
||||||
[
|
|
||||||
'start' => $budgetLimit->start_date->formatLocalized($format),
|
|
||||||
'end' => $budgetLimit->end_date->formatLocalized($format),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$amount = $budgetLimit->amount;
|
|
||||||
$leftInLimit = bcsub($amount, $expenses);
|
|
||||||
$hasOverspent = bccomp($leftInLimit, '0') === -1;
|
|
||||||
$left = $hasOverspent ? '0' : bcsub($amount, $expenses);
|
|
||||||
$spent = $hasOverspent ? $amount : $expenses;
|
|
||||||
$overspent = $hasOverspent ? app('steam')->positive($leftInLimit) : '0';
|
|
||||||
|
|
||||||
$return[$name] = [
|
|
||||||
'left' => $left,
|
|
||||||
'overspent' => $overspent,
|
|
||||||
'spent' => $spent,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array with the following values:
|
|
||||||
* 'name' => "no budget" in local language
|
|
||||||
* 'repetition_left' => left in budget repetition (always zero)
|
|
||||||
* 'repetition_overspent' => spent more than budget repetition? (always zero)
|
|
||||||
* 'spent' => actually spent in period for budget.
|
|
||||||
*
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function spentInPeriodWithout(Carbon $start, Carbon $end): string // get data + augment with info
|
|
||||||
{
|
|
||||||
// collector
|
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
|
||||||
$types = [TransactionType::WITHDRAWAL];
|
|
||||||
$collector->setAllAssetAccounts()->setTypes($types)->setRange($start, $end)->withoutBudget();
|
|
||||||
$transactions = $collector->getTransactions();
|
|
||||||
$sum = '0';
|
|
||||||
/** @var Transaction $entry */
|
|
||||||
foreach ($transactions as $entry) {
|
|
||||||
$sum = bcadd($entry->transaction_amount, $sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sum;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
|||||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
|
use FireflyIII\Support\Http\Controllers\AugumentData;
|
||||||
|
use FireflyIII\Support\Http\Controllers\ChartGeneration;
|
||||||
use FireflyIII\Support\Http\Controllers\DateCalculation;
|
use FireflyIII\Support\Http\Controllers\DateCalculation;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -42,7 +44,7 @@ use Log;
|
|||||||
*/
|
*/
|
||||||
class CategoryController extends Controller
|
class CategoryController extends Controller
|
||||||
{
|
{
|
||||||
use DateCalculation;
|
use DateCalculation, AugumentData, ChartGeneration;
|
||||||
/** @var GeneratorInterface Chart generation methods. */
|
/** @var GeneratorInterface Chart generation methods. */
|
||||||
protected $generator;
|
protected $generator;
|
||||||
|
|
||||||
@@ -156,7 +158,7 @@ class CategoryController extends Controller
|
|||||||
*
|
*
|
||||||
* @return JsonResponse
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function frontpage(CategoryRepositoryInterface $repository, AccountRepositoryInterface $accountRepository): JsonResponse
|
public function frontPage(CategoryRepositoryInterface $repository, AccountRepositoryInterface $accountRepository): JsonResponse
|
||||||
{
|
{
|
||||||
$start = session('start', Carbon::now()->startOfMonth());
|
$start = session('start', Carbon::now()->startOfMonth());
|
||||||
$end = session('end', Carbon::now()->endOfMonth());
|
$end = session('end', Carbon::now()->endOfMonth());
|
||||||
@@ -166,7 +168,7 @@ class CategoryController extends Controller
|
|||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
$cache->addProperty('chart.category.frontpage');
|
$cache->addProperty('chart.category.frontpage');
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return response()->json($cache->get()); // @codeCoverageIgnore
|
//return response()->json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
// currency repos:
|
// currency repos:
|
||||||
@@ -183,9 +185,11 @@ class CategoryController extends Controller
|
|||||||
/** @var Category $category */
|
/** @var Category $category */
|
||||||
foreach ($categories as $category) {
|
foreach ($categories as $category) {
|
||||||
$spentArray = $repository->spentInPeriodPerCurrency(new Collection([$category]), $accounts, $start, $end);
|
$spentArray = $repository->spentInPeriodPerCurrency(new Collection([$category]), $accounts, $start, $end);
|
||||||
foreach ($spentArray as $currencyId => $spent) {
|
foreach ($spentArray as $categoryId => $spentInfo) {
|
||||||
|
foreach($spentInfo['spent'] as $currencyId => $row) {
|
||||||
|
$spent= $row['spent'];
|
||||||
if (bccomp($spent, '0') === -1) {
|
if (bccomp($spent, '0') === -1) {
|
||||||
$currencies[$currencyId] = $currencies[$currencyId] ?? $currencyRepository->findNull($currencyId);
|
$currencies[$currencyId] = $currencies[$currencyId] ?? $currencyRepository->findNull((int)$currencyId);
|
||||||
$tempData[] = [
|
$tempData[] = [
|
||||||
'name' => $category->name,
|
'name' => $category->name,
|
||||||
'spent' => bcmul($spent, '-1'),
|
'spent' => bcmul($spent, '-1'),
|
||||||
@@ -195,14 +199,16 @@ class CategoryController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// no category per currency:
|
// no category per currency:
|
||||||
$noCategory = $repository->spentInPeriodPcWoCategory(new Collection, $start, $end);
|
$noCategory = $repository->spentInPeriodPcWoCategory(new Collection, $start, $end);
|
||||||
foreach ($noCategory as $currencyId => $spent) {
|
foreach ($noCategory as $currencyId => $spent) {
|
||||||
$currencies[$currencyId] = $currencies[$currencyId] ?? $currencyRepository->findNull($currencyId);
|
$currencies[$currencyId] = $currencies[$currencyId] ?? $currencyRepository->findNull($currencyId);
|
||||||
$tempData[] = [
|
$tempData[] = [
|
||||||
'name' => trans('firefly.no_category'),
|
'name' => trans('firefly.no_category'),
|
||||||
'spent' => bcmul($spent, '-1'),
|
'spent' => bcmul($spent['spent'], '-1'),
|
||||||
'spent_float' => (float)bcmul($spent, '-1'),
|
'spent_float' => (float)bcmul($spent['spent'], '-1'),
|
||||||
'currency_id' => $currencyId,
|
'currency_id' => $currencyId,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -392,108 +398,4 @@ class CategoryController extends Controller
|
|||||||
|
|
||||||
return response()->json($data);
|
return response()->json($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Chart for a specific period (start and end).
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param Category $category
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
|
||||||
*/
|
|
||||||
protected function makePeriodChart(Category $category, Carbon $start, Carbon $end): array // chart helper method.
|
|
||||||
{
|
|
||||||
$cache = new CacheProperties;
|
|
||||||
$cache->addProperty($start);
|
|
||||||
$cache->addProperty($end);
|
|
||||||
$cache->addProperty($category->id);
|
|
||||||
$cache->addProperty('chart.category.period-chart');
|
|
||||||
|
|
||||||
|
|
||||||
if ($cache->has()) {
|
|
||||||
//return $cache->get(); // @codeCoverageIgnore
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var AccountRepositoryInterface $accountRepository */
|
|
||||||
$accountRepository = app(AccountRepositoryInterface::class);
|
|
||||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
|
||||||
$repository = app(CategoryRepositoryInterface::class);
|
|
||||||
|
|
||||||
// chart data
|
|
||||||
$chartData = [
|
|
||||||
[
|
|
||||||
'label' => (string)trans('firefly.spent'),
|
|
||||||
'entries' => [],
|
|
||||||
'type' => 'bar',
|
|
||||||
'backgroundColor' => 'rgba(219, 68, 55, 0.5)', // red
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'label' => (string)trans('firefly.earned'),
|
|
||||||
'entries' => [],
|
|
||||||
'type' => 'bar',
|
|
||||||
'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'label' => (string)trans('firefly.sum'),
|
|
||||||
'entries' => [],
|
|
||||||
'type' => 'line',
|
|
||||||
'fill' => false,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
$step = $this->calculateStep($start, $end);
|
|
||||||
|
|
||||||
|
|
||||||
while ($start <= $end) {
|
|
||||||
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $start);
|
|
||||||
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $start);
|
|
||||||
$sum = bcadd($spent, $earned);
|
|
||||||
$label = trim(app('navigation')->periodShow($start, $step));
|
|
||||||
$chartData[0]['entries'][$label] = round(bcmul($spent, '-1'), 12);
|
|
||||||
$chartData[1]['entries'][$label] = round($earned, 12);
|
|
||||||
$chartData[2]['entries'][$label] = round($sum, 12);
|
|
||||||
|
|
||||||
switch ($step) {
|
|
||||||
default:
|
|
||||||
case '1D':
|
|
||||||
$start->addDay();
|
|
||||||
break;
|
|
||||||
case '1W':
|
|
||||||
$start->addDays(7);
|
|
||||||
break;
|
|
||||||
case '1M':
|
|
||||||
$start->addMonth();
|
|
||||||
break;
|
|
||||||
case '1Y':
|
|
||||||
$start->addYear();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = $this->generator->multiSet($chartData);
|
|
||||||
$cache->store($data);
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Small helper function for the revenue and expense account charts.
|
|
||||||
*
|
|
||||||
* @param array $names
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function expandNames(array $names): array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
foreach ($names as $entry) {
|
|
||||||
$result[$entry['name']] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ use FireflyIII\Helpers\Report\NetWorthInterface;
|
|||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
use FireflyIII\Models\Account;
|
use FireflyIII\Models\Account;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use FireflyIII\Support\Http\Controllers\BasicDataSupport;
|
use FireflyIII\Support\Http\Controllers\BasicDataSupport;
|
||||||
|
use FireflyIII\Support\Http\Controllers\ChartGeneration;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Log;
|
use Log;
|
||||||
@@ -40,7 +40,7 @@ use Log;
|
|||||||
*/
|
*/
|
||||||
class ReportController extends Controller
|
class ReportController extends Controller
|
||||||
{
|
{
|
||||||
use BasicDataSupport;
|
use BasicDataSupport, ChartGeneration;
|
||||||
/** @var GeneratorInterface Chart generation methods. */
|
/** @var GeneratorInterface Chart generation methods. */
|
||||||
protected $generator;
|
protected $generator;
|
||||||
|
|
||||||
@@ -97,7 +97,6 @@ class ReportController extends Controller
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
while ($current < $end) {
|
while ($current < $end) {
|
||||||
// get balances by date, grouped by currency.
|
// get balances by date, grouped by currency.
|
||||||
$result = $helper->getNetWorthByCurrency($filtered, $current);
|
$result = $helper->getNetWorthByCurrency($filtered, $current);
|
||||||
@@ -263,67 +262,4 @@ class ReportController extends Controller
|
|||||||
|
|
||||||
return response()->json($data);
|
return response()->json($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Collects the incomes and expenses for the given periods, grouped per month. Will cache its results.
|
|
||||||
*
|
|
||||||
* @param Collection $accounts
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
|
||||||
*/
|
|
||||||
protected function getChartData(Collection $accounts, Carbon $start, Carbon $end): array // chart helper function
|
|
||||||
{
|
|
||||||
$cache = new CacheProperties;
|
|
||||||
$cache->addProperty('chart.report.get-chart-data');
|
|
||||||
$cache->addProperty($start);
|
|
||||||
$cache->addProperty($accounts);
|
|
||||||
$cache->addProperty($end);
|
|
||||||
if ($cache->has()) {
|
|
||||||
return $cache->get(); // @codeCoverageIgnore
|
|
||||||
}
|
|
||||||
|
|
||||||
$currentStart = clone $start;
|
|
||||||
$spentArray = [];
|
|
||||||
$earnedArray = [];
|
|
||||||
|
|
||||||
/** @var AccountTaskerInterface $tasker */
|
|
||||||
$tasker = app(AccountTaskerInterface::class);
|
|
||||||
|
|
||||||
while ($currentStart <= $end) {
|
|
||||||
$currentEnd = app('navigation')->endOfPeriod($currentStart, '1M');
|
|
||||||
$earned = (string)array_sum(
|
|
||||||
array_map(
|
|
||||||
function ($item) {
|
|
||||||
return $item['sum'];
|
|
||||||
},
|
|
||||||
$tasker->getIncomeReport($currentStart, $currentEnd, $accounts)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$spent = (string)array_sum(
|
|
||||||
array_map(
|
|
||||||
function ($item) {
|
|
||||||
return $item['sum'];
|
|
||||||
},
|
|
||||||
$tasker->getExpenseReport($currentStart, $currentEnd, $accounts)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$label = $currentStart->format('Y-m') . '-01';
|
|
||||||
$spentArray[$label] = bcmul($spent, '-1');
|
|
||||||
$earnedArray[$label] = $earned;
|
|
||||||
$currentStart = app('navigation')->addPeriod($currentStart, '1M', 0);
|
|
||||||
}
|
|
||||||
$result = [
|
|
||||||
'spent' => $spentArray,
|
|
||||||
'earned' => $earnedArray,
|
|
||||||
];
|
|
||||||
$cache->store($result);
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,20 +25,12 @@ namespace FireflyIII\Http\Controllers\Json;
|
|||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
use FireflyIII\Models\Account;
|
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
|
||||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
|
||||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
|
||||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
|
||||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
|
||||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
|
||||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
|
use FireflyIII\Support\Http\Controllers\AutoCompleteCollector;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class AutoCompleteController.
|
* Class AutoCompleteController.
|
||||||
@@ -47,6 +39,7 @@ use Illuminate\Support\Collection;
|
|||||||
*/
|
*/
|
||||||
class AutoCompleteController extends Controller
|
class AutoCompleteController extends Controller
|
||||||
{
|
{
|
||||||
|
use AutoCompleteCollector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of all journals.
|
* List of all journals.
|
||||||
@@ -100,60 +93,44 @@ class AutoCompleteController extends Controller
|
|||||||
$unfiltered = null;
|
$unfiltered = null;
|
||||||
$filtered = null;
|
$filtered = null;
|
||||||
|
|
||||||
// search for all accounts.
|
switch ($subject) {
|
||||||
if ('all-accounts' === $subject) {
|
default:
|
||||||
|
break;
|
||||||
|
case 'all-accounts':
|
||||||
$unfiltered = $this->getAccounts(
|
$unfiltered = $this->getAccounts(
|
||||||
[AccountType::REVENUE, AccountType::EXPENSE, AccountType::BENEFICIARY, AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN,
|
[AccountType::REVENUE, AccountType::EXPENSE, AccountType::BENEFICIARY, AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN,
|
||||||
AccountType::DEBT, AccountType::MORTGAGE]
|
AccountType::DEBT, AccountType::MORTGAGE]
|
||||||
);
|
);
|
||||||
}
|
break;
|
||||||
|
case 'expense-accounts':
|
||||||
// search for expense accounts.
|
|
||||||
if ('expense-accounts' === $subject) {
|
|
||||||
$unfiltered = $this->getAccounts([AccountType::EXPENSE, AccountType::BENEFICIARY]);
|
$unfiltered = $this->getAccounts([AccountType::EXPENSE, AccountType::BENEFICIARY]);
|
||||||
}
|
break;
|
||||||
|
case 'revenue-accounts':
|
||||||
// search for revenue accounts.
|
|
||||||
if ('revenue-accounts' === $subject) {
|
|
||||||
$unfiltered = $this->getAccounts([AccountType::REVENUE]);
|
$unfiltered = $this->getAccounts([AccountType::REVENUE]);
|
||||||
}
|
break;
|
||||||
|
case 'asset-accounts':
|
||||||
// search for asset accounts.
|
|
||||||
if ('asset-accounts' === $subject) {
|
|
||||||
$unfiltered = $this->getAccounts([AccountType::ASSET, AccountType::DEFAULT]);
|
$unfiltered = $this->getAccounts([AccountType::ASSET, AccountType::DEFAULT]);
|
||||||
}
|
break;
|
||||||
|
case 'categories':
|
||||||
// search for categories.
|
|
||||||
if ('categories' === $subject) {
|
|
||||||
$unfiltered = $this->getCategories();
|
$unfiltered = $this->getCategories();
|
||||||
}
|
break;
|
||||||
|
case 'budgets':
|
||||||
// search for budgets.
|
|
||||||
if ('budgets' === $subject) {
|
|
||||||
$unfiltered = $this->getBudgets();
|
$unfiltered = $this->getBudgets();
|
||||||
}
|
break;
|
||||||
|
case 'tags':
|
||||||
// search for tags
|
|
||||||
if ('tags' === $subject) {
|
|
||||||
$unfiltered = $this->getTags();
|
$unfiltered = $this->getTags();
|
||||||
}
|
break;
|
||||||
|
case 'bills':
|
||||||
// search for bills
|
|
||||||
if ('bills' === $subject) {
|
|
||||||
$unfiltered = $this->getBills();
|
$unfiltered = $this->getBills();
|
||||||
}
|
break;
|
||||||
// search for currency names.
|
case 'currency-names':
|
||||||
if ('currency-names' === $subject) {
|
|
||||||
$unfiltered = $this->getCurrencyNames();
|
$unfiltered = $this->getCurrencyNames();
|
||||||
}
|
break;
|
||||||
if ('transaction_types' === $subject) {
|
case 'transaction-types':
|
||||||
|
case 'transaction_types':
|
||||||
$unfiltered = $this->getTransactionTypes();
|
$unfiltered = $this->getTransactionTypes();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if ('transaction-types' === $subject) {
|
|
||||||
$unfiltered = $this->getTransactionTypes();
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter results
|
|
||||||
$filtered = $this->filterResult($unfiltered, $search);
|
$filtered = $this->filterResult($unfiltered, $search);
|
||||||
|
|
||||||
if (null === $filtered) {
|
if (null === $filtered) {
|
||||||
@@ -204,6 +181,7 @@ class AutoCompleteController extends Controller
|
|||||||
$return, function (array $array) use ($search) {
|
$return, function (array $array) use ($search) {
|
||||||
$haystack = $array['name'];
|
$haystack = $array['name'];
|
||||||
$result = stripos($haystack, $search);
|
$result = stripos($haystack, $search);
|
||||||
|
|
||||||
return !(false === $result);
|
return !(false === $result);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -227,7 +205,7 @@ class AutoCompleteController extends Controller
|
|||||||
{
|
{
|
||||||
$search = (string)$request->get('search');
|
$search = (string)$request->get('search');
|
||||||
$cache = new CacheProperties;
|
$cache = new CacheProperties;
|
||||||
$cache->addProperty('ac-revenue-accounts');
|
$cache->addProperty('ac-journals');
|
||||||
// very unlikely a user will actually search for this string.
|
// very unlikely a user will actually search for this string.
|
||||||
$key = '' === $search ? 'skjf0893j89fj2398hd89dh289h2398hr7isd8900828u209ujnxs88929282u' : $search;
|
$key = '' === $search ? 'skjf0893j89fj2398hd89dh289h2398hr7isd8900828u209ujnxs88929282u' : $search;
|
||||||
$cache->addProperty($key);
|
$cache->addProperty($key);
|
||||||
@@ -257,121 +235,4 @@ class AutoCompleteController extends Controller
|
|||||||
|
|
||||||
return response()->json($return);
|
return response()->json($return);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $unfiltered
|
|
||||||
* @param string $query
|
|
||||||
*
|
|
||||||
* @return array|null
|
|
||||||
*/
|
|
||||||
private function filterResult(?array $unfiltered, string $query): ?array
|
|
||||||
{
|
|
||||||
if (null === $unfiltered) {
|
|
||||||
return null; // @codeCoverageIgnore
|
|
||||||
}
|
|
||||||
if ('' === $query) {
|
|
||||||
sort($unfiltered);
|
|
||||||
|
|
||||||
return $unfiltered;
|
|
||||||
}
|
|
||||||
$return = [];
|
|
||||||
if ('' !== $query) {
|
|
||||||
$return = array_values(
|
|
||||||
array_filter(
|
|
||||||
$unfiltered, function (string $value) use ($query) {
|
|
||||||
return !(false === stripos($value, $query));
|
|
||||||
}, ARRAY_FILTER_USE_BOTH
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
sort($return);
|
|
||||||
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $query
|
|
||||||
* @param array $types
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getAccounts(array $types): array
|
|
||||||
{
|
|
||||||
$repository = app(AccountRepositoryInterface::class);
|
|
||||||
// find everything:
|
|
||||||
/** @var Collection $collection */
|
|
||||||
$collection = $repository->getAccountsByType($types);
|
|
||||||
$filtered = $collection->filter(
|
|
||||||
function (Account $account) {
|
|
||||||
return $account->active === true;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
$return = array_values(array_unique($filtered->pluck('name')->toArray()));
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getBills(): array
|
|
||||||
{
|
|
||||||
$repository = app(BillRepositoryInterface::class);
|
|
||||||
|
|
||||||
return array_unique($repository->getActiveBills()->pluck('name')->toArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getBudgets(): array
|
|
||||||
{
|
|
||||||
$repository = app(BudgetRepositoryInterface::class);
|
|
||||||
|
|
||||||
return array_unique($repository->getBudgets()->pluck('name')->toArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getCategories(): array
|
|
||||||
{
|
|
||||||
$repository = app(CategoryRepositoryInterface::class);
|
|
||||||
|
|
||||||
return array_unique($repository->getCategories()->pluck('name')->toArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getCurrencyNames(): array
|
|
||||||
{
|
|
||||||
/** @var CurrencyRepositoryInterface $repository */
|
|
||||||
$repository = app(CurrencyRepositoryInterface::class);
|
|
||||||
|
|
||||||
return $repository->get()->pluck('name')->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getTags(): array
|
|
||||||
{
|
|
||||||
/** @var TagRepositoryInterface $repository */
|
|
||||||
$repository = app(TagRepositoryInterface::class);
|
|
||||||
|
|
||||||
return array_unique($repository->get()->pluck('tag')->toArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getTransactionTypes(): array
|
|
||||||
{
|
|
||||||
$repository = app(JournalRepositoryInterface::class);
|
|
||||||
|
|
||||||
return array_unique($repository->getTransactionTypes()->pluck('type')->toArray());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,10 +58,11 @@ class PreferencesController extends Controller
|
|||||||
public function index(AccountRepositoryInterface $repository)
|
public function index(AccountRepositoryInterface $repository)
|
||||||
{
|
{
|
||||||
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||||
|
$accountIds = $accounts->pluck('id')->toArray();
|
||||||
$viewRangePref = app('preferences')->get('viewRange', '1M');
|
$viewRangePref = app('preferences')->get('viewRange', '1M');
|
||||||
/** @noinspection NullPointerExceptionInspection */
|
/** @noinspection NullPointerExceptionInspection */
|
||||||
$viewRange = $viewRangePref->data;
|
$viewRange = $viewRangePref->data;
|
||||||
$frontPageAccounts = app('preferences')->get('frontPageAccounts', []);
|
$frontPageAccounts = app('preferences')->get('frontPageAccounts', $accountIds);
|
||||||
$language = app('preferences')->get('language', config('firefly.default_language', 'en_US'))->data;
|
$language = app('preferences')->get('language', config('firefly.default_language', 'en_US'))->data;
|
||||||
$listPageSize = app('preferences')->get('listPageSize', 50)->data;
|
$listPageSize = app('preferences')->get('listPageSize', 50)->data;
|
||||||
$customFiscalYear = app('preferences')->get('customFiscalYear', 0)->data;
|
$customFiscalYear = app('preferences')->get('customFiscalYear', 0)->data;
|
||||||
@@ -69,6 +70,12 @@ class PreferencesController extends Controller
|
|||||||
$fiscalYearStart = date('Y') . '-' . $fiscalYearStartStr;
|
$fiscalYearStart = date('Y') . '-' . $fiscalYearStartStr;
|
||||||
$tjOptionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data;
|
$tjOptionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data;
|
||||||
|
|
||||||
|
// an important fallback is that the frontPageAccount array gets refilled automatically
|
||||||
|
// when it turns up empty.
|
||||||
|
if (\count($frontPageAccounts->data) === 0) {
|
||||||
|
$frontPageAccounts = $accountIds;
|
||||||
|
}
|
||||||
|
|
||||||
return view(
|
return view(
|
||||||
'preferences.index',
|
'preferences.index',
|
||||||
compact(
|
compact(
|
||||||
@@ -98,7 +105,7 @@ class PreferencesController extends Controller
|
|||||||
{
|
{
|
||||||
// front page accounts
|
// front page accounts
|
||||||
$frontPageAccounts = [];
|
$frontPageAccounts = [];
|
||||||
if (\is_array($request->get('frontPageAccounts'))) {
|
if (\is_array($request->get('frontPageAccounts')) && \count($request->get('frontPageAccounts')) > 0) {
|
||||||
foreach ($request->get('frontPageAccounts') as $id) {
|
foreach ($request->get('frontPageAccounts') as $id) {
|
||||||
$frontPageAccounts[] = (int)$id;
|
$frontPageAccounts[] = (int)$id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -340,294 +340,4 @@ class ExpenseController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
/**
|
|
||||||
* Group by category (earnings).
|
|
||||||
*
|
|
||||||
* @param Collection $assets
|
|
||||||
* @param Collection $opposing
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
|
||||||
*/
|
|
||||||
protected function earnedByCategory(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info
|
|
||||||
{
|
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($assets);
|
|
||||||
$collector->setOpposingAccounts($opposing)->withCategoryInformation();
|
|
||||||
$set = $collector->getTransactions();
|
|
||||||
$sum = [];
|
|
||||||
// loop to support multi currency
|
|
||||||
foreach ($set as $transaction) {
|
|
||||||
$currencyId = $transaction->transaction_currency_id;
|
|
||||||
$categoryName = $transaction->transaction_category_name;
|
|
||||||
$categoryId = (int)$transaction->transaction_category_id;
|
|
||||||
// if null, grab from journal:
|
|
||||||
if (0 === $categoryId) {
|
|
||||||
$categoryName = $transaction->transaction_journal_category_name;
|
|
||||||
$categoryId = (int)$transaction->transaction_journal_category_id;
|
|
||||||
}
|
|
||||||
if (0 !== $categoryId) {
|
|
||||||
$categoryName = app('steam')->tryDecrypt($categoryName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if not set, set to zero:
|
|
||||||
if (!isset($sum[$categoryId][$currencyId])) {
|
|
||||||
$sum[$categoryId] = [
|
|
||||||
'grand_total' => '0',
|
|
||||||
'name' => $categoryName,
|
|
||||||
'per_currency' => [
|
|
||||||
$currencyId => [
|
|
||||||
'sum' => '0',
|
|
||||||
'category' => [
|
|
||||||
'id' => $categoryId,
|
|
||||||
'name' => $categoryName,
|
|
||||||
],
|
|
||||||
'currency' => [
|
|
||||||
'symbol' => $transaction->transaction_currency_symbol,
|
|
||||||
'dp' => $transaction->transaction_currency_dp,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// add amount
|
|
||||||
$sum[$categoryId]['per_currency'][$currencyId]['sum'] = bcadd(
|
|
||||||
$sum[$categoryId]['per_currency'][$currencyId]['sum'], $transaction->transaction_amount
|
|
||||||
);
|
|
||||||
$sum[$categoryId]['grand_total'] = bcadd($sum[$categoryId]['grand_total'], $transaction->transaction_amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
/**
|
|
||||||
* Earned in period for accounts.
|
|
||||||
*
|
|
||||||
* @param Collection $assets
|
|
||||||
* @param Collection $opposing
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function earnedInPeriod(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info
|
|
||||||
{
|
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($assets);
|
|
||||||
$collector->setOpposingAccounts($opposing);
|
|
||||||
$set = $collector->getTransactions();
|
|
||||||
$sum = [
|
|
||||||
'grand_sum' => '0',
|
|
||||||
'per_currency' => [],
|
|
||||||
];
|
|
||||||
// loop to support multi currency
|
|
||||||
foreach ($set as $transaction) {
|
|
||||||
$currencyId = $transaction->transaction_currency_id;
|
|
||||||
|
|
||||||
// if not set, set to zero:
|
|
||||||
if (!isset($sum['per_currency'][$currencyId])) {
|
|
||||||
$sum['per_currency'][$currencyId] = [
|
|
||||||
'sum' => '0',
|
|
||||||
'currency' => [
|
|
||||||
'symbol' => $transaction->transaction_currency_symbol,
|
|
||||||
'dp' => $transaction->transaction_currency_dp,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// add amount
|
|
||||||
$sum['per_currency'][$currencyId]['sum'] = bcadd($sum['per_currency'][$currencyId]['sum'], $transaction->transaction_amount);
|
|
||||||
$sum['grand_sum'] = bcadd($sum['grand_sum'], $transaction->transaction_amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
/**
|
|
||||||
* Spent by budget.
|
|
||||||
*
|
|
||||||
* @param Collection $assets
|
|
||||||
* @param Collection $opposing
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
|
||||||
*/
|
|
||||||
protected function spentByBudget(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info
|
|
||||||
{
|
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($assets);
|
|
||||||
$collector->setOpposingAccounts($opposing)->withBudgetInformation();
|
|
||||||
$set = $collector->getTransactions();
|
|
||||||
$sum = [];
|
|
||||||
// loop to support multi currency
|
|
||||||
foreach ($set as $transaction) {
|
|
||||||
$currencyId = $transaction->transaction_currency_id;
|
|
||||||
$budgetName = $transaction->transaction_budget_name;
|
|
||||||
$budgetId = (int)$transaction->transaction_budget_id;
|
|
||||||
// if null, grab from journal:
|
|
||||||
if (0 === $budgetId) {
|
|
||||||
$budgetName = $transaction->transaction_journal_budget_name;
|
|
||||||
$budgetId = (int)$transaction->transaction_journal_budget_id;
|
|
||||||
}
|
|
||||||
if (0 !== $budgetId) {
|
|
||||||
$budgetName = app('steam')->tryDecrypt($budgetName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if not set, set to zero:
|
|
||||||
if (!isset($sum[$budgetId][$currencyId])) {
|
|
||||||
$sum[$budgetId] = [
|
|
||||||
'grand_total' => '0',
|
|
||||||
'name' => $budgetName,
|
|
||||||
'per_currency' => [
|
|
||||||
$currencyId => [
|
|
||||||
'sum' => '0',
|
|
||||||
'budget' => [
|
|
||||||
'id' => $budgetId,
|
|
||||||
'name' => $budgetName,
|
|
||||||
],
|
|
||||||
'currency' => [
|
|
||||||
'symbol' => $transaction->transaction_currency_symbol,
|
|
||||||
'dp' => $transaction->transaction_currency_dp,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// add amount
|
|
||||||
$sum[$budgetId]['per_currency'][$currencyId]['sum'] = bcadd(
|
|
||||||
$sum[$budgetId]['per_currency'][$currencyId]['sum'], $transaction->transaction_amount
|
|
||||||
);
|
|
||||||
$sum[$budgetId]['grand_total'] = bcadd($sum[$budgetId]['grand_total'], $transaction->transaction_amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
/**
|
|
||||||
* Spent by category.
|
|
||||||
*
|
|
||||||
* @param Collection $assets
|
|
||||||
* @param Collection $opposing
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
|
||||||
*/
|
|
||||||
protected function spentByCategory(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info
|
|
||||||
{
|
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($assets);
|
|
||||||
$collector->setOpposingAccounts($opposing)->withCategoryInformation();
|
|
||||||
$set = $collector->getTransactions();
|
|
||||||
$sum = [];
|
|
||||||
// loop to support multi currency
|
|
||||||
foreach ($set as $transaction) {
|
|
||||||
$currencyId = $transaction->transaction_currency_id;
|
|
||||||
$categoryName = $transaction->transaction_category_name;
|
|
||||||
$categoryId = (int)$transaction->transaction_category_id;
|
|
||||||
// if null, grab from journal:
|
|
||||||
if (0 === $categoryId) {
|
|
||||||
$categoryName = $transaction->transaction_journal_category_name;
|
|
||||||
$categoryId = (int)$transaction->transaction_journal_category_id;
|
|
||||||
}
|
|
||||||
if (0 !== $categoryId) {
|
|
||||||
$categoryName = app('steam')->tryDecrypt($categoryName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if not set, set to zero:
|
|
||||||
if (!isset($sum[$categoryId][$currencyId])) {
|
|
||||||
$sum[$categoryId] = [
|
|
||||||
'grand_total' => '0',
|
|
||||||
'name' => $categoryName,
|
|
||||||
'per_currency' => [
|
|
||||||
$currencyId => [
|
|
||||||
'sum' => '0',
|
|
||||||
'category' => [
|
|
||||||
'id' => $categoryId,
|
|
||||||
'name' => $categoryName,
|
|
||||||
],
|
|
||||||
'currency' => [
|
|
||||||
'symbol' => $transaction->transaction_currency_symbol,
|
|
||||||
'dp' => $transaction->transaction_currency_dp,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// add amount
|
|
||||||
$sum[$categoryId]['per_currency'][$currencyId]['sum'] = bcadd(
|
|
||||||
$sum[$categoryId]['per_currency'][$currencyId]['sum'], $transaction->transaction_amount
|
|
||||||
);
|
|
||||||
$sum[$categoryId]['grand_total'] = bcadd($sum[$categoryId]['grand_total'], $transaction->transaction_amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
/**
|
|
||||||
* Spent in a period.
|
|
||||||
*
|
|
||||||
* @param Collection $assets
|
|
||||||
* @param Collection $opposing
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function spentInPeriod(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array // get data + augment with info
|
|
||||||
{
|
|
||||||
/** @var TransactionCollectorInterface $collector */
|
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($assets);
|
|
||||||
$collector->setOpposingAccounts($opposing);
|
|
||||||
$set = $collector->getTransactions();
|
|
||||||
$sum = [
|
|
||||||
'grand_sum' => '0',
|
|
||||||
'per_currency' => [],
|
|
||||||
];
|
|
||||||
// loop to support multi currency
|
|
||||||
foreach ($set as $transaction) {
|
|
||||||
$currencyId = (int)$transaction->transaction_currency_id;
|
|
||||||
|
|
||||||
// if not set, set to zero:
|
|
||||||
if (!isset($sum['per_currency'][$currencyId])) {
|
|
||||||
$sum['per_currency'][$currencyId] = [
|
|
||||||
'sum' => '0',
|
|
||||||
'currency' => [
|
|
||||||
'symbol' => $transaction->transaction_currency_symbol,
|
|
||||||
'dp' => $transaction->transaction_currency_dp,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// add amount
|
|
||||||
$sum['per_currency'][$currencyId]['sum'] = bcadd($sum['per_currency'][$currencyId]['sum'], $transaction->transaction_amount);
|
|
||||||
$sum['grand_sum'] = bcadd($sum['grand_sum'], $transaction->transaction_amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sum;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ use FireflyIII\Http\Requests\RuleFormRequest;
|
|||||||
use FireflyIII\Models\Bill;
|
use FireflyIII\Models\Bill;
|
||||||
use FireflyIII\Models\RuleGroup;
|
use FireflyIII\Models\RuleGroup;
|
||||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||||
|
use FireflyIII\Support\Http\Controllers\AugumentData;
|
||||||
|
use FireflyIII\Support\Http\Controllers\ModelInformation;
|
||||||
use FireflyIII\Support\Http\Controllers\RuleManagement;
|
use FireflyIII\Support\Http\Controllers\RuleManagement;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -40,7 +42,7 @@ use Throwable;
|
|||||||
*/
|
*/
|
||||||
class CreateController extends Controller
|
class CreateController extends Controller
|
||||||
{
|
{
|
||||||
use RuleManagement;
|
use RuleManagement, ModelInformation;
|
||||||
/** @var RuleRepositoryInterface Rule repository */
|
/** @var RuleRepositoryInterface Rule repository */
|
||||||
private $ruleRepos;
|
private $ruleRepos;
|
||||||
|
|
||||||
@@ -199,78 +201,4 @@ class CreateController extends Controller
|
|||||||
return $redirect;
|
return $redirect;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get actions based on a bill.
|
|
||||||
*
|
|
||||||
* @param Bill $bill
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function getActionsForBill(Bill $bill): array // get info and augument
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$result = view(
|
|
||||||
'rules.partials.action',
|
|
||||||
[
|
|
||||||
'oldAction' => 'link_to_bill',
|
|
||||||
'oldValue' => $bill->name,
|
|
||||||
'oldChecked' => false,
|
|
||||||
'count' => 1,
|
|
||||||
]
|
|
||||||
)->render();
|
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
Log::error(sprintf('Throwable was thrown in getActionsForBill(): %s', $e->getMessage()));
|
|
||||||
Log::error($e->getTraceAsString());
|
|
||||||
$result = 'Could not render view. See log files.';
|
|
||||||
}
|
|
||||||
|
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
||||||
return [$result];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create fake triggers to match the bill's properties
|
|
||||||
*
|
|
||||||
* @param Bill $bill
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function getTriggersForBill(Bill $bill): array // get info and augument
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
$triggers = ['currency_is', 'amount_more', 'amount_less', 'description_contains'];
|
|
||||||
$values = [
|
|
||||||
$bill->transactionCurrency()->first()->name,
|
|
||||||
round((float)$bill->amount_min, 12),
|
|
||||||
round((float)$bill->amount_max, 12),
|
|
||||||
$bill->name,
|
|
||||||
];
|
|
||||||
foreach ($triggers as $index => $trigger) {
|
|
||||||
try {
|
|
||||||
$string = view(
|
|
||||||
'rules.partials.trigger',
|
|
||||||
[
|
|
||||||
'oldTrigger' => $trigger,
|
|
||||||
'oldValue' => $values[$index],
|
|
||||||
'oldChecked' => false,
|
|
||||||
'count' => $index + 1,
|
|
||||||
]
|
|
||||||
)->render();
|
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
|
|
||||||
Log::debug(sprintf('Throwable was thrown in getTriggersForBill(): %s', $e->getMessage()));
|
|
||||||
Log::debug($e->getTraceAsString());
|
|
||||||
$string = '';
|
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
}
|
|
||||||
if ('' !== $string) {
|
|
||||||
$result[] = $string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ use FireflyIII\Models\Rule;
|
|||||||
use FireflyIII\Models\RuleAction;
|
use FireflyIII\Models\RuleAction;
|
||||||
use FireflyIII\Models\RuleTrigger;
|
use FireflyIII\Models\RuleTrigger;
|
||||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||||
|
use FireflyIII\Support\Http\Controllers\ModelInformation;
|
||||||
|
use FireflyIII\Support\Http\Controllers\RenderPartialViews;
|
||||||
use FireflyIII\Support\Http\Controllers\RuleManagement;
|
use FireflyIII\Support\Http\Controllers\RuleManagement;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -41,7 +43,7 @@ use Throwable;
|
|||||||
*/
|
*/
|
||||||
class EditController extends Controller
|
class EditController extends Controller
|
||||||
{
|
{
|
||||||
use RuleManagement;
|
use RuleManagement, RenderPartialViews;
|
||||||
|
|
||||||
/** @var RuleRepositoryInterface Rule repository */
|
/** @var RuleRepositoryInterface Rule repository */
|
||||||
private $ruleRepos;
|
private $ruleRepos;
|
||||||
@@ -146,81 +148,4 @@ class EditController extends Controller
|
|||||||
|
|
||||||
return $redirect;
|
return $redirect;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get current (from system) rule actions.
|
|
||||||
*
|
|
||||||
* @param Rule $rule
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function getCurrentActions(Rule $rule): array // get info from object and present.
|
|
||||||
{
|
|
||||||
$index = 0;
|
|
||||||
$actions = [];
|
|
||||||
$currentActions = $rule->ruleActions()->orderBy('order','ASC')->get();
|
|
||||||
/** @var RuleAction $entry */
|
|
||||||
foreach ($currentActions as $entry) {
|
|
||||||
$count = ($index + 1);
|
|
||||||
try {
|
|
||||||
$actions[] = view(
|
|
||||||
'rules.partials.action',
|
|
||||||
[
|
|
||||||
'oldAction' => $entry->action_type,
|
|
||||||
'oldValue' => $entry->action_value,
|
|
||||||
'oldChecked' => $entry->stop_processing,
|
|
||||||
'count' => $count,
|
|
||||||
]
|
|
||||||
)->render();
|
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
Log::debug(sprintf('Throwable was thrown in getCurrentActions(): %s', $e->getMessage()));
|
|
||||||
Log::error($e->getTraceAsString());
|
|
||||||
}
|
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
++$index;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get current (from DB) rule triggers.
|
|
||||||
*
|
|
||||||
* @param Rule $rule
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
protected function getCurrentTriggers(Rule $rule): array // get info from object and present.
|
|
||||||
{
|
|
||||||
$index = 0;
|
|
||||||
$triggers = [];
|
|
||||||
$currentTriggers = $rule->ruleTriggers()->orderBy('order','ASC')->get();
|
|
||||||
/** @var RuleTrigger $entry */
|
|
||||||
foreach ($currentTriggers as $entry) {
|
|
||||||
if ('user_action' !== $entry->trigger_type) {
|
|
||||||
$count = ($index + 1);
|
|
||||||
try {
|
|
||||||
$triggers[] = view(
|
|
||||||
'rules.partials.trigger',
|
|
||||||
[
|
|
||||||
'oldTrigger' => $entry->trigger_type,
|
|
||||||
'oldValue' => $entry->trigger_value,
|
|
||||||
'oldChecked' => $entry->stop_processing,
|
|
||||||
'count' => $count,
|
|
||||||
]
|
|
||||||
)->render();
|
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
Log::debug(sprintf('Throwable was thrown in getCurrentTriggers(): %s', $e->getMessage()));
|
|
||||||
Log::error($e->getTraceAsString());
|
|
||||||
}
|
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
++$index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $triggers;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,14 +23,15 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Http\Controllers\System;
|
namespace FireflyIII\Http\Controllers\System;
|
||||||
|
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Support\Http\Controllers\CronRunner;
|
||||||
use FireflyIII\Support\Cronjobs\RecurringCronjob;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class CronController
|
* Class CronController
|
||||||
*/
|
*/
|
||||||
class CronController
|
class CronController
|
||||||
{
|
{
|
||||||
|
use CronRunner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $token
|
* @param string $token
|
||||||
*
|
*
|
||||||
@@ -43,24 +44,4 @@ class CronController
|
|||||||
|
|
||||||
return implode("<br>\n", $results);
|
return implode("<br>\n", $results);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function runRecurring(): string
|
|
||||||
{
|
|
||||||
/** @var RecurringCronjob $recurring */
|
|
||||||
$recurring = app(RecurringCronjob::class);
|
|
||||||
try {
|
|
||||||
$result = $recurring->fire();
|
|
||||||
} catch (FireflyException $e) {
|
|
||||||
return $e->getMessage();
|
|
||||||
}
|
|
||||||
if (false === $result) {
|
|
||||||
return 'The recurring transaction cron job did not fire.';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'The recurring transaction cron job fired successfully.';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -147,7 +147,7 @@ class InstallController extends Controller
|
|||||||
return response()->json(['error' => true, 'message' => self::BASEDIR_ERROR]);
|
return response()->json(['error' => true, 'message' => self::BASEDIR_ERROR]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(['error' => true, 'message' => self::OTHER_ERROR]);
|
return response()->json(['error' => true, 'message' => self::OTHER_ERROR . ' ' . $e->getMessage()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(['error' => false, 'message' => 'OK']);
|
return response()->json(['error' => false, 'message' => 'OK']);
|
||||||
@@ -174,7 +174,7 @@ class InstallController extends Controller
|
|||||||
return response()->json(['error' => true, 'message' => self::BASEDIR_ERROR]);
|
return response()->json(['error' => true, 'message' => self::BASEDIR_ERROR]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(['error' => true, 'message' => self::OTHER_ERROR]);
|
return response()->json(['error' => true, 'message' => self::OTHER_ERROR . ' ' . $e->getMessage()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(['error' => false, 'message' => 'OK']);
|
return response()->json(['error' => false, 'message' => 'OK']);
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ use FireflyIII\Support\Http\Controllers\PeriodOverview;
|
|||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class TagController.
|
* Class TagController.
|
||||||
@@ -250,7 +251,10 @@ class TagController extends Controller
|
|||||||
public function store(TagFormRequest $request): RedirectResponse
|
public function store(TagFormRequest $request): RedirectResponse
|
||||||
{
|
{
|
||||||
$data = $request->collectTagData();
|
$data = $request->collectTagData();
|
||||||
$this->repository->store($data);
|
Log::debug('Data from request', $data);
|
||||||
|
|
||||||
|
$result = $this->repository->store($data);
|
||||||
|
Log::debug('Data after storage', $result->toArray());
|
||||||
|
|
||||||
session()->flash('success', (string)trans('firefly.created_tag', ['tag' => $data['tag']]));
|
session()->flash('success', (string)trans('firefly.created_tag', ['tag' => $data['tag']]));
|
||||||
app('preferences')->mark();
|
app('preferences')->mark();
|
||||||
|
|||||||
@@ -25,10 +25,8 @@ namespace FireflyIII\Http\Controllers\Transaction;
|
|||||||
use FireflyIII\Events\UpdatedTransactionJournal;
|
use FireflyIII\Events\UpdatedTransactionJournal;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
use FireflyIII\Models\Account;
|
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
|
||||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||||
use FireflyIII\Support\Http\Controllers\ModelInformation;
|
use FireflyIII\Support\Http\Controllers\ModelInformation;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -163,6 +161,7 @@ class ConvertController extends Controller
|
|||||||
|
|
||||||
if ($errors->count() > 0) {
|
if ($errors->count() > 0) {
|
||||||
Log::error('Errors while converting: ', $errors->toArray());
|
Log::error('Errors while converting: ', $errors->toArray());
|
||||||
|
|
||||||
return redirect(route('transactions.convert.index', [strtolower($destinationType->type), $journal->id]))->withErrors($errors)->withInput();
|
return redirect(route('transactions.convert.index', [strtolower($destinationType->type), $journal->id]))->withErrors($errors)->withInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,126 +173,4 @@ class ConvertController extends Controller
|
|||||||
|
|
||||||
return redirect(route('transactions.show', [$journal->id]));
|
return redirect(route('transactions.show', [$journal->id]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the destination account. Is complex.
|
|
||||||
*
|
|
||||||
* @param TransactionJournal $journal
|
|
||||||
* @param TransactionType $destinationType
|
|
||||||
* @param array $data
|
|
||||||
*
|
|
||||||
* @return Account
|
|
||||||
*
|
|
||||||
* @throws FireflyException
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
*/
|
|
||||||
protected function getDestinationAccount(TransactionJournal $journal, TransactionType $destinationType, array $data
|
|
||||||
): Account // helper for conversion. Get info from obj.
|
|
||||||
{
|
|
||||||
/** @var AccountRepositoryInterface $accountRepository */
|
|
||||||
$accountRepository = app(AccountRepositoryInterface::class);
|
|
||||||
$sourceAccount = $this->repository->getJournalSourceAccounts($journal)->first();
|
|
||||||
$destinationAccount = $this->repository->getJournalDestinationAccounts($journal)->first();
|
|
||||||
$sourceType = $journal->transactionType;
|
|
||||||
$joined = $sourceType->type . '-' . $destinationType->type;
|
|
||||||
switch ($joined) {
|
|
||||||
default:
|
|
||||||
throw new FireflyException('Cannot handle ' . $joined); // @codeCoverageIgnore
|
|
||||||
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT:
|
|
||||||
// one
|
|
||||||
$destination = $sourceAccount;
|
|
||||||
break;
|
|
||||||
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER:
|
|
||||||
// two
|
|
||||||
$destination = $accountRepository->findNull((int)$data['destination_account_asset']);
|
|
||||||
break;
|
|
||||||
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL:
|
|
||||||
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL:
|
|
||||||
// three and five
|
|
||||||
if ('' === $data['destination_account_expense'] || null === $data['destination_account_expense']) {
|
|
||||||
// destination is a cash account.
|
|
||||||
return $accountRepository->getCashAccount();
|
|
||||||
}
|
|
||||||
$data = [
|
|
||||||
'name' => $data['destination_account_expense'],
|
|
||||||
'accountType' => 'expense',
|
|
||||||
'account_type_id' => null,
|
|
||||||
'virtualBalance' => 0,
|
|
||||||
'active' => true,
|
|
||||||
'iban' => null,
|
|
||||||
];
|
|
||||||
$destination = $accountRepository->store($data);
|
|
||||||
break;
|
|
||||||
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER:
|
|
||||||
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT:
|
|
||||||
// four and six
|
|
||||||
$destination = $destinationAccount;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $destination;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the source account.
|
|
||||||
*
|
|
||||||
* @param TransactionJournal $journal
|
|
||||||
* @param TransactionType $destinationType
|
|
||||||
* @param array $data
|
|
||||||
*
|
|
||||||
* @return Account
|
|
||||||
*
|
|
||||||
* @throws FireflyException
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
*/
|
|
||||||
protected function getSourceAccount(TransactionJournal $journal, TransactionType $destinationType, array $data
|
|
||||||
): Account // helper for conversion. Get info from obj.
|
|
||||||
{
|
|
||||||
/** @var AccountRepositoryInterface $accountRepository */
|
|
||||||
$accountRepository = app(AccountRepositoryInterface::class);
|
|
||||||
$sourceAccount = $this->repository->getJournalSourceAccounts($journal)->first();
|
|
||||||
$destinationAccount = $this->repository->getJournalDestinationAccounts($journal)->first();
|
|
||||||
$sourceType = $journal->transactionType;
|
|
||||||
$joined = $sourceType->type . '-' . $destinationType->type;
|
|
||||||
switch ($joined) {
|
|
||||||
default:
|
|
||||||
throw new FireflyException('Cannot handle ' . $joined); // @codeCoverageIgnore
|
|
||||||
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT:
|
|
||||||
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT:
|
|
||||||
|
|
||||||
if ('' === $data['source_account_revenue'] || null === $data['source_account_revenue']) {
|
|
||||||
// destination is a cash account.
|
|
||||||
return $accountRepository->getCashAccount();
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = [
|
|
||||||
'name' => $data['source_account_revenue'],
|
|
||||||
'accountType' => 'revenue',
|
|
||||||
'virtualBalance' => 0,
|
|
||||||
'active' => true,
|
|
||||||
'account_type_id' => null,
|
|
||||||
'iban' => null,
|
|
||||||
];
|
|
||||||
$source = $accountRepository->store($data);
|
|
||||||
break;
|
|
||||||
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER:
|
|
||||||
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL:
|
|
||||||
$source = $sourceAccount;
|
|
||||||
break;
|
|
||||||
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL:
|
|
||||||
$source = $destinationAccount;
|
|
||||||
break;
|
|
||||||
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER:
|
|
||||||
$source = $accountRepository->findNull((int)$data['source_account_asset']);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $source;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Http\Controllers\Transaction;
|
namespace FireflyIII\Http\Controllers\Transaction;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Events\UpdatedTransactionJournal;
|
||||||
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
|
||||||
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
||||||
use FireflyIII\Helpers\Filter\TransactionViewFilter;
|
use FireflyIII\Helpers\Filter\TransactionViewFilter;
|
||||||
@@ -241,6 +242,9 @@ class MassController extends Controller
|
|||||||
// call repository update function.
|
// call repository update function.
|
||||||
$repository->update($journal, $data);
|
$repository->update($journal, $data);
|
||||||
|
|
||||||
|
// trigger rules
|
||||||
|
event(new UpdatedTransactionJournal($journal));
|
||||||
|
|
||||||
++$count;
|
++$count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -400,7 +400,7 @@ class SingleController extends Controller
|
|||||||
session()->flash('info', $this->attachments->getMessages()->get('attachments'));
|
session()->flash('info', $this->attachments->getMessages()->get('attachments'));
|
||||||
}
|
}
|
||||||
|
|
||||||
event(new StoredTransactionJournal($journal, $data['piggy_bank_id']));
|
event(new StoredTransactionJournal($journal));
|
||||||
|
|
||||||
session()->flash('success_uri', route('transactions.show', [$journal->id]));
|
session()->flash('success_uri', route('transactions.show', [$journal->id]));
|
||||||
session()->flash('success', (string)trans('firefly.stored_journal', ['description' => $journal->description]));
|
session()->flash('success', (string)trans('firefly.stored_journal', ['description' => $journal->description]));
|
||||||
|
|||||||
@@ -171,42 +171,4 @@ class SplitController extends Controller
|
|||||||
return redirect($this->getPreviousUri('transactions.edit-split.uri'));
|
return redirect($this->getPreviousUri('transactions.edit-split.uri'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get info from old input.
|
|
||||||
*
|
|
||||||
* @param $array
|
|
||||||
* @param $old
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
*/
|
|
||||||
protected function updateWithPrevious($array, $old): array // update object with new info
|
|
||||||
{
|
|
||||||
if (0 === \count($old) || !isset($old['transactions'])) {
|
|
||||||
return $array;
|
|
||||||
}
|
|
||||||
$old = $old['transactions'];
|
|
||||||
|
|
||||||
foreach ($old as $index => $row) {
|
|
||||||
if (isset($array[$index])) {
|
|
||||||
/** @noinspection SlowArrayOperationsInLoopInspection */
|
|
||||||
$array[$index] = array_merge($array[$index], $row);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// take some info from first transaction, that should at least exist.
|
|
||||||
$array[$index] = $row;
|
|
||||||
$array[$index]['currency_id'] = $array[0]['currency_id'];
|
|
||||||
$array[$index]['currency_code'] = $array[0]['currency_code'] ?? '';
|
|
||||||
$array[$index]['currency_symbol'] = $array[0]['currency_symbol'] ?? '';
|
|
||||||
$array[$index]['foreign_amount'] = round($array[0]['foreign_destination_amount'] ?? '0', 12);
|
|
||||||
$array[$index]['foreign_currency_id'] = $array[0]['foreign_currency_id'];
|
|
||||||
$array[$index]['foreign_currency_code'] = $array[0]['foreign_currency_code'];
|
|
||||||
$array[$index]['foreign_currency_symbol'] = $array[0]['foreign_currency_symbol'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $array;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,12 +54,17 @@ class SecureHeaders
|
|||||||
sprintf("script-src 'self' 'unsafe-eval' 'unsafe-inline' %s", $google),
|
sprintf("script-src 'self' 'unsafe-eval' 'unsafe-inline' %s", $google),
|
||||||
"style-src 'self' 'unsafe-inline'",
|
"style-src 'self' 'unsafe-inline'",
|
||||||
"base-uri 'self'",
|
"base-uri 'self'",
|
||||||
"form-action 'self'",
|
|
||||||
"font-src 'self'",
|
"font-src 'self'",
|
||||||
"connect-src 'self'",
|
"connect-src 'self'",
|
||||||
"img-src 'self' data: https://api.tiles.mapbox.com",
|
"img-src 'self' data: https://api.tiles.mapbox.com",
|
||||||
|
"manifest-src 'self'",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$route = $request->route();
|
||||||
|
if (null !== $route && $route->uri !== 'oauth/authorize') {
|
||||||
|
$csp[] = "form-action 'self'";
|
||||||
|
}
|
||||||
|
|
||||||
$featurePolicies = [
|
$featurePolicies = [
|
||||||
"geolocation 'none'",
|
"geolocation 'none'",
|
||||||
"midi 'none'",
|
"midi 'none'",
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ class TagFormRequest extends Request
|
|||||||
'description' => 'min:1|nullable',
|
'description' => 'min:1|nullable',
|
||||||
'date' => 'date|nullable',
|
'date' => 'date|nullable',
|
||||||
'latitude' => 'numeric|min:-90|max:90|nullable',
|
'latitude' => 'numeric|min:-90|max:90|nullable',
|
||||||
'longitude' => 'numeric|min:-90|max:90|nullable',
|
'longitude' => 'numeric|min:-180|max:180|nullable',
|
||||||
'zoom_level' => 'numeric|min:0|max:80|nullable',
|
'zoom_level' => 'numeric|min:0|max:80|nullable',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,8 +219,8 @@ class Amount implements ConverterInterface
|
|||||||
if (0 === strpos($value, '--')) {
|
if (0 === strpos($value, '--')) {
|
||||||
$value = substr($value, 2);
|
$value = substr($value, 2);
|
||||||
}
|
}
|
||||||
|
// have to strip the € because apparantly the Postbank (DE) thinks "1.000,00 €" is a normal way to format a number.
|
||||||
|
$value = trim((string)str_replace(['€'], '', $value));
|
||||||
$str = preg_replace('/[^\-\(\)\.\,0-9 ]/', '', $value);
|
$str = preg_replace('/[^\-\(\)\.\,0-9 ]/', '', $value);
|
||||||
$len = \strlen($str);
|
$len = \strlen($str);
|
||||||
if ('(' === $str[0] && ')' === $str[$len - 1]) {
|
if ('(' === $str[0] && ')' === $str[$len - 1]) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* RabobankDebitCredit.php
|
* BankDebitCredit.php
|
||||||
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||||
*
|
*
|
||||||
* This file is part of Firefly III.
|
* This file is part of Firefly III.
|
||||||
*
|
*
|
||||||
@@ -18,41 +18,40 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Import\Converter;
|
namespace FireflyIII\Import\Converter;
|
||||||
|
|
||||||
|
|
||||||
use Log;
|
use Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class RabobankDebitCredit.
|
*
|
||||||
|
* Class BankDebitCredit
|
||||||
*/
|
*/
|
||||||
class RabobankDebitCredit implements ConverterInterface
|
class BankDebitCredit implements ConverterInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert D or A to integer values.
|
* Convert a value.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
*
|
*
|
||||||
* @param $value
|
* @param $value
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
*/
|
||||||
public function convert($value): int
|
public function convert($value): int
|
||||||
{
|
{
|
||||||
Log::debug('Going to convert ', ['value' => $value]);
|
Log::debug('Going to convert ', ['value' => $value]);
|
||||||
|
$negative = [
|
||||||
if ('D' === $value) {
|
'D', // Old style Rabobank (NL). Short for "Debit"
|
||||||
Log::debug('Return -1');
|
'A', // New style Rabobank (NL). Short for "Af"
|
||||||
|
'Af', // ING (NL).
|
||||||
|
'Debet', // Triodos (NL)
|
||||||
|
];
|
||||||
|
if (\in_array(trim($value), $negative, true)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
// old format:
|
|
||||||
if ('A' === $value) {
|
|
||||||
Log::debug('Return -1');
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::debug('Return 1');
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,25 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateRecurringTransactions.php
|
||||||
|
* Copyright (c) 2018 thegrumpydictator@gmail.com
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III.
|
||||||
|
*
|
||||||
|
* Firefly III is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Firefly III 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
|
||||||
@@ -27,6 +48,8 @@ namespace FireflyIII\Jobs;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Events\RequestedReportOnJournals;
|
use FireflyIII\Events\RequestedReportOnJournals;
|
||||||
use FireflyIII\Events\StoredTransactionJournal;
|
use FireflyIII\Events\StoredTransactionJournal;
|
||||||
|
use FireflyIII\Factory\PiggyBankEventFactory;
|
||||||
|
use FireflyIII\Factory\PiggyBankFactory;
|
||||||
use FireflyIII\Models\Recurrence;
|
use FireflyIII\Models\Recurrence;
|
||||||
use FireflyIII\Models\RecurrenceMeta;
|
use FireflyIII\Models\RecurrenceMeta;
|
||||||
use FireflyIII\Models\RecurrenceRepetition;
|
use FireflyIII\Models\RecurrenceRepetition;
|
||||||
@@ -335,9 +358,23 @@ class CreateRecurringTransactions implements ShouldQueue
|
|||||||
|
|
||||||
// get piggy bank ID from meta data:
|
// get piggy bank ID from meta data:
|
||||||
$piggyBankId = $this->getPiggyId($recurrence);
|
$piggyBankId = $this->getPiggyId($recurrence);
|
||||||
|
Log::debug(sprintf('Piggy bank ID for recurrence #%d is #%d', $recurrence->id, $piggyBankId));
|
||||||
|
|
||||||
// trigger event:
|
// trigger event:
|
||||||
event(new StoredTransactionJournal($journal, $piggyBankId));
|
event(new StoredTransactionJournal($journal));
|
||||||
|
|
||||||
|
// link to piggy bank:
|
||||||
|
/** @var PiggyBankFactory $factory */
|
||||||
|
$factory = app(PiggyBankFactory::class);
|
||||||
|
$factory->setUser($recurrence->user);
|
||||||
|
|
||||||
|
$piggyBank = $factory->find($piggyBankId, null);
|
||||||
|
if (null !== $piggyBank) {
|
||||||
|
/** @var PiggyBankEventFactory $factory */
|
||||||
|
$factory = app(PiggyBankEventFactory::class);
|
||||||
|
$factory->create($journal, $piggyBank);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$collection->push($journal);
|
$collection->push($journal);
|
||||||
// update recurring thing:
|
// update recurring thing:
|
||||||
|
|||||||
@@ -143,49 +143,6 @@ class Account extends Model
|
|||||||
return $name;
|
return $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*
|
|
||||||
* @throws FireflyException
|
|
||||||
*/
|
|
||||||
public function getIbanAttribute($value): string
|
|
||||||
{
|
|
||||||
if ('' === (string)$value) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$result = Crypt::decrypt($value);
|
|
||||||
} catch (DecryptException $e) {
|
|
||||||
Log::error($e->getMessage());
|
|
||||||
Log::error($e->getTraceAsString());
|
|
||||||
throw new FireflyException('Cannot decrypt value "' . $value . '" for account #' . $this->id);
|
|
||||||
}
|
|
||||||
if (null === $result) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\DecryptException
|
|
||||||
*/
|
|
||||||
public function getNameAttribute($value): ?string
|
|
||||||
{
|
|
||||||
if ($this->encrypted) {
|
|
||||||
return Crypt::decrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the opening balance.
|
* Returns the opening balance.
|
||||||
*
|
*
|
||||||
@@ -237,31 +194,6 @@ class Account extends Model
|
|||||||
$query->whereIn('account_types.type', $types);
|
$query->whereIn('account_types.type', $types);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\EncryptException
|
|
||||||
*/
|
|
||||||
public function setIbanAttribute($value): void
|
|
||||||
{
|
|
||||||
$this->attributes['iban'] = Crypt::encrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\EncryptException
|
|
||||||
*/
|
|
||||||
public function setNameAttribute($value): void
|
|
||||||
{
|
|
||||||
$encrypt = config('firefly.encryption');
|
|
||||||
$this->attributes['name'] = $encrypt ? Crypt::encrypt($value) : $value;
|
|
||||||
$this->attributes['encrypted'] = $encrypt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Models;
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Crypt;
|
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
@@ -115,70 +114,6 @@ class Attachment extends Model
|
|||||||
return sprintf('at-%s.data', (string)$this->id);
|
return sprintf('at-%s.data', (string)$this->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
* @return null|string
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\DecryptException
|
|
||||||
*/
|
|
||||||
public function getDescriptionAttribute($value): ?string
|
|
||||||
{
|
|
||||||
if (null === $value || '' === $value) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Crypt::decrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
* @return null|string
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\DecryptException
|
|
||||||
*/
|
|
||||||
public function getFilenameAttribute($value): ?string
|
|
||||||
{
|
|
||||||
if (null === $value || '' === $value) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Crypt::decrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
* @return null|string
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\DecryptException
|
|
||||||
*/
|
|
||||||
public function getMimeAttribute($value): ?string
|
|
||||||
{
|
|
||||||
if (null === $value || '' === $value) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Crypt::decrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
* @return null|string
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\DecryptException
|
|
||||||
*/
|
|
||||||
public function getTitleAttribute($value): ?string
|
|
||||||
{
|
|
||||||
if (null === $value || '' === $value) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Crypt::decrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
* Get all of the notes.
|
* Get all of the notes.
|
||||||
@@ -188,57 +123,6 @@ class Attachment extends Model
|
|||||||
return $this->morphMany(Note::class, 'noteable');
|
return $this->morphMany(Note::class, 'noteable');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param string|null $value
|
|
||||||
*/
|
|
||||||
public function setDescriptionAttribute(string $value = null): void
|
|
||||||
{
|
|
||||||
if (null !== $value) {
|
|
||||||
$this->attributes['description'] = Crypt::encrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param string $value
|
|
||||||
*
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\EncryptException
|
|
||||||
*/
|
|
||||||
public function setFilenameAttribute(string $value): void
|
|
||||||
{
|
|
||||||
$this->attributes['filename'] = Crypt::encrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param string $value
|
|
||||||
*
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\EncryptException
|
|
||||||
*/
|
|
||||||
public function setMimeAttribute(string $value): void
|
|
||||||
{
|
|
||||||
$this->attributes['mime'] = Crypt::encrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param string $value
|
|
||||||
*
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\EncryptException
|
|
||||||
*/
|
|
||||||
public function setTitleAttribute(string $value = null): void
|
|
||||||
{
|
|
||||||
if (null !== $value) {
|
|
||||||
$this->attributes['title'] = Crypt::encrypt($value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
* @return BelongsTo
|
* @return BelongsTo
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Models;
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Crypt;
|
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
@@ -80,7 +79,7 @@ class Bill extends Model
|
|||||||
|
|
||||||
/** @var array Fields that can be filled */
|
/** @var array Fields that can be filled */
|
||||||
protected $fillable
|
protected $fillable
|
||||||
= ['name', 'match', 'amount_min', 'match_encrypted', 'name_encrypted', 'user_id', 'amount_max', 'date', 'repeat_freq', 'skip',
|
= ['name', 'match', 'amount_min', 'user_id', 'amount_max', 'date', 'repeat_freq', 'skip',
|
||||||
'automatch', 'active', 'transaction_currency_id'];
|
'automatch', 'active', 'transaction_currency_id'];
|
||||||
/** @var array Hidden from view */
|
/** @var array Hidden from view */
|
||||||
protected $hidden = ['amount_min_encrypted', 'amount_max_encrypted', 'name_encrypted', 'match_encrypted'];
|
protected $hidden = ['amount_min_encrypted', 'amount_max_encrypted', 'name_encrypted', 'match_encrypted'];
|
||||||
@@ -117,40 +116,6 @@ class Bill extends Model
|
|||||||
return $this->morphMany(Attachment::class, 'attachable');
|
return $this->morphMany(Attachment::class, 'attachable');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\DecryptException
|
|
||||||
*/
|
|
||||||
public function getMatchAttribute($value): string
|
|
||||||
{
|
|
||||||
if (1 === (int)$this->match_encrypted) {
|
|
||||||
return Crypt::decrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @return string|null
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\DecryptException
|
|
||||||
*/
|
|
||||||
public function getNameAttribute($value): ?string
|
|
||||||
{
|
|
||||||
if (1 === (int)$this->name_encrypted) {
|
|
||||||
return Crypt::decrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
* Get all of the notes.
|
* Get all of the notes.
|
||||||
@@ -180,32 +145,6 @@ class Bill extends Model
|
|||||||
$this->attributes['amount_min'] = (string)$value;
|
$this->attributes['amount_min'] = (string)$value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\EncryptException
|
|
||||||
*/
|
|
||||||
public function setMatchAttribute($value): void
|
|
||||||
{
|
|
||||||
$encrypt = config('firefly.encryption');
|
|
||||||
$this->attributes['match'] = $encrypt ? Crypt::encrypt($value) : $value;
|
|
||||||
$this->attributes['match_encrypted'] = $encrypt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\EncryptException
|
|
||||||
*/
|
|
||||||
public function setNameAttribute($value): void
|
|
||||||
{
|
|
||||||
$encrypt = config('firefly.encryption');
|
|
||||||
$this->attributes['name'] = $encrypt ? Crypt::encrypt($value) : $value;
|
|
||||||
$this->attributes['name_encrypted'] = $encrypt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
* @return BelongsTo
|
* @return BelongsTo
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Models;
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Crypt;
|
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
@@ -101,37 +100,6 @@ class Budget extends Model
|
|||||||
return $this->hasMany(BudgetLimit::class);
|
return $this->hasMany(BudgetLimit::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @return string|null
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\DecryptException
|
|
||||||
*/
|
|
||||||
public function getNameAttribute($value): ?string
|
|
||||||
{
|
|
||||||
if ($this->encrypted) {
|
|
||||||
return Crypt::decrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\EncryptException
|
|
||||||
*/
|
|
||||||
public function setNameAttribute($value): void
|
|
||||||
{
|
|
||||||
$encrypt = config('firefly.encryption');
|
|
||||||
$this->attributes['name'] = $encrypt ? Crypt::encrypt($value) : $value;
|
|
||||||
$this->attributes['encrypted'] = $encrypt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
* @return BelongsToMany
|
* @return BelongsToMany
|
||||||
|
|||||||
@@ -88,37 +88,6 @@ class Category extends Model
|
|||||||
throw new NotFoundHttpException;
|
throw new NotFoundHttpException;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @return string|null
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\DecryptException
|
|
||||||
*/
|
|
||||||
public function getNameAttribute($value): ?string
|
|
||||||
{
|
|
||||||
if ($this->encrypted) {
|
|
||||||
return Crypt::decrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\EncryptException
|
|
||||||
*/
|
|
||||||
public function setNameAttribute($value): void
|
|
||||||
{
|
|
||||||
$encrypt = config('firefly.encryption');
|
|
||||||
$this->attributes['name'] = $encrypt ? Crypt::encrypt($value) : $value;
|
|
||||||
$this->attributes['encrypted'] = $encrypt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
* @return BelongsToMany
|
* @return BelongsToMany
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Models;
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Crypt;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
@@ -104,23 +103,6 @@ class PiggyBank extends Model
|
|||||||
return $this->belongsTo(Account::class);
|
return $this->belongsTo(Account::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @return string|null
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\DecryptException
|
|
||||||
*/
|
|
||||||
public function getNameAttribute($value): ?string
|
|
||||||
{
|
|
||||||
if ($this->encrypted) {
|
|
||||||
return Crypt::decrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
* Get all of the piggy bank's notes.
|
* Get all of the piggy bank's notes.
|
||||||
@@ -148,20 +130,6 @@ class PiggyBank extends Model
|
|||||||
return $this->hasMany(PiggyBankRepetition::class);
|
return $this->hasMany(PiggyBankRepetition::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\EncryptException
|
|
||||||
*/
|
|
||||||
public function setNameAttribute($value): void
|
|
||||||
{
|
|
||||||
$encrypt = config('firefly.encryption');
|
|
||||||
$this->attributes['name'] = $encrypt ? Crypt::encrypt($value) : $value;
|
|
||||||
$this->attributes['encrypted'] = $encrypt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ class Preference extends Model
|
|||||||
= [
|
= [
|
||||||
'created_at' => 'datetime',
|
'created_at' => 'datetime',
|
||||||
'updated_at' => 'datetime',
|
'updated_at' => 'datetime',
|
||||||
|
'data' => 'array',
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @var array Fields that can be filled */
|
/** @var array Fields that can be filled */
|
||||||
@@ -81,54 +82,6 @@ class Preference extends Model
|
|||||||
throw new NotFoundHttpException;
|
throw new NotFoundHttpException;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*
|
|
||||||
* @throws FireflyException
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
*/
|
|
||||||
public function getDataAttribute($value)
|
|
||||||
{
|
|
||||||
$result = null;
|
|
||||||
try {
|
|
||||||
$data = Crypt::decrypt($value);
|
|
||||||
} catch (DecryptException $e) {
|
|
||||||
Log::error(sprintf('Could not decrypt preference: %s', $e->getMessage()), ['id' => $this->id, 'name' => $this->name, 'data' => $value]);
|
|
||||||
throw new FireflyException(
|
|
||||||
sprintf('Could not decrypt preference #%d. If this error persists, please run "php artisan cache:clear" on the command line.', $this->id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$serialized = true;
|
|
||||||
try {
|
|
||||||
unserialize($data, ['allowed_classes' => false]);
|
|
||||||
} /** @noinspection BadExceptionsProcessingInspection */ catch (Exception $e) {
|
|
||||||
$serialized = false;
|
|
||||||
}
|
|
||||||
if (!$serialized) {
|
|
||||||
$result = json_decode($data, true);
|
|
||||||
}
|
|
||||||
if ($serialized) {
|
|
||||||
Log::error(sprintf('Preference #%d ("%s") was stored as serialised object. It will be deleted and recreated.', $this->id, $this->name));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\EncryptException
|
|
||||||
*/
|
|
||||||
public function setDataAttribute($value): void
|
|
||||||
{
|
|
||||||
$this->attributes['data'] = Crypt::encrypt(json_encode($value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
* @return BelongsTo
|
* @return BelongsTo
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
|||||||
* @property int $id
|
* @property int $id
|
||||||
* @property \Carbon\Carbon $date
|
* @property \Carbon\Carbon $date
|
||||||
* @property int zoomLevel
|
* @property int zoomLevel
|
||||||
* @property float longitude
|
|
||||||
* @property float latitude
|
* @property float latitude
|
||||||
|
* @property float longitude
|
||||||
* @property string description
|
* @property string description
|
||||||
* @property string amount_sum
|
* @property string amount_sum
|
||||||
* @property string tagMode
|
* @property string tagMode
|
||||||
@@ -64,9 +64,11 @@ class Tag extends Model
|
|||||||
'deleted_at' => 'datetime',
|
'deleted_at' => 'datetime',
|
||||||
'date' => 'date',
|
'date' => 'date',
|
||||||
'zoomLevel' => 'int',
|
'zoomLevel' => 'int',
|
||||||
|
'latitude' => 'float',
|
||||||
|
'longitude' => 'float',
|
||||||
];
|
];
|
||||||
/** @var array Fields that can be filled */
|
/** @var array Fields that can be filled */
|
||||||
protected $fillable = ['user_id', 'tag', 'date', 'description', 'longitude', 'latitude', 'zoomLevel', 'tagMode'];
|
protected $fillable = ['user_id', 'tag', 'date', 'description', 'latitude', 'longitude', 'zoomLevel', 'tagMode'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Route binder. Converts the key in the URL to the specified object (or throw 404).
|
* Route binder. Converts the key in the URL to the specified object (or throw 404).
|
||||||
@@ -91,63 +93,6 @@ class Tag extends Model
|
|||||||
throw new NotFoundHttpException;
|
throw new NotFoundHttpException;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @return string|null
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\DecryptException
|
|
||||||
*/
|
|
||||||
public function getDescriptionAttribute($value): ?string
|
|
||||||
{
|
|
||||||
if (null === $value) {
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Crypt::decrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @return string|null
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\DecryptException
|
|
||||||
*/
|
|
||||||
public function getTagAttribute($value): ?string
|
|
||||||
{
|
|
||||||
if (null === $value) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Crypt::decrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\EncryptException
|
|
||||||
*/
|
|
||||||
public function setDescriptionAttribute($value): void
|
|
||||||
{
|
|
||||||
$this->attributes['description'] = Crypt::encrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\EncryptException
|
|
||||||
*/
|
|
||||||
public function setTagAttribute($value): void
|
|
||||||
{
|
|
||||||
$this->attributes['tag'] = Crypt::encrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
|
|||||||
100
app/Models/TransactionGroup.php
Normal file
100
app/Models/TransactionGroup.php
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* TransactionGroup.php
|
||||||
|
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III.
|
||||||
|
*
|
||||||
|
* Firefly III is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Firefly III 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TransactionGroup.
|
||||||
|
*/
|
||||||
|
class TransactionGroup extends Model
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that should be casted to native types.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $casts
|
||||||
|
= [
|
||||||
|
'created_at' => 'datetime',
|
||||||
|
'updated_at' => 'datetime',
|
||||||
|
'deleted_at' => 'datetime',
|
||||||
|
'title' => 'string',
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @var array Fields that can be filled */
|
||||||
|
protected $fillable
|
||||||
|
= ['user_id', 'title'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route binder. Converts the key in the URL to the specified object (or throw 404).
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
*
|
||||||
|
* @return TransactionGroup
|
||||||
|
* @throws NotFoundHttpException
|
||||||
|
*/
|
||||||
|
public static function routeBinder(string $value): TransactionGroup
|
||||||
|
{
|
||||||
|
if (auth()->check()) {
|
||||||
|
$groupId = (int)$value;
|
||||||
|
/** @var User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
/** @var TransactionGroup $group */
|
||||||
|
$group = $user->transactionGroups()->where('transaction_groups.id', $groupId)
|
||||||
|
->first(['transaction_groups.*']);
|
||||||
|
if (null !== $group) {
|
||||||
|
return $group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotFoundHttpException;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
* @return BelongsToMany
|
||||||
|
*/
|
||||||
|
public function transactionJournals(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(TransactionJournal::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
* @return BelongsTo
|
||||||
|
*/
|
||||||
|
public function user(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -188,23 +188,14 @@ class TransactionJournal extends Model
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
*
|
* @return BelongsToMany
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @return string|null
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\DecryptException
|
|
||||||
*/
|
*/
|
||||||
public function getDescriptionAttribute($value): ?string
|
public function transactionGroups(): BelongsToMany
|
||||||
{
|
{
|
||||||
if ($this->encrypted) {
|
return $this->belongsToMany(Category::class);
|
||||||
return Crypt::decrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isDeposit(): bool
|
public function isDeposit(): bool
|
||||||
@@ -315,20 +306,6 @@ class TransactionJournal extends Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\EncryptException
|
|
||||||
*/
|
|
||||||
public function setDescriptionAttribute($value): void
|
|
||||||
{
|
|
||||||
$encrypt = config('firefly.encryption');
|
|
||||||
$this->attributes['description'] = $encrypt ? Crypt::encrypt($value) : $value;
|
|
||||||
$this->attributes['encrypted'] = $encrypt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
* @return HasMany
|
* @return HasMany
|
||||||
|
|||||||
@@ -93,22 +93,6 @@ class TransactionJournalLink extends Model
|
|||||||
return $this->belongsTo(TransactionJournal::class, 'destination_id');
|
return $this->belongsTo(TransactionJournal::class, 'destination_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @return null|string
|
|
||||||
*/
|
|
||||||
public function getCommentAttribute($value): ?string
|
|
||||||
{
|
|
||||||
if (null !== $value) {
|
|
||||||
return app('steam')->tryDecrypt($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
* @return BelongsTo
|
* @return BelongsTo
|
||||||
@@ -127,23 +111,6 @@ class TransactionJournalLink extends Model
|
|||||||
return $this->morphMany(Note::class, 'noteable');
|
return $this->morphMany(Note::class, 'noteable');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @throws \Illuminate\Contracts\Encryption\EncryptException
|
|
||||||
*/
|
|
||||||
public function setCommentAttribute($value): void
|
|
||||||
{
|
|
||||||
if (null !== $value && \strlen($value) > 0) {
|
|
||||||
$this->attributes['comment'] = Crypt::encrypt($value);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->attributes['comment'] = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
* @return BelongsTo
|
* @return BelongsTo
|
||||||
|
|||||||
@@ -185,7 +185,11 @@ class FireflyServiceProvider extends ServiceProvider
|
|||||||
$this->app->bind(FiscalHelperInterface::class, FiscalHelper::class);
|
$this->app->bind(FiscalHelperInterface::class, FiscalHelper::class);
|
||||||
$this->app->bind(BalanceReportHelperInterface::class, BalanceReportHelper::class);
|
$this->app->bind(BalanceReportHelperInterface::class, BalanceReportHelper::class);
|
||||||
$this->app->bind(BudgetReportHelperInterface::class, BudgetReportHelper::class);
|
$this->app->bind(BudgetReportHelperInterface::class, BudgetReportHelper::class);
|
||||||
$this->app->bind(ExchangeRateInterface::class, FixerIOv2::class);
|
$class = (string)config(sprintf('firefly.cer_providers.%s', (string)config('firefly.cer_provider')));
|
||||||
|
if('' === $class) {
|
||||||
|
throw new FireflyException('Invalid currency exchange rate provider. Cannot continue.');
|
||||||
|
}
|
||||||
|
$this->app->bind(ExchangeRateInterface::class, $class);
|
||||||
|
|
||||||
// password verifier thing
|
// password verifier thing
|
||||||
$this->app->bind(Verifier::class, PwndVerifierV2::class);
|
$this->app->bind(Verifier::class, PwndVerifierV2::class);
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ use FireflyIII\Models\Transaction;
|
|||||||
use FireflyIII\Models\TransactionCurrency;
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
|
use FireflyIII\Services\Internal\Destroy\BudgetDestroyService;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -185,11 +186,9 @@ class BudgetRepository implements BudgetRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function destroy(Budget $budget): bool
|
public function destroy(Budget $budget): bool
|
||||||
{
|
{
|
||||||
try {
|
/** @var BudgetDestroyService $service */
|
||||||
$budget->delete();
|
$service = app(BudgetDestroyService::class);
|
||||||
} catch (Exception $e) {
|
$service->destroy($budget);
|
||||||
Log::error(sprintf('Could not delete budget: %s', $e->getMessage()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -220,26 +219,6 @@ class BudgetRepository implements BudgetRepositoryInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a budget.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
*
|
|
||||||
* @return Budget|null
|
|
||||||
*/
|
|
||||||
public function findByName(string $name): ?Budget
|
|
||||||
{
|
|
||||||
$budgets = $this->user->budgets()->get(['budgets.*']);
|
|
||||||
/** @var Budget $budget */
|
|
||||||
foreach ($budgets as $budget) {
|
|
||||||
if ($budget->name === $name) {
|
|
||||||
return $budget;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a budget or return NULL
|
* Find a budget or return NULL
|
||||||
*
|
*
|
||||||
@@ -401,6 +380,26 @@ class BudgetRepository implements BudgetRepositoryInterface
|
|||||||
return $amount;
|
return $amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getAvailableBudgetWithCurrency(Carbon $start, Carbon $end): array
|
||||||
|
{
|
||||||
|
$return = [];
|
||||||
|
$availableBudgets = $this->user->availableBudgets()
|
||||||
|
->where('start_date', $start->format('Y-m-d 00:00:00'))
|
||||||
|
->where('end_date', $end->format('Y-m-d 00:00:00'))->get();
|
||||||
|
/** @var AvailableBudget $availableBudget */
|
||||||
|
foreach ($availableBudgets as $availableBudget) {
|
||||||
|
$return[$availableBudget->transaction_currency_id] = $availableBudget->amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all available budget objects.
|
* Returns all available budget objects.
|
||||||
*
|
*
|
||||||
@@ -441,6 +440,8 @@ class BudgetRepository implements BudgetRepositoryInterface
|
|||||||
return bcdiv($total, (string)$days);
|
return bcdiv($total, (string)$days);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @noinspection MoreThanThreeArgumentsInspection */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Budget $budget
|
* @param Budget $budget
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
@@ -506,7 +507,6 @@ class BudgetRepository implements BudgetRepositoryInterface
|
|||||||
return $set;
|
return $set;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
/**
|
/**
|
||||||
* This method is being used to generate the budget overview in the year/multi-year report. Its used
|
* This method is being used to generate the budget overview in the year/multi-year report. Its used
|
||||||
* in both the year/multi-year budget overview AND in the accompanying chart.
|
* in both the year/multi-year budget overview AND in the accompanying chart.
|
||||||
@@ -600,6 +600,8 @@ class BudgetRepository implements BudgetRepositoryInterface
|
|||||||
return $set;
|
return $set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @noinspection MoreThanThreeArgumentsInspection */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
@@ -634,7 +636,6 @@ class BudgetRepository implements BudgetRepositoryInterface
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
/**
|
/**
|
||||||
* @param TransactionCurrency $currency
|
* @param TransactionCurrency $currency
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
@@ -662,6 +663,8 @@ class BudgetRepository implements BudgetRepositoryInterface
|
|||||||
return $availableBudget;
|
return $availableBudget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @noinspection MoreThanThreeArgumentsInspection */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Budget $budget
|
* @param Budget $budget
|
||||||
* @param int $order
|
* @param int $order
|
||||||
@@ -672,8 +675,6 @@ class BudgetRepository implements BudgetRepositoryInterface
|
|||||||
$budget->save();
|
$budget->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param User $user
|
* @param User $user
|
||||||
*/
|
*/
|
||||||
@@ -923,6 +924,8 @@ class BudgetRepository implements BudgetRepositoryInterface
|
|||||||
return $budget;
|
return $budget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @noinspection MoreThanThreeArgumentsInspection */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param AvailableBudget $availableBudget
|
* @param AvailableBudget $availableBudget
|
||||||
* @param array $data
|
* @param array $data
|
||||||
@@ -952,8 +955,6 @@ class BudgetRepository implements BudgetRepositoryInterface
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param BudgetLimit $budgetLimit
|
* @param BudgetLimit $budgetLimit
|
||||||
* @param array $data
|
* @param array $data
|
||||||
|
|||||||
@@ -80,15 +80,6 @@ interface BudgetRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function destroyBudgetLimit(BudgetLimit $budgetLimit): void;
|
public function destroyBudgetLimit(BudgetLimit $budgetLimit): void;
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a budget.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
*
|
|
||||||
* @return Budget|null
|
|
||||||
*/
|
|
||||||
public function findByName(string $name): ?Budget;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int|null $budgetId
|
* @param int|null $budgetId
|
||||||
*
|
*
|
||||||
@@ -128,6 +119,14 @@ interface BudgetRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function getAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end): string;
|
public function getAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getAvailableBudgetWithCurrency(Carbon $start, Carbon $end): array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all available budget objects.
|
* Returns all available budget objects.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -116,6 +116,68 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
return $collector->getTransactions();
|
return $collector->getTransactions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $categories
|
||||||
|
* @param Collection $accounts
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function earnedInPeriodPerCurrency(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array
|
||||||
|
{
|
||||||
|
/** @var TransactionCollectorInterface $collector */
|
||||||
|
$collector = app(TransactionCollectorInterface::class);
|
||||||
|
$collector->setUser($this->user);
|
||||||
|
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]);
|
||||||
|
|
||||||
|
if ($categories->count() > 0) {
|
||||||
|
$collector->setCategories($categories);
|
||||||
|
}
|
||||||
|
if ($categories->count() === 0) {
|
||||||
|
$collector->setCategories($this->getCategories());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($accounts->count() > 0) {
|
||||||
|
$collector->setAccounts($accounts);
|
||||||
|
}
|
||||||
|
if (0 === $accounts->count()) {
|
||||||
|
$collector->setAllAssetAccounts();
|
||||||
|
}
|
||||||
|
|
||||||
|
$set = $collector->getTransactions();
|
||||||
|
$return = [];
|
||||||
|
/** @var Transaction $transaction */
|
||||||
|
foreach ($set as $transaction) {
|
||||||
|
$jrnlCatId = (int)$transaction->transaction_journal_category_id;
|
||||||
|
$transCatId = (int)$transaction->transaction_category_id;
|
||||||
|
$categoryId = max($jrnlCatId, $transCatId);
|
||||||
|
$currencyId = (int)$transaction->transaction_currency_id;
|
||||||
|
$name = $transaction->transaction_category_name;
|
||||||
|
$name = '' === (string)$name ? $transaction->transaction_journal_category_name : $name;
|
||||||
|
// make array for category:
|
||||||
|
if (!isset($return[$categoryId])) {
|
||||||
|
$return[$categoryId] = [
|
||||||
|
'name' => $name,
|
||||||
|
'earned' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (!isset($return[$categoryId]['earned'][$currencyId])) {
|
||||||
|
$return[$categoryId]['earned'][$currencyId] = [
|
||||||
|
'earned' => '0',
|
||||||
|
'currency_id' => $currencyId,
|
||||||
|
'currency_symbol' => $transaction->transaction_currency_symbol,
|
||||||
|
'currency_code' => $transaction->transaction_currency_code,
|
||||||
|
'currency_decimal_places' => $transaction->transaction_currency_dp,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$return[$categoryId]['earned'][$currencyId]['earned']
|
||||||
|
= bcadd($return[$categoryId]['earned'][$currencyId]['earned'], $transaction->transaction_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a category.
|
* Find a category.
|
||||||
*
|
*
|
||||||
@@ -187,6 +249,8 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
return $this->user->categories()->whereIn('id', $categoryIds)->get();
|
return $this->user->categories()->whereIn('id', $categoryIds)->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @noinspection MoreThanThreeArgumentsInspection */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of all the categories belonging to a user.
|
* Returns a list of all the categories belonging to a user.
|
||||||
*
|
*
|
||||||
@@ -205,8 +269,6 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
return $set;
|
return $set;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Category $category
|
* @param Category $category
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
@@ -237,6 +299,8 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
return $lastJournalDate;
|
return $lastJournalDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @noinspection MoreThanThreeArgumentsInspection */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $categories
|
* @param Collection $categories
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
@@ -282,8 +346,6 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
@@ -367,6 +429,8 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @noinspection MoreThanThreeArgumentsInspection */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
@@ -419,6 +483,7 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
/** @noinspection MoreThanThreeArgumentsInspection */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $categories
|
* @param Collection $categories
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
@@ -435,7 +500,6 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
return (string)$set->sum('transaction_amount');
|
return (string)$set->sum('transaction_amount');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $categories
|
* @param Collection $categories
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
@@ -501,8 +565,16 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
/** @var Transaction $transaction */
|
/** @var Transaction $transaction */
|
||||||
foreach ($set as $transaction) {
|
foreach ($set as $transaction) {
|
||||||
$currencyId = $transaction->transaction_currency_id;
|
$currencyId = $transaction->transaction_currency_id;
|
||||||
$return[$currencyId] = $return[$currencyId] ?? '0';
|
if (!isset($return[$currencyId])) {
|
||||||
$return[$currencyId] = bcadd($return[$currencyId], $transaction->transaction_amount);
|
$return[$currencyId] = [
|
||||||
|
'spent' => '0',
|
||||||
|
'currency_id' => $currencyId,
|
||||||
|
'currency_symbol' => $transaction->transaction_currency_symbol,
|
||||||
|
'currency_code' => $transaction->transaction_currency_code,
|
||||||
|
'currency_decimal_places' => $transaction->transaction_currency_dp,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$return[$currencyId]['spent'] = bcadd($return[$currencyId]['spent'], $transaction->transaction_amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
@@ -521,7 +593,14 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
/** @var TransactionCollectorInterface $collector */
|
/** @var TransactionCollectorInterface $collector */
|
||||||
$collector = app(TransactionCollectorInterface::class);
|
$collector = app(TransactionCollectorInterface::class);
|
||||||
$collector->setUser($this->user);
|
$collector->setUser($this->user);
|
||||||
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setCategories($categories);
|
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]);
|
||||||
|
|
||||||
|
if ($categories->count() > 0) {
|
||||||
|
$collector->setCategories($categories);
|
||||||
|
}
|
||||||
|
if ($categories->count() === 0) {
|
||||||
|
$collector->setCategories($this->getCategories());
|
||||||
|
}
|
||||||
|
|
||||||
if ($accounts->count() > 0) {
|
if ($accounts->count() > 0) {
|
||||||
$collector->setAccounts($accounts);
|
$collector->setAccounts($accounts);
|
||||||
@@ -534,9 +613,31 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
$return = [];
|
$return = [];
|
||||||
/** @var Transaction $transaction */
|
/** @var Transaction $transaction */
|
||||||
foreach ($set as $transaction) {
|
foreach ($set as $transaction) {
|
||||||
$currencyId = $transaction->transaction_currency_id;
|
$jrnlCatId = (int)$transaction->transaction_journal_category_id;
|
||||||
$return[$currencyId] = $return[$currencyId] ?? '0';
|
$transCatId = (int)$transaction->transaction_category_id;
|
||||||
$return[$currencyId] = bcadd($return[$currencyId], $transaction->transaction_amount);
|
$categoryId = max($jrnlCatId, $transCatId);
|
||||||
|
$currencyId = (int)$transaction->transaction_currency_id;
|
||||||
|
$name = $transaction->transaction_category_name;
|
||||||
|
$name = '' === (string)$name ? $transaction->transaction_journal_category_name : $name;
|
||||||
|
|
||||||
|
// make array for category:
|
||||||
|
if (!isset($return[$categoryId])) {
|
||||||
|
$return[$categoryId] = [
|
||||||
|
'name' => $name,
|
||||||
|
'spent' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (!isset($return[$categoryId]['spent'][$currencyId])) {
|
||||||
|
$return[$categoryId]['spent'][$currencyId] = [
|
||||||
|
'spent' => '0',
|
||||||
|
'currency_id' => $currencyId,
|
||||||
|
'currency_symbol' => $transaction->transaction_currency_symbol,
|
||||||
|
'currency_code' => $transaction->transaction_currency_code,
|
||||||
|
'currency_decimal_places' => $transaction->transaction_currency_dp,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$return[$categoryId]['spent'][$currencyId]['spent']
|
||||||
|
= bcadd($return[$categoryId]['spent'][$currencyId]['spent'], $transaction->transaction_amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
@@ -690,4 +791,59 @@ class CategoryRepository implements CategoryRepositoryInterface
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A very cryptic method name that means:
|
||||||
|
*
|
||||||
|
* Get me the amount earned in this period, grouped per currency, where no category was set.
|
||||||
|
*
|
||||||
|
* @param Collection $accounts
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function earnedInPeriodPcWoCategory(Collection $accounts, Carbon $start, Carbon $end): array
|
||||||
|
{
|
||||||
|
/** @var TransactionCollectorInterface $collector */
|
||||||
|
$collector = app(TransactionCollectorInterface::class);
|
||||||
|
$collector->setUser($this->user);
|
||||||
|
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->withoutCategory();
|
||||||
|
|
||||||
|
if ($accounts->count() > 0) {
|
||||||
|
$collector->setAccounts($accounts);
|
||||||
|
}
|
||||||
|
if (0 === $accounts->count()) {
|
||||||
|
$collector->setAllAssetAccounts();
|
||||||
|
}
|
||||||
|
|
||||||
|
$set = $collector->getTransactions();
|
||||||
|
$set = $set->filter(
|
||||||
|
function (Transaction $transaction) {
|
||||||
|
if (bccomp($transaction->transaction_amount, '0') === 1) {
|
||||||
|
return $transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$return = [];
|
||||||
|
/** @var Transaction $transaction */
|
||||||
|
foreach ($set as $transaction) {
|
||||||
|
$currencyId = $transaction->transaction_currency_id;
|
||||||
|
if (!isset($return[$currencyId])) {
|
||||||
|
$return[$currencyId] = [
|
||||||
|
'spent' => '0',
|
||||||
|
'currency_id' => $currencyId,
|
||||||
|
'currency_symbol' => $transaction->transaction_currency_symbol,
|
||||||
|
'currency_code' => $transaction->transaction_currency_code,
|
||||||
|
'currency_decimal_places' => $transaction->transaction_currency_dp,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$return[$currencyId]['spent'] = bcadd($return[$currencyId]['spent'], $transaction->transaction_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,10 +191,23 @@ interface CategoryRepositoryInterface
|
|||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
* @param Carbon $end
|
* @param Carbon $end
|
||||||
*
|
*
|
||||||
* @return string
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function spentInPeriodPcWoCategory(Collection $accounts, Carbon $start, Carbon $end): array;
|
public function spentInPeriodPcWoCategory(Collection $accounts, Carbon $start, Carbon $end): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A very cryptic method name that means:
|
||||||
|
*
|
||||||
|
* Get me the amount earned in this period, grouped per currency, where no category was set.
|
||||||
|
*
|
||||||
|
* @param Collection $accounts
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function earnedInPeriodPcWoCategory(Collection $accounts, Carbon $start, Carbon $end): array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $categories
|
* @param Collection $categories
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
@@ -205,6 +218,16 @@ interface CategoryRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function spentInPeriodPerCurrency(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array;
|
public function spentInPeriodPerCurrency(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $categories
|
||||||
|
* @param Collection $accounts
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function earnedInPeriodPerCurrency(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
|
|||||||
@@ -39,20 +39,20 @@ use FireflyIII\Models\TransactionJournal;
|
|||||||
use FireflyIII\Models\TransactionJournalMeta;
|
use FireflyIII\Models\TransactionJournalMeta;
|
||||||
use FireflyIII\Services\Internal\Destroy\RecurrenceDestroyService;
|
use FireflyIII\Services\Internal\Destroy\RecurrenceDestroyService;
|
||||||
use FireflyIII\Services\Internal\Update\RecurrenceUpdateService;
|
use FireflyIII\Services\Internal\Update\RecurrenceUpdateService;
|
||||||
|
use FireflyIII\Support\Repositories\Recurring\CalculateRangeOccurrences;
|
||||||
|
use FireflyIII\Support\Repositories\Recurring\CalculateXOccurrences;
|
||||||
|
use FireflyIII\Support\Repositories\Recurring\FiltersWeekends;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Log;
|
use Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Class RecurringRepository
|
* Class RecurringRepository
|
||||||
*
|
|
||||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
|
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
|
|
||||||
*/
|
*/
|
||||||
class RecurringRepository implements RecurringRepositoryInterface
|
class RecurringRepository implements RecurringRepositoryInterface
|
||||||
{
|
{
|
||||||
|
use CalculateRangeOccurrences, CalculateXOccurrences, FiltersWeekends;
|
||||||
/** @var User */
|
/** @var User */
|
||||||
private $user;
|
private $user;
|
||||||
|
|
||||||
@@ -453,451 +453,4 @@ class RecurringRepository implements RecurringRepositoryInterface
|
|||||||
|
|
||||||
return $service->update($recurrence, $data);
|
return $service->update($recurrence, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters out all weekend entries, if necessary.
|
|
||||||
*
|
|
||||||
* @param RecurrenceRepetition $repetition
|
|
||||||
* @param array $dates
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
|
||||||
*/
|
|
||||||
private function filterWeekends(RecurrenceRepetition $repetition, array $dates): array
|
|
||||||
{
|
|
||||||
if ((int)$repetition->weekend === RecurrenceRepetition::WEEKEND_DO_NOTHING) {
|
|
||||||
Log::debug('Repetition will not be filtered on weekend days.');
|
|
||||||
|
|
||||||
return $dates;
|
|
||||||
}
|
|
||||||
$return = [];
|
|
||||||
/** @var Carbon $date */
|
|
||||||
foreach ($dates as $date) {
|
|
||||||
$isWeekend = $date->isWeekend();
|
|
||||||
if (!$isWeekend) {
|
|
||||||
$return[] = clone $date;
|
|
||||||
Log::debug(sprintf('Date is %s, not a weekend date.', $date->format('D d M Y')));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// is weekend and must set back to Friday?
|
|
||||||
if ($repetition->weekend === RecurrenceRepetition::WEEKEND_TO_FRIDAY) {
|
|
||||||
$clone = clone $date;
|
|
||||||
$clone->addDays(5 - $date->dayOfWeekIso);
|
|
||||||
Log::debug(
|
|
||||||
sprintf('Date is %s, and this is in the weekend, so corrected to %s (Friday).', $date->format('D d M Y'), $clone->format('D d M Y'))
|
|
||||||
);
|
|
||||||
$return[] = clone $clone;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// postpone to Monday?
|
|
||||||
if ($repetition->weekend === RecurrenceRepetition::WEEKEND_TO_MONDAY) {
|
|
||||||
$clone = clone $date;
|
|
||||||
$clone->addDays(8 - $date->dayOfWeekIso);
|
|
||||||
Log::debug(
|
|
||||||
sprintf('Date is %s, and this is in the weekend, so corrected to %s (Monday).', $date->format('D d M Y'), $clone->format('D d M Y'))
|
|
||||||
);
|
|
||||||
$return[] = $clone;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Log::debug(sprintf('Date is %s, removed from final result', $date->format('D d M Y')));
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter unique dates
|
|
||||||
Log::debug(sprintf('Count before filtering: %d', \count($dates)));
|
|
||||||
$collection = new Collection($return);
|
|
||||||
$filtered = $collection->unique();
|
|
||||||
$return = $filtered->toArray();
|
|
||||||
|
|
||||||
Log::debug(sprintf('Count after filtering: %d', \count($return)));
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of daily occurrences for a recurring transaction until date $end is reached. Will skip every $skipMod-1 occurrences.
|
|
||||||
*
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
* @param int $skipMod
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getDailyInRange(Carbon $start, Carbon $end, int $skipMod): array
|
|
||||||
{
|
|
||||||
$return = [];
|
|
||||||
$attempts = 0;
|
|
||||||
Log::debug('Rep is daily. Start of loop.');
|
|
||||||
while ($start <= $end) {
|
|
||||||
Log::debug(sprintf('Mutator is now: %s', $start->format('Y-m-d')));
|
|
||||||
if (0 === $attempts % $skipMod) {
|
|
||||||
Log::debug(sprintf('Attempts modulo skipmod is zero, include %s', $start->format('Y-m-d')));
|
|
||||||
$return[] = clone $start;
|
|
||||||
}
|
|
||||||
$start->addDay();
|
|
||||||
$attempts++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of daily occurrences for a recurring transaction until date $end is reached. Will skip every $skipMod-1 occurrences.
|
|
||||||
*
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
* @param int $skipMod
|
|
||||||
* @param string $moment
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
*/
|
|
||||||
private function getMonthlyInRange(Carbon $start, Carbon $end, int $skipMod, string $moment): array
|
|
||||||
{
|
|
||||||
$return = [];
|
|
||||||
$attempts = 0;
|
|
||||||
$dayOfMonth = (int)$moment;
|
|
||||||
Log::debug(sprintf('Day of month in repetition is %d', $dayOfMonth));
|
|
||||||
Log::debug(sprintf('Start is %s.', $start->format('Y-m-d')));
|
|
||||||
Log::debug(sprintf('End is %s.', $end->format('Y-m-d')));
|
|
||||||
if ($start->day > $dayOfMonth) {
|
|
||||||
Log::debug('Add a month.');
|
|
||||||
// day has passed already, add a month.
|
|
||||||
$start->addMonth();
|
|
||||||
}
|
|
||||||
Log::debug(sprintf('Start is now %s.', $start->format('Y-m-d')));
|
|
||||||
Log::debug('Start loop.');
|
|
||||||
while ($start < $end) {
|
|
||||||
Log::debug(sprintf('Mutator is now %s.', $start->format('Y-m-d')));
|
|
||||||
$domCorrected = min($dayOfMonth, $start->daysInMonth);
|
|
||||||
Log::debug(sprintf('DoM corrected is %d', $domCorrected));
|
|
||||||
$start->day = $domCorrected;
|
|
||||||
Log::debug(sprintf('Mutator is now %s.', $start->format('Y-m-d')));
|
|
||||||
Log::debug(sprintf('$attempts %% $skipMod === 0 is %s', var_export(0 === $attempts % $skipMod, true)));
|
|
||||||
Log::debug(sprintf('$start->lte($mutator) is %s', var_export($start->lte($start), true)));
|
|
||||||
Log::debug(sprintf('$end->gte($mutator) is %s', var_export($end->gte($start), true)));
|
|
||||||
if (0 === $attempts % $skipMod && $start->lte($start) && $end->gte($start)) {
|
|
||||||
Log::debug(sprintf('ADD %s to return!', $start->format('Y-m-d')));
|
|
||||||
$return[] = clone $start;
|
|
||||||
}
|
|
||||||
$attempts++;
|
|
||||||
$start->endOfMonth()->startOfDay()->addDay();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of daily occurrences for a recurring transaction until date $end is reached. Will skip every $skipMod-1 occurrences.
|
|
||||||
*
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
* @param int $skipMod
|
|
||||||
* @param string $moment
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getNdomInRange(Carbon $start, Carbon $end, int $skipMod, string $moment): array
|
|
||||||
{
|
|
||||||
$return = [];
|
|
||||||
$attempts = 0;
|
|
||||||
$start->startOfMonth();
|
|
||||||
// this feels a bit like a cop out but why reinvent the wheel?
|
|
||||||
$counters = [1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth',];
|
|
||||||
$daysOfWeek = [1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday',];
|
|
||||||
$parts = explode(',', $moment);
|
|
||||||
while ($start <= $end) {
|
|
||||||
$string = sprintf('%s %s of %s %s', $counters[$parts[0]], $daysOfWeek[$parts[1]], $start->format('F'), $start->format('Y'));
|
|
||||||
$newCarbon = new Carbon($string);
|
|
||||||
if (0 === $attempts % $skipMod) {
|
|
||||||
$return[] = clone $newCarbon;
|
|
||||||
}
|
|
||||||
$attempts++;
|
|
||||||
$start->endOfMonth()->addDay();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of daily occurrences for a recurring transaction until date $end is reached. Will skip every $skipMod-1 occurrences.
|
|
||||||
*
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
* @param int $skipMod
|
|
||||||
* @param string $moment
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
*/
|
|
||||||
private function getWeeklyInRange(Carbon $start, Carbon $end, int $skipMod, string $moment): array
|
|
||||||
{
|
|
||||||
$return = [];
|
|
||||||
$attempts = 0;
|
|
||||||
Log::debug('Rep is weekly.');
|
|
||||||
// monday = 1
|
|
||||||
// sunday = 7
|
|
||||||
$dayOfWeek = (int)$moment;
|
|
||||||
Log::debug(sprintf('DoW in repetition is %d, in mutator is %d', $dayOfWeek, $start->dayOfWeekIso));
|
|
||||||
if ($start->dayOfWeekIso > $dayOfWeek) {
|
|
||||||
// day has already passed this week, add one week:
|
|
||||||
$start->addWeek();
|
|
||||||
Log::debug(sprintf('Jump to next week, so mutator is now: %s', $start->format('Y-m-d')));
|
|
||||||
}
|
|
||||||
// today is wednesday (3), expected is friday (5): add two days.
|
|
||||||
// today is friday (5), expected is monday (1), subtract four days.
|
|
||||||
Log::debug(sprintf('Mutator is now: %s', $start->format('Y-m-d')));
|
|
||||||
$dayDifference = $dayOfWeek - $start->dayOfWeekIso;
|
|
||||||
$start->addDays($dayDifference);
|
|
||||||
Log::debug(sprintf('Mutator is now: %s', $start->format('Y-m-d')));
|
|
||||||
while ($start <= $end) {
|
|
||||||
if (0 === $attempts % $skipMod && $start->lte($start) && $end->gte($start)) {
|
|
||||||
Log::debug('Date is in range of start+end, add to set.');
|
|
||||||
$return[] = clone $start;
|
|
||||||
}
|
|
||||||
$attempts++;
|
|
||||||
$start->addWeek();
|
|
||||||
Log::debug(sprintf('Mutator is now (end of loop): %s', $start->format('Y-m-d')));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the number of daily occurrences for a recurring transaction, starting at the date, until $count is reached. It will skip
|
|
||||||
* over $skipMod -1 recurrences.
|
|
||||||
*
|
|
||||||
* @param Carbon $date
|
|
||||||
* @param int $count
|
|
||||||
* @param int $skipMod
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getXDailyOccurrences(Carbon $date, int $count, int $skipMod): array
|
|
||||||
{
|
|
||||||
$return = [];
|
|
||||||
$mutator = clone $date;
|
|
||||||
$total = 0;
|
|
||||||
$attempts = 0;
|
|
||||||
while ($total < $count) {
|
|
||||||
$mutator->addDay();
|
|
||||||
if (0 === $attempts % $skipMod) {
|
|
||||||
$return[] = clone $mutator;
|
|
||||||
$total++;
|
|
||||||
}
|
|
||||||
$attempts++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the number of monthly occurrences for a recurring transaction, starting at the date, until $count is reached. It will skip
|
|
||||||
* over $skipMod -1 recurrences.
|
|
||||||
*
|
|
||||||
* @param Carbon $date
|
|
||||||
* @param int $count
|
|
||||||
* @param int $skipMod
|
|
||||||
* @param string $moment
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getXMonthlyOccurrences(Carbon $date, int $count, int $skipMod, string $moment): array
|
|
||||||
{
|
|
||||||
$return = [];
|
|
||||||
$mutator = clone $date;
|
|
||||||
$total = 0;
|
|
||||||
$attempts = 0;
|
|
||||||
$mutator->addDay(); // always assume today has passed.
|
|
||||||
$dayOfMonth = (int)$moment;
|
|
||||||
if ($mutator->day > $dayOfMonth) {
|
|
||||||
// day has passed already, add a month.
|
|
||||||
$mutator->addMonth();
|
|
||||||
}
|
|
||||||
|
|
||||||
while ($total < $count) {
|
|
||||||
$domCorrected = min($dayOfMonth, $mutator->daysInMonth);
|
|
||||||
$mutator->day = $domCorrected;
|
|
||||||
if (0 === $attempts % $skipMod) {
|
|
||||||
$return[] = clone $mutator;
|
|
||||||
$total++;
|
|
||||||
}
|
|
||||||
$attempts++;
|
|
||||||
$mutator->endOfMonth()->addDay();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the number of NDOM occurrences for a recurring transaction, starting at the date, until $count is reached. It will skip
|
|
||||||
* over $skipMod -1 recurrences.
|
|
||||||
*
|
|
||||||
* @param Carbon $date
|
|
||||||
* @param int $count
|
|
||||||
* @param int $skipMod
|
|
||||||
* @param string $moment
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getXNDomOccurrences(Carbon $date, int $count, int $skipMod, string $moment): array
|
|
||||||
{
|
|
||||||
$return = [];
|
|
||||||
$total = 0;
|
|
||||||
$attempts = 0;
|
|
||||||
$mutator = clone $date;
|
|
||||||
$mutator->addDay(); // always assume today has passed.
|
|
||||||
$mutator->startOfMonth();
|
|
||||||
// this feels a bit like a cop out but why reinvent the wheel?
|
|
||||||
$counters = [1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth',];
|
|
||||||
$daysOfWeek = [1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday',];
|
|
||||||
$parts = explode(',', $moment);
|
|
||||||
|
|
||||||
while ($total < $count) {
|
|
||||||
$string = sprintf('%s %s of %s %s', $counters[$parts[0]], $daysOfWeek[$parts[1]], $mutator->format('F'), $mutator->format('Y'));
|
|
||||||
$newCarbon = new Carbon($string);
|
|
||||||
if (0 === $attempts % $skipMod) {
|
|
||||||
$return[] = clone $newCarbon;
|
|
||||||
$total++;
|
|
||||||
}
|
|
||||||
$attempts++;
|
|
||||||
$mutator->endOfMonth()->addDay();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the number of weekly occurrences for a recurring transaction, starting at the date, until $count is reached. It will skip
|
|
||||||
* over $skipMod -1 recurrences.
|
|
||||||
*
|
|
||||||
* @param Carbon $date
|
|
||||||
* @param int $count
|
|
||||||
* @param int $skipMod
|
|
||||||
* @param string $moment
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getXWeeklyOccurrences(Carbon $date, int $count, int $skipMod, string $moment): array
|
|
||||||
{
|
|
||||||
$return = [];
|
|
||||||
$total = 0;
|
|
||||||
$attempts = 0;
|
|
||||||
$mutator = clone $date;
|
|
||||||
// monday = 1
|
|
||||||
// sunday = 7
|
|
||||||
$mutator->addDay(); // always assume today has passed.
|
|
||||||
$dayOfWeek = (int)$moment;
|
|
||||||
if ($mutator->dayOfWeekIso > $dayOfWeek) {
|
|
||||||
// day has already passed this week, add one week:
|
|
||||||
$mutator->addWeek();
|
|
||||||
}
|
|
||||||
// today is wednesday (3), expected is friday (5): add two days.
|
|
||||||
// today is friday (5), expected is monday (1), subtract four days.
|
|
||||||
$dayDifference = $dayOfWeek - $mutator->dayOfWeekIso;
|
|
||||||
$mutator->addDays($dayDifference);
|
|
||||||
|
|
||||||
while ($total < $count) {
|
|
||||||
if (0 === $attempts % $skipMod) {
|
|
||||||
$return[] = clone $mutator;
|
|
||||||
$total++;
|
|
||||||
}
|
|
||||||
$attempts++;
|
|
||||||
$mutator->addWeek();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the number of yearly occurrences for a recurring transaction, starting at the date, until $count is reached. It will skip
|
|
||||||
* over $skipMod -1 recurrences.
|
|
||||||
*
|
|
||||||
* @param Carbon $date
|
|
||||||
* @param int $count
|
|
||||||
* @param int $skipMod
|
|
||||||
* @param string $moment
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getXYearlyOccurrences(Carbon $date, int $count, int $skipMod, string $moment): array
|
|
||||||
{
|
|
||||||
$return = [];
|
|
||||||
$mutator = clone $date;
|
|
||||||
$total = 0;
|
|
||||||
$attempts = 0;
|
|
||||||
$date = new Carbon($moment);
|
|
||||||
$date->year = $mutator->year;
|
|
||||||
if ($mutator > $date) {
|
|
||||||
$date->addYear();
|
|
||||||
}
|
|
||||||
$obj = clone $date;
|
|
||||||
while ($total < $count) {
|
|
||||||
if (0 === $attempts % $skipMod) {
|
|
||||||
$return[] = clone $obj;
|
|
||||||
$total++;
|
|
||||||
}
|
|
||||||
$obj->addYears(1);
|
|
||||||
$attempts++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of daily occurrences for a recurring transaction until date $end is reached. Will skip every $skipMod-1 occurrences.
|
|
||||||
*
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
* @param int $skipMod
|
|
||||||
* @param string $moment
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
|
||||||
*/
|
|
||||||
private function getYearlyInRange(Carbon $start, Carbon $end, int $skipMod, string $moment): array
|
|
||||||
{
|
|
||||||
$attempts = 0;
|
|
||||||
$date = new Carbon($moment);
|
|
||||||
$date->year = $start->year;
|
|
||||||
$return = [];
|
|
||||||
if ($start > $date) {
|
|
||||||
$date->addYear();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// is $date between $start and $end?
|
|
||||||
$obj = clone $date;
|
|
||||||
$count = 0;
|
|
||||||
while ($obj <= $end && $obj >= $start && $count < 10) {
|
|
||||||
if (0 === $attempts % $skipMod) {
|
|
||||||
$return[] = clone $obj;
|
|
||||||
}
|
|
||||||
$obj->addYears(1);
|
|
||||||
$count++;
|
|
||||||
$attempts++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,10 +253,9 @@ class TagRepository implements TagRepositoryInterface
|
|||||||
public function store(array $data): Tag
|
public function store(array $data): Tag
|
||||||
{
|
{
|
||||||
/** @var TagFactory $factory */
|
/** @var TagFactory $factory */
|
||||||
$factory = new TagFactory;
|
$factory = app(TagFactory::class);
|
||||||
$factory->setUser($this->user);
|
$factory->setUser($this->user);
|
||||||
return $factory->create($data);
|
return $factory->create($data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IsBoolean.php
|
||||||
|
* Copyright (c) 2018 thegrumpydictator@gmail.com
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III.
|
||||||
|
*
|
||||||
|
* Firefly III is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Firefly III 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Rules;
|
namespace FireflyIII\Rules;
|
||||||
|
|
||||||
use Illuminate\Contracts\Validation\Rule;
|
use Illuminate\Contracts\Validation\Rule;
|
||||||
|
|||||||
@@ -1,5 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ZeroOrMore.php
|
||||||
|
* Copyright (c) 2018 thegrumpydictator@gmail.com
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III.
|
||||||
|
*
|
||||||
|
* Firefly III is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Firefly III 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Rules;
|
namespace FireflyIII\Rules;
|
||||||
|
|
||||||
use Illuminate\Contracts\Validation\Rule;
|
use Illuminate\Contracts\Validation\Rule;
|
||||||
|
|||||||
125
app/Services/Currency/RatesApiIOv1.php
Normal file
125
app/Services/Currency/RatesApiIOv1.php
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* RatesApiIOv1.php
|
||||||
|
* Copyright (c) 2019 https://github.com/BoGnY
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III.
|
||||||
|
*
|
||||||
|
* Firefly III is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Firefly III 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Services\Currency;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Exception;
|
||||||
|
use FireflyIII\Models\CurrencyExchangeRate;
|
||||||
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
|
use FireflyIII\User;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Exception\GuzzleException;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class RatesApiIOv1.
|
||||||
|
*/
|
||||||
|
class RatesApiIOv1 implements ExchangeRateInterface
|
||||||
|
{
|
||||||
|
/** @var User */
|
||||||
|
protected $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if ('testing' === config('app.env')) {
|
||||||
|
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', \get_class($this)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionCurrency $fromCurrency
|
||||||
|
* @param TransactionCurrency $toCurrency
|
||||||
|
* @param Carbon $date
|
||||||
|
*
|
||||||
|
* @return CurrencyExchangeRate
|
||||||
|
*/
|
||||||
|
public function getRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date): CurrencyExchangeRate
|
||||||
|
{
|
||||||
|
// create new exchange rate with default values.
|
||||||
|
$rate = 0;
|
||||||
|
$exchangeRate = new CurrencyExchangeRate;
|
||||||
|
$exchangeRate->user()->associate($this->user);
|
||||||
|
$exchangeRate->fromCurrency()->associate($fromCurrency);
|
||||||
|
$exchangeRate->toCurrency()->associate($toCurrency);
|
||||||
|
$exchangeRate->date = $date;
|
||||||
|
$exchangeRate->rate = $rate;
|
||||||
|
$exchangeRate->updated_at = new Carbon;
|
||||||
|
$exchangeRate->created_at = new Carbon;
|
||||||
|
|
||||||
|
// build URI
|
||||||
|
$uri = sprintf(
|
||||||
|
'https://ratesapi.io/api/%s?base=%s&symbols=%s',
|
||||||
|
$date->format('Y-m-d'), $fromCurrency->code, $toCurrency->code
|
||||||
|
);
|
||||||
|
Log::debug(sprintf('Going to request exchange rate using URI %s', $uri));
|
||||||
|
$client = new Client;
|
||||||
|
try {
|
||||||
|
$res = $client->request('GET', $uri);
|
||||||
|
$statusCode = $res->getStatusCode();
|
||||||
|
$body = $res->getBody()->getContents();
|
||||||
|
} catch (GuzzleException|Exception $e) {
|
||||||
|
// don't care about error
|
||||||
|
$body = sprintf('Guzzle exception: %s', $e->getMessage());
|
||||||
|
$statusCode = 500;
|
||||||
|
}
|
||||||
|
Log::debug(sprintf('Result status code is %d', $statusCode));
|
||||||
|
Log::debug(sprintf('Result body is: %s', $body));
|
||||||
|
|
||||||
|
$content = null;
|
||||||
|
if (200 !== $statusCode) {
|
||||||
|
Log::error(sprintf('Something went wrong. Received error code %d and body "%s" from RatesApiIO.', $statusCode, $body));
|
||||||
|
}
|
||||||
|
$success = false;
|
||||||
|
// get rate from body:
|
||||||
|
if (200 === $statusCode) {
|
||||||
|
$content = json_decode($body, true);
|
||||||
|
$success = true;
|
||||||
|
}
|
||||||
|
if (null !== $content && true === $success) {
|
||||||
|
$code = $toCurrency->code;
|
||||||
|
$rate = (float)($content['rates'][$code] ?? 0);
|
||||||
|
Log::debug('Got the following rates from RatesApi: ', $content['rates'] ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
|
$exchangeRate->rate = $rate;
|
||||||
|
if (0.0 !== $rate) {
|
||||||
|
Log::debug('Rate is not zero, save it!');
|
||||||
|
$exchangeRate->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $exchangeRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
*
|
||||||
|
* @return mixed|void
|
||||||
|
*/
|
||||||
|
public function setUser(User $user)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
63
app/Services/Internal/Destroy/BudgetDestroyService.php
Normal file
63
app/Services/Internal/Destroy/BudgetDestroyService.php
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* BudgetDestroyService.php
|
||||||
|
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III.
|
||||||
|
*
|
||||||
|
* Firefly III is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Firefly III 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Services\Internal\Destroy;
|
||||||
|
|
||||||
|
use DB;
|
||||||
|
use Exception;
|
||||||
|
use FireflyIII\Models\Budget;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class BudgetDestroyService
|
||||||
|
*/
|
||||||
|
class BudgetDestroyService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if ('testing' === config('app.env')) {
|
||||||
|
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', \get_class($this)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Budget $budget
|
||||||
|
*/
|
||||||
|
public function destroy(Budget $budget): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$budget->delete();
|
||||||
|
} catch (Exception $e) { // @codeCoverageIgnore
|
||||||
|
Log::error(sprintf('Could not delete budget: %s', $e->getMessage())); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
|
||||||
|
// also delete all relations between categories and transaction journals:
|
||||||
|
DB::table('budget_transaction_journal')->where('budget_id', (int)$budget->id)->delete();
|
||||||
|
|
||||||
|
// also delete all relations between categories and transactions:
|
||||||
|
DB::table('budget_transaction')->where('budget_id', (int)$budget->id)->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user