mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-12-10 21:31:20 +00:00
Compare commits
384 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e84ca1df5 | ||
|
|
b28bdda510 | ||
|
|
72314e2d9f | ||
|
|
d22fb9f438 | ||
|
|
04b8552d27 | ||
|
|
5d6f44cd91 | ||
|
|
afc8ad7ff5 | ||
|
|
ae039bf1c7 | ||
|
|
8fa25e9d37 | ||
|
|
0daab491ec | ||
|
|
4f825bac1a | ||
|
|
39c8b79ebb | ||
|
|
7d66c90beb | ||
|
|
1b8b65582a | ||
|
|
d25971cb44 | ||
|
|
89a56e661d | ||
|
|
3482746d7c | ||
|
|
d5aeca6222 | ||
|
|
03f46638e1 | ||
|
|
c2b000d910 | ||
|
|
88eb702d7b | ||
|
|
b1d98f026f | ||
|
|
28dfe7b02c | ||
|
|
84f0ee183c | ||
|
|
7eedd6c2fc | ||
|
|
e7b80c6d10 | ||
|
|
d165609476 | ||
|
|
2f17521c06 | ||
|
|
8b52006959 | ||
|
|
8eb3d43123 | ||
|
|
a511368229 | ||
|
|
f0006a0743 | ||
|
|
7171e69715 | ||
|
|
2ab44fb33a | ||
|
|
7542175258 | ||
|
|
9dc4c50527 | ||
|
|
99d116f4ce | ||
|
|
9475fef8f6 | ||
|
|
60339a0f6a | ||
|
|
36113f84be | ||
|
|
139c2284b8 | ||
|
|
91909a70d7 | ||
|
|
a23d97563f | ||
|
|
bf538e2514 | ||
|
|
149b62f486 | ||
|
|
06dc8a499b | ||
|
|
5b8479f3a4 | ||
|
|
e803a5e26e | ||
|
|
959f798a7f | ||
|
|
5b8adbfd0c | ||
|
|
54ba18975a | ||
|
|
fdd2dedfc6 | ||
|
|
46f4fa1a7d | ||
|
|
28debb46be | ||
|
|
5f132be94d | ||
|
|
9f9feea159 | ||
|
|
3bd9e0bcd4 | ||
|
|
c80a76f8c0 | ||
|
|
c71f498587 | ||
|
|
e658d447ca | ||
|
|
33def5b45d | ||
|
|
9fc26a8ee0 | ||
|
|
1b304bf85e | ||
|
|
99983a5c8f | ||
|
|
d01b370cd7 | ||
|
|
1a643e2042 | ||
|
|
1aaf5fd288 | ||
|
|
8a758b8df0 | ||
|
|
211caa07dc | ||
|
|
ac66e89edb | ||
|
|
6fe5b50410 | ||
|
|
166cdad58b | ||
|
|
1a721ac6b5 | ||
|
|
6591fa9fb4 | ||
|
|
d804093f8b | ||
|
|
5261b784b0 | ||
|
|
5a188ceca3 | ||
|
|
ce56cc538d | ||
|
|
269433bf00 | ||
|
|
dae3371c69 | ||
|
|
38c1d332e2 | ||
|
|
b627d42160 | ||
|
|
4e923057ae | ||
|
|
35d0bd1985 | ||
|
|
085eb650e7 | ||
|
|
b4157e8ce0 | ||
|
|
81221038f0 | ||
|
|
9f37bf5875 | ||
|
|
140a5b20db | ||
|
|
e9b6b45fc4 | ||
|
|
f16760d607 | ||
|
|
9d457787f7 | ||
|
|
4e6afd5afc | ||
|
|
36354c3846 | ||
|
|
9f63dfb9cb | ||
|
|
cae4faad0a | ||
|
|
e389d0f7fa | ||
|
|
6b32213735 | ||
|
|
b3fe24b713 | ||
|
|
5bb7530642 | ||
|
|
0b61c16eb0 | ||
|
|
77aced6734 | ||
|
|
94a7b6b9bd | ||
|
|
f8bf6c163f | ||
|
|
eb0da038fb | ||
|
|
6cda9f2900 | ||
|
|
ecd4a862ff | ||
|
|
632d50a0d0 | ||
|
|
0f1cc46b71 | ||
|
|
60b225d61c | ||
|
|
23e540a57a | ||
|
|
1998412a3c | ||
|
|
7bbfb692de | ||
|
|
c6da990748 | ||
|
|
049e57d578 | ||
|
|
78ba0f749c | ||
|
|
9cc1bfb4b5 | ||
|
|
278b7ac52b | ||
|
|
645a29e22b | ||
|
|
b22d30bc65 | ||
|
|
2ee0490141 | ||
|
|
1fd783de69 | ||
|
|
8073896965 | ||
|
|
8a26e43c40 | ||
|
|
a302aba3ab | ||
|
|
0458058cb1 | ||
|
|
999bb5ed49 | ||
|
|
c9f4a1eb7b | ||
|
|
45aa76afce | ||
|
|
e89a77efb1 | ||
|
|
8f930d6dd5 | ||
|
|
9d62b4c70d | ||
|
|
834032f58e | ||
|
|
33db99ffd3 | ||
|
|
28b00f6507 | ||
|
|
6559076c48 | ||
|
|
60f6311e00 | ||
|
|
574a5630e0 | ||
|
|
d3294be1bc | ||
|
|
22fdc81de2 | ||
|
|
370e9b25d1 | ||
|
|
30f821af3e | ||
|
|
7a5aa1c39b | ||
|
|
f674df4422 | ||
|
|
c2da5931ec | ||
|
|
94f6bd34c7 | ||
|
|
e066a6421c | ||
|
|
ef338e2515 | ||
|
|
dcf549261c | ||
|
|
8b868b426a | ||
|
|
2ef1022c92 | ||
|
|
9b3abd3b19 | ||
|
|
db02fefcf4 | ||
|
|
523ae83811 | ||
|
|
4eb010f807 | ||
|
|
4958f28052 | ||
|
|
7e727b63ed | ||
|
|
138c38fbb5 | ||
|
|
2e61bb7375 | ||
|
|
fce4c9174d | ||
|
|
2220963899 | ||
|
|
e69e6c1ce8 | ||
|
|
0f09a9db4d | ||
|
|
53a6c10ada | ||
|
|
14772469ed | ||
|
|
55f13ef121 | ||
|
|
95648c37b3 | ||
|
|
ac98822a55 | ||
|
|
c460419166 | ||
|
|
d2a8819dd4 | ||
|
|
d393c693de | ||
|
|
d4a84ed198 | ||
|
|
e8c7986a58 | ||
|
|
809e40c5ce | ||
|
|
f445a95c26 | ||
|
|
909dc212fb | ||
|
|
eacc1da157 | ||
|
|
587ad1298d | ||
|
|
fae7dabbc2 | ||
|
|
3a813c30b4 | ||
|
|
3de46f55fa | ||
|
|
3aa922341c | ||
|
|
e94043edc2 | ||
|
|
da91645ec0 | ||
|
|
178072d3af | ||
|
|
811d8e330f | ||
|
|
d3c8d06114 | ||
|
|
82dc0045ba | ||
|
|
3d06f0ac14 | ||
|
|
b5c0ef01d9 | ||
|
|
3a5d3016c7 | ||
|
|
20690b4d5b | ||
|
|
2816a4a325 | ||
|
|
2f4f37778c | ||
|
|
c4507a7f75 | ||
|
|
9a0672e359 | ||
|
|
f128db35c6 | ||
|
|
a2cfaa0867 | ||
|
|
5850c5e20a | ||
|
|
e77a1e403f | ||
|
|
b72e8db7b1 | ||
|
|
07506784f4 | ||
|
|
0435e42b3d | ||
|
|
31884bbba6 | ||
|
|
6b38faf84e | ||
|
|
2f95f99890 | ||
|
|
9b78069f41 | ||
|
|
559c2042ac | ||
|
|
ae3b369e9a | ||
|
|
31a6565e17 | ||
|
|
f488bbde02 | ||
|
|
e668b88fb5 | ||
|
|
2d0aa4af96 | ||
|
|
6e67416c83 | ||
|
|
1ef28cbc02 | ||
|
|
58bdf14f6b | ||
|
|
9f4ecb0963 | ||
|
|
b1259a014f | ||
|
|
142d0b5af2 | ||
|
|
450e2bad1c | ||
|
|
089300d57e | ||
|
|
36f67793cb | ||
|
|
c335a9bbc8 | ||
|
|
029688a594 | ||
|
|
6f2eb33fd0 | ||
|
|
8351020913 | ||
|
|
220efca8d7 | ||
|
|
28579f7b80 | ||
|
|
f1d77bdb50 | ||
|
|
ca8b4cb11a | ||
|
|
31dbb7b111 | ||
|
|
352cdf75c8 | ||
|
|
d81c99bcda | ||
|
|
1e2c979341 | ||
|
|
d8664096f9 | ||
|
|
0d9a221b00 | ||
|
|
de85f17cac | ||
|
|
e3d6f4f00f | ||
|
|
d0e0054b00 | ||
|
|
735222a8ed | ||
|
|
66f299cd06 | ||
|
|
e3c5268143 | ||
|
|
df32493d77 | ||
|
|
86faf44153 | ||
|
|
e2f3e4b555 | ||
|
|
e57ed6015c | ||
|
|
9c34ca7fc4 | ||
|
|
7b94f4a441 | ||
|
|
693f8d0738 | ||
|
|
d6ecbc06bf | ||
|
|
c31674fffc | ||
|
|
b08de8cc00 | ||
|
|
4c503e4c7c | ||
|
|
dd4158c6b4 | ||
|
|
5fd7ea2b96 | ||
|
|
b9ff80eb5a | ||
|
|
afc725bbc8 | ||
|
|
0342c371cc | ||
|
|
2da4e6b048 | ||
|
|
f50550d79c | ||
|
|
3fa39a6805 | ||
|
|
3dbe6d4870 | ||
|
|
59c48268ab | ||
|
|
1f83c5195d | ||
|
|
49a95a08fe | ||
|
|
c86c5ccfe9 | ||
|
|
8a2497fc67 | ||
|
|
7c70732247 | ||
|
|
53fc4f2740 | ||
|
|
f3ade5621e | ||
|
|
ec2e08e33a | ||
|
|
b9a26faa4d | ||
|
|
1a434d0c83 | ||
|
|
9a2c6c2967 | ||
|
|
602b35d589 | ||
|
|
f42cd0c7c3 | ||
|
|
cb81855a17 | ||
|
|
89cf351ad0 | ||
|
|
3f70a3f06c | ||
|
|
9df360f010 | ||
|
|
9a26d6d49f | ||
|
|
bc4d801c12 | ||
|
|
f2d8e13576 | ||
|
|
ec0b5db973 | ||
|
|
46a0d1ce35 | ||
|
|
cb81446ca6 | ||
|
|
9350b4939c | ||
|
|
33e8c8c415 | ||
|
|
48fa86cc54 | ||
|
|
d5e6d1c578 | ||
|
|
0bc688795a | ||
|
|
eb76ed5591 | ||
|
|
839cbaf37a | ||
|
|
788fc9204d | ||
|
|
3e3e304ef3 | ||
|
|
447d453fdc | ||
|
|
36fd7884f3 | ||
|
|
54da08b2f2 | ||
|
|
3f02072ae9 | ||
|
|
a9c117703b | ||
|
|
c137255155 | ||
|
|
e7829ecc38 | ||
|
|
529bdafa31 | ||
|
|
e2af0caa41 | ||
|
|
80f96abf08 | ||
|
|
70da38193f | ||
|
|
13df973873 | ||
|
|
3ccb791674 | ||
|
|
ccf1a6c182 | ||
|
|
493543c1f5 | ||
|
|
5f5725e0e3 | ||
|
|
107dd42957 | ||
|
|
a9f3fe4d3a | ||
|
|
3e62e17b9e | ||
|
|
57855b1930 | ||
|
|
aa9e8227bb | ||
|
|
a80f083b6e | ||
|
|
474e066d4a | ||
|
|
4428ccefbf | ||
|
|
d568a6c8a9 | ||
|
|
97e9ad6cb2 | ||
|
|
00607d2a6d | ||
|
|
c2a425121d | ||
|
|
435694e9ea | ||
|
|
f59135a9ca | ||
|
|
102b106402 | ||
|
|
5c27c8e633 | ||
|
|
edd5215b21 | ||
|
|
94b173ae6b | ||
|
|
7d96b281b6 | ||
|
|
a5515ac89f | ||
|
|
fb863b0bf2 | ||
|
|
50882f309b | ||
|
|
ce854fbb43 | ||
|
|
6799268ec4 | ||
|
|
6fe5ce0485 | ||
|
|
cbeaf8e16a | ||
|
|
04de4c9b36 | ||
|
|
517731cb59 | ||
|
|
e34e43173c | ||
|
|
79d6055a78 | ||
|
|
7ac4d2a2f4 | ||
|
|
4984eda320 | ||
|
|
89e0791e2f | ||
|
|
922d487821 | ||
|
|
4b789979ac | ||
|
|
554b38ccff | ||
|
|
9614310208 | ||
|
|
d9ec3ac354 | ||
|
|
f326f08f7b | ||
|
|
0ae8418f32 | ||
|
|
309f9cd076 | ||
|
|
61f5ed3874 | ||
|
|
91178d2604 | ||
|
|
87dae6ea18 | ||
|
|
2e495c38d1 | ||
|
|
c2987aaf4c | ||
|
|
f4f4eecb7b | ||
|
|
84d9287251 | ||
|
|
b71f334744 | ||
|
|
ad306e4d01 | ||
|
|
e40d4ef829 | ||
|
|
48c16c3dcc | ||
|
|
c045193246 | ||
|
|
a816e59a97 | ||
|
|
892074eaf2 | ||
|
|
3e501e429d | ||
|
|
0f40accb4c | ||
|
|
3dae6c99a4 | ||
|
|
b185c83597 | ||
|
|
ef6b4120f1 | ||
|
|
a43eef01fc | ||
|
|
2cb9aa537f | ||
|
|
2edd49a8b4 | ||
|
|
a57554d380 | ||
|
|
c89486b6d9 | ||
|
|
f737cb7235 | ||
|
|
f1fe169553 | ||
|
|
2fc760780e | ||
|
|
8c3290bf6f | ||
|
|
495158b9c9 | ||
|
|
f9fc9b1889 | ||
|
|
11ff2ab9d1 | ||
|
|
52b138e6b2 |
12
.codeclimate.yml
Normal file
12
.codeclimate.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
exclude_patterns:
|
||||
- public/lib/
|
||||
- public/js/lib/
|
||||
- public/fonts/
|
||||
- public/css/jquery-ui/
|
||||
- public/css/bootstrap-multiselect.css
|
||||
- public/css/bootstrap-sortable.css
|
||||
- public/css/bootstrap-tagsinput.css
|
||||
- public/css/daterangepicker.css
|
||||
- public/css/google-fonts.css
|
||||
- .sandstorm/
|
||||
90
.env.docker
90
.env.docker
@@ -1,12 +1,24 @@
|
||||
# You can leave this on "local". If you change it to production most console commands will ask for extra confirmation.
|
||||
# Never set it to "testing".
|
||||
APP_ENV=${FF_APP_ENV}
|
||||
APP_DEBUG=false
|
||||
APP_NAME=FireflyIII
|
||||
APP_KEY=${FF_APP_KEY}
|
||||
APP_LOG=daily
|
||||
APP_LOG_LEVEL=warning
|
||||
APP_URL=http://localhost
|
||||
TRUSTED_PROXIES=
|
||||
|
||||
# Set to true if you want to see debug information in error screens.
|
||||
APP_DEBUG=${APP_DEBUG}
|
||||
|
||||
# This should be your email address
|
||||
SITE_OWNER=${SITE_OWNER}
|
||||
|
||||
# The encryption key for your database and sessions. Keep this very secure.
|
||||
# If you generate a new one all existing data must be considered LOST.
|
||||
# Change it to a string of exactly 32 chars or use command `php artisan key:generate` to generate it
|
||||
APP_KEY=${FF_APP_KEY}
|
||||
|
||||
# APP_URL and TRUSTED_PROXIES are useful when using Docker and/or a reverse proxy.
|
||||
APP_URL=${APP_URL}
|
||||
TRUSTED_PROXIES=${TRUSTED_PROXIES}
|
||||
|
||||
# Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III
|
||||
# If you use SQLite, set connection to `sqlite` and remove the database, username and password settings.
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=${FF_DB_HOST}
|
||||
DB_PORT=3306
|
||||
@@ -14,47 +26,65 @@ DB_DATABASE=${FF_DB_NAME}
|
||||
DB_USERNAME=${FF_DB_USER}
|
||||
DB_PASSWORD=${FF_DB_PASSWORD}
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
# 'daily' is the default logging mode giving you 5 daily rotated log files in /storage/logs/.
|
||||
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
|
||||
# Also available are 'syslog' and 'errorlog' which will log to the system itself.
|
||||
APP_LOG=syslog
|
||||
|
||||
# Log level. You can set this from least severe to most severe:
|
||||
# debug, info, notice, warning, error, critical, alert, emergency
|
||||
# If you set it to debug your logs will grow large, and fast. If you set it to emergency probably
|
||||
# nothing will get logged, ever.
|
||||
APP_LOG_LEVEL=info
|
||||
|
||||
# If you're looking for performance improvements, you could install memcached.
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
QUEUE_DRIVER=sync
|
||||
|
||||
# Cookie settings. Should not be necessary to change these.
|
||||
COOKIE_PATH="/"
|
||||
COOKIE_DOMAIN=
|
||||
COOKIE_SECURE=false
|
||||
|
||||
# If you want Firefly III to mail you, update these settings
|
||||
MAIL_DRIVER=${MAIL_DRIVER}
|
||||
MAIL_HOST=${MAIL_HOST}
|
||||
MAIL_PORT=${MAIL_PORT}
|
||||
MAIL_FROM=${MAIL_FROM}
|
||||
MAIL_USERNAME=${MAIL_USERNAME}
|
||||
MAIL_PASSWORD=${MAIL_PASSWORD}
|
||||
MAIL_ENCRYPTION=${MAIL_ENCRYPTION}
|
||||
|
||||
# Firefly III can send you the following messages
|
||||
SEND_REGISTRATION_MAIL=true
|
||||
SEND_ERROR_MESSAGE=false
|
||||
|
||||
# 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}
|
||||
|
||||
# If you wish to track your own behavior over Firefly III, set a valid analytics tracker ID here.
|
||||
ANALYTICS_ID=${ANALYTICS_ID}
|
||||
|
||||
# Most parts of the database are encrypted by default, but you can turn this off if you want to.
|
||||
# This makes it easier to migrate your database. Not that some fields will never be decrypted.
|
||||
USE_ENCRYPTION=true
|
||||
|
||||
# Leave the following configuration vars as is.
|
||||
# Unless you like to tinker and know what you're doing.
|
||||
APP_NAME=FireflyIII
|
||||
BROADCAST_DRIVER=log
|
||||
QUEUE_DRIVER=sync
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_HOST=smtp.mailtrap.io
|
||||
MAIL_PORT=2525
|
||||
MAIL_FROM=changeme@example.com
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
|
||||
SEND_REGISTRATION_MAIL=true
|
||||
SEND_ERROR_MESSAGE=true
|
||||
|
||||
CACHE_PREFIX=firefly
|
||||
|
||||
SEARCH_RESULT_LIMIT=50
|
||||
EXCHANGE_RATE_SERVICE=fixerio
|
||||
|
||||
MAPBOX_API_KEY=
|
||||
ANALYTICS_ID=
|
||||
SITE_OWNER=mail@example.com
|
||||
USE_ENCRYPTION=true
|
||||
|
||||
PUSHER_KEY=
|
||||
PUSHER_SECRET=
|
||||
PUSHER_ID=
|
||||
|
||||
DEMO_USERNAME=
|
||||
DEMO_PASSWORD=
|
||||
|
||||
IS_DOCKER=true
|
||||
IS_SANDSTORM=false
|
||||
IS_HEROKU=false
|
||||
|
||||
64
.env.example
64
.env.example
@@ -1,12 +1,24 @@
|
||||
# You can leave this on "local". If you change it to production most console commands will ask for extra confirmation.
|
||||
# Never set it to "testing".
|
||||
APP_ENV=local
|
||||
|
||||
# Set to true if you want to see debug information in error screens.
|
||||
APP_DEBUG=false
|
||||
APP_NAME=FireflyIII
|
||||
|
||||
# This should be your email address
|
||||
SITE_OWNER=mail@example.com
|
||||
|
||||
# The encryption key for your database and sessions. Keep this very secure.
|
||||
# If you generate a new one all existing data must be considered LOST.
|
||||
# Change it to a string of exactly 32 chars or use command `php artisan key:generate` to generate it
|
||||
APP_KEY=SomeRandomStringOf32CharsExactly
|
||||
APP_LOG=daily
|
||||
APP_LOG_LEVEL=notice
|
||||
|
||||
# APP_URL and TRUSTED_PROXIES are useful when using Docker and/or a reverse proxy.
|
||||
APP_URL=http://localhost
|
||||
TRUSTED_PROXIES=
|
||||
|
||||
# Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III
|
||||
# If you use SQLite, set connection to `sqlite` and remove the database, username and password settings.
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
@@ -14,19 +26,27 @@ DB_DATABASE=homestead
|
||||
DB_USERNAME=homestead
|
||||
DB_PASSWORD=secret
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
# 'daily' is the default logging mode giving you 5 daily rotated log files in /storage/logs/.
|
||||
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
|
||||
# Also available are 'syslog' and 'errorlog' which will log to the system itself.
|
||||
APP_LOG=daily
|
||||
|
||||
# Log level. You can set this from least severe to most severe:
|
||||
# debug, info, notice, warning, error, critical, alert, emergency
|
||||
# If you set it to debug your logs will grow large, and fast. If you set it to emergency probably
|
||||
# nothing will get logged, ever.
|
||||
APP_LOG_LEVEL=notice
|
||||
|
||||
# If you're looking for performance improvements, you could install memcached.
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
QUEUE_DRIVER=sync
|
||||
|
||||
# Cookie settings. Should not be necessary to change these.
|
||||
COOKIE_PATH="/"
|
||||
COOKIE_DOMAIN=
|
||||
COOKIE_SECURE=false
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
# If you want Firefly III to mail you, update these settings
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_HOST=smtp.mailtrap.io
|
||||
MAIL_PORT=2525
|
||||
@@ -35,26 +55,36 @@ MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
|
||||
# Firefly III can send you the following messages
|
||||
SEND_REGISTRATION_MAIL=true
|
||||
SEND_ERROR_MESSAGE=true
|
||||
|
||||
CACHE_PREFIX=firefly
|
||||
|
||||
SEARCH_RESULT_LIMIT=50
|
||||
EXCHANGE_RATE_SERVICE=fixerio
|
||||
|
||||
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
|
||||
MAPBOX_API_KEY=
|
||||
|
||||
# If you wish to track your own behavior over Firefly III, set a valid analytics tracker ID here.
|
||||
ANALYTICS_ID=
|
||||
SITE_OWNER=mail@example.com
|
||||
|
||||
# Most parts of the database are encrypted by default, but you can turn this off if you want to.
|
||||
# This makes it easier to migrate your database. Not that some fields will never be decrypted.
|
||||
USE_ENCRYPTION=true
|
||||
|
||||
# Leave the following configuration vars as is.
|
||||
# Unless you like to tinker and know what you're doing.
|
||||
APP_NAME=FireflyIII
|
||||
BROADCAST_DRIVER=log
|
||||
QUEUE_DRIVER=sync
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
CACHE_PREFIX=firefly
|
||||
SEARCH_RESULT_LIMIT=50
|
||||
EXCHANGE_RATE_SERVICE=fixerio
|
||||
PUSHER_KEY=
|
||||
PUSHER_SECRET=
|
||||
PUSHER_ID=
|
||||
|
||||
DEMO_USERNAME=
|
||||
DEMO_PASSWORD=
|
||||
|
||||
IS_DOCKER=false
|
||||
IS_SANDSTORM=false
|
||||
IS_HEROKU=false
|
||||
|
||||
72
.env.heroku
72
.env.heroku
@@ -1,12 +1,24 @@
|
||||
# You can leave this on "local". If you change it to production most console commands will ask for extra confirmation.
|
||||
# Never set it to "testing".
|
||||
APP_ENV=heroku
|
||||
APP_DEBUG=true
|
||||
APP_NAME=FireflyIII
|
||||
APP_KEY=7ahyYVPVsmxjdhsweWCauGeJfwc92NP2
|
||||
APP_LOG=errorlog
|
||||
APP_LOG_LEVEL=debug
|
||||
APP_URL=http://localhost
|
||||
TRUSTED_PROXIES=*
|
||||
|
||||
# Set to true if you want to see debug information in error screens.
|
||||
APP_DEBUG=false
|
||||
|
||||
# This should be your email address
|
||||
SITE_OWNER=heroku@example.com
|
||||
|
||||
# The encryption key for your database and sessions. Keep this very secure.
|
||||
# If you generate a new one all existing data must be considered LOST.
|
||||
# Change it to a string of exactly 32 chars or use command `php artisan key:generate` to generate it
|
||||
APP_KEY=7ahyYVPVsmxjdhsweWCauGeJfwc92NP2
|
||||
|
||||
# APP_URL and TRUSTED_PROXIES are useful when using Docker and/or a reverse proxy.
|
||||
APP_URL=http://localhost
|
||||
TRUSTED_PROXIES=
|
||||
|
||||
# Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III
|
||||
# If you use SQLite, set connection to `sqlite` and remove the database, username and password settings.
|
||||
DB_CONNECTION=pgsql
|
||||
|
||||
|
||||
@@ -14,19 +26,27 @@ DB_CONNECTION=pgsql
|
||||
|
||||
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
# 'daily' is the default logging mode giving you 5 daily rotated log files in /storage/logs/.
|
||||
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
|
||||
# Also available are 'syslog' and 'errorlog' which will log to the system itself.
|
||||
APP_LOG=errorlog
|
||||
|
||||
# Log level. You can set this from least severe to most severe:
|
||||
# debug, info, notice, warning, error, critical, alert, emergency
|
||||
# If you set it to debug your logs will grow large, and fast. If you set it to emergency probably
|
||||
# nothing will get logged, ever.
|
||||
APP_LOG_LEVEL=debug
|
||||
|
||||
# If you're looking for performance improvements, you could install memcached.
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
QUEUE_DRIVER=sync
|
||||
|
||||
# Cookie settings. Should not be necessary to change these.
|
||||
COOKIE_PATH="/"
|
||||
COOKIE_DOMAIN=
|
||||
COOKIE_SECURE=false
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
# If you want Firefly III to mail you, update these settings
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_HOST=smtp.mailtrap.io
|
||||
MAIL_PORT=2525
|
||||
@@ -35,26 +55,36 @@ MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
|
||||
# Firefly III can send you the following messages
|
||||
SEND_REGISTRATION_MAIL=true
|
||||
SEND_ERROR_MESSAGE=true
|
||||
|
||||
CACHE_PREFIX=firefly
|
||||
|
||||
SEARCH_RESULT_LIMIT=50
|
||||
EXCHANGE_RATE_SERVICE=fixerio
|
||||
|
||||
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
|
||||
MAPBOX_API_KEY=
|
||||
|
||||
# If you wish to track your own behavior over Firefly III, set a valid analytics tracker ID here.
|
||||
ANALYTICS_ID=
|
||||
SITE_OWNER=heroku@example.com
|
||||
|
||||
# Most parts of the database are encrypted by default, but you can turn this off if you want to.
|
||||
# This makes it easier to migrate your database. Not that some fields will never be decrypted.
|
||||
USE_ENCRYPTION=true
|
||||
|
||||
# Leave the following configuration vars as is.
|
||||
# Unless you like to tinker and know what you're doing.
|
||||
APP_NAME=FireflyIII
|
||||
BROADCAST_DRIVER=log
|
||||
QUEUE_DRIVER=sync
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
CACHE_PREFIX=firefly
|
||||
SEARCH_RESULT_LIMIT=50
|
||||
EXCHANGE_RATE_SERVICE=fixerio
|
||||
PUSHER_KEY=
|
||||
PUSHER_SECRET=
|
||||
PUSHER_ID=
|
||||
|
||||
DEMO_USERNAME=
|
||||
DEMO_PASSWORD=
|
||||
|
||||
IS_DOCKER=false
|
||||
IS_SANDSTORM=false
|
||||
IS_HEROKU=true
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
# You can leave this on "local". If you change it to production most console commands will ask for extra confirmation.
|
||||
# Never set it to "testing".
|
||||
APP_ENV=local
|
||||
|
||||
# Set to true if you want to see debug information in error screens.
|
||||
APP_DEBUG=false
|
||||
APP_NAME=FireflyIII
|
||||
|
||||
# This should be your email address
|
||||
SITE_OWNER=sandstorm@example.com
|
||||
|
||||
# The encryption key for your database and sessions. Keep this very secure.
|
||||
# If you generate a new one all existing data must be considered LOST.
|
||||
# Change it to a string of exactly 32 chars or use command `php artisan key:generate` to generate it
|
||||
APP_KEY=SomeRandomStringOf32CharsExactly
|
||||
APP_LOG=syslog
|
||||
APP_LOG_LEVEL=info
|
||||
|
||||
# APP_URL and TRUSTED_PROXIES are useful when using Docker and/or a reverse proxy.
|
||||
APP_URL=http://localhost
|
||||
TRUSTED_PROXIES=
|
||||
|
||||
# Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III
|
||||
# If you use SQLite, set connection to `sqlite` and remove the database, username and password settings.
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
@@ -14,19 +26,27 @@ DB_DATABASE=firefly
|
||||
DB_USERNAME=firefly
|
||||
DB_PASSWORD=firefly
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
# 'daily' is the default logging mode giving you 5 daily rotated log files in /storage/logs/.
|
||||
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
|
||||
# Also available are 'syslog' and 'errorlog' which will log to the system itself.
|
||||
APP_LOG=syslog
|
||||
|
||||
# Log level. You can set this from least severe to most severe:
|
||||
# debug, info, notice, warning, error, critical, alert, emergency
|
||||
# If you set it to debug your logs will grow large, and fast. If you set it to emergency probably
|
||||
# nothing will get logged, ever.
|
||||
APP_LOG_LEVEL=info
|
||||
|
||||
# If you're looking for performance improvements, you could install memcached.
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
QUEUE_DRIVER=sync
|
||||
|
||||
# Cookie settings. Should not be necessary to change these.
|
||||
COOKIE_PATH="/"
|
||||
COOKIE_DOMAIN=
|
||||
COOKIE_SECURE=false
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
# If you want Firefly III to mail you, update these settings
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_HOST=smtp.mailtrap.io
|
||||
MAIL_PORT=2525
|
||||
@@ -35,26 +55,36 @@ MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
|
||||
# Firefly III can send you the following messages
|
||||
SEND_REGISTRATION_MAIL=true
|
||||
SEND_ERROR_MESSAGE=true
|
||||
|
||||
CACHE_PREFIX=firefly
|
||||
|
||||
SEARCH_RESULT_LIMIT=50
|
||||
EXCHANGE_RATE_SERVICE=fixerio
|
||||
|
||||
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
|
||||
MAPBOX_API_KEY=
|
||||
|
||||
# If you wish to track your own behavior over Firefly III, set a valid analytics tracker ID here.
|
||||
ANALYTICS_ID=
|
||||
SITE_OWNER=mail@example.com
|
||||
|
||||
# Most parts of the database are encrypted by default, but you can turn this off if you want to.
|
||||
# This makes it easier to migrate your database. Not that some fields will never be decrypted.
|
||||
USE_ENCRYPTION=true
|
||||
|
||||
# Leave the following configuration vars as is.
|
||||
# Unless you like to tinker and know what you're doing.
|
||||
APP_NAME=FireflyIII
|
||||
BROADCAST_DRIVER=log
|
||||
QUEUE_DRIVER=sync
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
CACHE_PREFIX=firefly
|
||||
SEARCH_RESULT_LIMIT=50
|
||||
EXCHANGE_RATE_SERVICE=fixerio
|
||||
PUSHER_KEY=
|
||||
PUSHER_SECRET=
|
||||
PUSHER_ID=
|
||||
|
||||
DEMO_USERNAME=
|
||||
DEMO_PASSWORD=
|
||||
|
||||
IS_DOCKER=false
|
||||
IS_SANDSTORM=true
|
||||
IS_HEROKU=false
|
||||
|
||||
@@ -27,7 +27,7 @@ REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_DRIVER=log
|
||||
MAIL_HOST=smtp.mailtrap.io
|
||||
MAIL_PORT=2525
|
||||
MAIL_FROM=changeme@example.com
|
||||
@@ -36,7 +36,7 @@ MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
|
||||
SEND_REGISTRATION_MAIL=true
|
||||
SEND_ERROR_MESSAGE=true
|
||||
SEND_ERROR_MESSAGE=false
|
||||
|
||||
CACHE_PREFIX=firefly
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Feature requests
|
||||
|
||||
I am always interested in expanding Firefly III's many features. If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.org/requested-features/).
|
||||
I am always interested in expanding Firefly III's many features. Just open a ticket or [drop me a line](mailto:thegrumpydictator@gmail.com).
|
||||
|
||||
## Pull requests
|
||||
|
||||
0
.github/SUPPORT.md → .github/support.md
vendored
0
.github/SUPPORT.md → .github/support.md
vendored
@@ -3,10 +3,9 @@
|
||||
# This script only runs once, when the app connects to sandstorm.
|
||||
set -euo pipefail
|
||||
|
||||
|
||||
echo "In build.sh"
|
||||
|
||||
cd /opt/app
|
||||
|
||||
cp .env.sandstorm .env
|
||||
|
||||
if [ -f /opt/app/composer.json ] ; then
|
||||
|
||||
@@ -1,3 +1,48 @@
|
||||
# 4.7.1
|
||||
- A brand new API. Read about it in the [documentation](http://firefly-iii.readthedocs.io/en/latest/).
|
||||
- Add support for Spanish. [issue 1194](https://github.com/firefly-iii/firefly-iii/issues/1194)
|
||||
- Some custom preferences are selected by default for a better user experience.
|
||||
- Some new currencies [issue 1211](https://github.com/firefly-iii/firefly-iii/issues/1211)
|
||||
- Fixed [issue 1155](https://github.com/firefly-iii/firefly-iii/issues/1155) (reported by [ndandanov](https://github.com/ndandanov))
|
||||
- [Issue 1156](https://github.com/firefly-iii/firefly-iii/issues/1156) [issue 1182](https://github.com/firefly-iii/firefly-iii/issues/1182) and other issues related to SQLite databases.
|
||||
- Multi-page budget overview was broken (reported by [jinformatique](https://github.com/jinformatique))
|
||||
- Importing CSV files with semi-colons in them did not work [issue 1172](https://github.com/firefly-iii/firefly-iii/issues/1172) [issue 1183](https://github.com/firefly-iii/firefly-iii/issues/1183) [issue 1210](https://github.com/firefly-iii/firefly-iii/issues/1210)
|
||||
- Could not use account number that was in use by a deleted account [issue 1174](https://github.com/firefly-iii/firefly-iii/issues/1174)
|
||||
- Fixed spelling error that lead to 404's [issue 1175](https://github.com/firefly-iii/firefly-iii/issues/1175) [issue 1190](https://github.com/firefly-iii/firefly-iii/issues/1190)
|
||||
- Fixed tag autocomplete [issue 1178](https://github.com/firefly-iii/firefly-iii/issues/1178)
|
||||
- Better links for "new transaction" buttons [issue 1185](https://github.com/firefly-iii/firefly-iii/issues/1185)
|
||||
- Cache errors in budget charts [issue 1192](https://github.com/firefly-iii/firefly-iii/issues/1192)
|
||||
- Deleting transactions that are linked to other other transactions would lead to errors [issue 1209](https://github.com/firefly-iii/firefly-iii/issues/1209)
|
||||
|
||||
# 4.7.0
|
||||
- Support for Russian and Portuguese (Brazil)
|
||||
- Support for the Spectre API (Salt Edge)
|
||||
- Many strings now translatable thanks to [Nik-vr](https://github.com/Nik-vr) ([issue 1118](https://github.com/firefly-iii/firefly-iii/issues/1118), [issue 1116](https://github.com/firefly-iii/firefly-iii/issues/1116), [issue 1109](https://github.com/firefly-iii/firefly-iii/issues/1109), )
|
||||
- Many buttons to quickly create stuff
|
||||
- Sum of tables in reports, requested by [MacPaille](https://github.com/MacPaille) ([issue 1106](https://github.com/firefly-iii/firefly-iii/issues/1106))
|
||||
- Future versions of Firefly III will notify you there is a new version, as suggested by [8bitgentleman](https://github.com/8bitgentleman) in [issue 1050](https://github.com/firefly-iii/firefly-iii/issues/1050)
|
||||
- Improved net worth box [issue 1101](https://github.com/firefly-iii/firefly-iii/issues/1101) ([Nik-vr](https://github.com/Nik-vr))
|
||||
- Nice dropdown in transaction list [issue 1082](https://github.com/firefly-iii/firefly-iii/issues/1082)
|
||||
- Better support for local fonts thanks to [devlearner](https://github.com/devlearner) ([issue 1145](https://github.com/firefly-iii/firefly-iii/issues/1145))
|
||||
- Improve attachment support and view capabilities (suggested by [trinhit](https://github.com/trinhit) in [issue 1146](https://github.com/firefly-iii/firefly-iii/issues/1146))
|
||||
- Whole new [read me file](https://github.com/firefly-iii/firefly-iii/blob/master/readme.md), [new end user documentation](https://firefly-iii.readthedocs.io/en/latest/) and an [updated website](https://www.firefly-iii.org/)!
|
||||
- Many charts and info-blocks now scale property ([issue 989](https://github.com/firefly-iii/firefly-iii/issues/989) and [issue 1040](https://github.com/firefly-iii/firefly-iii/issues/1040))
|
||||
- Charts work in IE thanks to [devlearner](https://github.com/devlearner) ([issue 1107](https://github.com/firefly-iii/firefly-iii/issues/1107))
|
||||
- Various fixes in import routine
|
||||
- Bug that left charts empty ([issue 1088](https://github.com/firefly-iii/firefly-iii/issues/1088)), reported by various users amongst which [jinformatique](https://github.com/jinformatique)
|
||||
- [Issue 1124](https://github.com/firefly-iii/firefly-iii/issues/1124), as reported by [gavu](https://github.com/gavu)
|
||||
- [Issue 1125](https://github.com/firefly-iii/firefly-iii/issues/1125), as reported by [gavu](https://github.com/gavu)
|
||||
- [Issue 1126](https://github.com/firefly-iii/firefly-iii/issues/1126), as reported by [gavu](https://github.com/gavu)
|
||||
- [Issue 1131](https://github.com/firefly-iii/firefly-iii/issues/1131), as reported by [dp87](https://github.com/dp87)
|
||||
- [Issue 1129](https://github.com/firefly-iii/firefly-iii/issues/1129), as reported by [gavu](https://github.com/gavu)
|
||||
- [Issue 1132](https://github.com/firefly-iii/firefly-iii/issues/1132), as reported by [gavu](https://github.com/gavu)
|
||||
- Issue with cache in Sandstorm ([issue 1130](https://github.com/firefly-iii/firefly-iii/issues/1130))
|
||||
- [Issue 1134](https://github.com/firefly-iii/firefly-iii/issues/1134)
|
||||
- [Issue 1140](https://github.com/firefly-iii/firefly-iii/issues/1140)
|
||||
- [Issue 1141](https://github.com/firefly-iii/firefly-iii/issues/1141), reported by [ErikFontanel](https://github.com/ErikFontanel)
|
||||
- [Issue 1142](https://github.com/firefly-iii/firefly-iii/issues/1142)
|
||||
- Removed many access rights from the demo user
|
||||
|
||||
# 4.6.13
|
||||
- [Issue 1074](https://github.com/firefly-iii/firefly-iii/issues/1074), suggested by [MacPaille](https://github.com/MacPaille)
|
||||
- [Issue 1077](https://github.com/firefly-iii/firefly-iii/issues/1077), suggested by [wtercato](https://github.com/wtercato)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Runs every time we create a new grain!
|
||||
echo "Now in launcher.sh"
|
||||
|
||||
# Create a bunch of folders under the clean /var that php, nginx, and mysql expect to exist
|
||||
mkdir -p /var/lib/mysql
|
||||
@@ -30,7 +31,6 @@ mkdir -p /var/storage/framework/views
|
||||
mkdir -p /var/storage/logs
|
||||
mkdir -p /var/storage/upload
|
||||
|
||||
|
||||
# Ensure mysql tables created
|
||||
HOME=/etc/mysql /usr/bin/mysql_install_db --force
|
||||
|
||||
@@ -58,5 +58,9 @@ echo "Migrating..."
|
||||
php /opt/app/artisan migrate --seed --force
|
||||
echo "Done!"
|
||||
|
||||
echo "Clear cache.."
|
||||
php /opt/app/artisan cache:clear
|
||||
echo "Done"
|
||||
|
||||
# Start nginx.
|
||||
/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 = (
|
||||
appTitle = (defaultText = "Firefly III"),
|
||||
appVersion = 7,
|
||||
appMarketingVersion = (defaultText = "4.6.13"),
|
||||
appVersion = 9,
|
||||
appMarketingVersion = (defaultText = "4.7.1"),
|
||||
|
||||
actions = [
|
||||
# Define your "new document" handlers here.
|
||||
|
||||
@@ -57,6 +57,7 @@ http {
|
||||
fastcgi_index index.php;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_read_timeout 900;
|
||||
|
||||
|
||||
fastcgi_param QUERY_STRING $query_string;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
# When you change this file, you must take manual action. Read this doc:
|
||||
# - https://docs.sandstorm.io/en/latest/vagrant-spk/customizing/#setupsh
|
||||
echo "Now in setup.sh"
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -14,8 +15,11 @@ apt-get install -y python-software-properties software-properties-common
|
||||
# install all languages
|
||||
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/# id_ID.UTF-8 UTF-8/id_ID.UTF-8 UTF-8/g' /etc/locale.gen
|
||||
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/# 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
|
||||
dpkg-reconfigure --frontend=noninteractive locales
|
||||
|
||||
|
||||
|
||||
105
.scrutinizer.yml
105
.scrutinizer.yml
@@ -1,51 +1,58 @@
|
||||
# .scrutinizer.yml
|
||||
tools:
|
||||
external_code_coverage: false
|
||||
filter:
|
||||
paths:
|
||||
---
|
||||
build:
|
||||
nodes:
|
||||
analysis:
|
||||
project_setup:
|
||||
override: true
|
||||
tests:
|
||||
override:
|
||||
- php-scrutinizer-run
|
||||
checks:
|
||||
javascript: true
|
||||
php:
|
||||
align_assignments: true
|
||||
avoid_fixme_comments: true
|
||||
avoid_multiple_statements_on_same_line: true
|
||||
avoid_perl_style_comments: true
|
||||
avoid_todo_comments: true
|
||||
duplication: false
|
||||
encourage_single_quotes: true
|
||||
newline_at_end_of_file: true
|
||||
no_goto: true
|
||||
no_long_variable_names:
|
||||
maximum: "20"
|
||||
no_short_method_names:
|
||||
minimum: "3"
|
||||
no_short_variable_names:
|
||||
minimum: "3"
|
||||
optional_parameters_at_the_end: true
|
||||
parameter_doc_comments: true
|
||||
remove_extra_empty_lines: true
|
||||
return_doc_comment_if_not_inferrable: true
|
||||
return_doc_comments: true
|
||||
uppercase_constants: true
|
||||
use_self_instead_of_fqcn: true
|
||||
coding_style:
|
||||
php:
|
||||
spaces:
|
||||
around_operators:
|
||||
concatenation: true
|
||||
other:
|
||||
after_type_cast: false
|
||||
filter:
|
||||
excluded_paths:
|
||||
- database/migrations/*
|
||||
- bootstrap/*
|
||||
- config/*
|
||||
- docker/*
|
||||
- public/js/lib/*
|
||||
- public/lib/adminlte/js/*
|
||||
- public/lib/bootstrap/js/*
|
||||
- resources/*
|
||||
- routes/*
|
||||
- storage/*
|
||||
paths:
|
||||
- app/*
|
||||
- public/js/ff/*
|
||||
excluded_paths:
|
||||
- "database/migrations/*"
|
||||
- "bootstrap/*"
|
||||
- "config/*"
|
||||
- "docker/*"
|
||||
- "public/js/lib/*"
|
||||
- "public/lib/adminlte/js/*"
|
||||
- "public/lib/bootstrap/js/*"
|
||||
- "resources/*"
|
||||
- "routes/*"
|
||||
- "storage/*"
|
||||
checks:
|
||||
php:
|
||||
use_self_instead_of_fqcn: true
|
||||
uppercase_constants: true
|
||||
return_doc_comments: true
|
||||
return_doc_comment_if_not_inferrable: true
|
||||
remove_extra_empty_lines: true
|
||||
parameter_doc_comments: true
|
||||
optional_parameters_at_the_end: true
|
||||
no_short_variable_names:
|
||||
minimum: '3'
|
||||
no_short_method_names:
|
||||
minimum: '3'
|
||||
no_long_variable_names:
|
||||
maximum: '20'
|
||||
no_goto: true
|
||||
newline_at_end_of_file: true
|
||||
encourage_single_quotes: true
|
||||
avoid_todo_comments: true
|
||||
avoid_perl_style_comments: true
|
||||
avoid_fixme_comments: true
|
||||
avoid_multiple_statements_on_same_line: true
|
||||
align_assignments: true
|
||||
duplication: false
|
||||
javascript: true
|
||||
|
||||
coding_style:
|
||||
php:
|
||||
spaces:
|
||||
around_operators:
|
||||
concatenation: true
|
||||
other:
|
||||
after_type_cast: false
|
||||
tools:
|
||||
external_code_coverage: false
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
language: php
|
||||
php:
|
||||
- 7.1
|
||||
- 7.2
|
||||
|
||||
cache:
|
||||
directories:
|
||||
@@ -24,6 +23,7 @@ script:
|
||||
|
||||
after_success:
|
||||
- travis_retry php vendor/bin/php-coveralls -x storage/build/clover-all.xml
|
||||
- bash <(curl -s https://codecov.io/bash) -f storage/build/clover-all.xml
|
||||
|
||||
# safelist
|
||||
branches:
|
||||
|
||||
89
README.md
89
README.md
@@ -1,89 +0,0 @@
|
||||
# Firefly III: A personal finances manager
|
||||
|
||||
[](https://secure.php.net/downloads.php) [](https://packagist.org/packages/grumpydictator/firefly-iii) [](https://www.gnu.org/licenses/gpl.html) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA)
|
||||
|
||||
[](https://firefly-iii.org/static/screenshots/4.6.12/index.png) [](https://firefly-iii.org/static/screenshots/4.6.12/account.png)
|
||||
|
||||
[](https://firefly-iii.org/static/screenshots/4.6.12/budget.png) [](https://firefly-iii.org/static/screenshots/4.6.12/category.png)
|
||||
|
||||
[](https://firefly-iii.org/static/screenshots/4.6.12/report1.png) [](https://firefly-iii.org/static/screenshots/4.6.12/report2.png)
|
||||
|
||||
"Firefly III" is a financial manager for your personal finances. It can help you keep track of your expenses and income.
|
||||
Firefly III supports the use of budgets. You can categorize and tag your transactions.
|
||||
It also supports credit cards, shared household accounts and savings accounts.
|
||||
There are many financial reports available.
|
||||
|
||||
## Want to try Firefly III?
|
||||
|
||||
There is a **[demo site](https://demo.firefly-iii.org)** with an example financial administration already present. You can use Docker, Heroku or Sandstorm.io (see below) to quickly setup your own instance.
|
||||
|
||||
## Install Firefly III
|
||||
|
||||
### Using docker
|
||||
|
||||
You can use docker-compose to [set up your personal secure](https://firefly-iii.org/using-docker.html) Firefly III environment. Advanced users can use the Dockerfile directly.
|
||||
|
||||
### Using vagrant (or other VMs)
|
||||
|
||||
You can install Firefly III on any Linux or Windows machine. You'll need a web server (preferrably on Linux) and access to the command line. Please read the [installation guide](https://firefly-iii.org/using-installing.html).
|
||||
|
||||
### Using Heroku
|
||||
|
||||
[](https://heroku.com/deploy?template=https://github.com/firefly-iii/firefly-iii/tree/master)
|
||||
|
||||
Register for a free Heroku account and instantly run Firefly III on your very own cloud instance.
|
||||
|
||||
### Using Sandstorm.io
|
||||
|
||||
You can find Firefly III in [the Sandstorm.io marketplace](https://apps.sandstorm.io/app/uws252ya9mep4t77tevn85333xzsgrpgth8q4y1rhknn1hammw70). You can run it on your own installation or on Oasis.
|
||||
|
||||
### Other options
|
||||
|
||||
Firefly III is also available as a package on [https://softaculous.com/](Softaculous) and [AMPPS](https://www.ampps.com/).
|
||||
|
||||
## More about Firefly III
|
||||
|
||||
Personal financial management is pretty difficult, and everybody has their own approach to it.
|
||||
Some people make budgets, other people limit their cashflow by throwing away their credit cards,
|
||||
others try to increase their current cashflow. There are tons of ways to save and earn money.
|
||||
|
||||
Firefly works on the principle that if you know where you're money is going, you can stop it from going there.
|
||||
|
||||
### Some advantages of using Firefly
|
||||
|
||||
- Firefly can import any CSV file, so migrating from other systems is easy.
|
||||
- Firefly runs on your own server, so you are fully in control of your data. Remember, there is no such thing as "the cloud", it’s just somebody else’s computer!
|
||||
- Firefly has lots of features without being fancy or bloated.
|
||||
- If you feel you're missing something you can just ask me and I'll add it!
|
||||
|
||||
Firefly III has become pretty awesome over the years! (but please excuse me for bragging, it's just that I'm proud of it).
|
||||
[You can read more about Firefly III, and its features, on the website](https://firefly-iii.org/).
|
||||
|
||||
### Contributing
|
||||
|
||||
Please read [CONTRIBUTING.md](https://github.com/firefly-iii/firefly-iii/blob/master/.github/CONTRIBUTING.md) for details on contributing, and the process for submitting pull requests. Please check out the [code of conduct](https://github.com/firefly-iii/firefly-iii/blob/master/CODE_OF_CONDUCT.md) as well.
|
||||
|
||||
### Versioning
|
||||
|
||||
We use [SemVer](http://semver.org/) for versioning. For the versions available, see [the tags](https://github.com/firefly-iii/firefly-iii/tags) on this repository.
|
||||
|
||||
### Authors
|
||||
|
||||
* James Cole
|
||||
* Over time, [many people have contributed to Firefly III](https://github.com/firefly-iii/firefly-iii/graphs/contributors).
|
||||
|
||||
### License
|
||||
|
||||
This work [is licensed](https://github.com/firefly-iii/firefly-iii/blob/master/LICENSE) under the [GPL v3](https://www.gnu.org/licenses/gpl.html).
|
||||
|
||||
### Other stuff
|
||||
|
||||
If you like Firefly III and if it helps you save lots of money, why not send me [a dime for every dollar saved](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA) (this is a joke, although the Paypal form works just fine, try it!)
|
||||
|
||||
If you want to contact me, please open an issue or [email me](mailto:thegrumpydictator@gmail.com).
|
||||
|
||||
### Alternatives
|
||||
|
||||
If you are looking for alternatives, check out [Kickball's Awesome-Selfhosted list](https://github.com/Kickball/awesome-selfhosted) which features not only Firefly III but also noteworthy alternatives such as [Silverstrike](https://github.com/agstrike/silverstrike).
|
||||
|
||||
[](https://travis-ci.org/firefly-iii/firefly-iii) [](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/?branch=master) [](https://coveralls.io/github/firefly-iii/firefly-iii?branch=master)
|
||||
88
app/Api/V1/Controllers/AboutController.php
Normal file
88
app/Api/V1/Controllers/AboutController.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* AboutController.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\Api\V1\Controllers;
|
||||
|
||||
use DB;
|
||||
use FireflyIII\Transformers\UserTransformer;
|
||||
use Illuminate\Http\Request;
|
||||
use League\Fractal\Manager;
|
||||
use League\Fractal\Resource\Item;
|
||||
use League\Fractal\Serializer\JsonApiSerializer;
|
||||
|
||||
|
||||
/**
|
||||
* Class AboutController
|
||||
*/
|
||||
class AboutController extends Controller
|
||||
{
|
||||
/**
|
||||
* AccountController constructor.
|
||||
*
|
||||
* @throws \FireflyIII\Exceptions\FireflyException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function about()
|
||||
{
|
||||
$search = ['~', '#'];
|
||||
$replace = ['\~', '# '];
|
||||
$phpVersion = str_replace($search, $replace, PHP_VERSION);
|
||||
$phpOs = str_replace($search, $replace, php_uname());
|
||||
$currentDriver = DB::getDriverName();
|
||||
$data
|
||||
= [
|
||||
'version' => config('firefly.version'),
|
||||
'api_version' => config('firefly.api_version'),
|
||||
'php_version' => $phpVersion,
|
||||
'os' => $phpOs,
|
||||
'driver' => $currentDriver,
|
||||
|
||||
];
|
||||
|
||||
return response()->json(['data' => $data], 200)->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function user(Request $request)
|
||||
{
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
$resource = new Item(auth()->user(), new UserTransformer($this->parameters), 'users');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
}
|
||||
263
app/Api/V1/Controllers/AccountController.php
Normal file
263
app/Api/V1/Controllers/AccountController.php
Normal file
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
/**
|
||||
* AccountController.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\Api\V1\Controllers;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\AccountRequest;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Transformers\AccountTransformer;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use League\Fractal\Manager;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection as FractalCollection;
|
||||
use League\Fractal\Resource\Item;
|
||||
use League\Fractal\Serializer\JsonApiSerializer;
|
||||
use Preferences;
|
||||
|
||||
/**
|
||||
* Class AccountController
|
||||
*/
|
||||
class AccountController extends Controller
|
||||
{
|
||||
|
||||
/** @var CurrencyRepositoryInterface */
|
||||
private $currencyRepository;
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* AccountController constructor.
|
||||
*
|
||||
* @throws \FireflyIII\Exceptions\FireflyException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
/** @var AccountRepositoryInterface repository */
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->repository->setUser(auth()->user());
|
||||
|
||||
$this->currencyRepository = app(CurrencyRepositoryInterface::class);
|
||||
$this->currencyRepository->setUser(auth()->user());
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \FireflyIII\Models\Account $account
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function delete(Account $account)
|
||||
{
|
||||
$this->repository->destroy($account, null);
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
// create some objects:
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
|
||||
// read type from URI
|
||||
$type = $request->get('type') ?? 'all';
|
||||
$this->parameters->set('type', $type);
|
||||
|
||||
// types to get, page size:
|
||||
$types = $this->mapTypes($this->parameters->get('type'));
|
||||
$pageSize = intval(Preferences::getForUser(auth()->user(), 'listPageSize', 50)->data);
|
||||
|
||||
// get list of accounts. Count it and split it.
|
||||
$collection = $this->repository->getAccountsByType($types);
|
||||
$count = $collection->count();
|
||||
$accounts = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
|
||||
// make paginator:
|
||||
$paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page'));
|
||||
$paginator->setPath(route('api.v1.accounts.index') . $this->buildParams());
|
||||
|
||||
// present to user.
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
$resource = new FractalCollection($accounts, new AccountTransformer($this->parameters), 'accounts');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Account $account
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function show(Request $request, Account $account)
|
||||
{
|
||||
$manager = new Manager();
|
||||
|
||||
// add include parameter:
|
||||
$include = $request->get('include') ?? '';
|
||||
$manager->parseIncludes($include);
|
||||
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
$resource = new Item($account, new AccountTransformer($this->parameters), 'accounts');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccountRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function store(AccountRequest $request)
|
||||
{
|
||||
$data = $request->getAll();
|
||||
// if currency ID is 0, find the currency by the code:
|
||||
if ($data['currency_id'] === 0) {
|
||||
$currency = $this->currencyRepository->findByCodeNull($data['currency_code']);
|
||||
$data['currency_id'] = is_null($currency) ? 0 : $currency->id;
|
||||
}
|
||||
$account = $this->repository->store($data);
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
$resource = new Item($account, new AccountTransformer($this->parameters), 'accounts');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update account.
|
||||
*
|
||||
* @param AccountRequest $request
|
||||
* @param Account $account
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function update(AccountRequest $request, Account $account)
|
||||
{
|
||||
$data = $request->getAll();
|
||||
// if currency ID is 0, find the currency by the code:
|
||||
if ($data['currency_id'] === 0) {
|
||||
$currency = $this->currencyRepository->findByCodeNull($data['currency_code']);
|
||||
$data['currency_id'] = is_null($currency) ? 0 : $currency->id;
|
||||
}
|
||||
// set correct type:
|
||||
$data['type'] = config('firefly.shortNamesByFullName.' . $account->accountType->type);
|
||||
$this->repository->update($account, $data);
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
$resource = new Item($account, new AccountTransformer($this->parameters), 'accounts');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function mapTypes(string $type): array
|
||||
{
|
||||
$types = [
|
||||
'all' => [
|
||||
AccountType::DEFAULT,
|
||||
AccountType::CASH,
|
||||
AccountType::ASSET,
|
||||
AccountType::EXPENSE,
|
||||
AccountType::REVENUE,
|
||||
AccountType::INITIAL_BALANCE,
|
||||
AccountType::BENEFICIARY,
|
||||
AccountType::IMPORT,
|
||||
AccountType::RECONCILIATION,
|
||||
AccountType::LOAN,
|
||||
],
|
||||
'asset' => [
|
||||
AccountType::DEFAULT,
|
||||
AccountType::ASSET,
|
||||
],
|
||||
'cash' => [
|
||||
AccountType::CASH,
|
||||
],
|
||||
'expense' => [
|
||||
AccountType::EXPENSE,
|
||||
AccountType::BENEFICIARY,
|
||||
],
|
||||
'revenue' => [
|
||||
AccountType::REVENUE,
|
||||
],
|
||||
'special' => [
|
||||
AccountType::CASH,
|
||||
AccountType::INITIAL_BALANCE,
|
||||
AccountType::IMPORT,
|
||||
AccountType::RECONCILIATION,
|
||||
AccountType::LOAN,
|
||||
],
|
||||
'hidden' => [
|
||||
AccountType::INITIAL_BALANCE,
|
||||
AccountType::IMPORT,
|
||||
AccountType::RECONCILIATION,
|
||||
AccountType::LOAN,
|
||||
],
|
||||
AccountType::DEFAULT => [AccountType::DEFAULT],
|
||||
AccountType::CASH => [AccountType::CASH],
|
||||
AccountType::ASSET => [AccountType::ASSET],
|
||||
AccountType::EXPENSE => [AccountType::EXPENSE],
|
||||
AccountType::REVENUE => [AccountType::REVENUE],
|
||||
AccountType::INITIAL_BALANCE => [AccountType::INITIAL_BALANCE],
|
||||
AccountType::BENEFICIARY => [AccountType::BENEFICIARY],
|
||||
AccountType::IMPORT => [AccountType::IMPORT],
|
||||
AccountType::RECONCILIATION => [AccountType::RECONCILIATION],
|
||||
AccountType::LOAN => [AccountType::LOAN],
|
||||
];
|
||||
if (isset($types[$type])) {
|
||||
return $types[$type];
|
||||
}
|
||||
|
||||
return $types['all']; // @codeCoverageIgnore
|
||||
}
|
||||
}
|
||||
164
app/Api/V1/Controllers/BillController.php
Normal file
164
app/Api/V1/Controllers/BillController.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/**
|
||||
* BillController.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\Api\V1\Controllers;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\BillRequest;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Transformers\BillTransformer;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use League\Fractal\Manager;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection as FractalCollection;
|
||||
use League\Fractal\Resource\Item;
|
||||
use League\Fractal\Serializer\JsonApiSerializer;
|
||||
use Preferences;
|
||||
|
||||
/**
|
||||
* Class BillController
|
||||
*/
|
||||
class BillController extends Controller
|
||||
{
|
||||
/** @var BillRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* BillController constructor.
|
||||
*
|
||||
* @throws \FireflyIII\Exceptions\FireflyException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
/** @var BillRepositoryInterface repository */
|
||||
$this->repository = app(BillRepositoryInterface::class);
|
||||
$this->repository->setUser(auth()->user());
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \FireflyIII\Models\Bill $bill
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function delete(Bill $bill)
|
||||
{
|
||||
$this->repository->destroy($bill);
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$pageSize = intval(Preferences::getForUser(auth()->user(), 'listPageSize', 50)->data);
|
||||
$paginator = $this->repository->getPaginator($pageSize);
|
||||
/** @var Collection $bills */
|
||||
$bills = $paginator->getCollection();
|
||||
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
$resource = new FractalCollection($bills, new BillTransformer($this->parameters), 'bills');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function show(Request $request, Bill $bill)
|
||||
{
|
||||
$manager = new Manager();
|
||||
// add include parameter:
|
||||
$include = $request->get('include') ?? '';
|
||||
$manager->parseIncludes($include);
|
||||
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
$resource = new Item($bill, new BillTransformer($this->parameters), 'bills');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BillRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function store(BillRequest $request)
|
||||
{
|
||||
$bill = $this->repository->store($request->getAll());
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
$resource = new Item($bill, new BillTransformer($this->parameters), 'bills');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param BillRequest $request
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function update(BillRequest $request, Bill $bill)
|
||||
{
|
||||
$data = $request->getAll();
|
||||
$bill = $this->repository->update($bill, $data);
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
$resource = new Item($bill, new BillTransformer($this->parameters), 'bills');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
|
||||
}
|
||||
}
|
||||
121
app/Api/V1/Controllers/Controller.php
Normal file
121
app/Api/V1/Controllers/Controller.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
/**
|
||||
* Controller.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\Api\V1\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Carbon\Exceptions\InvalidDateException;
|
||||
use FireflyConfig;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
|
||||
/**
|
||||
* Class Controller.
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||
|
||||
/** @var ParameterBag */
|
||||
protected $parameters;
|
||||
|
||||
/**
|
||||
* Controller constructor.
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// is site a demo site?
|
||||
$isDemoSite = FireflyConfig::get('is_demo_site', config('firefly.configuration.is_demo_site'))->data;
|
||||
|
||||
// do not expose API on demo site:
|
||||
if (true === $isDemoSite) {
|
||||
throw new FireflyException('The API is not available on the demo site.');
|
||||
}
|
||||
|
||||
// get global parameters
|
||||
$this->parameters = $this->getParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function buildParams(): string
|
||||
{
|
||||
$return = '?';
|
||||
$params = [];
|
||||
foreach ($this->parameters as $key => $value) {
|
||||
if($key === 'page') {
|
||||
continue;
|
||||
}
|
||||
if ($value instanceof Carbon) {
|
||||
$params[$key] = $value->format('Y-m-d');
|
||||
}
|
||||
if (!$value instanceof Carbon) {
|
||||
$params[$key] = $value;
|
||||
}
|
||||
}
|
||||
$return .= http_build_query($params);
|
||||
if (strlen($return) === 1) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ParameterBag
|
||||
*/
|
||||
private function getParameters(): ParameterBag
|
||||
{
|
||||
$bag = new ParameterBag;
|
||||
$page = (int)request()->get('page');
|
||||
if ($page === 0) {
|
||||
$page = 1;
|
||||
}
|
||||
$bag->set('page', $page);
|
||||
|
||||
// some date fields:
|
||||
$dates = ['start', 'end', 'date'];
|
||||
foreach ($dates as $field) {
|
||||
$date = request()->get($field);
|
||||
$obj = null;
|
||||
if (!is_null($date)) {
|
||||
try {
|
||||
$obj = new Carbon($date);
|
||||
} catch (InvalidDateException $e) {
|
||||
// don't care
|
||||
}
|
||||
}
|
||||
$bag->set($field, $obj);
|
||||
}
|
||||
|
||||
return $bag;
|
||||
|
||||
}
|
||||
}
|
||||
333
app/Api/V1/Controllers/TransactionController.php
Normal file
333
app/Api/V1/Controllers/TransactionController.php
Normal file
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
/**
|
||||
* TransactionController.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\Api\V1\Controllers;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\TransactionRequest;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
||||
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
|
||||
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Transformers\TransactionTransformer;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use League\Fractal\Manager;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection as FractalCollection;
|
||||
use League\Fractal\Resource\Item;
|
||||
use League\Fractal\Serializer\JsonApiSerializer;
|
||||
use Log;
|
||||
use Preferences;
|
||||
|
||||
/**
|
||||
* Class TransactionController
|
||||
*/
|
||||
class TransactionController extends Controller
|
||||
{
|
||||
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* TransactionController constructor.
|
||||
*
|
||||
* @throws \FireflyIII\Exceptions\FireflyException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
/** @var JournalRepositoryInterface repository */
|
||||
$this->repository = app(JournalRepositoryInterface::class);
|
||||
$this->repository->setUser(auth()->user());
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \FireflyIII\Models\Transaction $transaction
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function delete(Transaction $transaction)
|
||||
{
|
||||
$journal = $transaction->transactionJournal;
|
||||
$this->repository->destroy($journal);
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$pageSize = intval(Preferences::getForUser(auth()->user(), 'listPageSize', 50)->data);
|
||||
|
||||
// read type from URI
|
||||
$type = $request->get('type') ?? 'default';
|
||||
$this->parameters->set('type', $type);
|
||||
|
||||
// types to get, page size:
|
||||
$types = $this->mapTypes($this->parameters->get('type'));
|
||||
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
// collect transactions using the journal collector
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setUser(auth()->user());
|
||||
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
|
||||
$collector->setAllAssetAccounts();
|
||||
|
||||
// remove internal transfer filter:
|
||||
if (in_array(TransactionType::TRANSFER, $types)) {
|
||||
$collector->removeFilter(InternalTransferFilter::class);
|
||||
}
|
||||
|
||||
if (!is_null($this->parameters->get('start')) && !is_null($this->parameters->get('end'))) {
|
||||
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
|
||||
}
|
||||
$collector->setLimit($pageSize)->setPage($this->parameters->get('page'));
|
||||
$collector->setTypes($types);
|
||||
$paginator = $collector->getPaginatedJournals();
|
||||
$paginator->setPath(route('api.v1.transactions.index') . $this->buildParams());
|
||||
$transactions = $paginator->getCollection();
|
||||
|
||||
|
||||
$resource = new FractalCollection($transactions, new TransactionTransformer($this->parameters), 'transactions');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Transaction $transaction
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function show(Request $request, Transaction $transaction)
|
||||
{
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
// add include parameter:
|
||||
$include = $request->get('include') ?? '';
|
||||
$manager->parseIncludes($include);
|
||||
|
||||
// collect transactions using the journal collector
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setUser(auth()->user());
|
||||
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
|
||||
// filter on specific journals.
|
||||
$collector->setJournals(new Collection([$transaction->transactionJournal]));
|
||||
|
||||
// add filter to remove transactions:
|
||||
$transactionType = $transaction->transactionJournal->transactionType->type;
|
||||
if ($transactionType === TransactionType::WITHDRAWAL) {
|
||||
$collector->addFilter(PositiveAmountFilter::class);
|
||||
}
|
||||
if (!($transactionType === TransactionType::WITHDRAWAL)) {
|
||||
$collector->addFilter(NegativeAmountFilter::class);
|
||||
}
|
||||
|
||||
$transactions = $collector->getJournals();
|
||||
$resource = new FractalCollection($transactions, new TransactionTransformer($this->parameters), 'transactions');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function store(TransactionRequest $request, JournalRepositoryInterface $repository)
|
||||
{
|
||||
$data = $request->getAll();
|
||||
$data['user'] = auth()->user()->id;
|
||||
$journal = $repository->store($data);
|
||||
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
// add include parameter:
|
||||
$include = $request->get('include') ?? '';
|
||||
$manager->parseIncludes($include);
|
||||
|
||||
// collect transactions using the journal collector
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setUser(auth()->user());
|
||||
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
|
||||
// filter on specific journals.
|
||||
$collector->setJournals(new Collection([$journal]));
|
||||
|
||||
// add filter to remove transactions:
|
||||
$transactionType = $journal->transactionType->type;
|
||||
if ($transactionType === TransactionType::WITHDRAWAL) {
|
||||
$collector->addFilter(PositiveAmountFilter::class);
|
||||
}
|
||||
if (!($transactionType === TransactionType::WITHDRAWAL)) {
|
||||
$collector->addFilter(NegativeAmountFilter::class);
|
||||
}
|
||||
|
||||
$transactions = $collector->getJournals();
|
||||
$resource = new FractalCollection($transactions, new TransactionTransformer($this->parameters), 'transactions');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param TransactionRequest $request
|
||||
* @param JournalRepositoryInterface $repository
|
||||
* @param Transaction $transaction
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function update(TransactionRequest $request, JournalRepositoryInterface $repository, Transaction $transaction)
|
||||
{
|
||||
$data = $request->getAll();
|
||||
$data['user'] = auth()->user()->id;
|
||||
|
||||
Log::debug('Inside transaction update');
|
||||
|
||||
$journal = $repository->update($transaction->transactionJournal, $data);
|
||||
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
// add include parameter:
|
||||
$include = $request->get('include') ?? '';
|
||||
$manager->parseIncludes($include);
|
||||
|
||||
// needs a lot of extra data to match the journal collector. Or just expand that one.
|
||||
// collect transactions using the journal collector
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setUser(auth()->user());
|
||||
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
|
||||
// filter on specific journals.
|
||||
$collector->setJournals(new Collection([$journal]));
|
||||
|
||||
// add filter to remove transactions:
|
||||
$transactionType = $journal->transactionType->type;
|
||||
if ($transactionType === TransactionType::WITHDRAWAL) {
|
||||
$collector->addFilter(PositiveAmountFilter::class);
|
||||
}
|
||||
if (!($transactionType === TransactionType::WITHDRAWAL)) {
|
||||
$collector->addFilter(NegativeAmountFilter::class);
|
||||
}
|
||||
|
||||
$transactions = $collector->getJournals();
|
||||
$resource = new FractalCollection($transactions, new TransactionTransformer($this->parameters), 'transactions');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function mapTypes(string $type): array
|
||||
{
|
||||
$types = [
|
||||
'all' => [
|
||||
TransactionType::WITHDRAWAL,
|
||||
TransactionType::DEPOSIT,
|
||||
TransactionType::TRANSFER,
|
||||
TransactionType::OPENING_BALANCE,
|
||||
TransactionType::RECONCILIATION,
|
||||
],
|
||||
'withdrawal' => [
|
||||
TransactionType::WITHDRAWAL,
|
||||
],
|
||||
'withdrawals' => [
|
||||
TransactionType::WITHDRAWAL,
|
||||
],
|
||||
'expense' => [
|
||||
TransactionType::WITHDRAWAL,
|
||||
],
|
||||
'income' => [
|
||||
TransactionType::DEPOSIT,
|
||||
],
|
||||
'deposit' => [
|
||||
TransactionType::DEPOSIT,
|
||||
],
|
||||
'deposits' => [
|
||||
TransactionType::DEPOSIT,
|
||||
],
|
||||
'transfer' => [
|
||||
TransactionType::TRANSFER,
|
||||
],
|
||||
'transfers' => [
|
||||
TransactionType::TRANSFER,
|
||||
],
|
||||
'opening_balance' => [
|
||||
TransactionType::OPENING_BALANCE,
|
||||
],
|
||||
'reconciliation' => [
|
||||
TransactionType::RECONCILIATION,
|
||||
],
|
||||
'reconciliations' => [
|
||||
TransactionType::RECONCILIATION,
|
||||
],
|
||||
'special' => [
|
||||
TransactionType::OPENING_BALANCE,
|
||||
TransactionType::RECONCILIATION,
|
||||
],
|
||||
'specials' => [
|
||||
TransactionType::OPENING_BALANCE,
|
||||
TransactionType::RECONCILIATION,
|
||||
],
|
||||
'default' => [
|
||||
TransactionType::WITHDRAWAL,
|
||||
TransactionType::DEPOSIT,
|
||||
TransactionType::TRANSFER,
|
||||
],
|
||||
];
|
||||
if (isset($types[$type])) {
|
||||
return $types[$type];
|
||||
}
|
||||
|
||||
return $types['default']; // @codeCoverageIgnore
|
||||
|
||||
}
|
||||
}
|
||||
192
app/Api/V1/Controllers/UserController.php
Normal file
192
app/Api/V1/Controllers/UserController.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
/**
|
||||
* UserController.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\Api\V1\Controllers;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\UserRequest;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\Transformers\UserTransformer;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use League\Fractal\Manager;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection as FractalCollection;
|
||||
use League\Fractal\Resource\Item;
|
||||
use League\Fractal\Serializer\JsonApiSerializer;
|
||||
use Preferences;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
|
||||
|
||||
/**
|
||||
* Class UserController
|
||||
*/
|
||||
class UserController extends Controller
|
||||
{
|
||||
|
||||
/** @var UserRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* UserController constructor.
|
||||
*
|
||||
* @throws \FireflyIII\Exceptions\FireflyException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
/** @var UserRepositoryInterface repository */
|
||||
$this->repository = app(UserRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \FireflyIII\User $user
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function delete(User $user)
|
||||
{
|
||||
if (auth()->user()->hasRole('owner')) {
|
||||
$this->repository->destroy($user);
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
throw new AccessDeniedException(''); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
// user preferences
|
||||
$pageSize = intval(Preferences::getForUser(auth()->user(), 'listPageSize', 50)->data);
|
||||
|
||||
// make manager
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
// build collection
|
||||
$collection = $this->repository->all();
|
||||
$count = $collection->count();
|
||||
$users = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
|
||||
// make paginator:
|
||||
$paginator = new LengthAwarePaginator($users, $count, $pageSize, $this->parameters->get('page'));
|
||||
$paginator->setPath(route('api.v1.users.index') . $this->buildParams());
|
||||
|
||||
// make resource
|
||||
$resource = new FractalCollection($users, new UserTransformer($this->parameters), 'users');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param User $user
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function show(Request $request, User $user)
|
||||
{
|
||||
// make manager
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
// add include parameter:
|
||||
$include = $request->get('include') ?? '';
|
||||
$manager->parseIncludes($include);
|
||||
|
||||
// make resource
|
||||
$resource = new Item($user, new UserTransformer($this->parameters), 'users');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UserRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function store(UserRequest $request)
|
||||
{
|
||||
$data = $request->getAll();
|
||||
$user = $this->repository->store($data);
|
||||
|
||||
// make manager
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
// add include parameter:
|
||||
$include = $request->get('include') ?? '';
|
||||
$manager->parseIncludes($include);
|
||||
|
||||
// make resource
|
||||
$resource = new Item($user, new UserTransformer($this->parameters), 'users');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UserRequest $request
|
||||
* @param User $user
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function update(UserRequest $request, User $user)
|
||||
{
|
||||
$data = $request->getAll();
|
||||
$user = $this->repository->update($user, $data);
|
||||
|
||||
// make manager
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
// add include parameter:
|
||||
$include = $request->get('include') ?? '';
|
||||
$manager->parseIncludes($include);
|
||||
|
||||
// make resource
|
||||
$resource = new Item($user, new UserTransformer($this->parameters), 'users');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
107
app/Api/V1/Requests/AccountRequest.php
Normal file
107
app/Api/V1/Requests/AccountRequest.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/**
|
||||
* AccountRequest.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\Api\V1\Requests;
|
||||
|
||||
/**
|
||||
* Class AccountRequest
|
||||
*/
|
||||
class AccountRequest extends Request
|
||||
{
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
// Only allow authenticated users
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
$data = [
|
||||
'name' => $this->string('name'),
|
||||
'active' => $this->boolean('active'),
|
||||
'accountType' => $this->string('type'),
|
||||
'account_type_id' => null,
|
||||
'currency_id' => $this->integer('currency_id'),
|
||||
'currency_code' => $this->string('currency_code'),
|
||||
'virtualBalance' => $this->string('virtual_balance'),
|
||||
'iban' => $this->string('iban'),
|
||||
'BIC' => $this->string('bic'),
|
||||
'accountNumber' => $this->string('account_number'),
|
||||
'accountRole' => $this->string('account_role'),
|
||||
'openingBalance' => $this->string('opening_balance'),
|
||||
'openingBalanceDate' => $this->date('opening_balance_date'),
|
||||
'ccType' => $this->string('cc_type'),
|
||||
'ccMonthlyPaymentDate' => $this->string('cc_monthly_payment_date'),
|
||||
'notes' => $this->string('notes'),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$accountRoles = join(',', config('firefly.accountRoles'));
|
||||
$types = join(',', array_keys(config('firefly.subTitlesByIdentifier')));
|
||||
$ccPaymentTypes = join(',', array_keys(config('firefly.ccTypes')));
|
||||
$rules = [
|
||||
'name' => 'required|min:1|uniqueAccountForUser',
|
||||
'opening_balance' => 'numeric|required_with:opening_balance_date|nullable',
|
||||
'opening_balance_date' => 'date|required_with:opening_balance|nullable',
|
||||
'iban' => 'iban|nullable',
|
||||
'bic' => 'bic|nullable',
|
||||
'virtual_balance' => 'numeric|nullable',
|
||||
'currency_id' => 'numeric|exists:transaction_currencies,id|required_without:currency_code',
|
||||
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code|required_without:currency_id',
|
||||
'account_number' => 'between:1,255|nullable|uniqueAccountNumberForUser',
|
||||
'account_role' => 'in:' . $accountRoles . '|required_if:type,asset',
|
||||
'active' => 'required|boolean',
|
||||
'cc_type' => 'in:' . $ccPaymentTypes . '|required_if:account_role,ccAsset',
|
||||
'cc_monthly_payment_date' => 'date' . '|required_if:account_role,ccAsset|required_if:cc_type,monthlyFull',
|
||||
'type' => 'required|in:' . $types,
|
||||
'notes' => 'min:0|max:65536',
|
||||
];
|
||||
switch ($this->method()) {
|
||||
default:
|
||||
break;
|
||||
case 'PUT':
|
||||
case 'PATCH':
|
||||
$account = $this->route()->parameter('account');
|
||||
$rules['name'] .= ':' . $account->id;
|
||||
$rules['account_number'] .= ':' . $account->id;
|
||||
$rules['type'] = 'in:' . $types;
|
||||
break;
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
119
app/Api/V1/Requests/BillRequest.php
Normal file
119
app/Api/V1/Requests/BillRequest.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
/**
|
||||
* BillRequest.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\Api\V1\Requests;
|
||||
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
/**
|
||||
* Class BillRequest
|
||||
*/
|
||||
class BillRequest extends Request
|
||||
{
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
// Only allow authenticated users
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
$data = [
|
||||
'name' => $this->string('name'),
|
||||
'match' => $this->string('match'),
|
||||
'amount_min' => $this->string('amount_min'),
|
||||
'amount_max' => $this->string('amount_max'),
|
||||
//'currency_id' => $this->integer('currency_id'),
|
||||
//'currency_code' => $this->string('currency_code'),
|
||||
'date' => $this->date('date'),
|
||||
'repeat_freq' => $this->string('repeat_freq'),
|
||||
'skip' => $this->integer('skip'),
|
||||
'automatch' => $this->boolean('automatch'),
|
||||
'active' => $this->boolean('active'),
|
||||
'notes' => $this->string('notes'),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'name' => 'required|between:1,255|uniqueObjectForUser:bills,name',
|
||||
'match' => 'required|between:1,255|uniqueObjectForUser:bills,match',
|
||||
'amount_min' => 'required|numeric|more:0',
|
||||
'amount_max' => 'required|numeric|more:0',
|
||||
//'currency_id' => 'numeric|exists:transaction_currencies,id|required_without:currency_code',
|
||||
//'currency_code' => 'min:3|max:3|exists:transaction_currencies,code|required_without:currency_id',
|
||||
'date' => 'required|date',
|
||||
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
|
||||
'skip' => 'required|between:0,31',
|
||||
'automatch' => 'required|boolean',
|
||||
'active' => 'required|boolean',
|
||||
'notes' => 'between:1,65536',
|
||||
];
|
||||
switch ($this->method()) {
|
||||
default:
|
||||
break;
|
||||
case 'PUT':
|
||||
case 'PATCH':
|
||||
$bill = $this->route()->parameter('bill');
|
||||
$rules['name'] .= ',' . $bill->id;
|
||||
$rules['match'] .= ',' . $bill->id;
|
||||
break;
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the validator instance.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator) {
|
||||
$data = $validator->getData();
|
||||
$min = floatval($data['amount_min'] ?? 0);
|
||||
$max = floatval($data['amount_max'] ?? 0);
|
||||
if ($min > $max) {
|
||||
$validator->errors()->add('amount_min', trans('validation.amount_min_over_max'));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
32
app/Api/V1/Requests/Request.php
Normal file
32
app/Api/V1/Requests/Request.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* Request.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\Api\V1\Requests;
|
||||
|
||||
use FireflyIII\Http\Requests\Request as FireflyIIIRequest;
|
||||
/**
|
||||
* Class Request.
|
||||
*/
|
||||
class Request extends FireflyIIIRequest
|
||||
{
|
||||
|
||||
}
|
||||
500
app/Api/V1/Requests/TransactionRequest.php
Normal file
500
app/Api/V1/Requests/TransactionRequest.php
Normal file
@@ -0,0 +1,500 @@
|
||||
<?php
|
||||
/**
|
||||
* TransactionRequest.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\Api\V1\Requests;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Rules\BelongsUser;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
|
||||
/**
|
||||
* Class TransactionRequest
|
||||
*/
|
||||
class TransactionRequest extends Request
|
||||
{
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
// Only allow authenticated users
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
$data = [
|
||||
// basic fields for journal:
|
||||
'type' => $this->string('type'),
|
||||
'date' => $this->date('date'),
|
||||
'description' => $this->string('description'),
|
||||
'piggy_bank_id' => $this->integer('piggy_bank_id'),
|
||||
'piggy_bank_name' => $this->string('piggy_bank_name'),
|
||||
'bill_id' => $this->integer('bill_id'),
|
||||
'bill_name' => $this->string('bill_name'),
|
||||
'tags' => explode(',', $this->string('tags')),
|
||||
|
||||
// then, custom fields for journal
|
||||
'interest_date' => $this->date('interest_date'),
|
||||
'book_date' => $this->date('book_date'),
|
||||
'process_date' => $this->date('process_date'),
|
||||
'due_date' => $this->date('due_date'),
|
||||
'payment_date' => $this->date('payment_date'),
|
||||
'invoice_date' => $this->date('invoice_date'),
|
||||
'internal_reference' => $this->string('internal_reference'),
|
||||
'notes' => $this->string('notes'),
|
||||
|
||||
// then, transactions (see below).
|
||||
'transactions' => [],
|
||||
|
||||
];
|
||||
foreach ($this->get('transactions') as $index => $transaction) {
|
||||
$array = [
|
||||
'description' => $transaction['description'] ?? null,
|
||||
'amount' => $transaction['amount'],
|
||||
'currency_id' => isset($transaction['currency_id']) ? intval($transaction['currency_id']) : null,
|
||||
'currency_code' => isset($transaction['currency_code']) ? $transaction['currency_code'] : null,
|
||||
'foreign_amount' => $transaction['foreign_amount'] ?? null,
|
||||
'foreign_currency_id' => isset($transaction['foreign_currency_id']) ? intval($transaction['foreign_currency_id']) : null,
|
||||
'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null,
|
||||
'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : null,
|
||||
'budget_name' => $transaction['budget_name'] ?? null,
|
||||
'category_id' => isset($transaction['category_id']) ? intval($transaction['category_id']) : null,
|
||||
'category_name' => $transaction['category_name'] ?? null,
|
||||
'source_id' => isset($transaction['source_id']) ? intval($transaction['source_id']) : null,
|
||||
'source_name' => isset($transaction['source_name']) ? strval($transaction['source_name']) : null,
|
||||
'destination_id' => isset($transaction['destination_id']) ? intval($transaction['destination_id']) : null,
|
||||
'destination_name' => isset($transaction['destination_name']) ? strval($transaction['destination_name']) : null,
|
||||
'reconciled' => $transaction['reconciled'] ?? false,
|
||||
'identifier' => $index,
|
||||
];
|
||||
$data['transactions'][] = $array;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
// basic fields for journal:
|
||||
'type' => 'required|in:withdrawal,deposit,transfer',
|
||||
'date' => 'required|date',
|
||||
'description' => 'between:1,255',
|
||||
'piggy_bank_id' => ['numeric', 'nullable', 'mustExist:piggy_banks,id', new BelongsUser],
|
||||
'piggy_bank_name' => ['between:1,255', 'nullable', new BelongsUser],
|
||||
'bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser],
|
||||
'bill_name' => ['between:1,255', 'nullable', new BelongsUser],
|
||||
'tags' => 'between:1,255',
|
||||
|
||||
// then, custom fields for journal
|
||||
'interest_date' => 'date|nullable',
|
||||
'book_date' => 'date|nullable',
|
||||
'process_date' => 'date|nullable',
|
||||
'due_date' => 'date|nullable',
|
||||
'payment_date' => 'date|nullable',
|
||||
'invoice_date' => 'date|nullable',
|
||||
'internal_reference' => 'min:1,max:255|nullable',
|
||||
'notes' => 'min:1,max:50000|nullable',
|
||||
|
||||
// transaction rules (in array for splits):
|
||||
'transactions.*.description' => 'nullable|between:1,255',
|
||||
'transactions.*.amount' => 'required|numeric|more:0',
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|required_without:transactions.*.currency_code',
|
||||
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code|required_without:transactions.*.currency_id',
|
||||
'transactions.*.foreign_amount' => 'numeric|more:0',
|
||||
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
|
||||
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
|
||||
'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUser],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser],
|
||||
'transactions.*.category_name' => 'between:1,255|nullable',
|
||||
'transactions.*.reconciled' => 'boolean|nullable',
|
||||
// basic rules will be expanded later.
|
||||
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser],
|
||||
'transactions.*.source_name' => 'between:1,255|nullable',
|
||||
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser],
|
||||
'transactions.*.destination_name' => 'between:1,255|nullable',
|
||||
];
|
||||
|
||||
switch ($this->method()) {
|
||||
default:
|
||||
break;
|
||||
case 'PUT':
|
||||
case 'PATCH':
|
||||
unset($rules['type'], $rules['piggy_bank_id'], $rules['piggy_bank_name']);
|
||||
break;
|
||||
}
|
||||
|
||||
return $rules;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the validator instance.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator) {
|
||||
$this->atLeastOneTransaction($validator);
|
||||
$this->checkValidDescriptions($validator);
|
||||
$this->equalToJournalDescription($validator);
|
||||
$this->emptySplitDescriptions($validator);
|
||||
$this->foreignCurrencyInformation($validator);
|
||||
$this->validateAccountInformation($validator);
|
||||
$this->validateSplitAccounts($validator);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an error when this asset account is invalid.
|
||||
*
|
||||
* @param Validator $validator
|
||||
* @param int|null $accountId
|
||||
* @param null|string $accountName
|
||||
* @param string $idField
|
||||
* @param string $nameField
|
||||
*/
|
||||
protected function assetAccountExists(Validator $validator, ?int $accountId, ?string $accountName, string $idField, string $nameField): void
|
||||
{
|
||||
|
||||
$accountId = intval($accountId);
|
||||
$accountName = strval($accountName);
|
||||
// both empty? hard exit.
|
||||
if ($accountId < 1 && strlen($accountName) === 0) {
|
||||
$validator->errors()->add($idField, trans('validation.filled', ['attribute' => $idField]));
|
||||
|
||||
return;
|
||||
}
|
||||
// ID belongs to user and is asset account:
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$set = $repository->getAccountsById([$accountId]);
|
||||
if ($set->count() === 1) {
|
||||
/** @var Account $first */
|
||||
$first = $set->first();
|
||||
if ($first->accountType->type !== AccountType::ASSET) {
|
||||
$validator->errors()->add($idField, trans('validation.belongs_user'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// we ignore the account name at this point.
|
||||
return;
|
||||
}
|
||||
|
||||
$account = $repository->findByName($accountName, [AccountType::ASSET]);
|
||||
if (is_null($account)) {
|
||||
$validator->errors()->add($nameField, trans('validation.belongs_user'));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when there are no transactions in the array of data.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
protected function atLeastOneTransaction(Validator $validator): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
$transactions = $data['transactions'] ?? [];
|
||||
// need at least one transaction
|
||||
if (count($transactions) === 0) {
|
||||
$validator->errors()->add('description', trans('validation.at_least_one_transaction'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the "description" field when the user has submitted no descriptions and no
|
||||
* journal description.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
protected function checkValidDescriptions(Validator $validator)
|
||||
{
|
||||
$data = $validator->getData();
|
||||
$transactions = $data['transactions'] ?? [];
|
||||
$journalDescription = strval($data['description'] ?? '');
|
||||
$validDescriptions = 0;
|
||||
foreach ($transactions as $index => $transaction) {
|
||||
if (strlen(strval($transaction['description'] ?? '')) > 0) {
|
||||
$validDescriptions++;
|
||||
}
|
||||
}
|
||||
|
||||
// no valid descriptions and empty journal description? error.
|
||||
if ($validDescriptions === 0 && strlen($journalDescription) === 0) {
|
||||
$validator->errors()->add('description', trans('validation.filled', ['attribute' => trans('validation.attributes.description')]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when the user submits a split transaction (more than 1 transactions)
|
||||
* but does not give them a description.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
protected function emptySplitDescriptions(Validator $validator): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
$transactions = $data['transactions'] ?? [];
|
||||
foreach ($transactions as $index => $transaction) {
|
||||
$description = strval($transaction['description'] ?? '');
|
||||
// filled description is mandatory for split transactions.
|
||||
if (count($transactions) > 1 && strlen($description) === 0) {
|
||||
$validator->errors()->add(
|
||||
'transactions.' . $index . '.description',
|
||||
trans('validation.filled', ['attribute' => trans('validation.attributes.transaction_description')])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when any transaction descriptions are equal to the journal description.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
protected function equalToJournalDescription(Validator $validator): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
$transactions = $data['transactions'] ?? [];
|
||||
$journalDescription = strval($data['description'] ?? '');
|
||||
foreach ($transactions as $index => $transaction) {
|
||||
$description = strval($transaction['description'] ?? '');
|
||||
// description cannot be equal to journal description.
|
||||
if ($description === $journalDescription) {
|
||||
$validator->errors()->add('transactions.' . $index . '.description', trans('validation.equal_description'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the transactions contain foreign amounts, there must also be foreign currency information.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
protected function foreignCurrencyInformation(Validator $validator): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
$transactions = $data['transactions'] ?? [];
|
||||
foreach ($transactions as $index => $transaction) {
|
||||
// must have currency info.
|
||||
if (isset($transaction['foreign_amount'])
|
||||
&& !(isset($transaction['foreign_currency_id'])
|
||||
|| isset($transaction['foreign_currency_code']))) {
|
||||
$validator->errors()->add(
|
||||
'transactions.' . $index . '.foreign_amount',
|
||||
trans('validation.require_currency_info')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an error when the given opposing account (of type $type) is invalid.
|
||||
* Empty data is allowed, system will default to cash.
|
||||
*
|
||||
* @param Validator $validator
|
||||
* @param string $type
|
||||
* @param int|null $accountId
|
||||
* @param null|string $accountName
|
||||
* @param string $idField
|
||||
*/
|
||||
protected function opposingAccountExists(Validator $validator, string $type, ?int $accountId, ?string $accountName, string $idField): void {
|
||||
$accountId = intval($accountId);
|
||||
$accountName = strval($accountName);
|
||||
// both empty? done!
|
||||
if ($accountId < 1 && strlen($accountName) === 0) {
|
||||
return;
|
||||
}
|
||||
if ($accountId !== 0) {
|
||||
// ID belongs to user and is $type account:
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$set = $repository->getAccountsById([$accountId]);
|
||||
if ($set->count() === 1) {
|
||||
/** @var Account $first */
|
||||
$first = $set->first();
|
||||
if ($first->accountType->type !== $type) {
|
||||
$validator->errors()->add($idField, trans('validation.belongs_user'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// we ignore the account name at this point.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// not having an opposing account by this name is NOT a problem.
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given account information. Switches on given transaction type.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function validateAccountInformation(Validator $validator): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
$transactions = $data['transactions'] ?? [];
|
||||
if (!isset($data['type'])) {
|
||||
// the journal may exist in the request:
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $this->route()->parameter('transaction');
|
||||
if (is_null($transaction)) {
|
||||
return; // @codeCoverageIgnore
|
||||
}
|
||||
$data['type'] = strtolower($transaction->transactionJournal->transactionType->type);
|
||||
}
|
||||
foreach ($transactions as $index => $transaction) {
|
||||
$sourceId = isset($transaction['source_id']) ? intval($transaction['source_id']) : null;
|
||||
$sourceName = $transaction['source_name'] ?? null;
|
||||
$destinationId = isset($transaction['destination_id']) ? intval($transaction['destination_id']) : null;
|
||||
$destinationName = $transaction['destination_name'] ?? null;
|
||||
|
||||
switch ($data['type']) {
|
||||
case 'withdrawal':
|
||||
$idField = 'transactions.' . $index . '.source_id';
|
||||
$nameField = 'transactions.' . $index . '.source_name';
|
||||
$this->assetAccountExists($validator, $sourceId, $sourceName, $idField, $nameField);
|
||||
|
||||
$idField = 'transactions.' . $index . '.destination_id';
|
||||
$this->opposingAccountExists($validator, AccountType::EXPENSE, $destinationId, $destinationName, $idField);
|
||||
break;
|
||||
case 'deposit':
|
||||
$idField = 'transactions.' . $index . '.source_id';
|
||||
$this->opposingAccountExists($validator, AccountType::REVENUE, $sourceId, $sourceName, $idField);
|
||||
|
||||
$idField = 'transactions.' . $index . '.destination_id';
|
||||
$nameField = 'transactions.' . $index . '.destination_name';
|
||||
$this->assetAccountExists($validator, $destinationId, $destinationName, $idField, $nameField);
|
||||
break;
|
||||
case 'transfer':
|
||||
$idField = 'transactions.' . $index . '.source_id';
|
||||
$nameField = 'transactions.' . $index . '.source_name';
|
||||
$this->assetAccountExists($validator, $sourceId, $sourceName, $idField, $nameField);
|
||||
|
||||
$idField = 'transactions.' . $index . '.destination_id';
|
||||
$nameField = 'transactions.' . $index . '.destination_name';
|
||||
$this->assetAccountExists($validator, $destinationId, $destinationName, $idField, $nameField);
|
||||
break;
|
||||
default:
|
||||
throw new FireflyException(sprintf('The validator cannot handle transaction type "%s" in validateAccountInformation().', $data['type']));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function validateSplitAccounts(Validator $validator)
|
||||
{
|
||||
$data = $validator->getData();
|
||||
$count = isset($data['transactions']) ? count($data['transactions']) : 0;
|
||||
if ($count < 2) {
|
||||
return;
|
||||
}
|
||||
// this is pretty much impossible:
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!isset($data['type'])) {
|
||||
// the journal may exist in the request:
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $this->route()->parameter('transaction');
|
||||
if (is_null($transaction)) {
|
||||
return;
|
||||
}
|
||||
$data['type'] = strtolower($transaction->transactionJournal->transactionType->type);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
// collect all source ID's and destination ID's, if present:
|
||||
$sources = [];
|
||||
$destinations = [];
|
||||
|
||||
foreach ($data['transactions'] as $transaction) {
|
||||
$sources[] = isset($transaction['source_id']) ? intval($transaction['source_id']) : 0;
|
||||
$destinations[] = isset($transaction['destination_id']) ? intval($transaction['destination_id']) : 0;
|
||||
}
|
||||
$destinations = array_unique($destinations);
|
||||
$sources = array_unique($sources);
|
||||
// switch on type:
|
||||
switch ($data['type']) {
|
||||
case 'withdrawal':
|
||||
if (count($sources) > 1) {
|
||||
$validator->errors()->add('transactions.0.source_id', trans('validation.all_accounts_equal'));
|
||||
}
|
||||
break;
|
||||
case 'deposit':
|
||||
if (count($destinations) > 1) {
|
||||
$validator->errors()->add('transactions.0.destination_id', trans('validation.all_accounts_equal'));
|
||||
}
|
||||
break;
|
||||
case 'transfer':
|
||||
if (count($sources) > 1 || count($destinations) > 1) {
|
||||
$validator->errors()->add('transactions.0.source_id', trans('validation.all_accounts_equal'));
|
||||
$validator->errors()->add('transactions.0.destination_id', trans('validation.all_accounts_equal'));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new FireflyException(
|
||||
sprintf('The validator cannot handle transaction type "%s" in validateSplitAccounts().', $data['type'])
|
||||
);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
89
app/Api/V1/Requests/UserRequest.php
Normal file
89
app/Api/V1/Requests/UserRequest.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* UserRequest.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\Api\V1\Requests;
|
||||
|
||||
use FireflyIII\User;
|
||||
|
||||
|
||||
/**
|
||||
* Class UserRequest
|
||||
*/
|
||||
class UserRequest extends Request
|
||||
{
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
// Only allow authenticated users
|
||||
if (!auth()->check()) {
|
||||
return false; // @codeCoverageIgnore
|
||||
}
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
if (!$user->hasRole('owner')) {
|
||||
return false; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
$data = [
|
||||
'email' => $this->string('email'),
|
||||
'blocked' => $this->boolean('blocked'),
|
||||
'blocked_code' => $this->string('blocked_code'),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'email' => 'required|email|unique:users,email,',
|
||||
'blocked' => 'required|boolean',
|
||||
'blocked_code' => 'in:email_changed',
|
||||
];
|
||||
switch ($this->method()) {
|
||||
default:
|
||||
break;
|
||||
case 'PUT':
|
||||
case 'PATCH':
|
||||
$user = $this->route()->parameter('user');
|
||||
$rules['email'] = 'required|email|unique:users,email,' . $user->id;
|
||||
break;
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -178,7 +178,7 @@ class CreateImport extends Command
|
||||
$cwd = getcwd();
|
||||
$validTypes = config('import.options.file.import_formats');
|
||||
$type = strtolower($this->option('type'));
|
||||
if (null === $user->id) {
|
||||
if (null === $user) {
|
||||
$this->error(sprintf('There is no user with ID %d.', $this->option('user')));
|
||||
|
||||
return false;
|
||||
|
||||
@@ -85,8 +85,8 @@ class Import extends Command
|
||||
$monolog->pushHandler($handler);
|
||||
|
||||
// actually start job:
|
||||
$type = 'csv' === $job->file_type ? 'file' : $job->file_type;
|
||||
$key = sprintf('import.routine.%s', $type);
|
||||
$type = 'csv' === $job->file_type ? 'file' : $job->file_type;
|
||||
$key = sprintf('import.routine.%s', $type);
|
||||
$className = config($key);
|
||||
if (null === $className || !class_exists($className)) {
|
||||
throw new FireflyException(sprintf('Cannot find import routine class for job of type "%s".', $type)); // @codeCoverageIgnore
|
||||
|
||||
@@ -220,7 +220,7 @@ class UpgradeDatabase extends Command
|
||||
|
||||
// when mismatch in transaction:
|
||||
if (!(intval($transaction->transaction_currency_id) === intval($currency->id))) {
|
||||
$transaction->foreign_currency_id = $transaction->transaction_currency_id;
|
||||
$transaction->foreign_currency_id = intval($transaction->transaction_currency_id);
|
||||
$transaction->foreign_amount = $transaction->amount;
|
||||
$transaction->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
@@ -412,12 +412,10 @@ class UpgradeDatabase extends Command
|
||||
if (!(intval($transaction->transaction_currency_id) === intval($currency->id)) && null === $transaction->foreign_amount) {
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Transaction #%d has a currency setting (#%d) (%s) that should be #%d (%s). Amount remains %s, currency is changed.',
|
||||
'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.',
|
||||
$transaction->id,
|
||||
$transaction->transaction_currency_id,
|
||||
$this->var_dump_ret(intval($transaction->transaction_currency_id)),
|
||||
$currency->id,
|
||||
$this->var_dump_ret(intval($currency->id)),
|
||||
$transaction->amount
|
||||
)
|
||||
);
|
||||
@@ -504,19 +502,4 @@ class UpgradeDatabase extends Command
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $mixed
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function var_dump_ret($mixed = null): string
|
||||
{
|
||||
ob_start();
|
||||
var_dump($mixed);
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
return trim($content);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ trait VerifiesAccessToken
|
||||
/**
|
||||
* Abstract method to make sure trait knows about method "option".
|
||||
*
|
||||
* @param null $key
|
||||
* @param string|null $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -55,7 +55,7 @@ trait VerifiesAccessToken
|
||||
$repository = app(UserRepositoryInterface::class);
|
||||
$user = $repository->find($userId);
|
||||
|
||||
if (null === $user->id) {
|
||||
if (null === $user) {
|
||||
Log::error(sprintf('verifyAccessToken(): no such user for input "%d"', $userId));
|
||||
|
||||
return false;
|
||||
|
||||
@@ -25,8 +25,11 @@ namespace FireflyIII\Exceptions;
|
||||
use ErrorException;
|
||||
use Exception;
|
||||
use FireflyIII\Jobs\MailError;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Request;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Class Handler
|
||||
@@ -62,6 +65,36 @@ class Handler extends ExceptionHandler
|
||||
*/
|
||||
public function render($request, Exception $exception)
|
||||
{
|
||||
if ($exception instanceof ValidationException && $request->expectsJson()) {
|
||||
// ignore it: controller will handle it.
|
||||
return parent::render($request, $exception);
|
||||
}
|
||||
if ($exception instanceof NotFoundHttpException && $request->expectsJson()) {
|
||||
return response()->json(['message' => 'Resource not found', 'exception' => 'NotFoundHttpException'], 404);
|
||||
}
|
||||
|
||||
if ($exception instanceof AuthenticationException && $request->expectsJson()) {
|
||||
return response()->json(['message' => 'Unauthenticated', 'exception' => 'AuthenticationException'], 401);
|
||||
}
|
||||
|
||||
|
||||
if ($request->expectsJson()) {
|
||||
$isDebug = config('app.debug', false);
|
||||
if ($isDebug) {
|
||||
return response()->json(
|
||||
[
|
||||
'message' => $exception->getMessage(),
|
||||
'exception' => get_class($exception),
|
||||
'line' => $exception->getLine(),
|
||||
'file' => $exception->getFile(),
|
||||
'trace' => $exception->getTrace(),
|
||||
], 500
|
||||
);
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'Internal Firefly III Exception. See log files.', 'exception' => get_class($exception)], 500);
|
||||
}
|
||||
|
||||
if ($exception instanceof FireflyException || $exception instanceof ErrorException) {
|
||||
$isDebug = env('APP_DEBUG', false);
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// @var AttachmentRepositoryInterface repository
|
||||
/** @var AttachmentRepositoryInterface repository */
|
||||
$this->repository = app(AttachmentRepositoryInterface::class);
|
||||
// make storage:
|
||||
$this->uploadDisk = Storage::disk('upload');
|
||||
|
||||
171
app/Factory/AccountFactory.php
Normal file
171
app/Factory/AccountFactory.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
/**
|
||||
* AccountFactory.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\Factory;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Services\Internal\Support\AccountServiceTrait;
|
||||
use FireflyIII\User;
|
||||
|
||||
/**
|
||||
* Factory to create or return accounts.
|
||||
*
|
||||
* Class AccountFactory
|
||||
*/
|
||||
class AccountFactory
|
||||
{
|
||||
use AccountServiceTrait;
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
public function create(array $data): Account
|
||||
{
|
||||
$type = $this->getAccountType($data['account_type_id'], $data['accountType']);
|
||||
$data['iban'] = $this->filterIban($data['iban']);
|
||||
|
||||
|
||||
// account may exist already:
|
||||
$existingAccount = $this->find($data['name'], $type->type);
|
||||
if (null !== $existingAccount) {
|
||||
return $existingAccount;
|
||||
}
|
||||
|
||||
|
||||
// create it:
|
||||
$databaseData
|
||||
= [
|
||||
'user_id' => $this->user->id,
|
||||
'account_type_id' => $type->id,
|
||||
'name' => $data['name'],
|
||||
'virtual_balance' => strlen(strval($data['virtualBalance'])) === 0 ? '0' : $data['virtualBalance'],
|
||||
'active' => true === $data['active'] ? true : false,
|
||||
'iban' => $data['iban'],
|
||||
];
|
||||
|
||||
// remove virtual balance when not an asset account:
|
||||
if ($type->type !== AccountType::ASSET) {
|
||||
$databaseData['virtual_balance'] = '0';
|
||||
}
|
||||
|
||||
$newAccount = Account::create($databaseData);
|
||||
$this->updateMetadata($newAccount, $data);
|
||||
|
||||
if ($this->validIBData($data) && $type->type === AccountType::ASSET) {
|
||||
$this->updateIB($newAccount, $data);
|
||||
}
|
||||
if (!$this->validIBData($data) && $type->type === AccountType::ASSET) {
|
||||
$this->deleteIB($newAccount);
|
||||
}
|
||||
// update note:
|
||||
if (isset($data['notes'])) {
|
||||
$this->updateNote($newAccount, $data['notes']);
|
||||
}
|
||||
|
||||
return $newAccount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accountName
|
||||
* @param string $accountType
|
||||
*
|
||||
* @return Account|null
|
||||
*/
|
||||
public function find(string $accountName, string $accountType): ?Account
|
||||
{
|
||||
$type = AccountType::whereType($accountType)->first();
|
||||
$accounts = $this->user->accounts()->where('account_type_id', $type->id)->get(['accounts.*']);
|
||||
|
||||
/** @var Account $object */
|
||||
foreach ($accounts as $object) {
|
||||
if ($object->name === $accountName) {
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accountName
|
||||
* @param string $accountType
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
public function findOrCreate(string $accountName, string $accountType): Account
|
||||
{
|
||||
$type = AccountType::whereType($accountType)->first();
|
||||
$accounts = $this->user->accounts()->where('account_type_id', $type->id)->get(['accounts.*']);
|
||||
|
||||
/** @var Account $object */
|
||||
foreach ($accounts as $object) {
|
||||
if ($object->name === $accountName) {
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->create(
|
||||
[
|
||||
'user_id' => $this->user->id,
|
||||
'name' => $accountName,
|
||||
'account_type_id' => $type->id,
|
||||
'accountType' => null,
|
||||
'virtualBalance' => '0',
|
||||
'iban' => null,
|
||||
'active' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $accountTypeId
|
||||
* @param null|string $accountType
|
||||
*
|
||||
* @return AccountType|null
|
||||
*/
|
||||
protected function getAccountType(?int $accountTypeId, ?string $accountType): ?AccountType
|
||||
{
|
||||
$accountTypeId = intval($accountTypeId);
|
||||
if ($accountTypeId > 0) {
|
||||
return AccountType::find($accountTypeId);
|
||||
}
|
||||
$type = config('firefly.accountTypeByIdentifier.' . strval($accountType));
|
||||
|
||||
return AccountType::whereType($type)->first();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
43
app/Factory/AccountMetaFactory.php
Normal file
43
app/Factory/AccountMetaFactory.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* AccountMetaFactory.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\Factory;
|
||||
|
||||
use FireflyIII\Models\AccountMeta;
|
||||
|
||||
/**
|
||||
* Class AccountMetaFactory
|
||||
*/
|
||||
class AccountMetaFactory
|
||||
{
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return AccountMeta|null
|
||||
*/
|
||||
public function create(array $data): ?AccountMeta
|
||||
{
|
||||
return AccountMeta::create($data);
|
||||
}
|
||||
|
||||
}
|
||||
137
app/Factory/BillFactory.php
Normal file
137
app/Factory/BillFactory.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
/**
|
||||
* BillFactory.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\Factory;
|
||||
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Services\Internal\Support\BillServiceTrait;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class BillFactory
|
||||
*/
|
||||
class BillFactory
|
||||
{
|
||||
use BillServiceTrait;
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return Bill|null
|
||||
*/
|
||||
public function create(array $data): ?Bill
|
||||
{
|
||||
$matchArray = explode(',', $data['match']);
|
||||
$matchArray = array_unique($matchArray);
|
||||
$match = join(',', $matchArray);
|
||||
|
||||
/** @var Bill $bill */
|
||||
$bill = Bill::create(
|
||||
[
|
||||
'name' => $data['name'],
|
||||
'match' => $match,
|
||||
'amount_min' => $data['amount_min'],
|
||||
'user_id' => $this->user->id,
|
||||
'amount_max' => $data['amount_max'],
|
||||
'date' => $data['date'],
|
||||
'repeat_freq' => $data['repeat_freq'],
|
||||
'skip' => $data['skip'],
|
||||
'automatch' => $data['automatch'],
|
||||
'active' => $data['active'],
|
||||
]
|
||||
);
|
||||
|
||||
// update note:
|
||||
if (isset($data['notes'])) {
|
||||
$this->updateNote($bill, $data['notes']);
|
||||
}
|
||||
|
||||
return $bill;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $billId
|
||||
* @param null|string $billName
|
||||
*
|
||||
* @return Bill|null
|
||||
*/
|
||||
public function find(?int $billId, ?string $billName): ?Bill
|
||||
{
|
||||
$billId = intval($billId);
|
||||
$billName = strval($billName);
|
||||
|
||||
// first find by ID:
|
||||
if ($billId > 0) {
|
||||
/** @var Bill $bill */
|
||||
$bill = $this->user->bills()->find($billId);
|
||||
if (!is_null($bill)) {
|
||||
return $bill;
|
||||
}
|
||||
}
|
||||
|
||||
// then find by name:
|
||||
if (strlen($billName) > 0) {
|
||||
$bill = $this->findByName($billName);
|
||||
if (!is_null($bill)) {
|
||||
return $bill;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return Bill|null
|
||||
*/
|
||||
public function findByName(string $name): ?Bill
|
||||
{
|
||||
/** @var Collection $collection */
|
||||
$collection = $this->user->bills()->get();
|
||||
/** @var Bill $bill */
|
||||
foreach ($collection as $bill) {
|
||||
Log::debug(sprintf('"%s" vs. "%s"', $bill->name, $name));
|
||||
if ($bill->name === $name) {
|
||||
return $bill;
|
||||
}
|
||||
}
|
||||
Log::debug(sprintf('Bill::Find by name returns NULL based on "%s"', $name));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
public function setUser(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
}
|
||||
100
app/Factory/BudgetFactory.php
Normal file
100
app/Factory/BudgetFactory.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
/**
|
||||
* BudgetFactory.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\Factory;
|
||||
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class BudgetFactory
|
||||
*/
|
||||
class BudgetFactory
|
||||
{
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
|
||||
/**
|
||||
* @param int|null $budgetId
|
||||
* @param null|string $budgetName
|
||||
*
|
||||
* @return Budget|null
|
||||
*/
|
||||
public function find(?int $budgetId, ?string $budgetName): ?Budget
|
||||
{
|
||||
$budgetId = intval($budgetId);
|
||||
$budgetName = strval($budgetName);
|
||||
|
||||
if (strlen($budgetName) === 0 && $budgetId === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// first by ID:
|
||||
if ($budgetId > 0) {
|
||||
/** @var Budget $budget */
|
||||
$budget = $this->user->budgets()->find($budgetId);
|
||||
if (!is_null($budget)) {
|
||||
return $budget;
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen($budgetName) > 0) {
|
||||
$budget = $this->findByName($budgetName);
|
||||
if (!is_null($budget)) {
|
||||
return $budget;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return Budget|null
|
||||
*/
|
||||
public function findByName(string $name): ?Budget
|
||||
{
|
||||
/** @var Collection $collection */
|
||||
$collection = $this->user->budgets()->get();
|
||||
/** @var Budget $budget */
|
||||
foreach ($collection as $budget) {
|
||||
if ($budget->name === $name) {
|
||||
return $budget;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
public function setUser(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
}
|
||||
109
app/Factory/CategoryFactory.php
Normal file
109
app/Factory/CategoryFactory.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/**
|
||||
* CategoryFactory.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\Factory;
|
||||
|
||||
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class CategoryFactory
|
||||
*/
|
||||
class CategoryFactory
|
||||
{
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return Category|null
|
||||
*/
|
||||
public function findByName(string $name): ?Category
|
||||
{
|
||||
/** @var Collection $collection */
|
||||
$collection = $this->user->categories()->get();
|
||||
/** @var Category $category */
|
||||
foreach ($collection as $category) {
|
||||
if ($category->name === $name) {
|
||||
return $category;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $categoryId
|
||||
* @param null|string $categoryName
|
||||
*
|
||||
* @return Category|null
|
||||
*/
|
||||
public function findOrCreate(?int $categoryId, ?string $categoryName): ?Category
|
||||
{
|
||||
$categoryId = intval($categoryId);
|
||||
$categoryName = strval($categoryName);
|
||||
|
||||
Log::debug(sprintf('Going to find category with ID %d and name "%s"', $categoryId, $categoryName));
|
||||
|
||||
if (strlen($categoryName) === 0 && $categoryId === 0) {
|
||||
return null;
|
||||
}
|
||||
// first by ID:
|
||||
if ($categoryId > 0) {
|
||||
/** @var Category $category */
|
||||
$category = $this->user->categories()->find($categoryId);
|
||||
if (!is_null($category)) {
|
||||
return $category;
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen($categoryName) > 0) {
|
||||
$category = $this->findByName($categoryName);
|
||||
if (!is_null($category)) {
|
||||
return $category;
|
||||
}
|
||||
|
||||
return Category::create(
|
||||
[
|
||||
'user_id' => $this->user->id,
|
||||
'name' => $categoryName,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
public function setUser(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
}
|
||||
88
app/Factory/PiggyBankEventFactory.php
Normal file
88
app/Factory/PiggyBankEventFactory.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* PiggyBankEventFactory.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\Factory;
|
||||
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Create piggy bank events.
|
||||
*
|
||||
* Class PiggyBankEventFactory
|
||||
*/
|
||||
class PiggyBankEventFactory
|
||||
{
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param PiggyBank|null $piggyBank
|
||||
*
|
||||
* @return PiggyBankEvent|null
|
||||
*/
|
||||
public function create(TransactionJournal $journal, ?PiggyBank $piggyBank): ?PiggyBankEvent
|
||||
{
|
||||
Log::debug(sprintf('Now in PiggyBankEventCreate for a %s', $journal->transactionType->type));
|
||||
if (is_null($piggyBank)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// is a transfer?
|
||||
if (!(TransactionType::TRANSFER === $journal->transactionType->type)) {
|
||||
Log::info(sprintf('Will not connect %s #%d to a piggy bank.', $journal->transactionType->type, $journal->id));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var PiggyBankRepositoryInterface $piggyRepos */
|
||||
$piggyRepos = app(PiggyBankRepositoryInterface::class);
|
||||
$piggyRepos->setUser($journal->user);
|
||||
|
||||
// repetition exists?
|
||||
$repetition = $piggyRepos->getRepetition($piggyBank, $journal->date);
|
||||
if (null === $repetition->id) {
|
||||
Log::error(sprintf('No piggy bank repetition on %s!', $journal->date->format('Y-m-d')));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// get the amount
|
||||
$amount = $piggyRepos->getExactAmount($piggyBank, $repetition, $journal);
|
||||
if (0 === bccomp($amount, '0')) {
|
||||
Log::debug('Amount is zero, will not create event.');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// update amount
|
||||
$piggyRepos->addAmountToRepetition($repetition, $amount);
|
||||
$event = $piggyRepos->createEventWithJournal($piggyBank, $amount, $journal);
|
||||
|
||||
Log::debug(sprintf('Created piggy bank event #%d', $event->id));
|
||||
|
||||
return $event;
|
||||
}
|
||||
}
|
||||
100
app/Factory/PiggyBankFactory.php
Normal file
100
app/Factory/PiggyBankFactory.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
/**
|
||||
* PiggyBankFactory.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\Factory;
|
||||
|
||||
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\User;
|
||||
|
||||
/**
|
||||
* Class PiggyBankFactory
|
||||
*/
|
||||
class PiggyBankFactory
|
||||
{
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @param int|null $piggyBankId
|
||||
* @param null|string $piggyBankName
|
||||
*
|
||||
* @return PiggyBank|null
|
||||
*/
|
||||
public function find(?int $piggyBankId, ?string $piggyBankName): ?PiggyBank
|
||||
{
|
||||
$piggyBankId = intval($piggyBankId);
|
||||
$piggyBankName = strval($piggyBankName);
|
||||
if (strlen($piggyBankName) === 0 && $piggyBankId === 0) {
|
||||
return null;
|
||||
}
|
||||
// first find by ID:
|
||||
if ($piggyBankId > 0) {
|
||||
/** @var PiggyBank $piggyBank */
|
||||
$piggyBank = $this->user->piggyBanks()->find($piggyBankId);
|
||||
if (!is_null($piggyBank)) {
|
||||
return $piggyBank;
|
||||
}
|
||||
}
|
||||
|
||||
// then find by name:
|
||||
if (strlen($piggyBankName) > 0) {
|
||||
/** @var PiggyBank $piggyBank */
|
||||
$piggyBank = $this->findByName($piggyBankName);
|
||||
if (!is_null($piggyBank)) {
|
||||
return $piggyBank;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return PiggyBank|null
|
||||
*/
|
||||
public function findByName(string $name): ?PiggyBank
|
||||
{
|
||||
$set = $this->user->piggyBanks()->get();
|
||||
/** @var PiggyBank $piggy */
|
||||
foreach ($set as $piggy) {
|
||||
if ($piggy->name === $name) {
|
||||
return $piggy;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
public function setUser(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
102
app/Factory/TagFactory.php
Normal file
102
app/Factory/TagFactory.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* TagFactory.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\Factory;
|
||||
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class TagFactory
|
||||
*/
|
||||
class TagFactory
|
||||
{
|
||||
/** @var Collection */
|
||||
private $tags;
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return Tag|null
|
||||
*/
|
||||
public function create(array $data): ?Tag
|
||||
{
|
||||
return Tag::create(
|
||||
[
|
||||
'user_id' => $this->user->id,
|
||||
'tag' => $data['tag'],
|
||||
'tagMode' => 'nothing',
|
||||
'date' => $data['date'],
|
||||
'description' => $data['description'],
|
||||
'latitude' => $data['latitude'],
|
||||
'longitude ' => $data['longitude'],
|
||||
'zoomLevel' => $data['zoom_level'],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tag
|
||||
*
|
||||
* @return Tag|null
|
||||
*/
|
||||
public function findOrCreate(string $tag): ?Tag
|
||||
{
|
||||
if (is_null($this->tags)) {
|
||||
$this->tags = $this->user->tags()->get();
|
||||
}
|
||||
|
||||
/** @var Tag $object */
|
||||
foreach ($this->tags as $object) {
|
||||
if ($object->tag === $tag) {
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
|
||||
$newTag = $this->create(
|
||||
[
|
||||
'tag' => $tag,
|
||||
'date' => null,
|
||||
'description' => null,
|
||||
'latitude' => null,
|
||||
'longitude' => null,
|
||||
'zoom_level' => null,
|
||||
]
|
||||
);
|
||||
$this->tags->push($newTag);
|
||||
|
||||
return $newTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
}
|
||||
67
app/Factory/TransactionCurrencyFactory.php
Normal file
67
app/Factory/TransactionCurrencyFactory.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* TransactionCurrencyFactory.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\Factory;
|
||||
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
|
||||
/**
|
||||
* Class TransactionCurrencyFactory
|
||||
*/
|
||||
class TransactionCurrencyFactory
|
||||
{
|
||||
/**
|
||||
* @param int|null $currencyId
|
||||
* @param null|string $currencyCode
|
||||
*
|
||||
* @return TransactionCurrency|null
|
||||
*/
|
||||
public function find(?int $currencyId, ?string $currencyCode): ?TransactionCurrency
|
||||
{
|
||||
$currencyCode = strval($currencyCode);
|
||||
$currencyId = intval($currencyId);
|
||||
|
||||
if (strlen($currencyCode) === 0 && intval($currencyId) === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// first by ID:
|
||||
if ($currencyId > 0) {
|
||||
$currency = TransactionCurrency::find($currencyId);
|
||||
if (!is_null($currency)) {
|
||||
return $currency;
|
||||
}
|
||||
}
|
||||
// then by code:
|
||||
if (strlen($currencyCode) > 0) {
|
||||
$currency = TransactionCurrency::whereCode($currencyCode)->first();
|
||||
if (!is_null($currency)) {
|
||||
return $currency;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
148
app/Factory/TransactionFactory.php
Normal file
148
app/Factory/TransactionFactory.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/**
|
||||
* TransactionFactory.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\Factory;
|
||||
|
||||
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Services\Internal\Support\TransactionServiceTrait;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class TransactionFactory
|
||||
*/
|
||||
class TransactionFactory
|
||||
{
|
||||
use TransactionServiceTrait;
|
||||
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return Transaction
|
||||
*/
|
||||
public function create(array $data): Transaction
|
||||
{
|
||||
$currencyId = isset($data['currency']) ? $data['currency']->id : $data['currency_id'];
|
||||
|
||||
return Transaction::create(
|
||||
[
|
||||
'reconciled' => $data['reconciled'],
|
||||
'account_id' => $data['account']->id,
|
||||
'transaction_journal_id' => $data['transaction_journal']->id,
|
||||
'description' => $data['description'],
|
||||
'transaction_currency_id' => $currencyId,
|
||||
'amount' => $data['amount'],
|
||||
'foreign_amount' => $data['foreign_amount'],
|
||||
'foreign_currency_id' => null,
|
||||
'identifier' => $data['identifier'],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a pair of transactions based on the data given in the array.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @param array $data
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function createPair(TransactionJournal $journal, array $data): Collection
|
||||
{
|
||||
// all this data is the same for both transactions:
|
||||
$currency = $this->findCurrency($data['currency_id'], $data['currency_code']);
|
||||
$description = $journal->description === $data['description'] ? null : $data['description'];
|
||||
|
||||
// type of source account depends on journal type:
|
||||
$sourceType = $this->accountType($journal, 'source');
|
||||
$sourceAccount = $this->findAccount($sourceType, $data['source_id'], $data['source_name']);
|
||||
|
||||
// same for destination account:
|
||||
$destinationType = $this->accountType($journal, 'destination');
|
||||
$destinationAccount = $this->findAccount($destinationType, $data['destination_id'], $data['destination_name']);
|
||||
// first make a "negative" (source) transaction based on the data in the array.
|
||||
$source = $this->create(
|
||||
[
|
||||
'description' => $description,
|
||||
'amount' => app('steam')->negative(strval($data['amount'])),
|
||||
'foreign_amount' => null,
|
||||
'currency' => $currency,
|
||||
'account' => $sourceAccount,
|
||||
'transaction_journal' => $journal,
|
||||
'reconciled' => $data['reconciled'],
|
||||
'identifier' => $data['identifier'],
|
||||
]
|
||||
);
|
||||
// then make a "positive" transaction based on the data in the array.
|
||||
$dest = $this->create(
|
||||
[
|
||||
'description' => $description,
|
||||
'amount' => app('steam')->positive(strval($data['amount'])),
|
||||
'foreign_amount' => null,
|
||||
'currency' => $currency,
|
||||
'account' => $destinationAccount,
|
||||
'transaction_journal' => $journal,
|
||||
'reconciled' => $data['reconciled'],
|
||||
'identifier' => $data['identifier'],
|
||||
]
|
||||
);
|
||||
|
||||
// set foreign currency
|
||||
$foreign = $this->findCurrency($data['foreign_currency_id'], $data['foreign_currency_code']);
|
||||
$this->setForeignCurrency($source, $foreign);
|
||||
$this->setForeignCurrency($dest, $foreign);
|
||||
|
||||
// set foreign amount:
|
||||
if (!is_null($data['foreign_amount'])) {
|
||||
$this->setForeignAmount($source, app('steam')->negative(strval($data['foreign_amount'])));
|
||||
$this->setForeignAmount($dest, app('steam')->positive(strval($data['foreign_amount'])));
|
||||
}
|
||||
|
||||
// set budget:
|
||||
$budget = $this->findBudget($data['budget_id'], $data['budget_name']);
|
||||
$this->setBudget($source, $budget);
|
||||
$this->setBudget($dest, $budget);
|
||||
|
||||
// set category
|
||||
$category = $this->findCategory($data['category_id'], $data['category_name']);
|
||||
$this->setCategory($source, $category);
|
||||
$this->setCategory($dest, $category);
|
||||
|
||||
return new Collection([$source, $dest]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
public function setUser(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
154
app/Factory/TransactionJournalFactory.php
Normal file
154
app/Factory/TransactionJournalFactory.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
/**
|
||||
* TransactionJournalFactory.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\Factory;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Services\Internal\Support\JournalServiceTrait;
|
||||
use FireflyIII\User;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class TransactionJournalFactory
|
||||
*/
|
||||
class TransactionJournalFactory
|
||||
{
|
||||
use JournalServiceTrait;
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* Create a new transaction journal and associated transactions.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return TransactionJournal
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function create(array $data): TransactionJournal
|
||||
{
|
||||
Log::debug('Start of TransactionJournalFactory::create()');
|
||||
// store basic journal first.
|
||||
$type = $this->findTransactionType($data['type']);
|
||||
$defaultCurrency = app('amount')->getDefaultCurrencyByUser($this->user);
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => $data['user'],
|
||||
'transaction_type_id' => $type->id,
|
||||
'bill_id' => null,
|
||||
'transaction_currency_id' => $defaultCurrency->id,
|
||||
'description' => $data['description'],
|
||||
'date' => $data['date']->format('Y-m-d'),
|
||||
'order' => 0,
|
||||
'tag_count' => 0,
|
||||
'completed' => 0,
|
||||
]
|
||||
);
|
||||
|
||||
// store basic transactions:
|
||||
$factory = app(TransactionFactory::class);
|
||||
$factory->setUser($this->user);
|
||||
|
||||
/** @var array $trData */
|
||||
foreach ($data['transactions'] as $trData) {
|
||||
$factory->createPair($journal, $trData);
|
||||
}
|
||||
$journal->completed = true;
|
||||
$journal->save();
|
||||
|
||||
// link bill:
|
||||
$this->connectBill($journal, $data);
|
||||
|
||||
// link piggy bank (if transfer)
|
||||
$this->connectPiggyBank($journal, $data);
|
||||
|
||||
// link tags:
|
||||
$this->connectTags($journal, $data);
|
||||
|
||||
// store note:
|
||||
$this->storeNote($journal, strval($data['notes']));
|
||||
|
||||
// store date meta fields (if present):
|
||||
$this->storeMeta($journal, $data, 'interest_date');
|
||||
$this->storeMeta($journal, $data, 'book_date');
|
||||
$this->storeMeta($journal, $data, 'process_date');
|
||||
$this->storeMeta($journal, $data, 'due_date');
|
||||
$this->storeMeta($journal, $data, 'payment_date');
|
||||
$this->storeMeta($journal, $data, 'invoice_date');
|
||||
$this->storeMeta($journal, $data, 'internal_reference');
|
||||
Log::debug('End of TransactionJournalFactory::create()');
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user.
|
||||
*
|
||||
* @param User $user
|
||||
*/
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param array $data
|
||||
*/
|
||||
protected function connectPiggyBank(TransactionJournal $journal, array $data): void
|
||||
{
|
||||
/** @var PiggyBankFactory $factory */
|
||||
$factory = app(PiggyBankFactory::class);
|
||||
$factory->setUser($this->user);
|
||||
|
||||
$piggyBank = $factory->find($data['piggy_bank_id'], $data['piggy_bank_name']);
|
||||
if (!is_null($piggyBank)) {
|
||||
/** @var PiggyBankEventFactory $factory */
|
||||
$factory = app(PiggyBankEventFactory::class);
|
||||
$factory->create($journal, $piggyBank);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the transaction type. Since this is mandatory, will throw an exception when nothing comes up. Will always
|
||||
* use TransactionType repository.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return TransactionType
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function findTransactionType(string $type): TransactionType
|
||||
{
|
||||
$factory = app(TransactionTypeFactory::class);
|
||||
$transactionType = $factory->find($type);
|
||||
if (is_null($transactionType)) {
|
||||
throw new FireflyException(sprintf('Could not find transaction type for "%s"', $type)); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
return $transactionType;
|
||||
}
|
||||
|
||||
}
|
||||
72
app/Factory/TransactionJournalMetaFactory.php
Normal file
72
app/Factory/TransactionJournalMetaFactory.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* TransactionJournalMetaFactory.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\Factory;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Models\TransactionJournalMeta;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class TransactionJournalMetaFactory
|
||||
*/
|
||||
class TransactionJournalMetaFactory
|
||||
{
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return TransactionJournalMeta|null
|
||||
*/
|
||||
public function updateOrCreate(array $data): ?TransactionJournalMeta
|
||||
{
|
||||
$value = $data['data'];
|
||||
/** @var TransactionJournalMeta $entry */
|
||||
$entry = $data['journal']->transactionJournalMeta()->where('name', $data['name'])->first();
|
||||
if (is_null($value) && !is_null($entry)) {
|
||||
try {
|
||||
$entry->delete();
|
||||
} catch (Exception $e) { // @codeCoverageIgnore
|
||||
Log::error(sprintf('Could not delete transaction journal meta: %s', $e->getMessage())); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($data['data'] instanceof Carbon) {
|
||||
$value = $data['data']->toW3cString();
|
||||
}
|
||||
|
||||
|
||||
if (null === $entry) {
|
||||
$entry = new TransactionJournalMeta();
|
||||
$entry->transactionJournal()->associate($data['journal']);
|
||||
$entry->name = $data['name'];
|
||||
}
|
||||
$entry->data = $value;
|
||||
$entry->save();
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
}
|
||||
44
app/Factory/TransactionTypeFactory.php
Normal file
44
app/Factory/TransactionTypeFactory.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* TransactionTypeFactory.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\Factory;
|
||||
|
||||
use FireflyIII\Models\TransactionType;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
* Class TransactionTypeFactory
|
||||
*/
|
||||
class TransactionTypeFactory
|
||||
{
|
||||
/**
|
||||
* @param string $type
|
||||
*
|
||||
* @return TransactionType|null
|
||||
*/
|
||||
public function find(string $type): ?TransactionType
|
||||
{
|
||||
return TransactionType::whereType(ucfirst($type))->first();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,6 +26,7 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Steam;
|
||||
@@ -173,7 +174,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
$startBalance = $dayBeforeBalance;
|
||||
$currency = $currencyRepos->find(intval($account->getMeta('currency_id')));
|
||||
|
||||
// @var Transaction $journal
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($journals as $transaction) {
|
||||
$transaction->before = $startBalance;
|
||||
$transactionAmount = $transaction->transaction_amount;
|
||||
|
||||
@@ -194,7 +194,9 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
||||
*/
|
||||
private function summarizeByBudget(Collection $collection): array
|
||||
{
|
||||
$result = [];
|
||||
$result = [
|
||||
'sum' => '0',
|
||||
];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($collection as $transaction) {
|
||||
$jrnlBudId = intval($transaction->transaction_journal_budget_id);
|
||||
@@ -202,6 +204,7 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
||||
$budgetId = max($jrnlBudId, $transBudId);
|
||||
$result[$budgetId] = $result[$budgetId] ?? '0';
|
||||
$result[$budgetId] = bcadd($transaction->transaction_amount, $result[$budgetId]);
|
||||
$result['sum'] = bcadd($result['sum'], $transaction->transaction_amount);
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
@@ -102,7 +102,12 @@ class Support
|
||||
*/
|
||||
protected function getObjectSummary(array $spent, array $earned): array
|
||||
{
|
||||
$return = [];
|
||||
$return = [
|
||||
'sum' => [
|
||||
'spent' => '0',
|
||||
'earned' => '0',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
@@ -110,10 +115,11 @@ class Support
|
||||
*/
|
||||
foreach ($spent as $objectId => $entry) {
|
||||
if (!isset($return[$objectId])) {
|
||||
$return[$objectId] = ['spent' => 0, 'earned' => 0];
|
||||
$return[$objectId] = ['spent' => '0', 'earned' => '0'];
|
||||
}
|
||||
|
||||
$return[$objectId]['spent'] = $entry;
|
||||
$return['sum']['spent'] = bcadd($return['sum']['spent'], $entry);
|
||||
}
|
||||
unset($entry);
|
||||
|
||||
@@ -123,10 +129,11 @@ class Support
|
||||
*/
|
||||
foreach ($earned as $objectId => $entry) {
|
||||
if (!isset($return[$objectId])) {
|
||||
$return[$objectId] = ['spent' => 0, 'earned' => 0];
|
||||
$return[$objectId] = ['spent' => '0', 'earned' => '0'];
|
||||
}
|
||||
|
||||
$return[$objectId]['earned'] = $entry;
|
||||
$return['sum']['earned'] = bcadd($return['sum']['earned'], $entry);
|
||||
}
|
||||
|
||||
return $return;
|
||||
@@ -139,12 +146,15 @@ class Support
|
||||
*/
|
||||
protected function summarizeByAccount(Collection $collection): array
|
||||
{
|
||||
$result = [];
|
||||
$result = [
|
||||
'sum' => '0',
|
||||
];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($collection as $transaction) {
|
||||
$accountId = $transaction->account_id;
|
||||
$result[$accountId] = $result[$accountId] ?? '0';
|
||||
$result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]);
|
||||
$result['sum'] = bcadd($result['sum'], $transaction->transaction_amount);
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
@@ -61,61 +61,6 @@ class StoredJournalEventHandler
|
||||
$this->ruleGroupRepository = $ruleGroupRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method connects a new transfer to a piggy bank.
|
||||
*
|
||||
* @param StoredTransactionJournal $event
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
||||
*/
|
||||
public function connectToPiggyBank(StoredTransactionJournal $event): bool
|
||||
{
|
||||
$journal = $event->journal;
|
||||
$piggyBankId = $event->piggyBankId;
|
||||
$piggyBank = $this->repository->find($piggyBankId);
|
||||
|
||||
// is a transfer?
|
||||
if (!$this->journalRepository->isTransfer($journal)) {
|
||||
Log::info(sprintf('Will not connect %s #%d to a piggy bank.', $journal->transactionType->type, $journal->id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// piggy exists?
|
||||
if (null === $piggyBank->id) {
|
||||
Log::error(sprintf('There is no piggy bank with ID #%d', $piggyBankId));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// repetition exists?
|
||||
$repetition = $this->repository->getRepetition($piggyBank, $journal->date);
|
||||
if (null === $repetition->id) {
|
||||
Log::error(sprintf('No piggy bank repetition on %s!', $journal->date->format('Y-m-d')));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// get the amount
|
||||
$amount = $this->repository->getExactAmount($piggyBank, $repetition, $journal);
|
||||
if (0 === bccomp($amount, '0')) {
|
||||
Log::debug('Amount is zero, will not create event.');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// update amount
|
||||
$this->repository->addAmountToRepetition($repetition, $amount);
|
||||
$event = $this->repository->createEventWithJournal($piggyBank, $amount, $journal);
|
||||
|
||||
Log::debug(sprintf('Created piggy bank event #%d', $event->id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method grabs all the users rules and processes them.
|
||||
*
|
||||
|
||||
@@ -51,6 +51,7 @@ class UpdatedJournalEventHandler
|
||||
|
||||
/**
|
||||
* This method will check all the rules when a journal is updated.
|
||||
* TODO move to factory.
|
||||
*
|
||||
* @param UpdatedTransactionJournal $updatedJournalEvent
|
||||
*
|
||||
@@ -81,6 +82,7 @@ class UpdatedJournalEventHandler
|
||||
|
||||
/**
|
||||
* This method calls a special bill scanner that will check if the updated journal is part of a bill.
|
||||
* TODO move to factory.
|
||||
*
|
||||
* @param UpdatedTransactionJournal $updatedJournalEvent
|
||||
*
|
||||
|
||||
@@ -25,11 +25,11 @@ namespace FireflyIII\Handlers\Events;
|
||||
use FireflyIII\Events\RegisteredUser;
|
||||
use FireflyIII\Events\RequestedNewPassword;
|
||||
use FireflyIII\Events\UserChangedEmail;
|
||||
use FireflyIII\Factories\RoleFactory;
|
||||
use FireflyIII\Mail\ConfirmEmailChangeMail;
|
||||
use FireflyIII\Mail\RegisteredUser as RegisteredUserMail;
|
||||
use FireflyIII\Mail\RequestedNewPassword as RequestedNewPasswordMail;
|
||||
use FireflyIII\Mail\UndoEmailChangeMail;
|
||||
use FireflyIII\Models\Role;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Auth\Events\Login;
|
||||
@@ -74,11 +74,12 @@ class UserEventHandler
|
||||
*/
|
||||
public function checkSingleUserIsAdmin(Login $event): bool
|
||||
{
|
||||
Log::debug('In checkSingleUserIsAdmin');
|
||||
/** @var UserRepositoryInterface $repository */
|
||||
$repository = app(UserRepositoryInterface::class);
|
||||
|
||||
/** @var User $user */
|
||||
$user = $event->user;
|
||||
$count = User::count();
|
||||
$count = $repository->count();
|
||||
|
||||
if ($count > 1) {
|
||||
// if more than one user, do nothing.
|
||||
@@ -93,17 +94,16 @@ class UserEventHandler
|
||||
return true;
|
||||
}
|
||||
// user is the only user but does not have role "owner".
|
||||
$role = Role::where('name', 'owner')->first();
|
||||
$role = $repository->getRole('owner');
|
||||
if (is_null($role)) {
|
||||
// create role, does not exist. Very strange situation so let's raise a big fuss about it.
|
||||
$role = Role::create(['name' => 'owner', 'display_name' => 'Site Owner', 'description' => 'User runs this instance of FF3']);
|
||||
$role = $repository->createRole('owner', 'Site Owner', 'User runs this instance of FF3');
|
||||
Log::error('Could not find role "owner". This is weird.');
|
||||
}
|
||||
|
||||
Log::info(sprintf('Gave user #%d role #%d ("%s")', $user->id, $role->id, $role->name));
|
||||
// give user the role
|
||||
$user->attachRole($role);
|
||||
$user->save();
|
||||
$repository->attachRole($user, 'owner');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@ namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use FireflyConfig;
|
||||
use FireflyIII\Events\RequestedVersionCheckStatus;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Services\Github\Object\Release;
|
||||
use FireflyIII\Services\Github\Request\UpdateRequest;
|
||||
use FireflyIII\User;
|
||||
use Log;
|
||||
|
||||
@@ -45,6 +48,7 @@ class VersionCheckEventHandler
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/** @var User $user */
|
||||
$user = $event->user;
|
||||
if (!$user->hasRole('owner')) {
|
||||
@@ -55,7 +59,7 @@ class VersionCheckEventHandler
|
||||
$lastCheckTime = FireflyConfig::get('last_update_check', time());
|
||||
$now = time();
|
||||
if ($now - $lastCheckTime->data < 604800) {
|
||||
Log::debug('Checked for updates less than a week ago.');
|
||||
Log::debug(sprintf('Checked for updates less than a week ago (on %s).', date('Y-m-d H:i:s', $lastCheckTime->data)));
|
||||
|
||||
return;
|
||||
|
||||
@@ -71,8 +75,42 @@ class VersionCheckEventHandler
|
||||
return;
|
||||
}
|
||||
|
||||
// actually check for update and inform the user.
|
||||
$current = config('firefly.version');
|
||||
/** @var UpdateRequest $request */
|
||||
$request = app(UpdateRequest::class);
|
||||
$check = -2;
|
||||
$first = new Release(['id' => '0', 'title' => '0', 'updated' => '2017-01-01', 'content' => '']);
|
||||
try {
|
||||
$request->call();
|
||||
$releases = $request->getReleases();
|
||||
// first entry should be the latest entry:
|
||||
/** @var Release $first */
|
||||
$first = reset($releases);
|
||||
$check = version_compare($current, $first->getTitle());
|
||||
FireflyConfig::set('last_update_check', time());
|
||||
} catch (FireflyException $e) {
|
||||
Log::error(sprintf('Could not check for updates: %s', $e->getMessage()));
|
||||
}
|
||||
$string = 'no result: ' . $check;
|
||||
if ($check === -2) {
|
||||
$string = strval(trans('firefly.update_check_error'));
|
||||
}
|
||||
if ($check === -1) {
|
||||
// there is a new FF version!
|
||||
$monthAndDayFormat = (string)trans('config.month_and_day');
|
||||
$string = strval(
|
||||
trans(
|
||||
'firefly.update_new_version_alert',
|
||||
['your_version' => $current, 'new_version' => $first->getTitle(), 'date' => $first->getUpdated()->formatLocalized($monthAndDayFormat)]
|
||||
)
|
||||
);
|
||||
}
|
||||
if ($check !== 0) {
|
||||
// flash info
|
||||
session()->flash('info', $string);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,8 +50,9 @@ class AttachmentHelper implements AttachmentHelperInterface
|
||||
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
|
||||
protected $uploadDisk;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* AttachmentHelper constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
@@ -79,6 +79,9 @@ class MetaPieChart implements MetaPieChartInterface
|
||||
/** @var User */
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* MetaPieChart constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->accounts = new Collection;
|
||||
@@ -273,11 +276,13 @@ class MetaPieChart implements MetaPieChartInterface
|
||||
$collector->setBudgets($this->budgets);
|
||||
$collector->setCategories($this->categories);
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($this->tags->count() > 0) {
|
||||
$collector->setTags($this->tags);
|
||||
$collector->withCategoryInformation();
|
||||
$collector->withBudgetInformation();
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
return $collector->getJournals();
|
||||
}
|
||||
@@ -294,7 +299,7 @@ class MetaPieChart implements MetaPieChartInterface
|
||||
{
|
||||
if (0 === count($fields) && $this->tags->count() > 0) {
|
||||
// do a special group on tags:
|
||||
return $this->groupByTag($set);
|
||||
return $this->groupByTag($set); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$grouped = [];
|
||||
@@ -338,6 +343,8 @@ class MetaPieChart implements MetaPieChartInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Collection $set
|
||||
*
|
||||
* @return array
|
||||
|
||||
@@ -37,6 +37,7 @@ use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
@@ -68,6 +69,8 @@ class JournalCollector implements JournalCollectorInterface
|
||||
'transaction_journals.description',
|
||||
'transaction_journals.date',
|
||||
'transaction_journals.encrypted',
|
||||
'transaction_journals.created_at',
|
||||
'transaction_journals.updated_at',
|
||||
'transaction_types.type as transaction_type_type',
|
||||
'transaction_journals.bill_id',
|
||||
'transaction_journals.updated_at',
|
||||
@@ -232,7 +235,7 @@ class JournalCollector implements JournalCollectorInterface
|
||||
$countQuery->getQuery()->groups = null;
|
||||
$countQuery->getQuery()->orders = null;
|
||||
$countQuery->groupBy('accounts.user_id');
|
||||
$this->count = $countQuery->count();
|
||||
$this->count = intval($countQuery->count());
|
||||
|
||||
return $this->count;
|
||||
}
|
||||
@@ -243,6 +246,18 @@ class JournalCollector implements JournalCollectorInterface
|
||||
public function getJournals(): Collection
|
||||
{
|
||||
$this->run = true;
|
||||
|
||||
// find query set in cache.
|
||||
$hash = hash('sha256', $this->query->toSql() . serialize($this->query->getBindings()));
|
||||
$key = 'query-' . substr($hash, -8);
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($key);
|
||||
if ($cache->has()) {
|
||||
Log::debug(sprintf('Return cache of query with ID "%s".', $key));
|
||||
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
/** @var Collection $set */
|
||||
$set = $this->query->get(array_values($this->fields));
|
||||
|
||||
@@ -258,11 +273,22 @@ class JournalCollector implements JournalCollectorInterface
|
||||
if (null !== $transaction->bill_name) {
|
||||
$transaction->bill_name = Steam::decrypt(intval($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);
|
||||
}
|
||||
|
||||
);
|
||||
Log::debug(sprintf('Cached query with ID "%s".', $key));
|
||||
$cache->store($set);
|
||||
|
||||
return $set;
|
||||
}
|
||||
@@ -328,7 +354,7 @@ class JournalCollector implements JournalCollectorInterface
|
||||
*/
|
||||
public function setAfter(Carbon $after): JournalCollectorInterface
|
||||
{
|
||||
$afterStr = $after->format('Y-m-d');
|
||||
$afterStr = $after->format('Y-m-d 00:00:00');
|
||||
$this->query->where('transaction_journals.date', '>=', $afterStr);
|
||||
Log::debug(sprintf('JournalCollector range is now after %s (inclusive)', $afterStr));
|
||||
|
||||
@@ -364,7 +390,7 @@ class JournalCollector implements JournalCollectorInterface
|
||||
*/
|
||||
public function setBefore(Carbon $before): JournalCollectorInterface
|
||||
{
|
||||
$beforeStr = $before->format('Y-m-d');
|
||||
$beforeStr = $before->format('Y-m-d 00:00:00');
|
||||
$this->query->where('transaction_journals.date', '<=', $beforeStr);
|
||||
Log::debug(sprintf('JournalCollector range is now before %s (inclusive)', $beforeStr));
|
||||
|
||||
@@ -471,6 +497,23 @@ class JournalCollector implements JournalCollectorInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $journals
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setJournals(Collection $journals): JournalCollectorInterface
|
||||
{
|
||||
$ids = $journals->pluck('id')->toArray();
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) use ($ids) {
|
||||
$q->whereIn('transaction_journals.id', $ids);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $limit
|
||||
*
|
||||
@@ -551,8 +594,8 @@ class JournalCollector implements JournalCollectorInterface
|
||||
public function setRange(Carbon $start, Carbon $end): JournalCollectorInterface
|
||||
{
|
||||
if ($start <= $end) {
|
||||
$startStr = $start->format('Y-m-d');
|
||||
$endStr = $end->format('Y-m-d');
|
||||
$startStr = $start->format('Y-m-d 00:00:00');
|
||||
$endStr = $end->format('Y-m-d 23:59:59');
|
||||
$this->query->where('transaction_journals.date', '>=', $startStr);
|
||||
$this->query->where('transaction_journals.date', '<=', $endStr);
|
||||
Log::debug(sprintf('JournalCollector range is now %s - %s (inclusive)', $startStr, $endStr));
|
||||
|
||||
@@ -42,6 +42,13 @@ interface JournalCollectorInterface
|
||||
*/
|
||||
public function addFilter(string $filter): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param Collection $journals
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setJournals(Collection $journals): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param string $amount
|
||||
*
|
||||
|
||||
@@ -28,6 +28,7 @@ use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@@ -136,7 +137,7 @@ class PopupReport implements PopupReportInterface
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAccounts($attributes['accounts'])->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
|
||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||
->setRange($attributes['startDate'], $attributes['endDate'])->withOpposingAccount()
|
||||
->setCategory($category);
|
||||
$journals = $collector->getJournals();
|
||||
|
||||
@@ -182,6 +183,9 @@ class PopupReport implements PopupReportInterface
|
||||
*/
|
||||
public function byIncome(Account $account, array $attributes): Collection
|
||||
{
|
||||
/** @var JournalRepositoryInterface $repository */
|
||||
$repository = app(JournalRepositoryInterface::class);
|
||||
$repository->setUser($account->user);
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])
|
||||
@@ -191,9 +195,10 @@ class PopupReport implements PopupReportInterface
|
||||
|
||||
// filter the set so the destinations outside of $attributes['accounts'] are not included.
|
||||
$journals = $journals->filter(
|
||||
function (Transaction $transaction) use ($report) {
|
||||
function (Transaction $transaction) use ($report, $repository) {
|
||||
// get the destinations:
|
||||
$destinations = $transaction->transactionJournal->destinationAccountList()->pluck('id')->toArray();
|
||||
$journal = $transaction->transactionJournal;
|
||||
$destinations = $repository->getJournalDestinationAccounts($journal)->pluck('id')->toArray();
|
||||
|
||||
// do these intersect with the current list?
|
||||
return !empty(array_intersect($report, $destinations));
|
||||
|
||||
@@ -24,9 +24,11 @@ namespace FireflyIII\Http\Controllers\Account;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Factory\TransactionJournalFactory;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Requests\ReconciliationFormRequest;
|
||||
use FireflyIII\Http\Requests\ReconciliationStoreRequest;
|
||||
use FireflyIII\Http\Requests\ReconciliationUpdateRequest;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
@@ -35,8 +37,10 @@ use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Update\TransactionUpdateService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use Session;
|
||||
@@ -48,6 +52,9 @@ use Session;
|
||||
*/
|
||||
class ReconcileController extends Controller
|
||||
{
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -60,6 +67,7 @@ class ReconcileController extends Controller
|
||||
function ($request, $next) {
|
||||
app('view')->share('mainTitleIcon', 'fa-credit-card');
|
||||
app('view')->share('title', trans('firefly.accounts'));
|
||||
$this->repository = app(JournalRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@@ -80,10 +88,10 @@ class ReconcileController extends Controller
|
||||
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
|
||||
|
||||
// journal related code
|
||||
$pTransaction = $journal->positiveTransaction();
|
||||
$pTransaction = $this->repository->getFirstPosTransaction($journal);
|
||||
$preFilled = [
|
||||
'date' => $journal->dateAsString(),
|
||||
'category' => $journal->categoryAsString(),
|
||||
'date' => $this->repository->getJournalDate($journal, null),
|
||||
'category' => $this->repository->getJournalCategoryName($journal),
|
||||
'tags' => join(',', $journal->tags->pluck('tag')->toArray()),
|
||||
'amount' => $pTransaction->amount,
|
||||
];
|
||||
@@ -126,10 +134,8 @@ class ReconcileController extends Controller
|
||||
$clearedAmount = '0';
|
||||
$route = route('accounts.reconcile.submit', [$account->id, $start->format('Ymd'), $end->format('Ymd')]);
|
||||
// get sum of transaction amounts:
|
||||
/** @var JournalRepositoryInterface $repository */
|
||||
$repository = app(JournalRepositoryInterface::class);
|
||||
$transactions = $repository->getTransactionsById($transactionIds);
|
||||
$cleared = $repository->getTransactionsById($clearedIds);
|
||||
$transactions = $this->repository->getTransactionsById($transactionIds);
|
||||
$cleared = $this->repository->getTransactionsById($clearedIds);
|
||||
$countCleared = 0;
|
||||
|
||||
/** @var Transaction $transaction */
|
||||
@@ -182,7 +188,7 @@ class ReconcileController extends Controller
|
||||
/** @var CurrencyRepositoryInterface $currencyRepos */
|
||||
$currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$currencyId = intval($account->getMeta('currency_id'));
|
||||
$currency = $currencyRepos->find($currencyId);
|
||||
$currency = $currencyRepos->findNull($currencyId);
|
||||
if (0 === $currencyId) {
|
||||
$currency = app('amount')->getDefaultCurrency(); // @codeCoverageIgnore
|
||||
}
|
||||
@@ -220,71 +226,105 @@ class ReconcileController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param JournalRepositoryInterface $repository
|
||||
* @param TransactionJournal $journal
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
|
||||
*/
|
||||
public function show(JournalRepositoryInterface $repository, TransactionJournal $journal)
|
||||
public function show(TransactionJournal $journal)
|
||||
{
|
||||
|
||||
if (TransactionType::RECONCILIATION !== $journal->transactionType->type) {
|
||||
return redirect(route('transactions.show', [$journal->id]));
|
||||
}
|
||||
$subTitle = trans('firefly.reconciliation') . ' "' . $journal->description . '"';
|
||||
|
||||
// get main transaction:
|
||||
$transaction = $repository->getAssetTransaction($journal);
|
||||
$transaction = $this->repository->getAssetTransaction($journal);
|
||||
$account = $transaction->account;
|
||||
|
||||
return view('accounts.reconcile.show', compact('journal', 'subTitle', 'transaction', 'account'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param ReconciliationStoreRequest $request
|
||||
* @param Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function submit(Request $request, Account $account, Carbon $start, Carbon $end)
|
||||
public function submit(ReconciliationStoreRequest $request, JournalRepositoryInterface $repository, Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
/** @var JournalRepositoryInterface $repository */
|
||||
$repository = app(JournalRepositoryInterface::class);
|
||||
$transactions = $repository->getTransactionsById($request->get('transactions') ?? []);
|
||||
Log::debug('In ReconcileController::submit()');
|
||||
$data = $request->getAll();
|
||||
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$repository->reconcile($transaction); // mark as reconciled.
|
||||
foreach ($data['transactions'] as $transactionId) {
|
||||
$repository->reconcileById(intval($transactionId));
|
||||
}
|
||||
Log::debug('Reconciled all transactions.');
|
||||
|
||||
// create reconciliation transaction (if necessary):
|
||||
if ('create' === $request->get('reconcile')) {
|
||||
if ('create' === $data['reconcile']) {
|
||||
// get "opposing" account.
|
||||
/** @var AccountRepositoryInterface $accountRepos */
|
||||
$accountRepos = app(AccountRepositoryInterface::class);
|
||||
$reconciliation = $accountRepos->getReconciliation($account);
|
||||
$difference = $request->get('difference');
|
||||
|
||||
// store journal between these two.
|
||||
$data = [
|
||||
'what' => 'Reconciliation',
|
||||
'source' => $account,
|
||||
'destination' => $reconciliation,
|
||||
'category' => '',
|
||||
'budget_id' => 0,
|
||||
'amount' => $difference,
|
||||
'currency_id' => $account->getMeta('currency_id'),
|
||||
'description' => trans(
|
||||
'firefly.reconcilliation_transaction_title',
|
||||
['from' => $start->formatLocalized($this->monthAndDayFormat), 'to' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
),
|
||||
'date' => $request->get('end'),
|
||||
'notes' => join(',', $transactions->pluck('id')->toArray()),
|
||||
|
||||
$difference = $data['difference'];
|
||||
$source = $reconciliation;
|
||||
$destination = $account;
|
||||
if (bccomp($difference, '0') === 1) {
|
||||
// amount is positive. Add it to reconciliation?
|
||||
$source = $account;
|
||||
$destination = $reconciliation;
|
||||
|
||||
}
|
||||
|
||||
// data for journal
|
||||
$description = trans(
|
||||
'firefly.reconcilliation_transaction_title',
|
||||
['from' => $start->formatLocalized($this->monthAndDayFormat), 'to' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
$journalData = [
|
||||
'type' => 'Reconciliation',
|
||||
'description' => $description,
|
||||
'user' => auth()->user()->id,
|
||||
'date' => $data['end'],
|
||||
'bill_id' => null,
|
||||
'bill_name' => null,
|
||||
'piggy_bank_id' => null,
|
||||
'piggy_bank_name' => null,
|
||||
'tags' => null,
|
||||
'interest_date' => null,
|
||||
'transactions' => [[
|
||||
'currency_id' => intval($account->getMeta('currency_id')),
|
||||
'currency_code' => null,
|
||||
'description' => null,
|
||||
'amount' => app('steam')->positive($difference),
|
||||
'source_id' => $source->id,
|
||||
'source_name' => null,
|
||||
'destination_id' => $destination->id,
|
||||
'destination_name' => null,
|
||||
'reconciled' => true,
|
||||
'identifier' => 0,
|
||||
'foreign_currency_id' => null,
|
||||
'foreign_currency_code' => null,
|
||||
'foreign_amount' => null,
|
||||
'budget_id' => null,
|
||||
'budget_name' => null,
|
||||
'category_id' => null,
|
||||
'category_name' => null,
|
||||
],
|
||||
],
|
||||
'notes' => join(', ', $data['transactions']),
|
||||
];
|
||||
$journal = $repository->store($data);
|
||||
// reconcile this transaction too:
|
||||
$transaction = $journal->transactions()->first();
|
||||
$repository->reconcile($transaction);
|
||||
|
||||
$journal = $repository->store($journalData);
|
||||
}
|
||||
Log::debug('End of routine.');
|
||||
|
||||
Session::flash('success', trans('firefly.reconciliation_stored'));
|
||||
|
||||
@@ -313,7 +353,7 @@ class ReconcileController extends Controller
|
||||
/** @var CurrencyRepositoryInterface $currencyRepos */
|
||||
$currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$currencyId = intval($account->getMeta('currency_id'));
|
||||
$currency = $currencyRepos->find($currencyId);
|
||||
$currency = $currencyRepos->findNull($currencyId);
|
||||
if (0 === $currencyId) {
|
||||
$currency = app('amount')->getDefaultCurrency(); // @codeCoverageIgnore
|
||||
}
|
||||
@@ -339,13 +379,12 @@ class ReconcileController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReconciliationFormRequest $request
|
||||
* @param AccountRepositoryInterface $repository
|
||||
* @param TransactionJournal $journal
|
||||
* @param ReconciliationUpdateRequest $request
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function update(ReconciliationFormRequest $request, AccountRepositoryInterface $repository, TransactionJournal $journal)
|
||||
public function update(ReconciliationUpdateRequest $request, TransactionJournal $journal)
|
||||
{
|
||||
if (TransactionType::RECONCILIATION !== $journal->transactionType->type) {
|
||||
return redirect(route('transactions.show', [$journal->id]));
|
||||
@@ -356,8 +395,53 @@ class ReconcileController extends Controller
|
||||
return redirect(route('accounts.reconcile.edit', [$journal->id]))->withInput();
|
||||
}
|
||||
// update journal using account repository. Keep it consistent.
|
||||
$data = $request->getJournalData();
|
||||
$repository->updateReconciliation($journal, $data);
|
||||
$submitted = $request->getJournalData();
|
||||
|
||||
// amount pos neg influences the accounts:
|
||||
$source = $this->repository->getJournalSourceAccounts($journal)->first();
|
||||
$destination = $this->repository->getJournalDestinationAccounts($journal)->first();
|
||||
if (bccomp($submitted['amount'], '0') === 1) {
|
||||
// amount is positive, switch accounts:
|
||||
list($source, $destination) = [$destination, $source];
|
||||
|
||||
}
|
||||
// expand data with journal data:
|
||||
$data = [
|
||||
'type' => $journal->transactionType->type,
|
||||
'description' => $journal->description,
|
||||
'user' => $journal->user_id,
|
||||
'date' => $journal->date,
|
||||
'bill_id' => null,
|
||||
'bill_name' => null,
|
||||
'piggy_bank_id' => null,
|
||||
'piggy_bank_name' => null,
|
||||
'tags' => $submitted['tags'],
|
||||
'interest_date' => null,
|
||||
'book_date' => null,
|
||||
'transactions' => [[
|
||||
'currency_id' => intval($journal->transaction_currency_id),
|
||||
'currency_code' => null,
|
||||
'description' => null,
|
||||
'amount' => app('steam')->positive($submitted['amount']),
|
||||
'source_id' => $source->id,
|
||||
'source_name' => null,
|
||||
'destination_id' => $destination->id,
|
||||
'destination_name' => null,
|
||||
'reconciled' => true,
|
||||
'identifier' => 0,
|
||||
'foreign_currency_id' => null,
|
||||
'foreign_currency_code' => null,
|
||||
'foreign_amount' => null,
|
||||
'budget_id' => null,
|
||||
'budget_name' => null,
|
||||
'category_id' => null,
|
||||
'category_name' => $submitted['category'],
|
||||
],
|
||||
],
|
||||
'notes' => $this->repository->getNoteText($journal),
|
||||
];
|
||||
|
||||
$this->repository->update($journal, $data);
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
if (1 === intval($request->get('return_to_edit'))) {
|
||||
|
||||
@@ -39,7 +39,6 @@ use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Preferences;
|
||||
use Steam;
|
||||
use View;
|
||||
@@ -51,6 +50,13 @@ use View;
|
||||
*/
|
||||
class AccountController extends Controller
|
||||
{
|
||||
/** @var CurrencyRepositoryInterface */
|
||||
private $currencyRepos;
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $journalRepos;
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -64,6 +70,10 @@ class AccountController extends Controller
|
||||
app('view')->share('mainTitleIcon', 'fa-credit-card');
|
||||
app('view')->share('title', trans('firefly.accounts'));
|
||||
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$this->journalRepos = app(JournalRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
@@ -77,9 +87,7 @@ class AccountController extends Controller
|
||||
*/
|
||||
public function create(Request $request, string $what = 'asset')
|
||||
{
|
||||
/** @var CurrencyRepositoryInterface $repository */
|
||||
$repository = app(CurrencyRepositoryInterface::class);
|
||||
$allCurrencies = $repository->get();
|
||||
$allCurrencies = $this->currencyRepos->get();
|
||||
$currencySelectList = ExpandedForm::makeSelectList($allCurrencies);
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
||||
@@ -102,16 +110,15 @@ class AccountController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccountRepositoryInterface $repository
|
||||
* @param Account $account
|
||||
* @param Account $account
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function delete(AccountRepositoryInterface $repository, Account $account)
|
||||
public function delete(Account $account)
|
||||
{
|
||||
$typeName = config('firefly.shortNamesByFullName.' . $account->accountType->type);
|
||||
$subTitle = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]);
|
||||
$accountList = ExpandedForm::makeSelectListWithEmpty($repository->getAccountsByType([$account->accountType->type]));
|
||||
$accountList = ExpandedForm::makeSelectListWithEmpty($this->repository->getAccountsByType([$account->accountType->type]));
|
||||
unset($accountList[$account->id]);
|
||||
|
||||
// put previous url in session
|
||||
@@ -127,14 +134,14 @@ class AccountController extends Controller
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function destroy(Request $request, AccountRepositoryInterface $repository, Account $account)
|
||||
public function destroy(Request $request, Account $account)
|
||||
{
|
||||
$type = $account->accountType->type;
|
||||
$typeName = config('firefly.shortNamesByFullName.' . $type);
|
||||
$name = $account->name;
|
||||
$moveTo = $repository->find(intval($request->get('move_account_before_delete')));
|
||||
$moveTo = $this->repository->findNull(intval($request->get('move_account_before_delete')));
|
||||
|
||||
$repository->destroy($account, $moveTo);
|
||||
$this->repository->destroy($account, $moveTo);
|
||||
|
||||
$request->session()->flash('success', strval(trans('firefly.' . $typeName . '_deleted', ['name' => $name])));
|
||||
Preferences::mark();
|
||||
@@ -154,17 +161,13 @@ class AccountController extends Controller
|
||||
* @return View
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @throws FireflyException
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function edit(Request $request, Account $account, AccountRepositoryInterface $repository)
|
||||
{
|
||||
/** @var CurrencyRepositoryInterface $currencyRepos */
|
||||
$currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$what = config('firefly.shortNamesByFullName')[$account->accountType->type];
|
||||
$subTitle = trans('firefly.edit_' . $what . '_account', ['name' => $account->name]);
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
||||
$allCurrencies = $currencyRepos->get();
|
||||
$allCurrencies = $this->currencyRepos->get();
|
||||
$currencySelectList = ExpandedForm::makeSelectList($allCurrencies);
|
||||
$roles = [];
|
||||
foreach (config('firefly.accountRoles') as $role) {
|
||||
@@ -180,11 +183,9 @@ class AccountController extends Controller
|
||||
// pre fill some useful values.
|
||||
|
||||
// the opening balance is tricky:
|
||||
$openingBalanceAmount = $account->getOpeningBalanceAmount();
|
||||
$openingBalanceAmount = '0' === $account->getOpeningBalanceAmount() ? '' : $openingBalanceAmount;
|
||||
$openingBalanceDate = $account->getOpeningBalanceDate();
|
||||
$openingBalanceDate = 1900 === $openingBalanceDate->year ? null : $openingBalanceDate->format('Y-m-d');
|
||||
$currency = $currencyRepos->find(intval($account->getMeta('currency_id')));
|
||||
$openingBalanceAmount = strval($repository->getOpeningBalanceAmount($account));
|
||||
$openingBalanceDate = $repository->getOpeningBalanceDate($account);
|
||||
$currency = $this->currencyRepos->findNull(intval($account->getMeta('currency_id')));
|
||||
|
||||
$preFilled = [
|
||||
'accountNumber' => $account->getMeta('accountNumber'),
|
||||
@@ -197,9 +198,10 @@ class AccountController extends Controller
|
||||
'virtualBalance' => $account->virtual_balance,
|
||||
'currency_id' => $currency->id,
|
||||
'notes' => '',
|
||||
'active' => $account->active,
|
||||
];
|
||||
/** @var Note $note */
|
||||
$note = $repository->getNote($account);
|
||||
$note = $this->repository->getNote($account);
|
||||
if (null !== $note) {
|
||||
$preFilled['notes'] = $note->text;
|
||||
}
|
||||
@@ -224,19 +226,18 @@ class AccountController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param AccountRepositoryInterface $repository
|
||||
* @param string $what
|
||||
* @param Request $request
|
||||
* @param string $what
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function index(Request $request, AccountRepositoryInterface $repository, string $what)
|
||||
public function index(Request $request, string $what)
|
||||
{
|
||||
$what = $what ?? 'asset';
|
||||
$subTitle = trans('firefly.' . $what . '_accounts');
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
||||
$types = config('firefly.accountTypesByIdentifier.' . $what);
|
||||
$collection = $repository->getAccountsByType($types);
|
||||
$collection = $this->repository->getAccountsByType($types);
|
||||
$total = $collection->count();
|
||||
$page = 0 === intval($request->get('page')) ? 1 : intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('listPageSize', 50)->data);
|
||||
@@ -272,10 +273,9 @@ class AccountController extends Controller
|
||||
/**
|
||||
* Show an account.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param JournalRepositoryInterface $repository
|
||||
* @param Account $account
|
||||
* @param string $moment
|
||||
* @param Request $request
|
||||
* @param Account $account
|
||||
* @param string $moment
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||
*
|
||||
@@ -284,82 +284,58 @@ class AccountController extends Controller
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function show(Request $request, JournalRepositoryInterface $repository, Account $account, string $moment = '')
|
||||
public function show(Request $request, Account $account, Carbon $start = null, Carbon $end = null)
|
||||
{
|
||||
if (AccountType::INITIAL_BALANCE === $account->accountType->type) {
|
||||
return $this->redirectToOriginalAccount($account);
|
||||
}
|
||||
/** @var CurrencyRepositoryInterface $currencyRepos */
|
||||
$currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
|
||||
$page = intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('listPageSize', 50)->data);
|
||||
$chartUri = route('chart.account.single', [$account->id]);
|
||||
$start = null;
|
||||
$end = null;
|
||||
$periods = new Collection;
|
||||
$currencyId = intval($account->getMeta('currency_id'));
|
||||
$currency = $currencyRepos->find($currencyId);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
if (null === $start) {
|
||||
$start = session('start');
|
||||
}
|
||||
if (null === $end) {
|
||||
$end = app('navigation')->endOfPeriod($start, $range);
|
||||
}
|
||||
if ($end < $start) {
|
||||
throw new FireflyException('End is after start!'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
|
||||
$page = intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('listPageSize', 50)->data);
|
||||
$currencyId = intval($account->getMeta('currency_id'));
|
||||
$currency = $this->currencyRepos->findNull($currencyId);
|
||||
if (0 === $currencyId) {
|
||||
$currency = app('amount')->getDefaultCurrency(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
// prep for "all" view.
|
||||
if ('all' === $moment) {
|
||||
$subTitle = trans('firefly.all_journals_for_account', ['name' => $account->name]);
|
||||
$chartUri = route('chart.account.all', [$account->id]);
|
||||
$first = $repository->first();
|
||||
$start = $first->date ?? new Carbon;
|
||||
$end = new Carbon;
|
||||
}
|
||||
|
||||
// prep for "specific date" view.
|
||||
if (strlen($moment) > 0 && 'all' !== $moment) {
|
||||
$start = new Carbon($moment);
|
||||
$end = app('navigation')->endOfPeriod($start, $range);
|
||||
$fStart = $start->formatLocalized($this->monthAndDayFormat);
|
||||
$fEnd = $end->formatLocalized($this->monthAndDayFormat);
|
||||
$subTitle = trans('firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $fStart, 'end' => $fEnd]);
|
||||
$chartUri = route('chart.account.period', [$account->id, $start->format('Y-m-d')]);
|
||||
$periods = $this->getPeriodOverview($account);
|
||||
}
|
||||
|
||||
// prep for current period view
|
||||
if (0 === strlen($moment)) {
|
||||
$start = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range));
|
||||
$end = clone session('end', app('navigation')->endOfPeriod(new Carbon, $range));
|
||||
$fStart = $start->formatLocalized($this->monthAndDayFormat);
|
||||
$fEnd = $end->formatLocalized($this->monthAndDayFormat);
|
||||
$subTitle = trans('firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $fStart, 'end' => $fEnd]);
|
||||
$periods = $this->getPeriodOverview($account);
|
||||
}
|
||||
|
||||
// grab journals:
|
||||
$fStart = $start->formatLocalized($this->monthAndDayFormat);
|
||||
$fEnd = $end->formatLocalized($this->monthAndDayFormat);
|
||||
$subTitle = trans('firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $fStart, 'end' => $fEnd]);
|
||||
$chartUri = route('chart.account.period', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]);
|
||||
$periods = $this->getPeriodOverview($account, $end);
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page);
|
||||
if (null !== $start) {
|
||||
$collector->setRange($start, $end);
|
||||
}
|
||||
$transactions = $collector->getPaginatedJournals();
|
||||
$transactions->setPath(route('accounts.show', [$account->id, $moment]));
|
||||
$transactions->setPath(route('accounts.show', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]));
|
||||
|
||||
return view(
|
||||
'accounts.show',
|
||||
compact('account', 'currency', 'moment', 'periods', 'subTitleIcon', 'transactions', 'subTitle', 'start', 'end', 'chartUri')
|
||||
compact('account', 'currency', 'periods', 'subTitleIcon', 'transactions', 'subTitle', 'start', 'end', 'chartUri')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccountFormRequest $request
|
||||
* @param AccountRepositoryInterface $repository
|
||||
* @param AccountFormRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function store(AccountFormRequest $request, AccountRepositoryInterface $repository)
|
||||
public function store(AccountFormRequest $request)
|
||||
{
|
||||
$data = $request->getAccountData();
|
||||
$account = $repository->store($data);
|
||||
$account = $this->repository->store($data);
|
||||
$request->session()->flash('success', strval(trans('firefly.stored_new_account', ['name' => $account->name])));
|
||||
Preferences::mark();
|
||||
|
||||
@@ -390,10 +366,10 @@ class AccountController extends Controller
|
||||
*
|
||||
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function update(AccountFormRequest $request, AccountRepositoryInterface $repository, Account $account)
|
||||
public function update(AccountFormRequest $request, Account $account)
|
||||
{
|
||||
$data = $request->getAccountData();
|
||||
$repository->update($account, $data);
|
||||
$this->repository->update($account, $data);
|
||||
|
||||
$request->session()->flash('success', strval(trans('firefly.updated_account', ['name' => $account->name])));
|
||||
Preferences::mark();
|
||||
@@ -435,56 +411,55 @@ class AccountController extends Controller
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
||||
*/
|
||||
private function getPeriodOverview(Account $account): Collection
|
||||
private function getPeriodOverview(Account $account, ?Carbon $date): Collection
|
||||
{
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$start = $repository->oldestJournalDate($account);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = app('navigation')->startOfPeriod($start, $range);
|
||||
$end = app('navigation')->endOfX(new Carbon, $range, null);
|
||||
$entries = new Collection;
|
||||
$count = 0;
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = $this->repository->oldestJournalDate($account);
|
||||
$end = $date ?? new Carbon;
|
||||
if ($end < $start) {
|
||||
list($start, $end) = [$end, $start]; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
// properties for cache
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('account-show-period-entries');
|
||||
$cache->addProperty($account->id);
|
||||
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
Log::debug('Going to get period expenses and incomes.');
|
||||
while ($end >= $start && $count < 90) {
|
||||
$end = app('navigation')->startOfPeriod($end, $range);
|
||||
$currentEnd = app('navigation')->endOfPeriod($end, $range);
|
||||
$dates = app('navigation')->blockPeriods($start, $end, $range);
|
||||
$entries = new Collection;
|
||||
// loop dates
|
||||
foreach ($dates as $date) {
|
||||
|
||||
// try a collector for income:
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($end, $currentEnd)->setTypes([TransactionType::DEPOSIT])->withOpposingAccount();
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($date['start'], $date['end'])->setTypes([TransactionType::DEPOSIT])
|
||||
->withOpposingAccount();
|
||||
$earned = strval($collector->getJournals()->sum('transaction_amount'));
|
||||
|
||||
// try a collector for expenses:
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($end, $currentEnd)->setTypes([TransactionType::WITHDRAWAL])->withOpposingAccount();
|
||||
$spent = strval($collector->getJournals()->sum('transaction_amount'));
|
||||
$dateStr = $end->format('Y-m-d');
|
||||
$dateName = app('navigation')->periodShow($end, $range);
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($date['start'], $date['end'])->setTypes([TransactionType::WITHDRAWAL])
|
||||
->withOpposingAccount();
|
||||
$spent = strval($collector->getJournals()->sum('transaction_amount'));
|
||||
|
||||
$dateName = app('navigation')->periodShow($date['start'], $date['period']);
|
||||
$entries->push(
|
||||
[
|
||||
'string' => $dateStr,
|
||||
'name' => $dateName,
|
||||
'spent' => $spent,
|
||||
'earned' => $earned,
|
||||
'date' => clone $end,]
|
||||
'start' => $date['start']->format('Y-m-d'),
|
||||
'end' => $date['end']->format('Y-m-d'),
|
||||
]
|
||||
);
|
||||
$end = app('navigation')->subtractPeriod($end, $range, 1);
|
||||
++$count;
|
||||
}
|
||||
|
||||
$cache->store($entries);
|
||||
|
||||
return $entries;
|
||||
|
||||
@@ -125,7 +125,7 @@ class UserController extends Controller
|
||||
$list = ['twoFactorAuthEnabled', 'twoFactorAuthSecret'];
|
||||
$preferences = Preferences::getArrayForUser($user, $list);
|
||||
$user->isAdmin = $user->hasRole('owner');
|
||||
$is2faEnabled = true === $preferences['twoFactorAuthEnabled'];
|
||||
$is2faEnabled = 1 === $preferences['twoFactorAuthEnabled'];
|
||||
$has2faSecret = null !== $preferences['twoFactorAuthSecret'];
|
||||
$user->has2FA = ($is2faEnabled && $has2faSecret) ? true : false;
|
||||
$user->prefs = $preferences;
|
||||
|
||||
@@ -22,7 +22,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use File;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Requests\AttachmentFormRequest;
|
||||
use FireflyIII\Models\Attachment;
|
||||
@@ -40,6 +39,9 @@ use View;
|
||||
*/
|
||||
class AttachmentController extends Controller
|
||||
{
|
||||
/** @var AttachmentRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -52,6 +54,7 @@ class AttachmentController extends Controller
|
||||
function ($request, $next) {
|
||||
app('view')->share('mainTitleIcon', 'fa-paperclip');
|
||||
app('view')->share('title', trans('firefly.attachments'));
|
||||
$this->repository = app(AttachmentRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@@ -74,17 +77,16 @@ class AttachmentController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param AttachmentRepositoryInterface $repository
|
||||
* @param Attachment $attachment
|
||||
* @param Request $request
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function destroy(Request $request, AttachmentRepositoryInterface $repository, Attachment $attachment)
|
||||
public function destroy(Request $request, Attachment $attachment)
|
||||
{
|
||||
$name = $attachment->filename;
|
||||
|
||||
$repository->destroy($attachment);
|
||||
$this->repository->destroy($attachment);
|
||||
|
||||
$request->session()->flash('success', strval(trans('firefly.attachment_deleted', ['name' => $name])));
|
||||
Preferences::mark();
|
||||
@@ -93,17 +95,16 @@ class AttachmentController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AttachmentRepositoryInterface $repository
|
||||
* @param Attachment $attachment
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function download(AttachmentRepositoryInterface $repository, Attachment $attachment)
|
||||
public function download(Attachment $attachment)
|
||||
{
|
||||
if ($repository->exists($attachment)) {
|
||||
$content = $repository->getContent($attachment);
|
||||
if ($this->repository->exists($attachment)) {
|
||||
$content = $this->repository->getContent($attachment);
|
||||
$quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\'));
|
||||
|
||||
/** @var LaravelResponse $response */
|
||||
@@ -145,37 +146,15 @@ class AttachmentController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
public function preview(Attachment $attachment)
|
||||
{
|
||||
$image = 'images/page_green.png';
|
||||
|
||||
if ('application/pdf' === $attachment->mime) {
|
||||
$image = 'images/page_white_acrobat.png';
|
||||
}
|
||||
$file = public_path($image);
|
||||
$response = Response::make(File::get($file));
|
||||
$response->header('Content-Type', 'image/png');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AttachmentFormRequest $request
|
||||
* @param AttachmentRepositoryInterface $repository
|
||||
* @param Attachment $attachment
|
||||
* @param AttachmentFormRequest $request
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(AttachmentFormRequest $request, AttachmentRepositoryInterface $repository, Attachment $attachment)
|
||||
public function update(AttachmentFormRequest $request, Attachment $attachment)
|
||||
{
|
||||
$data = $request->getAttachmentData();
|
||||
$repository->update($attachment, $data);
|
||||
$this->repository->update($attachment, $data);
|
||||
|
||||
$request->session()->flash('success', strval(trans('firefly.attachment_updated', ['name' => $attachment->filename])));
|
||||
Preferences::mark();
|
||||
@@ -191,4 +170,25 @@ class AttachmentController extends Controller
|
||||
// redirect to previous URL.
|
||||
return redirect($this->getPreviousUri('attachments.edit.uri'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function view(Attachment $attachment)
|
||||
{
|
||||
if ($this->repository->exists($attachment)) {
|
||||
$content = $this->repository->getContent($attachment);
|
||||
|
||||
return Response::make(
|
||||
$content, 200, [
|
||||
'Content-Type' => $attachment->mime,
|
||||
'Content-Disposition' => 'inline; filename="' . $attachment->filename . '"',
|
||||
]
|
||||
);
|
||||
}
|
||||
throw new FireflyException('Could not find the indicated attachment. The file is no longer there.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ class ResetPasswordController extends Controller
|
||||
if (true === $singleUserMode && $userCount > 0) {
|
||||
$allowRegistration = false;
|
||||
}
|
||||
|
||||
return view('auth.passwords.reset')->with(
|
||||
['token' => $token, 'email' => $request->email, 'allowRegistration' => $allowRegistration]
|
||||
);
|
||||
|
||||
@@ -30,10 +30,14 @@ use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Note;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Transformers\BillTransformer;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use League\Fractal\Manager;
|
||||
use League\Fractal\Resource\Item;
|
||||
use League\Fractal\Serializer\DataArraySerializer;
|
||||
use Preferences;
|
||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
use URL;
|
||||
use View;
|
||||
|
||||
@@ -167,36 +171,26 @@ class BillController extends Controller
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function index(Request $request, BillRepositoryInterface $repository)
|
||||
public function index(BillRepositoryInterface $repository)
|
||||
{
|
||||
/** @var Carbon $start */
|
||||
$start = session('start');
|
||||
/** @var Carbon $end */
|
||||
$start = session('start');
|
||||
$end = session('end');
|
||||
$page = 0 === intval($request->get('page')) ? 1 : intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('listPageSize', 50)->data);
|
||||
$collection = $repository->getBills();
|
||||
$total = $collection->count();
|
||||
$collection = $collection->slice(($page - 1) * $pageSize, $pageSize);
|
||||
|
||||
$collection->each(
|
||||
function (Bill $bill) use ($repository, $start, $end) {
|
||||
// paid in this period?
|
||||
$bill->paidDates = $repository->getPaidDatesInRange($bill, $start, $end);
|
||||
$bill->payDates = $repository->getPayDatesInRange($bill, $start, $end);
|
||||
$lastPaidDate = $this->lastPaidDate($repository->getPaidDatesInRange($bill, $start, $end), $start);
|
||||
if ($bill->paidDates->count() >= $bill->payDates->count()) {
|
||||
// if all bills have been been paid, jump to next period.
|
||||
$lastPaidDate = $end;
|
||||
}
|
||||
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, $lastPaidDate);
|
||||
$paginator = $repository->getPaginator($pageSize);
|
||||
$parameters = new ParameterBag();
|
||||
$parameters->set('start', $start);
|
||||
$parameters->set('end', $end);
|
||||
$transformer = new BillTransformer($parameters);
|
||||
/** @var Collection $bills */
|
||||
$bills = $paginator->getCollection()->map(
|
||||
function (Bill $bill) use ($transformer) {
|
||||
return $transformer->transform($bill);
|
||||
}
|
||||
);
|
||||
// paginate bills
|
||||
$bills = new LengthAwarePaginator($collection, $total, $pageSize, $page);
|
||||
$bills->setPath(route('bills.index'));
|
||||
|
||||
return view('bills.index', compact('bills'));
|
||||
$paginator->setPath(route('bills.index'));
|
||||
|
||||
return view('bills.index', compact('bills', 'paginator'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -235,15 +229,24 @@ class BillController extends Controller
|
||||
*/
|
||||
public function show(Request $request, BillRepositoryInterface $repository, Bill $bill)
|
||||
{
|
||||
/** @var Carbon $date */
|
||||
$date = session('start');
|
||||
/** @var Carbon $end */
|
||||
$subTitle = $bill->name;
|
||||
$start = session('start');
|
||||
$end = session('end');
|
||||
$year = $date->year;
|
||||
$year = $start->year;
|
||||
$page = intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('listPageSize', 50)->data);
|
||||
$yearAverage = $repository->getYearAverage($bill, $date);
|
||||
$yearAverage = $repository->getYearAverage($bill, $start);
|
||||
$overallAverage = $repository->getOverallAverage($bill);
|
||||
$manager = new Manager();
|
||||
$manager->setSerializer(new DataArraySerializer());
|
||||
$manager->parseIncludes(['attachments']);
|
||||
|
||||
// Make a resource out of the data and
|
||||
$parameters = new ParameterBag();
|
||||
$parameters->set('start', $start);
|
||||
$parameters->set('end', $end);
|
||||
$resource = new Item($bill, new BillTransformer($parameters), 'bill');
|
||||
$object = $manager->createData($resource)->toArray();
|
||||
|
||||
// use collector:
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
@@ -253,18 +256,8 @@ class BillController extends Controller
|
||||
$transactions = $collector->getPaginatedJournals();
|
||||
$transactions->setPath(route('bills.show', [$bill->id]));
|
||||
|
||||
$bill->paidDates = $repository->getPaidDatesInRange($bill, $date, $end);
|
||||
$bill->payDates = $repository->getPayDatesInRange($bill, $date, $end);
|
||||
$lastPaidDate = $this->lastPaidDate($repository->getPaidDatesInRange($bill, $date, $end), $date);
|
||||
if ($bill->paidDates->count() >= $bill->payDates->count()) {
|
||||
// if all bills have been been paid, jump to next period.
|
||||
$lastPaidDate = $end;
|
||||
}
|
||||
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, $lastPaidDate);
|
||||
$hideBill = true;
|
||||
$subTitle = e($bill->name);
|
||||
|
||||
return view('bills.show', compact('transactions', 'yearAverage', 'overallAverage', 'year', 'hideBill', 'bill', 'subTitle'));
|
||||
return view('bills.show', compact('transactions', 'yearAverage', 'overallAverage', 'year', 'object', 'bill', 'subTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -335,28 +328,4 @@ class BillController extends Controller
|
||||
|
||||
return redirect($this->getPreviousUri('bills.edit.uri'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest date in the set, or start when set is empty.
|
||||
*
|
||||
* @param Collection $dates
|
||||
* @param Carbon $default
|
||||
*
|
||||
* @return Carbon
|
||||
*/
|
||||
private function lastPaidDate(Collection $dates, Carbon $default): Carbon
|
||||
{
|
||||
if (0 === $dates->count()) {
|
||||
return $default; // @codeCoverageIgnore
|
||||
}
|
||||
$latest = $dates->first();
|
||||
/** @var Carbon $date */
|
||||
foreach ($dates as $date) {
|
||||
if ($date->gte($latest)) {
|
||||
$latest = $date;
|
||||
}
|
||||
}
|
||||
|
||||
return $latest;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,9 +82,9 @@ class BudgetController extends Controller
|
||||
*/
|
||||
public function amount(Request $request, BudgetRepositoryInterface $repository, Budget $budget)
|
||||
{
|
||||
$amount = strval($request->get('amount'));
|
||||
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
|
||||
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
|
||||
$amount = strval($request->get('amount'));
|
||||
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
|
||||
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
|
||||
$budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount);
|
||||
if (0 === bccomp($amount, '0')) {
|
||||
$budgetLimit = null;
|
||||
@@ -198,13 +198,13 @@ class BudgetController extends Controller
|
||||
$prev->subDay();
|
||||
$prev = app('navigation')->startOfPeriod($prev, $range);
|
||||
$this->repository->cleanupBudgets();
|
||||
$budgets = $this->repository->getActiveBudgets();
|
||||
$total = $budgets->count();
|
||||
$budgets = $budgets->slice(($page - 1) * $pageSize, $pageSize);
|
||||
$allBudgets = $this->repository->getActiveBudgets();
|
||||
$total = $allBudgets->count();
|
||||
$budgets = $allBudgets->slice(($page - 1) * $pageSize, $pageSize);
|
||||
$inactive = $this->repository->getInactiveBudgets();
|
||||
$periodStart = $start->formatLocalized($this->monthAndDayFormat);
|
||||
$periodEnd = $end->formatLocalized($this->monthAndDayFormat);
|
||||
$budgetInformation = $this->repository->collectBudgetInformation($budgets, $start, $end);
|
||||
$budgetInformation = $this->repository->collectBudgetInformation($allBudgets, $start, $end);
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$available = $this->repository->getAvailableBudget($defaultCurrency, $start, $end);
|
||||
$spent = array_sum(array_column($budgetInformation, 'spent'));
|
||||
@@ -252,7 +252,7 @@ class BudgetController extends Controller
|
||||
'currentMonth',
|
||||
'next',
|
||||
'nextText',
|
||||
'prev',
|
||||
'prev', 'allBudgets',
|
||||
'prevText',
|
||||
'periodStart',
|
||||
'periodEnd',
|
||||
@@ -420,11 +420,12 @@ class BudgetController extends Controller
|
||||
$end = Carbon::createFromFormat('Y-m-d', $request->string('end'));
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$amount = $request->get('amount');
|
||||
|
||||
$page = $request->integer('page') === 0 ? 1 : $request->integer('page');
|
||||
$this->repository->cleanupBudgets();
|
||||
$this->repository->setAvailableBudget($defaultCurrency, $start, $end, $amount);
|
||||
Preferences::mark();
|
||||
|
||||
return redirect(route('budgets.index', [$start->format('Y-m-d')]));
|
||||
return redirect(route('budgets.index', [$start->format('Y-m-d')]) . '?page=' . $page);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -503,7 +504,7 @@ class BudgetController extends Controller
|
||||
{
|
||||
$data = $request->getBudgetData();
|
||||
$budget = $this->repository->store($data);
|
||||
|
||||
$this->repository->cleanupBudgets();
|
||||
$request->session()->flash('success', strval(trans('firefly.stored_new_budget', ['name' => $budget->name])));
|
||||
Preferences::mark();
|
||||
|
||||
@@ -530,6 +531,7 @@ class BudgetController extends Controller
|
||||
$this->repository->update($budget, $data);
|
||||
|
||||
$request->session()->flash('success', strval(trans('firefly.updated_budget', ['name' => $budget->name])));
|
||||
$this->repository->cleanupBudgets();
|
||||
Preferences::mark();
|
||||
|
||||
if (1 === intval($request->get('return_to_edit'))) {
|
||||
@@ -549,13 +551,14 @@ class BudgetController extends Controller
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function updateIncome(Carbon $start, Carbon $end)
|
||||
public function updateIncome(Request $request, Carbon $start, Carbon $end)
|
||||
{
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$available = $this->repository->getAvailableBudget($defaultCurrency, $start, $end);
|
||||
$available = round($available, $defaultCurrency->decimal_places);
|
||||
$page = intval($request->get('page'));
|
||||
|
||||
return view('budgets.income', compact('available', 'start', 'end'));
|
||||
return view('budgets.income', compact('available', 'start', 'end', 'page'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -611,21 +614,19 @@ class BudgetController extends Controller
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
Log::debug('Going to get period expenses and incomes.');
|
||||
while ($end >= $start) {
|
||||
$end = app('navigation')->startOfPeriod($end, $range);
|
||||
$currentEnd = app('navigation')->endOfPeriod($end, $range);
|
||||
$dates = app('navigation')->blockPeriods($start, $end, $range);
|
||||
foreach ($dates as $date) {
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutBudget()->withOpposingAccount()->setTypes([TransactionType::WITHDRAWAL]);
|
||||
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutBudget()->withOpposingAccount()->setTypes(
|
||||
[TransactionType::WITHDRAWAL]
|
||||
);
|
||||
$set = $collector->getJournals();
|
||||
$sum = strval($set->sum('transaction_amount') ?? '0');
|
||||
$journals = $set->count();
|
||||
$dateStr = $end->format('Y-m-d');
|
||||
$dateName = app('navigation')->periodShow($end, $range);
|
||||
$entries->push(['string' => $dateStr, 'name' => $dateName, 'count' => $journals, 'sum' => $sum, 'date' => clone $end]);
|
||||
$end = app('navigation')->subtractPeriod($end, $range, 1);
|
||||
$dateStr = $date['end']->format('Y-m-d');
|
||||
$dateName = app('navigation')->periodShow($date['end'], $date['period']);
|
||||
$entries->push(['string' => $dateStr, 'name' => $dateName, 'count' => $journals, 'sum' => $sum, 'date' => clone $date['end']]);
|
||||
}
|
||||
$cache->store($entries);
|
||||
|
||||
|
||||
@@ -46,6 +46,13 @@ use View;
|
||||
*/
|
||||
class CategoryController extends Controller
|
||||
{
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $accountRepos;
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $journalRepos;
|
||||
/** @var CategoryRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -57,6 +64,9 @@ class CategoryController extends Controller
|
||||
function ($request, $next) {
|
||||
app('view')->share('title', trans('firefly.categories'));
|
||||
app('view')->share('mainTitleIcon', 'fa-bar-chart');
|
||||
$this->journalRepos = app(JournalRepositoryInterface::class);
|
||||
$this->repository = app(CategoryRepositoryInterface::class);
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@@ -95,16 +105,15 @@ class CategoryController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param Category $category
|
||||
* @param Request $request
|
||||
* @param Category $category
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function destroy(Request $request, CategoryRepositoryInterface $repository, Category $category)
|
||||
public function destroy(Request $request, Category $category)
|
||||
{
|
||||
$name = $category->name;
|
||||
$repository->destroy($category);
|
||||
$this->repository->destroy($category);
|
||||
|
||||
$request->session()->flash('success', strval(trans('firefly.deleted_category', ['name' => $name])));
|
||||
Preferences::mark();
|
||||
@@ -132,21 +141,21 @@ class CategoryController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param Request $request
|
||||
*
|
||||
* @return View
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function index(Request $request, CategoryRepositoryInterface $repository)
|
||||
public function index(Request $request)
|
||||
{
|
||||
$page = 0 === intval($request->get('page')) ? 1 : intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('listPageSize', 50)->data);
|
||||
$collection = $repository->getCategories();
|
||||
$collection = $this->repository->getCategories();
|
||||
$total = $collection->count();
|
||||
$collection = $collection->slice(($page - 1) * $pageSize, $pageSize);
|
||||
|
||||
$collection->each(
|
||||
function (Category $category) use ($repository) {
|
||||
$category->lastActivity = $repository->lastUseDate($category, new Collection);
|
||||
function (Category $category) {
|
||||
$category->lastActivity = $this->repository->lastUseDate($category, new Collection);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -158,13 +167,12 @@ class CategoryController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param JournalRepositoryInterface $repository
|
||||
* @param string $moment
|
||||
* @param Request $request
|
||||
* @param string $moment
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function noCategory(Request $request, JournalRepositoryInterface $repository, string $moment = '')
|
||||
public function noCategory(Request $request, string $moment = '')
|
||||
{
|
||||
// default values:
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
@@ -177,27 +185,27 @@ class CategoryController extends Controller
|
||||
// prep for "all" view.
|
||||
if ('all' === $moment) {
|
||||
$subTitle = trans('firefly.all_journals_without_category');
|
||||
$first = $repository->first();
|
||||
$first = $this->journalRepos->first();
|
||||
$start = $first->date ?? new Carbon;
|
||||
$end = new Carbon;
|
||||
}
|
||||
|
||||
// prep for "specific date" view.
|
||||
if (strlen($moment) > 0 && 'all' !== $moment) {
|
||||
$start = new Carbon($moment);
|
||||
$start = app('navigation')->startOfPeriod(new Carbon($moment), $range);
|
||||
$end = app('navigation')->endOfPeriod($start, $range);
|
||||
$subTitle = trans(
|
||||
'firefly.without_category_between',
|
||||
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
$periods = $this->getNoCategoryPeriodOverview();
|
||||
$periods = $this->getNoCategoryPeriodOverview($start);
|
||||
}
|
||||
|
||||
// prep for current period
|
||||
if (0 === strlen($moment)) {
|
||||
$start = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range));
|
||||
$end = clone session('end', app('navigation')->endOfPeriod(new Carbon, $range));
|
||||
$periods = $this->getNoCategoryPeriodOverview();
|
||||
$periods = $this->getNoCategoryPeriodOverview($start);
|
||||
$subTitle = trans(
|
||||
'firefly.without_category_between',
|
||||
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
@@ -248,14 +256,14 @@ class CategoryController extends Controller
|
||||
|
||||
// prep for "specific date" view.
|
||||
if (strlen($moment) > 0 && 'all' !== $moment) {
|
||||
$start = new Carbon($moment);
|
||||
$start = app('navigation')->startOfPeriod(new Carbon($moment), $range);
|
||||
$end = app('navigation')->endOfPeriod($start, $range);
|
||||
$subTitle = trans(
|
||||
'firefly.journals_in_period_for_category',
|
||||
['name' => $category->name,
|
||||
'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat),]
|
||||
);
|
||||
$periods = $this->getPeriodOverview($category);
|
||||
$periods = $this->getPeriodOverview($category, $start);
|
||||
$path = route('categories.show', [$category->id, $moment]);
|
||||
}
|
||||
|
||||
@@ -265,7 +273,7 @@ class CategoryController extends Controller
|
||||
$start = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range));
|
||||
/** @var Carbon $end */
|
||||
$end = clone session('end', app('navigation')->endOfPeriod(new Carbon, $range));
|
||||
$periods = $this->getPeriodOverview($category);
|
||||
$periods = $this->getPeriodOverview($category, $start);
|
||||
$subTitle = trans(
|
||||
'firefly.journals_in_period_for_category',
|
||||
['name' => $category->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
|
||||
@@ -336,38 +344,36 @@ class CategoryController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $theDate
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function getNoCategoryPeriodOverview(): Collection
|
||||
private function getNoCategoryPeriodOverview(Carbon $theDate): Collection
|
||||
{
|
||||
$repository = app(JournalRepositoryInterface::class);
|
||||
$first = $repository->first();
|
||||
$start = $first->date ?? new Carbon;
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = app('navigation')->startOfPeriod($start, $range);
|
||||
$end = app('navigation')->endOfX(new Carbon, $range, null);
|
||||
$entries = new Collection;
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$first = $this->journalRepos->first();
|
||||
$start = $first->date ?? new Carbon;
|
||||
$end = $theDate ?? new Carbon;
|
||||
|
||||
// properties for cache
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('no-budget-period-entries');
|
||||
$cache->addProperty('no-category-period-entries');
|
||||
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
Log::debug(sprintf('Going to get period expenses and incomes between %s and %s.', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||
while ($end >= $start) {
|
||||
Log::debug('Loop!');
|
||||
$end = app('navigation')->startOfPeriod($end, $range);
|
||||
$currentEnd = app('navigation')->endOfPeriod($end, $range);
|
||||
$dates = app('navigation')->blockPeriods($start, $end, $range);
|
||||
$entries = new Collection;
|
||||
|
||||
foreach ($dates as $date) {
|
||||
|
||||
// count journals without category in this period:
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutCategory()
|
||||
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutCategory()
|
||||
->withOpposingAccount()->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]);
|
||||
$collector->removeFilter(InternalTransferFilter::class);
|
||||
$count = $collector->getJournals()->count();
|
||||
@@ -375,7 +381,7 @@ class CategoryController extends Controller
|
||||
// amount transferred
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutCategory()
|
||||
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutCategory()
|
||||
->withOpposingAccount()->setTypes([TransactionType::TRANSFER]);
|
||||
$collector->removeFilter(InternalTransferFilter::class);
|
||||
$transferred = Steam::positive($collector->getJournals()->sum('transaction_amount'));
|
||||
@@ -383,17 +389,20 @@ class CategoryController extends Controller
|
||||
// amount spent
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutCategory()->withOpposingAccount()->setTypes([TransactionType::WITHDRAWAL]);
|
||||
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutCategory()->withOpposingAccount()->setTypes(
|
||||
[TransactionType::WITHDRAWAL]
|
||||
);
|
||||
$spent = $collector->getJournals()->sum('transaction_amount');
|
||||
|
||||
// amount earned
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutCategory()->withOpposingAccount()->setTypes([TransactionType::DEPOSIT]);
|
||||
$earned = $collector->getJournals()->sum('transaction_amount');
|
||||
|
||||
$dateStr = $end->format('Y-m-d');
|
||||
$dateName = app('navigation')->periodShow($end, $range);
|
||||
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutCategory()->withOpposingAccount()->setTypes(
|
||||
[TransactionType::DEPOSIT]
|
||||
);
|
||||
$earned = $collector->getJournals()->sum('transaction_amount');
|
||||
$dateStr = $date['end']->format('Y-m-d');
|
||||
$dateName = app('navigation')->periodShow($date['end'], $date['period']);
|
||||
$entries->push(
|
||||
[
|
||||
'string' => $dateStr,
|
||||
@@ -402,10 +411,9 @@ class CategoryController extends Controller
|
||||
'spent' => $spent,
|
||||
'earned' => $earned,
|
||||
'transferred' => $transferred,
|
||||
'date' => clone $end,
|
||||
'date' => clone $date['end'],
|
||||
]
|
||||
);
|
||||
$end = app('navigation')->subtractPeriod($end, $range, 1);
|
||||
}
|
||||
Log::debug('End of loops');
|
||||
$cache->store($entries);
|
||||
@@ -418,45 +426,39 @@ class CategoryController extends Controller
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function getPeriodOverview(Category $category): Collection
|
||||
private function getPeriodOverview(Category $category, Carbon $date): Collection
|
||||
{
|
||||
/** @var CategoryRepositoryInterface $repository */
|
||||
$repository = app(CategoryRepositoryInterface::class);
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
$first = $repository->firstUseDate($category);
|
||||
if (null === $first) {
|
||||
$first = new Carbon; // @codeCoverageIgnore
|
||||
}
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$first = app('navigation')->startOfPeriod($first, $range);
|
||||
$end = app('navigation')->endOfX(new Carbon, $range, null);
|
||||
$entries = new Collection;
|
||||
$count = 0;
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$first = $this->journalRepos->first();
|
||||
$start = $first->date ?? new Carbon;
|
||||
$end = $date ?? new Carbon;
|
||||
$accounts = $this->accountRepos->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
|
||||
// properties for entries with their amounts.
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($first);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($range);
|
||||
$cache->addProperty('categories.entries');
|
||||
$cache->addProperty($category->id);
|
||||
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
while ($end >= $first && $count < 90) {
|
||||
$end = app('navigation')->startOfPeriod($end, $range);
|
||||
$currentEnd = app('navigation')->endOfPeriod($end, $range);
|
||||
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $end, $currentEnd);
|
||||
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $end, $currentEnd);
|
||||
$dateStr = $end->format('Y-m-d');
|
||||
$dateName = app('navigation')->periodShow($end, $range);
|
||||
|
||||
$dates = app('navigation')->blockPeriods($start, $end, $range);
|
||||
$entries = new Collection;
|
||||
|
||||
foreach ($dates as $date) {
|
||||
$spent = $this->repository->spentInPeriod(new Collection([$category]), $accounts, $date['start'], $date['end']);
|
||||
$earned = $this->repository->earnedInPeriod(new Collection([$category]), $accounts, $date['start'], $date['end']);
|
||||
$dateStr = $date['end']->format('Y-m-d');
|
||||
$dateName = app('navigation')->periodShow($date['end'], $date['period']);
|
||||
|
||||
// amount transferred
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->setCategory($category)
|
||||
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->setCategory($category)
|
||||
->withOpposingAccount()->setTypes([TransactionType::TRANSFER]);
|
||||
$collector->removeFilter(InternalTransferFilter::class);
|
||||
$transferred = Steam::positive($collector->getJournals()->sum('transaction_amount'));
|
||||
@@ -469,11 +471,9 @@ class CategoryController extends Controller
|
||||
'earned' => $earned,
|
||||
'sum' => bcadd($earned, $spent),
|
||||
'transferred' => $transferred,
|
||||
'date' => clone $end,
|
||||
'date' => clone $date['end'],
|
||||
]
|
||||
);
|
||||
$end = app('navigation')->subtractPeriod($end, $range, 1);
|
||||
++$count;
|
||||
}
|
||||
$cache->store($entries);
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use Steam;
|
||||
|
||||
/** checked
|
||||
* Class AccountController.
|
||||
@@ -58,44 +57,6 @@ class AccountController extends Controller
|
||||
$this->generator = app(GeneratorInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function all(Account $account)
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('chart.account.all');
|
||||
$cache->addProperty($account->id);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$start = $repository->oldestJournalDate($account);
|
||||
$end = new Carbon;
|
||||
$format = (string)trans('config.month_and_day');
|
||||
$range = Steam::balanceInRange($account, $start, $end);
|
||||
$current = clone $start;
|
||||
$previous = array_values($range)[0];
|
||||
$chartData = [];
|
||||
|
||||
while ($end >= $current) {
|
||||
$theDate = $current->format('Y-m-d');
|
||||
$balance = $range[$theDate] ?? $previous;
|
||||
$label = $current->formatLocalized($format);
|
||||
$chartData[$label] = $balance;
|
||||
$previous = $balance;
|
||||
$current->addDay();
|
||||
}
|
||||
|
||||
$data = $this->generator->singleSet($account->name, $chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the balances for all the user's expense accounts.
|
||||
@@ -118,8 +79,8 @@ class AccountController extends Controller
|
||||
$start->subDay();
|
||||
|
||||
$accounts = $repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY]);
|
||||
$startBalances = Steam::balancesByAccounts($accounts, $start);
|
||||
$endBalances = Steam::balancesByAccounts($accounts, $end);
|
||||
$startBalances = app('steam')->balancesByAccounts($accounts, $start);
|
||||
$endBalances = app('steam')->balancesByAccounts($accounts, $end);
|
||||
$chartData = [];
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
@@ -341,34 +302,57 @@ class AccountController extends Controller
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function period(Account $account, Carbon $start)
|
||||
public function period(Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$end = app('navigation')->endOfPeriod($start, $range);
|
||||
$cache = new CacheProperties();
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('chart.account.period');
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('chart.account.period');
|
||||
$cache->addProperty($account->id);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$format = (string)trans('config.month_and_day');
|
||||
$range = Steam::balanceInRange($account, $start, $end);
|
||||
$current = clone $start;
|
||||
$previous = array_values($range)[0];
|
||||
$chartData = [];
|
||||
|
||||
while ($end >= $current) {
|
||||
$theDate = $current->format('Y-m-d');
|
||||
$balance = $range[$theDate] ?? $previous;
|
||||
$label = $current->formatLocalized($format);
|
||||
$chartData[$label] = $balance;
|
||||
$previous = $balance;
|
||||
$current->addDay();
|
||||
// depending on diff, do something with range of chart.
|
||||
$step = '1D';
|
||||
$months = $start->diffInMonths($end);
|
||||
if ($months > 3) {
|
||||
$step = '1W'; // @codeCoverageIgnore
|
||||
}
|
||||
if ($months > 24) {
|
||||
$step = '1M'; // @codeCoverageIgnore
|
||||
}
|
||||
if ($months > 100) {
|
||||
$step = '1Y'; // @codeCoverageIgnore
|
||||
}
|
||||
$chartData = [];
|
||||
$current = clone $start;
|
||||
switch ($step) {
|
||||
case '1D':
|
||||
$format = (string)trans('config.month_and_day');
|
||||
$range = app('steam')->balanceInRange($account, $start, $end);
|
||||
$previous = array_values($range)[0];
|
||||
while ($end >= $current) {
|
||||
$theDate = $current->format('Y-m-d');
|
||||
$balance = $range[$theDate] ?? $previous;
|
||||
$label = $current->formatLocalized($format);
|
||||
$chartData[$label] = floatval($balance);
|
||||
$previous = $balance;
|
||||
$current->addDay();
|
||||
}
|
||||
break;
|
||||
// @codeCoverageIgnoreStart
|
||||
case '1W':
|
||||
case '1M':
|
||||
case '1Y':
|
||||
while ($end >= $current) {
|
||||
$balance = floatval(app('steam')->balance($account, $current));
|
||||
$label = app('navigation')->periodShow($current, $step);
|
||||
$chartData[$label] = $balance;
|
||||
$current = app('navigation')->addPeriod($current, $step, 1);
|
||||
}
|
||||
break;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$data = $this->generator->singleSet($account->name, $chartData);
|
||||
$cache->store($data);
|
||||
|
||||
@@ -411,8 +395,8 @@ class AccountController extends Controller
|
||||
$accounts = $repository->getAccountsByType([AccountType::REVENUE]);
|
||||
|
||||
$start->subDay();
|
||||
$startBalances = Steam::balancesByAccounts($accounts, $start);
|
||||
$endBalances = Steam::balancesByAccounts($accounts, $end);
|
||||
$startBalances = app('steam')->balancesByAccounts($accounts, $start);
|
||||
$endBalances = app('steam')->balancesByAccounts($accounts, $end);
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$id = $account->id;
|
||||
@@ -432,49 +416,6 @@ class AccountController extends Controller
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows an account's balance for a single month.
|
||||
*
|
||||
* @param Account $account
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function single(Account $account)
|
||||
{
|
||||
$start = clone session('start', Carbon::now()->startOfMonth());
|
||||
$end = clone session('end', Carbon::now()->endOfMonth());
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('chart.account.single');
|
||||
$cache->addProperty($account->id);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$format = (string)trans('config.month_and_day');
|
||||
$range = Steam::balanceInRange($account, $start, $end);
|
||||
$current = clone $start;
|
||||
$previous = array_values($range)[0];
|
||||
$chartData = [];
|
||||
|
||||
while ($end >= $current) {
|
||||
$theDate = $current->format('Y-m-d');
|
||||
$balance = $range[$theDate] ?? $previous;
|
||||
$label = $current->formatLocalized($format);
|
||||
$chartData[$label] = $balance;
|
||||
$previous = $balance;
|
||||
$current->addDay();
|
||||
}
|
||||
|
||||
$data = $this->generator->singleSet($account->name, $chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
@@ -500,14 +441,14 @@ class AccountController extends Controller
|
||||
|
||||
$chartData = [];
|
||||
foreach ($accounts as $account) {
|
||||
$currency = $repository->find(intval($account->getMeta('currency_id')));
|
||||
$currency = $repository->findNull(intval($account->getMeta('currency_id')));
|
||||
$currentSet = [
|
||||
'label' => $account->name,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'entries' => [],
|
||||
];
|
||||
$currentStart = clone $start;
|
||||
$range = Steam::balanceInRange($account, $start, clone $end);
|
||||
$range = app('steam')->balanceInRange($account, $start, clone $end);
|
||||
$previous = array_values($range)[0];
|
||||
while ($currentStart <= $end) {
|
||||
$format = $currentStart->format('Y-m-d');
|
||||
|
||||
@@ -37,7 +37,6 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use Steam;
|
||||
|
||||
@@ -78,37 +77,48 @@ class BudgetController extends Controller
|
||||
*/
|
||||
public function budget(Budget $budget)
|
||||
{
|
||||
$first = $this->repository->firstUseDate($budget);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$currentStart = app('navigation')->startOfPeriod($first, $range);
|
||||
$last = session('end', new Carbon);
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($first);
|
||||
$cache->addProperty($last);
|
||||
$start = $this->repository->firstUseDate($budget);
|
||||
$end = session('end', new Carbon);
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('chart.budget.budget');
|
||||
$cache->addProperty($budget->id);
|
||||
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$final = clone $last;
|
||||
$final->addYears(2);
|
||||
// depending on diff, do something with range of chart.
|
||||
$step = '1D';
|
||||
$months = $start->diffInMonths($end);
|
||||
if ($months > 3) {
|
||||
$step = '1W';
|
||||
}
|
||||
if ($months > 24) {
|
||||
$step = '1M';
|
||||
}
|
||||
if ($months > 60) {
|
||||
$step = '1Y'; // @codeCoverageIgnore
|
||||
}
|
||||
$budgetCollection = new Collection([$budget]);
|
||||
$last = app('navigation')->endOfX($last, $range, $final); // not to overshoot.
|
||||
$entries = [];
|
||||
while ($currentStart < $last) {
|
||||
// periodspecific dates:
|
||||
$currentEnd = app('navigation')->endOfPeriod($currentStart, $range);
|
||||
// sub another day because reasons.
|
||||
$currentEnd->subDay();
|
||||
$spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd);
|
||||
$format = app('navigation')->periodShow($currentStart, $range);
|
||||
$entries[$format] = bcmul($spent, '-1');
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDays(2);
|
||||
$chartData = [];
|
||||
$current = clone $start;
|
||||
$current = app('navigation')->startOfPeriod($current, $step);
|
||||
|
||||
while ($end >= $current) {
|
||||
$currentEnd = app('navigation')->endOfPeriod($current, $step);
|
||||
if ($step === '1Y') {
|
||||
$currentEnd->subDay(); // @codeCoverageIgnore
|
||||
}
|
||||
$spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $current, $currentEnd);
|
||||
$label = app('navigation')->periodShow($current, $step);
|
||||
$chartData[$label] = floatval(bcmul($spent, '-1'));
|
||||
$current = clone $currentEnd;
|
||||
$current->addDay();
|
||||
}
|
||||
|
||||
$data = $this->generator->singleSet(strval(trans('firefly.spent')), $entries);
|
||||
$data = $this->generator->singleSet(strval(trans('firefly.spent')), $chartData);
|
||||
|
||||
$cache->store($data);
|
||||
|
||||
@@ -140,6 +150,7 @@ class BudgetController extends Controller
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('chart.budget.budget.limit');
|
||||
$cache->addProperty($budgetLimit->id);
|
||||
$cache->addProperty($budget->id);
|
||||
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
|
||||
@@ -197,7 +197,7 @@ class ExpenseReportController extends Controller
|
||||
$collection->push($expenseAccount);
|
||||
|
||||
$revenue = $this->accountRepository->findByName($expenseAccount->name, [AccountType::REVENUE]);
|
||||
if (!is_null($revenue->id)) {
|
||||
if (!is_null($revenue)) {
|
||||
$collection->push($revenue);
|
||||
}
|
||||
$combined[$expenseAccount->name] = $collection;
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use Exception;
|
||||
use FireflyIII\Http\Middleware\IsDemoUser;
|
||||
use Illuminate\Http\Request;
|
||||
use Log;
|
||||
use Monolog\Handler\RotatingFileHandler;
|
||||
@@ -34,6 +36,15 @@ use Monolog\Handler\RotatingFileHandler;
|
||||
*/
|
||||
class DebugController extends Controller
|
||||
{
|
||||
/**
|
||||
* HomeController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(IsDemoUser::class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
@@ -74,7 +85,11 @@ class DebugController extends Controller
|
||||
if ($handler instanceof RotatingFileHandler) {
|
||||
$logFile = $handler->getUrl();
|
||||
if (null !== $logFile) {
|
||||
$logContent = file_get_contents($logFile);
|
||||
try {
|
||||
$logContent = file_get_contents($logFile);
|
||||
} catch (Exception $e) {
|
||||
// don't care
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,20 +200,25 @@ class HomeController extends Controller
|
||||
'login', 'logout', 'password.reset', 'profile.confirm-email-change', 'profile.undo-email-change',
|
||||
'register', 'report.options', 'routes', 'rule-groups.down', 'rule-groups.up', 'rules.up', 'rules.down',
|
||||
'rules.select', 'search.search', 'test-flash', 'transactions.link.delete', 'transactions.link.switch',
|
||||
'two-factor.lost', 'report.options',
|
||||
'two-factor.lost', 'reports.options', 'debug', 'import.create-job', 'import.download', 'import.start', 'import.status.json',
|
||||
'preferences.delete-code', 'rules.test-triggers', 'piggy-banks.remove-money', 'piggy-banks.add-money',
|
||||
'accounts.reconcile.transactions', 'accounts.reconcile.overview', 'export.download',
|
||||
'transactions.clone', 'two-factor.index',
|
||||
];
|
||||
$return = ' ';
|
||||
/** @var Route $route */
|
||||
foreach ($set as $route) {
|
||||
$name = $route->getName();
|
||||
if (null !== $name && in_array('GET', $route->methods()) && strlen($name) > 0) {
|
||||
|
||||
$found = false;
|
||||
foreach ($ignore as $string) {
|
||||
if (false !== strpos($name, $string)) {
|
||||
if (!(false === stripos($name, $string))) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$found) {
|
||||
if ($found === false) {
|
||||
$return .= 'touch ' . $route->getName() . '.md;';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class ConfigurationController extends Controller
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
$this->middleware(IsDemoUser::class)->except(['index']);
|
||||
$this->middleware(IsDemoUser::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,7 @@ use Log;
|
||||
use Response;
|
||||
use View;
|
||||
|
||||
|
||||
/**
|
||||
* Class FileController.
|
||||
*/
|
||||
@@ -57,8 +58,7 @@ class IndexController extends Controller
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
|
||||
$this->middleware(IsDemoUser::class)->except(['create', 'index']);
|
||||
$this->middleware(IsDemoUser::class)->except(['index']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,6 +100,7 @@ class IndexController extends Controller
|
||||
$config['initial-config-complete'] = false;
|
||||
$config['has-file-upload'] = false;
|
||||
$config['delimiter'] = "\t" === $config['delimiter'] ? 'tab' : $config['delimiter'];
|
||||
unset($config['stage']);
|
||||
|
||||
$result = json_encode($config, JSON_PRETTY_PRINT);
|
||||
$name = sprintf('"%s"', addcslashes('import-configuration-' . date('Y-m-d') . '.json', '"\\'));
|
||||
|
||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Http\Controllers\Import;
|
||||
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Middleware\IsDemoUser;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use Response;
|
||||
@@ -47,6 +48,7 @@ class StatusController extends Controller
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
$this->middleware(IsDemoUser::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,20 +95,29 @@ class StatusController extends Controller
|
||||
$result['percentage'] = round(($job->extended_status['done'] / $job->extended_status['steps']) * 100, 0);
|
||||
$result['show_percentage'] = true;
|
||||
}
|
||||
|
||||
if ('finished' === $job->status) {
|
||||
$tagId = $job->extended_status['tag'];
|
||||
/** @var TagRepositoryInterface $repository */
|
||||
$repository = app(TagRepositoryInterface::class);
|
||||
$tag = $repository->find($tagId);
|
||||
$result['finished'] = true;
|
||||
$result['finishedText'] = trans('import.status_finished_job', ['link' => route('tags.show', [$tag->id, 'all']), 'tag' => $tag->tag]);
|
||||
$result['finished'] = true;
|
||||
$tagId = intval($job->extended_status['tag']);
|
||||
if ($tagId !== 0) {
|
||||
/** @var TagRepositoryInterface $repository */
|
||||
$repository = app(TagRepositoryInterface::class);
|
||||
$tag = $repository->find($tagId);
|
||||
$count = $tag->transactionJournals()->count();
|
||||
$result['finishedText'] = trans(
|
||||
'import.status_finished_job', ['count' => $count, 'link' => route('tags.show', [$tag->id, 'all']), 'tag' => $tag->tag]
|
||||
);
|
||||
}
|
||||
|
||||
if ($tagId === 0) {
|
||||
$result['finishedText'] = trans('import.status_finished_no_tag'); // @codeCoverageIgnore
|
||||
}
|
||||
}
|
||||
|
||||
if ('running' === $job->status) {
|
||||
$result['started'] = true;
|
||||
$result['running'] = true;
|
||||
}
|
||||
$result['percentage'] = $result['percentage'] > 100 ? 100 : $result['percentage'];
|
||||
|
||||
return Response::json($result);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class JavascriptController extends Controller
|
||||
{
|
||||
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
$preference = Preferences::get('currencyPreference', config('firefly.default_currency', 'EUR'));
|
||||
$default = $currencyRepository->findByCode($preference->data);
|
||||
$default = $currencyRepository->findByCodeNull($preference->data);
|
||||
|
||||
$data = ['accounts' => []];
|
||||
|
||||
@@ -95,13 +95,13 @@ class JavascriptController extends Controller
|
||||
*/
|
||||
public function variables(Request $request, AccountRepositoryInterface $repository, CurrencyRepositoryInterface $currencyRepository)
|
||||
{
|
||||
$account = $repository->find(intval($request->get('account')));
|
||||
$account = $repository->findNull(intval($request->get('account')));
|
||||
$currencyId = 0;
|
||||
if (null !== $account) {
|
||||
$currencyId = intval($account->getMeta('currency_id'));
|
||||
}
|
||||
/** @var TransactionCurrency $currency */
|
||||
$currency = $currencyRepository->find($currencyId);
|
||||
$currency = $currencyRepository->findNull($currencyId);
|
||||
if (0 === $currencyId) {
|
||||
$currency = app('amount')->getDefaultCurrency();
|
||||
}
|
||||
|
||||
@@ -25,11 +25,13 @@ namespace FireflyIII\Http\Controllers\Json;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
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\Support\CacheProperties;
|
||||
use Response;
|
||||
|
||||
@@ -170,7 +172,7 @@ class BoxController extends Controller
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function netWorth(AccountRepositoryInterface $repository)
|
||||
public function netWorth(AccountRepositoryInterface $repository, CurrencyRepositoryInterface $currencyRepos)
|
||||
{
|
||||
$date = new Carbon(date('Y-m-d')); // needed so its per day.
|
||||
/** @var Carbon $start */
|
||||
@@ -193,16 +195,32 @@ class BoxController extends Controller
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
$netWorth = [];
|
||||
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
$currency = app('amount')->getDefaultCurrency();
|
||||
$balances = app('steam')->balancesByAccounts($accounts, $date);
|
||||
$sum = '0';
|
||||
foreach ($balances as $entry) {
|
||||
$sum = bcadd($sum, $entry);
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$accountCurrency = $currency;
|
||||
$balance = $balances[$account->id] ?? '0';
|
||||
$currencyId = intval($account->getMeta('currency_id'));
|
||||
if ($currencyId !== 0) {
|
||||
$accountCurrency = $currencyRepos->findNull($currencyId);
|
||||
}
|
||||
if (!isset($netWorth[$accountCurrency->id])) {
|
||||
$netWorth[$accountCurrency->id]['currency'] = $accountCurrency;
|
||||
$netWorth[$accountCurrency->id]['sum'] = '0';
|
||||
}
|
||||
$netWorth[$accountCurrency->id]['sum'] = bcadd($netWorth[$accountCurrency->id]['sum'], $balance);
|
||||
}
|
||||
|
||||
$return = [];
|
||||
foreach ($netWorth as $currencyId => $data) {
|
||||
$return[$currencyId] = app('amount')->formatAnything($data['currency'], $data['sum'], false);
|
||||
}
|
||||
$return = [
|
||||
'net_worth' => app('amount')->formatAnything($currency, $sum, false),
|
||||
'net_worths' => array_values($return),
|
||||
];
|
||||
|
||||
$cache->store($return);
|
||||
|
||||
@@ -45,9 +45,8 @@ class FrontpageController extends Controller
|
||||
$info = [];
|
||||
/** @var PiggyBank $piggyBank */
|
||||
foreach ($set as $piggyBank) {
|
||||
$rep = $piggyBank->currentRelevantRep();
|
||||
$amount = strval($rep->currentamount);
|
||||
if (null !== $rep->id && 1 === bccomp($amount, '0')) {
|
||||
$amount = $repository->getCurrentAmount($piggyBank);
|
||||
if (1 === bccomp($amount, '0')) {
|
||||
// percentage!
|
||||
$pct = round(($amount / $piggyBank->targetamount) * 100);
|
||||
|
||||
|
||||
@@ -85,14 +85,28 @@ class NewUserController extends Controller
|
||||
$this->createSavingsAccount($request, $repository);
|
||||
|
||||
// also store currency preference from input:
|
||||
$currency = $currencyRepository->find(intval($request->input('amount_currency_id_bank_balance')));
|
||||
$currency = $currencyRepository->findNull(intval($request->input('amount_currency_id_bank_balance')));
|
||||
|
||||
if (null !== $currency->id) {
|
||||
if (null !== $currency) {
|
||||
// store currency preference:
|
||||
Preferences::set('currencyPreference', $currency->code);
|
||||
Preferences::mark();
|
||||
}
|
||||
|
||||
// set default optional fields:
|
||||
$visibleFields = [
|
||||
'interest_date' => true,
|
||||
'book_date' => false,
|
||||
'process_date' => false,
|
||||
'due_date' => false,
|
||||
'payment_date' => false,
|
||||
'invoice_date' => false,
|
||||
'internal_reference' => false,
|
||||
'notes' => true,
|
||||
'attachments' => true,
|
||||
];
|
||||
Preferences::set('transaction_journal_optional_fields', $visibleFields);
|
||||
|
||||
Session::flash('success', strval(trans('firefly.stored_new_accounts_new_user')));
|
||||
Preferences::mark();
|
||||
|
||||
@@ -112,6 +126,7 @@ class NewUserController extends Controller
|
||||
'iban' => null,
|
||||
'accountType' => 'asset',
|
||||
'virtualBalance' => 0,
|
||||
'account_type_id' => null,
|
||||
'active' => true,
|
||||
'accountRole' => 'defaultAsset',
|
||||
'openingBalance' => $request->input('bank_balance'),
|
||||
@@ -136,6 +151,7 @@ class NewUserController extends Controller
|
||||
'name' => $request->get('bank_name') . ' savings account',
|
||||
'iban' => null,
|
||||
'accountType' => 'asset',
|
||||
'account_type_id' => null,
|
||||
'virtualBalance' => 0,
|
||||
'active' => true,
|
||||
'accountRole' => 'savingAsset',
|
||||
|
||||
@@ -67,12 +67,12 @@ class PiggyBankController extends Controller
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function add(PiggyBank $piggyBank)
|
||||
public function add(PiggyBank $piggyBank, PiggyBankRepositoryInterface $repository)
|
||||
{
|
||||
/** @var Carbon $date */
|
||||
$date = session('end', Carbon::now()->endOfMonth());
|
||||
$leftOnAccount = $piggyBank->leftOnAccount($date);
|
||||
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount ?? '0';
|
||||
$savedSoFar = $repository->getCurrentAmount($piggyBank);
|
||||
$leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);
|
||||
$maxAmount = min($leftOnAccount, $leftToSave);
|
||||
|
||||
@@ -86,12 +86,12 @@ class PiggyBankController extends Controller
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function addMobile(PiggyBank $piggyBank)
|
||||
public function addMobile(PiggyBank $piggyBank, PiggyBankRepositoryInterface $repository)
|
||||
{
|
||||
/** @var Carbon $date */
|
||||
$date = session('end', Carbon::now()->endOfMonth());
|
||||
$leftOnAccount = $piggyBank->leftOnAccount($date);
|
||||
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount ?? '0';
|
||||
$savedSoFar = $repository->getCurrentAmount($piggyBank);
|
||||
$leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);
|
||||
$maxAmount = min($leftOnAccount, $leftToSave);
|
||||
|
||||
@@ -212,7 +212,8 @@ class PiggyBankController extends Controller
|
||||
Log::debug('Looping piggues');
|
||||
/** @var PiggyBank $piggyBank */
|
||||
foreach ($collection as $piggyBank) {
|
||||
$piggyBank->savedSoFar = $piggyBank->currentRelevantRep()->currentamount ?? '0';
|
||||
|
||||
$piggyBank->savedSoFar = $piggyRepository->getCurrentAmount($piggyBank);
|
||||
$piggyBank->percentage = 0 !== bccomp('0', $piggyBank->savedSoFar) ? intval($piggyBank->savedSoFar / $piggyBank->targetamount * 100) : 0;
|
||||
$piggyBank->leftToSave = bcsub($piggyBank->targetamount, strval($piggyBank->savedSoFar));
|
||||
$piggyBank->percentage = $piggyBank->percentage > 100 ? 100 : $piggyBank->percentage;
|
||||
@@ -383,7 +384,7 @@ class PiggyBankController extends Controller
|
||||
{
|
||||
$note = $piggyBank->notes()->first();
|
||||
$events = $repository->getEvents($piggyBank);
|
||||
$subTitle = e($piggyBank->name);
|
||||
$subTitle = $piggyBank->name;
|
||||
|
||||
return view('piggy-banks.show', compact('piggyBank', 'events', 'subTitle', 'note'));
|
||||
}
|
||||
|
||||
@@ -59,16 +59,16 @@ class ReportController extends Controller
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
// @var AccountRepositoryInterface $repository
|
||||
/** @var AccountRepositoryInterface accountRepository */
|
||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||
|
||||
// @var BudgetRepositoryInterface $repository
|
||||
/** @var BudgetRepositoryInterface budgetRepository */
|
||||
$this->budgetRepository = app(BudgetRepositoryInterface::class);
|
||||
|
||||
// @var CategoryRepositoryInterface categoryRepository
|
||||
/** @var CategoryRepositoryInterface categoryRepository */
|
||||
$this->categoryRepository = app(CategoryRepositoryInterface::class);
|
||||
|
||||
// @var PopupReportInterface popupHelper
|
||||
/** @var PopupReportInterface popupHelper */
|
||||
$this->popupHelper = app(PopupReportInterface::class);
|
||||
|
||||
return $next($request);
|
||||
|
||||
@@ -128,7 +128,7 @@ class PreferencesController extends Controller
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter) // it's unused but the class does some validation.
|
||||
*/
|
||||
public function postCode(TokenFormRequest $request)
|
||||
public function postCode(/** @scrutinizer ignore-unused */ TokenFormRequest $request)
|
||||
{
|
||||
Preferences::set('twoFactorAuthEnabled', 1);
|
||||
Preferences::set('twoFactorAuthSecret', Session::get('two-factor-secret'));
|
||||
|
||||
@@ -129,12 +129,12 @@ class CategoryController extends Controller
|
||||
$report = [];
|
||||
/** @var Category $category */
|
||||
foreach ($categories as $category) {
|
||||
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end);
|
||||
if (0 !== bccomp($spent, '0')) {
|
||||
$report[$category->id] = ['name' => $category->name, 'spent' => $spent, 'id' => $category->id];
|
||||
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end);
|
||||
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $end);
|
||||
if (0 !== bccomp($spent, '0') || 0 !== bccomp($earned, '0')) {
|
||||
$report[$category->id] = ['name' => $category->name, 'spent' => $spent, 'earned' => $earned, 'id' => $category->id];
|
||||
}
|
||||
}
|
||||
|
||||
// sort the result
|
||||
// Obtain a list of columns
|
||||
$sum = [];
|
||||
|
||||
@@ -313,7 +313,7 @@ class ExpenseController extends Controller
|
||||
$collection->push($expenseAccount);
|
||||
|
||||
$revenue = $this->accountRepository->findByName($expenseAccount->name, [AccountType::REVENUE]);
|
||||
if (!is_null($revenue->id)) {
|
||||
if (!is_null($revenue)) {
|
||||
$collection->push($revenue);
|
||||
}
|
||||
$combined[$expenseAccount->name] = $collection;
|
||||
@@ -568,7 +568,7 @@ class ExpenseController extends Controller
|
||||
];
|
||||
// loop to support multi currency
|
||||
foreach ($set as $transaction) {
|
||||
$currencyId = $transaction->transaction_currency_id;
|
||||
$currencyId = intval($transaction->transaction_currency_id);
|
||||
|
||||
// if not set, set to zero:
|
||||
if (!isset($sum['per_currency'][$currencyId])) {
|
||||
|
||||
@@ -48,6 +48,9 @@ class ReportController extends Controller
|
||||
/** @var ReportHelperInterface */
|
||||
protected $helper;
|
||||
|
||||
/** @var BudgetRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -55,13 +58,13 @@ class ReportController extends Controller
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->helper = app(ReportHelperInterface::class);
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
app('view')->share('title', trans('firefly.reports'));
|
||||
app('view')->share('mainTitleIcon', 'fa-line-chart');
|
||||
View::share('subTitleIcon', 'fa-calendar');
|
||||
$this->helper = app(ReportHelperInterface::class);
|
||||
$this->repository = app(BudgetRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@@ -87,6 +90,7 @@ class ReportController extends Controller
|
||||
if ($start < session('first')) {
|
||||
$start = session('first');
|
||||
}
|
||||
$this->repository->cleanupBudgets();
|
||||
|
||||
View::share(
|
||||
'subTitle', trans(
|
||||
@@ -120,6 +124,7 @@ class ReportController extends Controller
|
||||
if ($start < session('first')) {
|
||||
$start = session('first');
|
||||
}
|
||||
$this->repository->cleanupBudgets();
|
||||
|
||||
View::share(
|
||||
'subTitle',
|
||||
@@ -157,6 +162,7 @@ class ReportController extends Controller
|
||||
if ($start < session('first')) {
|
||||
$start = session('first');
|
||||
}
|
||||
$this->repository->cleanupBudgets();
|
||||
|
||||
View::share(
|
||||
'subTitle',
|
||||
@@ -195,6 +201,7 @@ class ReportController extends Controller
|
||||
if ($start < session('first')) {
|
||||
$start = session('first');
|
||||
}
|
||||
$this->repository->cleanupBudgets();
|
||||
|
||||
View::share(
|
||||
'subTitle',
|
||||
@@ -233,6 +240,7 @@ class ReportController extends Controller
|
||||
if ($start < session('first')) {
|
||||
$start = session('first');
|
||||
}
|
||||
$this->repository->cleanupBudgets();
|
||||
|
||||
View::share(
|
||||
'subTitle',
|
||||
@@ -265,6 +273,7 @@ class ReportController extends Controller
|
||||
$customFiscalYear = Preferences::get('customFiscalYear', 0)->data;
|
||||
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
$accountList = join(',', $accounts->pluck('id')->toArray());
|
||||
$this->repository->cleanupBudgets();
|
||||
|
||||
return view('reports.index', compact('months', 'accounts', 'start', 'accountList', 'customFiscalYear'));
|
||||
}
|
||||
@@ -391,6 +400,7 @@ class ReportController extends Controller
|
||||
if ($start < session('first')) {
|
||||
$start = session('first');
|
||||
}
|
||||
$this->repository->cleanupBudgets();
|
||||
|
||||
View::share(
|
||||
'subTitle',
|
||||
|
||||
@@ -31,11 +31,11 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Update\JournalUpdateService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Preferences;
|
||||
use Session;
|
||||
use View;
|
||||
|
||||
/**
|
||||
@@ -43,6 +43,8 @@ use View;
|
||||
*/
|
||||
class BulkController extends Controller
|
||||
{
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
|
||||
/**
|
||||
@@ -54,6 +56,7 @@ class BulkController extends Controller
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->repository = app(JournalRepositoryInterface::class);
|
||||
app('view')->share('title', trans('firefly.transactions'));
|
||||
app('view')->share('mainTitleIcon', 'fa-repeat');
|
||||
|
||||
@@ -77,8 +80,8 @@ class BulkController extends Controller
|
||||
$messages = [];
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$sources = $journal->sourceAccountList();
|
||||
$destinations = $journal->destinationAccountList();
|
||||
$sources = $this->repository->getJournalSourceAccounts($journal);
|
||||
$destinations = $this->repository->getJournalDestinationAccounts($journal);
|
||||
if ($sources->count() > 1) {
|
||||
$messages[] = trans('firefly.cannot_edit_multiple_source', ['description' => $journal->description, 'id' => $journal->id]);
|
||||
continue;
|
||||
@@ -88,13 +91,13 @@ class BulkController extends Controller
|
||||
$messages[] = trans('firefly.cannot_edit_multiple_dest', ['description' => $journal->description, 'id' => $journal->id]);
|
||||
continue;
|
||||
}
|
||||
if (TransactionType::OPENING_BALANCE === $journal->transactionType->type) {
|
||||
if (TransactionType::OPENING_BALANCE === $this->repository->getTransactionType($journal)) {
|
||||
$messages[] = trans('firefly.cannot_edit_opening_balance');
|
||||
continue;
|
||||
}
|
||||
|
||||
// cannot edit reconciled transactions / journals:
|
||||
if ($journal->transactions->first()->reconciled) {
|
||||
if ($this->repository->isJournalReconciled($journal)) {
|
||||
$messages[] = trans('firefly.cannot_edit_reconciled', ['description' => $journal->description, 'id' => $journal->id]);
|
||||
continue;
|
||||
}
|
||||
@@ -138,11 +141,14 @@ class BulkController extends Controller
|
||||
*/
|
||||
public function update(BulkEditJournalRequest $request, JournalRepositoryInterface $repository)
|
||||
{
|
||||
/** @var JournalUpdateService $service */
|
||||
$service = app(JournalUpdateService::class);
|
||||
$journalIds = $request->get('journals');
|
||||
$ignoreCategory = intval($request->get('ignore_category')) === 1;
|
||||
$ignoreBudget = intval($request->get('ignore_budget')) === 1;
|
||||
$ignoreTags = intval($request->get('ignore_tags')) === 1;
|
||||
$count = 0;
|
||||
$count = 0;
|
||||
|
||||
if (is_array($journalIds)) {
|
||||
foreach ($journalIds as $journalId) {
|
||||
$journal = $repository->find(intval($journalId));
|
||||
@@ -152,6 +158,7 @@ class BulkController extends Controller
|
||||
// update category if not told to ignore
|
||||
if ($ignoreCategory === false) {
|
||||
Log::debug(sprintf('Set category to %s', $request->string('category')));
|
||||
|
||||
$repository->updateCategory($journal, $request->string('category'));
|
||||
}
|
||||
// update budget if not told to ignore (and is withdrawal)
|
||||
@@ -161,7 +168,7 @@ class BulkController extends Controller
|
||||
}
|
||||
if ($ignoreTags === false) {
|
||||
Log::debug(sprintf('Set tags to %s', $request->string('budget_id')));
|
||||
$repository->updateTags($journal, explode(',', $request->string('tags')));
|
||||
$repository->updateTags($journal,['tags' => explode(',', $request->string('tags'))]);
|
||||
}
|
||||
// update tags if not told to ignore (and is withdrawal)
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ class ConvertController extends Controller
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $accounts;
|
||||
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* ConvertController constructor.
|
||||
*/
|
||||
@@ -53,7 +56,8 @@ class ConvertController extends Controller
|
||||
// some useful repositories:
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->accounts = app(AccountRepositoryInterface::class);
|
||||
$this->accounts = app(AccountRepositoryInterface::class);
|
||||
$this->repository = app(JournalRepositoryInterface::class);
|
||||
|
||||
app('view')->share('title', trans('firefly.transactions'));
|
||||
app('view')->share('mainTitleIcon', 'fa-exchange');
|
||||
@@ -76,8 +80,7 @@ class ConvertController extends Controller
|
||||
return $this->redirectToAccount($journal);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
$positiveAmount = $journal->amountPositive();
|
||||
$positiveAmount = $this->repository->getJournalTotal($journal);
|
||||
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getActiveAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
|
||||
$sourceType = $journal->transactionType;
|
||||
$subTitle = trans('firefly.convert_to_' . $destinationType->type, ['description' => $journal->description]);
|
||||
@@ -98,8 +101,8 @@ class ConvertController extends Controller
|
||||
}
|
||||
|
||||
// get source and destination account:
|
||||
$sourceAccount = $journal->sourceAccountList()->first();
|
||||
$destinationAccount = $journal->destinationAccountList()->first();
|
||||
$sourceAccount = $this->repository->getJournalSourceAccounts($journal)->first();
|
||||
$destinationAccount = $this->repository->getJournalDestinationAccounts($journal)->first();
|
||||
|
||||
return view(
|
||||
'transactions.convert',
|
||||
@@ -183,8 +186,8 @@ class ConvertController extends Controller
|
||||
{
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$sourceAccount = $journal->sourceAccountList()->first();
|
||||
$destinationAccount = $journal->destinationAccountList()->first();
|
||||
$sourceAccount = $this->repository->getJournalSourceAccounts($journal)->first();
|
||||
$destinationAccount = $this->repository->getJournalDestinationAccounts($journal)->first();
|
||||
$sourceType = $journal->transactionType;
|
||||
$joined = $sourceType->type . '-' . $destinationType->type;
|
||||
switch ($joined) {
|
||||
@@ -196,7 +199,7 @@ class ConvertController extends Controller
|
||||
break;
|
||||
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER:
|
||||
// two
|
||||
$destination = $accountRepository->find(intval($data['destination_account_asset']));
|
||||
$destination = $accountRepository->findNull(intval($data['destination_account_asset']));
|
||||
break;
|
||||
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL:
|
||||
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL:
|
||||
@@ -239,8 +242,8 @@ class ConvertController extends Controller
|
||||
{
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$sourceAccount = $journal->sourceAccountList()->first();
|
||||
$destinationAccount = $journal->destinationAccountList()->first();
|
||||
$sourceAccount = $this->repository->getJournalSourceAccounts($journal)->first();
|
||||
$destinationAccount = $this->repository->getJournalDestinationAccounts($journal)->first();
|
||||
$sourceType = $journal->transactionType;
|
||||
$joined = $sourceType->type . '-' . $destinationType->type;
|
||||
switch ($joined) {
|
||||
@@ -273,7 +276,7 @@ class ConvertController extends Controller
|
||||
$source = $destinationAccount;
|
||||
break;
|
||||
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER:
|
||||
$source = $accountRepository->find(intval($data['source_account_asset']));
|
||||
$source = $accountRepository->findNull(intval($data['source_account_asset']));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@ use URL;
|
||||
*/
|
||||
class LinkController extends Controller
|
||||
{
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $journalRepository;
|
||||
/** @var LinkTypeRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -50,6 +55,9 @@ class LinkController extends Controller
|
||||
app('view')->share('title', trans('firefly.transactions'));
|
||||
app('view')->share('mainTitleIcon', 'fa-repeat');
|
||||
|
||||
$this->journalRepository = app(JournalRepositoryInterface::class);
|
||||
$this->repository = app(LinkTypeRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
@@ -70,14 +78,13 @@ class LinkController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LinkTypeRepositoryInterface $repository
|
||||
* @param TransactionJournalLink $link
|
||||
* @param TransactionJournalLink $link
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function destroy(LinkTypeRepositoryInterface $repository, TransactionJournalLink $link)
|
||||
public function destroy(TransactionJournalLink $link)
|
||||
{
|
||||
$repository->destroyLink($link);
|
||||
$this->repository->destroyLink($link);
|
||||
|
||||
Session::flash('success', strval(trans('firefly.deleted_link')));
|
||||
Preferences::mark();
|
||||
@@ -86,19 +93,14 @@ class LinkController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param JournalLinkRequest $request
|
||||
* @param LinkTypeRepositoryInterface $repository
|
||||
* @param JournalRepositoryInterface $journalRepository
|
||||
* @param TransactionJournal $journal
|
||||
* @param JournalLinkRequest $request
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function store(
|
||||
JournalLinkRequest $request,
|
||||
LinkTypeRepositoryInterface $repository,
|
||||
JournalRepositoryInterface $journalRepository,
|
||||
TransactionJournal $journal
|
||||
) {
|
||||
public function store(JournalLinkRequest $request, TransactionJournal $journal)
|
||||
{
|
||||
|
||||
Log::debug('We are here (store)');
|
||||
$linkInfo = $request->getLinkInfo();
|
||||
if (0 === $linkInfo['transaction_journal_id']) {
|
||||
@@ -106,30 +108,28 @@ class LinkController extends Controller
|
||||
|
||||
return redirect(route('transactions.show', [$journal->id]));
|
||||
}
|
||||
$other = $journalRepository->find($linkInfo['transaction_journal_id']);
|
||||
$alreadyLinked = $repository->findLink($journal, $other);
|
||||
$other = $this->journalRepository->find($linkInfo['transaction_journal_id']);
|
||||
$alreadyLinked = $this->repository->findLink($journal, $other);
|
||||
if ($alreadyLinked) {
|
||||
Session::flash('error', trans('firefly.journals_error_linked'));
|
||||
|
||||
return redirect(route('transactions.show', [$journal->id]));
|
||||
}
|
||||
Log::debug(sprintf('Journal is %d, opposing is %d', $journal->id, $other->id));
|
||||
|
||||
$repository->storeLink($linkInfo, $other, $journal);
|
||||
$this->repository->storeLink($linkInfo, $other, $journal);
|
||||
Session::flash('success', trans('firefly.journals_linked'));
|
||||
|
||||
return redirect(route('transactions.show', [$journal->id]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LinkTypeRepositoryInterface $repository
|
||||
* @param TransactionJournalLink $link
|
||||
* @param TransactionJournalLink $link
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function switchLink(LinkTypeRepositoryInterface $repository, TransactionJournalLink $link)
|
||||
public function switchLink(TransactionJournalLink $link)
|
||||
{
|
||||
$repository->switchLink($link);
|
||||
$this->repository->switchLink($link);
|
||||
|
||||
return redirect(URL::previous());
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ namespace FireflyIII\Http\Controllers\Transaction;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Requests\MassDeleteJournalRequest;
|
||||
use FireflyIII\Http\Requests\MassEditJournalRequest;
|
||||
use FireflyIII\Http\Requests\MassEditBulkJournalRequest;
|
||||
use FireflyIII\Http\Requests\MassEditJournalRequest;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
@@ -43,6 +43,9 @@ use View;
|
||||
*/
|
||||
class MassController extends Controller
|
||||
{
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -54,6 +57,7 @@ class MassController extends Controller
|
||||
function ($request, $next) {
|
||||
app('view')->share('title', trans('firefly.transactions'));
|
||||
app('view')->share('mainTitleIcon', 'fa-repeat');
|
||||
$this->repository = app(JournalRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@@ -76,12 +80,11 @@ class MassController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MassDeleteJournalRequest $request
|
||||
* @param JournalRepositoryInterface $repository
|
||||
* @param MassDeleteJournalRequest $request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function destroy(MassDeleteJournalRequest $request, JournalRepositoryInterface $repository)
|
||||
public function destroy(MassDeleteJournalRequest $request)
|
||||
{
|
||||
$ids = $request->get('confirm_mass_delete');
|
||||
$set = new Collection;
|
||||
@@ -89,7 +92,7 @@ class MassController extends Controller
|
||||
/** @var int $journalId */
|
||||
foreach ($ids as $journalId) {
|
||||
/** @var TransactionJournal $journal */
|
||||
$journal = $repository->find(intval($journalId));
|
||||
$journal = $this->repository->find(intval($journalId));
|
||||
if (null !== $journal->id && intval($journalId) === $journal->id) {
|
||||
$set->push($journal);
|
||||
}
|
||||
@@ -100,7 +103,7 @@ class MassController extends Controller
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($set as $journal) {
|
||||
$repository->delete($journal);
|
||||
$this->repository->delete($journal);
|
||||
++$count;
|
||||
}
|
||||
|
||||
@@ -112,6 +115,8 @@ class MassController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO this code is a mess.
|
||||
*
|
||||
* @param Collection $journals
|
||||
*
|
||||
* @return View
|
||||
@@ -134,8 +139,8 @@ class MassController extends Controller
|
||||
$messages = [];
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$sources = $journal->sourceAccountList();
|
||||
$destinations = $journal->destinationAccountList();
|
||||
$sources = $this->repository->getJournalSourceAccounts($journal);
|
||||
$destinations = $this->repository->getJournalDestinationAccounts($journal);
|
||||
if ($sources->count() > 1) {
|
||||
$messages[] = trans('firefly.cannot_edit_multiple_source', ['description' => $journal->description, 'id' => $journal->id]);
|
||||
continue;
|
||||
@@ -145,13 +150,13 @@ class MassController extends Controller
|
||||
$messages[] = trans('firefly.cannot_edit_multiple_dest', ['description' => $journal->description, 'id' => $journal->id]);
|
||||
continue;
|
||||
}
|
||||
if (TransactionType::OPENING_BALANCE === $journal->transactionType->type) {
|
||||
if (TransactionType::OPENING_BALANCE === $this->repository->getTransactionType($journal)) {
|
||||
$messages[] = trans('firefly.cannot_edit_opening_balance');
|
||||
continue;
|
||||
}
|
||||
|
||||
// cannot edit reconciled transactions / journals:
|
||||
if ($journal->transactions->first()->reconciled) {
|
||||
if ($this->repository->isJournalReconciled($journal)) {
|
||||
$messages[] = trans('firefly.cannot_edit_reconciled', ['description' => $journal->description, 'id' => $journal->id]);
|
||||
continue;
|
||||
}
|
||||
@@ -169,11 +174,11 @@ class MassController extends Controller
|
||||
// collect some useful meta data for the mass edit:
|
||||
$filtered->each(
|
||||
function (TransactionJournal $journal) {
|
||||
$transaction = $journal->positiveTransaction();
|
||||
$transaction = $this->repository->getFirstPosTransaction($journal);
|
||||
$currency = $transaction->transactionCurrency;
|
||||
$journal->amount = floatval($transaction->amount);
|
||||
$sources = $journal->sourceAccountList();
|
||||
$destinations = $journal->destinationAccountList();
|
||||
$sources = $this->repository->getJournalSourceAccounts($journal);
|
||||
$destinations = $this->repository->getJournalDestinationAccounts($journal);
|
||||
$journal->transaction_count = $journal->transactions()->count();
|
||||
$journal->currency_symbol = $currency->symbol;
|
||||
$journal->transaction_type_type = $journal->transactionType->type;
|
||||
@@ -202,6 +207,8 @@ class MassController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO this cannot work with new update service.
|
||||
*
|
||||
* @param MassEditJournalRequest $request
|
||||
* @param JournalRepositoryInterface $repository
|
||||
*
|
||||
@@ -216,11 +223,12 @@ class MassController extends Controller
|
||||
$journal = $repository->find(intval($journalId));
|
||||
if (!is_null($journal)) {
|
||||
// get optional fields:
|
||||
$what = strtolower($journal->transactionTypeStr());
|
||||
$sourceAccountId = $request->get('source_account_id')[$journal->id] ?? 0;
|
||||
$sourceAccountName = $request->get('source_account_name')[$journal->id] ?? '';
|
||||
$destAccountId = $request->get('destination_account_id')[$journal->id] ?? 0;
|
||||
$destAccountName = $request->get('destination_account_name')[$journal->id] ?? '';
|
||||
$what = strtolower($this->repository->getTransactionType($journal));
|
||||
$sourceAccountId = $request->get('source_account_id')[$journal->id] ?? null;
|
||||
$currencyId = $request->get('transaction_currency_id')[$journal->id] ?? 1;
|
||||
$sourceAccountName = $request->get('source_account_name')[$journal->id] ?? null;
|
||||
$destAccountId = $request->get('destination_account_id')[$journal->id] ?? null;
|
||||
$destAccountName = $request->get('destination_account_name')[$journal->id] ?? null;
|
||||
$budgetId = $request->get('budget_id')[$journal->id] ?? 0;
|
||||
$category = $request->get('category')[$journal->id];
|
||||
$tags = $journal->tags->pluck('tag')->toArray();
|
||||
@@ -228,29 +236,47 @@ class MassController extends Controller
|
||||
$foreignAmount = isset($request->get('foreign_amount')[$journal->id]) ? round($request->get('foreign_amount')[$journal->id], 12) : null;
|
||||
$foreignCurrencyId = isset($request->get('foreign_currency_id')[$journal->id]) ?
|
||||
intval($request->get('foreign_currency_id')[$journal->id]) : null;
|
||||
|
||||
$notes = $repository->getNoteText($journal);
|
||||
// build data array
|
||||
$data = [
|
||||
'id' => $journal->id,
|
||||
'what' => $what,
|
||||
'description' => $request->get('description')[$journal->id],
|
||||
'source_account_id' => intval($sourceAccountId),
|
||||
'source_account_name' => $sourceAccountName,
|
||||
'destination_account_id' => intval($destAccountId),
|
||||
'destination_account_name' => $destAccountName,
|
||||
'amount' => $foreignAmount,
|
||||
'native_amount' => $amount,
|
||||
'source_amount' => $amount,
|
||||
'date' => new Carbon($request->get('date')[$journal->id]),
|
||||
'interest_date' => $journal->interest_date,
|
||||
'book_date' => $journal->book_date,
|
||||
'process_date' => $journal->process_date,
|
||||
'budget_id' => intval($budgetId),
|
||||
'currency_id' => $foreignCurrencyId,
|
||||
'foreign_amount' => $foreignAmount,
|
||||
'destination_amount' => $foreignAmount,
|
||||
'category' => $category,
|
||||
'tags' => $tags,
|
||||
'id' => $journal->id,
|
||||
'what' => $what,
|
||||
'description' => $request->get('description')[$journal->id],
|
||||
'date' => new Carbon($request->get('date')[$journal->id]),
|
||||
'bill_id' => null,
|
||||
'bill_name' => null,
|
||||
'notes' => $notes,
|
||||
'transactions' => [[
|
||||
|
||||
'category_id' => null,
|
||||
'category_name' => $category,
|
||||
'budget_id' => intval($budgetId),
|
||||
'budget_name' => null,
|
||||
'source_id' => intval($sourceAccountId),
|
||||
'source_name' => $sourceAccountName,
|
||||
'destination_id' => intval($destAccountId),
|
||||
'destination_name' => $destAccountName,
|
||||
'amount' => $amount,
|
||||
'identifier' => 0,
|
||||
'reconciled' => false,
|
||||
'currency_id' => intval($currencyId),
|
||||
'currency_code' => null,
|
||||
'description' => null,
|
||||
'foreign_amount' => null,
|
||||
'foreign_currency_id' => $foreignCurrencyId,
|
||||
'foreign_currency_code' => null,
|
||||
//'native_amount' => $amount,
|
||||
//'source_amount' => $amount,
|
||||
//'foreign_amount' => $foreignAmount,
|
||||
//'destination_amount' => $foreignAmount,
|
||||
//'amount' => $foreignAmount,
|
||||
]],
|
||||
'currency_id' => $foreignCurrencyId,
|
||||
'tags' => $tags,
|
||||
'interest_date' => $journal->interest_date,
|
||||
'book_date' => $journal->book_date,
|
||||
'process_date' => $journal->process_date,
|
||||
|
||||
];
|
||||
// call repository update function.
|
||||
$repository->update($journal, $data);
|
||||
|
||||
@@ -104,13 +104,13 @@ class SingleController extends Controller
|
||||
*/
|
||||
public function cloneTransaction(TransactionJournal $journal)
|
||||
{
|
||||
$source = $journal->sourceAccountList()->first();
|
||||
$destination = $journal->destinationAccountList()->first();
|
||||
$budget = $journal->budgets()->first();
|
||||
$budgetId = null === $budget ? 0 : $budget->id;
|
||||
$category = $journal->categories()->first();
|
||||
$categoryName = null === $category ? '' : $category->name;
|
||||
$tags = join(',', $journal->tags()->get()->pluck('tag')->toArray());
|
||||
$source = $this->repository->getJournalSourceAccounts($journal)->first();
|
||||
$destination = $this->repository->getJournalDestinationAccounts($journal)->first();
|
||||
$budgetId = $this->repository->getJournalBudgetId($journal);
|
||||
$categoryName = $this->repository->getJournalCategoryName($journal);
|
||||
|
||||
$tags = join(',', $this->repository->getTags($journal));
|
||||
// todo less direct database access. Use collector?
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $journal->transactions()->first();
|
||||
$amount = app('steam')->positive($transaction->amount);
|
||||
@@ -171,6 +171,14 @@ class SingleController extends Controller
|
||||
$subTitle = trans('form.add_new_' . $what);
|
||||
$subTitleIcon = 'fa-plus';
|
||||
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
|
||||
$source = intval($request->get('source'));
|
||||
|
||||
if (($what === 'withdrawal' || $what === 'transfer') && $source > 0) {
|
||||
$preFilled['source_account_id'] = $source;
|
||||
}
|
||||
if ($what === 'deposit' && $source > 0) {
|
||||
$preFilled['destination_account_id'] = $source;
|
||||
}
|
||||
|
||||
Session::put('preFilled', $preFilled);
|
||||
|
||||
@@ -230,7 +238,7 @@ class SingleController extends Controller
|
||||
$type = $transactionJournal->transactionTypeStr();
|
||||
Session::flash('success', strval(trans('firefly.deleted_' . strtolower($type), ['description' => $transactionJournal->description])));
|
||||
|
||||
$this->repository->delete($transactionJournal);
|
||||
$this->repository->destroy($transactionJournal);
|
||||
|
||||
Preferences::mark();
|
||||
|
||||
@@ -242,53 +250,56 @@ class SingleController extends Controller
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function edit(TransactionJournal $journal)
|
||||
public function edit(TransactionJournal $journal, JournalRepositoryInterface $repository)
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($this->isOpeningBalance($journal)) {
|
||||
$transactionType = $repository->getTransactionType($journal);
|
||||
|
||||
// redirect to account:
|
||||
if ($transactionType === TransactionType::OPENING_BALANCE) {
|
||||
return $this->redirectToAccount($journal);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
// redirect to reconcile edit:
|
||||
if ($transactionType === TransactionType::RECONCILIATION) {
|
||||
return redirect(route('accounts.reconcile.edit', [$journal->id]));
|
||||
}
|
||||
|
||||
// redirect to split edit:
|
||||
if ($this->isSplitJournal($journal)) {
|
||||
return redirect(route('transactions.split.edit', [$journal->id]));
|
||||
}
|
||||
|
||||
$what = strtolower($journal->transactionTypeStr());
|
||||
$what = strtolower($transactionType);
|
||||
$assetAccounts = $this->groupedAccountList();
|
||||
$budgetList = ExpandedForm::makeSelectListWithEmpty($this->budgets->getBudgets());
|
||||
|
||||
if (TransactionType::RECONCILIATION === $journal->transactionType->type) {
|
||||
return redirect(route('accounts.reconcile.edit', [$journal->id]));
|
||||
}
|
||||
|
||||
// view related code
|
||||
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
|
||||
|
||||
// journal related code
|
||||
$sourceAccounts = $journal->sourceAccountList();
|
||||
$destinationAccounts = $journal->destinationAccountList();
|
||||
$sourceAccounts = $repository->getJournalSourceAccounts($journal);
|
||||
$destinationAccounts = $repository->getJournalDestinationAccounts($journal);
|
||||
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
|
||||
$pTransaction = $journal->positiveTransaction();
|
||||
$pTransaction = $repository->getFirstPosTransaction($journal);
|
||||
$foreignCurrency = null !== $pTransaction->foreignCurrency ? $pTransaction->foreignCurrency : $pTransaction->transactionCurrency;
|
||||
$preFilled = [
|
||||
'date' => $journal->dateAsString(),
|
||||
'interest_date' => $journal->dateAsString('interest_date'),
|
||||
'book_date' => $journal->dateAsString('book_date'),
|
||||
'process_date' => $journal->dateAsString('process_date'),
|
||||
'category' => $journal->categoryAsString(),
|
||||
'budget_id' => $journal->budgetId(),
|
||||
'tags' => join(',', $journal->tags->pluck('tag')->toArray()),
|
||||
'date' => $repository->getJournalDate($journal, null), // $journal->dateAsString()
|
||||
'interest_date' => $repository->getJournalDate($journal, 'interest_date'),
|
||||
'book_date' => $repository->getJournalDate($journal, 'book_date'),
|
||||
'process_date' => $repository->getJournalDate($journal, 'process_date'),
|
||||
'category' => $repository->getJournalCategoryName($journal),
|
||||
'budget_id' => $repository->getJournalBudgetId($journal),
|
||||
'tags' => join(',', $repository->getTags($journal)),
|
||||
'source_account_id' => $sourceAccounts->first()->id,
|
||||
'source_account_name' => $sourceAccounts->first()->edit_name,
|
||||
'destination_account_id' => $destinationAccounts->first()->id,
|
||||
'destination_account_name' => $destinationAccounts->first()->edit_name,
|
||||
|
||||
// new custom fields:
|
||||
'due_date' => $journal->dateAsString('due_date'),
|
||||
'payment_date' => $journal->dateAsString('payment_date'),
|
||||
'invoice_date' => $journal->dateAsString('invoice_date'),
|
||||
'interal_reference' => $journal->getMeta('internal_reference'),
|
||||
'notes' => '',
|
||||
'due_date' => $repository->getJournalDate($journal, 'due_date'),
|
||||
'payment_date' => $repository->getJournalDate($journal, 'payment_date'),
|
||||
'invoice_date' => $repository->getJournalDate($journal, 'invoice_date'),
|
||||
'interal_reference' => $repository->getMetaField($journal, 'internal_reference'),
|
||||
'notes' => $repository->getNoteText($journal),
|
||||
|
||||
// amount fields
|
||||
'amount' => $pTransaction->amount,
|
||||
@@ -301,11 +312,6 @@ class SingleController extends Controller
|
||||
'foreign_currency' => $foreignCurrency,
|
||||
'destination_currency' => $foreignCurrency,
|
||||
];
|
||||
/** @var Note $note */
|
||||
$note = $this->repository->getNote($journal);
|
||||
if (null !== $note) {
|
||||
$preFilled['notes'] = $note->text;
|
||||
}
|
||||
|
||||
// amounts for withdrawals and deposits:
|
||||
// amount, native_amount, source_amount, destination_amount
|
||||
@@ -340,6 +346,8 @@ class SingleController extends Controller
|
||||
$createAnother = 1 === intval($request->get('create_another'));
|
||||
$data = $request->getJournalData();
|
||||
$journal = $repository->store($data);
|
||||
|
||||
|
||||
if (null === $journal->id) {
|
||||
// error!
|
||||
Log::error('Could not store transaction journal: ', $journal->getErrors()->toArray());
|
||||
@@ -416,7 +424,7 @@ class SingleController extends Controller
|
||||
event(new UpdatedTransactionJournal($journal));
|
||||
// update, get events by date and sort DESC
|
||||
|
||||
$type = strtolower($journal->transactionTypeStr());
|
||||
$type = strtolower($this->repository->getTransactionType($journal));
|
||||
Session::flash('success', strval(trans('firefly.updated_' . $type, ['description' => $data['description']])));
|
||||
Preferences::mark();
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Requests\SplitJournalFormRequest;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Note;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
@@ -37,7 +36,6 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Log;
|
||||
use Preferences;
|
||||
use Session;
|
||||
use Steam;
|
||||
@@ -97,7 +95,7 @@ class SplitController extends Controller
|
||||
public function edit(Request $request, TransactionJournal $journal)
|
||||
{
|
||||
if ($this->isOpeningBalance($journal)) {
|
||||
return $this->redirectToAccount($journal);
|
||||
return $this->redirectToAccount($journal); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
|
||||
@@ -125,20 +123,11 @@ class SplitController extends Controller
|
||||
Session::forget('transactions.edit-split.fromUpdate');
|
||||
|
||||
return view(
|
||||
'transactions.split.edit',
|
||||
compact(
|
||||
'subTitleIcon',
|
||||
'currencies',
|
||||
'optionalFields',
|
||||
'preFilled',
|
||||
'subTitle',
|
||||
'uploadSize',
|
||||
'assetAccounts',
|
||||
'budgets',
|
||||
'journal',
|
||||
'accountArray',
|
||||
'previous'
|
||||
)
|
||||
'transactions.split.edit', compact(
|
||||
'subTitleIcon', 'currencies', 'optionalFields', 'preFilled', 'subTitle', 'uploadSize', 'assetAccounts', 'budgets',
|
||||
'journal', 'accountArray',
|
||||
'previous'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -151,10 +140,11 @@ class SplitController extends Controller
|
||||
public function update(SplitJournalFormRequest $request, TransactionJournal $journal)
|
||||
{
|
||||
if ($this->isOpeningBalance($journal)) {
|
||||
return $this->redirectToAccount($journal);
|
||||
return $this->redirectToAccount($journal); // @codeCoverageIgnore
|
||||
}
|
||||
$data = $this->arrayFromInput($request);
|
||||
$journal = $this->repository->updateSplitJournal($journal, $data);
|
||||
$data = $request->getAll();
|
||||
$journal = $this->repository->update($journal, $data);
|
||||
|
||||
/** @var array $files */
|
||||
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
|
||||
// save attachments:
|
||||
@@ -168,8 +158,8 @@ class SplitController extends Controller
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
$type = strtolower($journal->transactionTypeStr());
|
||||
Session::flash('success', strval(trans('firefly.updated_' . $type, ['description' => $data['journal_description']])));
|
||||
$type = strtolower($this->repository->getTransactionType($journal));
|
||||
Session::flash('success', strval(trans('firefly.updated_' . $type, ['description' => $data['description']])));
|
||||
Preferences::mark();
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
@@ -185,39 +175,6 @@ class SplitController extends Controller
|
||||
return redirect($this->getPreviousUri('transactions.edit-split.uri'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SplitJournalFormRequest $request
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function arrayFromInput(SplitJournalFormRequest $request): array
|
||||
{
|
||||
$tags = null === $request->get('tags') ? '' : $request->get('tags');
|
||||
$array = [
|
||||
'journal_description' => $request->get('journal_description'),
|
||||
'journal_source_account_id' => $request->get('journal_source_account_id'),
|
||||
'journal_source_account_name' => $request->get('journal_source_account_name'),
|
||||
'journal_destination_account_id' => $request->get('journal_destination_account_id'),
|
||||
'what' => $request->get('what'),
|
||||
'date' => $request->get('date'),
|
||||
// all custom fields:
|
||||
'interest_date' => $request->get('interest_date'),
|
||||
'book_date' => $request->get('book_date'),
|
||||
'process_date' => $request->get('process_date'),
|
||||
'due_date' => $request->get('due_date'),
|
||||
'payment_date' => $request->get('payment_date'),
|
||||
'invoice_date' => $request->get('invoice_date'),
|
||||
'internal_reference' => $request->get('internal_reference'),
|
||||
'notes' => $request->get('notes'),
|
||||
'tags' => explode(',', $tags),
|
||||
|
||||
// transactions.
|
||||
'transactions' => $this->getTransactionDataFromRequest($request),
|
||||
];
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SplitJournalFormRequest|Request $request
|
||||
* @param TransactionJournal $journal
|
||||
@@ -226,36 +183,29 @@ class SplitController extends Controller
|
||||
*/
|
||||
private function arrayFromJournal(Request $request, TransactionJournal $journal): array
|
||||
{
|
||||
$sourceAccounts = $journal->sourceAccountList();
|
||||
$destinationAccounts = $journal->destinationAccountList();
|
||||
$notes = '';
|
||||
/** @var Note $note */
|
||||
|
||||
$note = $this->repository->getNote($journal);
|
||||
if (null !== $note) {
|
||||
$notes = $note->text;
|
||||
}
|
||||
$array = [
|
||||
$sourceAccounts = $this->repository->getJournalSourceAccounts($journal);
|
||||
$destinationAccounts = $this->repository->getJournalDestinationAccounts($journal);
|
||||
$array = [
|
||||
'journal_description' => $request->old('journal_description', $journal->description),
|
||||
'journal_amount' => $journal->amountPositive(),
|
||||
'journal_amount' => $this->repository->getJournalTotal($journal),
|
||||
'sourceAccounts' => $sourceAccounts,
|
||||
'journal_source_account_id' => $request->old('journal_source_account_id', $sourceAccounts->first()->id),
|
||||
'journal_source_account_name' => $request->old('journal_source_account_name', $sourceAccounts->first()->name),
|
||||
'journal_destination_account_id' => $request->old('journal_destination_account_id', $destinationAccounts->first()->id),
|
||||
'destinationAccounts' => $destinationAccounts,
|
||||
'what' => strtolower($journal->transactionTypeStr()),
|
||||
'date' => $request->old('date', $journal->date->format('Y-m-d')),
|
||||
'what' => strtolower($this->repository->getTransactionType($journal)),
|
||||
'date' => $request->old('date', $this->repository->getJournalDate($journal, null)),
|
||||
'tags' => join(',', $journal->tags->pluck('tag')->toArray()),
|
||||
|
||||
// all custom fields:
|
||||
'interest_date' => $request->old('interest_date', $journal->getMeta('interest_date')),
|
||||
'book_date' => $request->old('book_date', $journal->getMeta('book_date')),
|
||||
'process_date' => $request->old('process_date', $journal->getMeta('process_date')),
|
||||
'due_date' => $request->old('due_date', $journal->getMeta('due_date')),
|
||||
'payment_date' => $request->old('payment_date', $journal->getMeta('payment_date')),
|
||||
'invoice_date' => $request->old('invoice_date', $journal->getMeta('invoice_date')),
|
||||
'internal_reference' => $request->old('internal_reference', $journal->getMeta('internal_reference')),
|
||||
'notes' => $request->old('notes', $notes),
|
||||
'interest_date' => $request->old('interest_date', $this->repository->getMetaField($journal, 'interest_date')),
|
||||
'book_date' => $request->old('book_date', $this->repository->getMetaField($journal, 'book_date')),
|
||||
'process_date' => $request->old('process_date', $this->repository->getMetaField($journal, 'process_date')),
|
||||
'due_date' => $request->old('due_date', $this->repository->getMetaField($journal, 'due_date')),
|
||||
'payment_date' => $request->old('payment_date', $this->repository->getMetaField($journal, 'payment_date')),
|
||||
'invoice_date' => $request->old('invoice_date', $this->repository->getMetaField($journal, 'invoice_date')),
|
||||
'internal_reference' => $request->old('internal_reference', $this->repository->getMetaField($journal, 'internal_reference')),
|
||||
'notes' => $request->old('notes', $this->repository->getNoteText($journal)),
|
||||
|
||||
// transactions.
|
||||
'transactions' => $this->getTransactionDataFromJournal($journal),
|
||||
@@ -295,12 +245,14 @@ class SplitController extends Controller
|
||||
'foreign_currency_code' => $transaction['foreign_currency_code'],
|
||||
'foreign_currency_symbol' => $transaction['foreign_currency_symbol'],
|
||||
];
|
||||
|
||||
// set initial category and/or budget:
|
||||
if (1 === count($transactions) && 0 === $index) {
|
||||
$set['budget_id'] = $journal->budgetId();
|
||||
$set['category'] = $journal->categoryAsString();
|
||||
if ($set['budget_id'] === 0) {
|
||||
$set['budget_id'] = $this->repository->getJournalBudgetId($journal);
|
||||
}
|
||||
if (strlen($set['category']) === 0) {
|
||||
$set['category'] = $this->repository->getJournalCategoryName($journal);
|
||||
}
|
||||
|
||||
|
||||
$return[] = $set;
|
||||
}
|
||||
@@ -308,35 +260,6 @@ class SplitController extends Controller
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SplitJournalFormRequest|Request $request
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getTransactionDataFromRequest(SplitJournalFormRequest $request): array
|
||||
{
|
||||
$return = [];
|
||||
$transactions = $request->get('transactions');
|
||||
foreach ($transactions as $transaction) {
|
||||
$return[] = [
|
||||
'description' => $transaction['description'],
|
||||
'source_account_id' => $transaction['source_account_id'] ?? 0,
|
||||
'source_account_name' => $transaction['source_account_name'] ?? '',
|
||||
'destination_account_id' => $transaction['destination_account_id'] ?? 0,
|
||||
'destination_account_name' => $transaction['destination_account_name'] ?? '',
|
||||
'amount' => round($transaction['amount'] ?? 0, 12),
|
||||
'foreign_amount' => !isset($transaction['foreign_amount']) ? null : round($transaction['foreign_amount'] ?? 0, 12),
|
||||
'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : 0,
|
||||
'category' => $transaction['category'] ?? '',
|
||||
'transaction_currency_id' => intval($transaction['transaction_currency_id']),
|
||||
'foreign_currency_id' => $transaction['foreign_currency_id'] ?? null,
|
||||
];
|
||||
}
|
||||
Log::debug(sprintf('Found %d splits in request data.', count($return)));
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $array
|
||||
* @param $old
|
||||
|
||||
@@ -272,7 +272,7 @@ class TransactionController extends Controller
|
||||
$return = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($collection as $transaction) {
|
||||
$currencyId = $transaction->transaction_currency_id;
|
||||
$currencyId = intval($transaction->transaction_currency_id);
|
||||
|
||||
// save currency information:
|
||||
if (!isset($return[$currencyId])) {
|
||||
|
||||
@@ -44,6 +44,7 @@ use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
|
||||
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
||||
use Laravel\Passport\Http\Middleware\CreateFreshApiToken;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
@@ -84,7 +85,7 @@ class Kernel extends HttpKernel
|
||||
StartFireflySession::class,
|
||||
ShareErrorsFromSession::class,
|
||||
VerifyCsrfToken::class,
|
||||
//SubstituteBindings::class,
|
||||
CreateFreshApiToken::class,
|
||||
],
|
||||
|
||||
// MUST NOT be logged in. Does not care about 2FA or confirmation.
|
||||
@@ -95,7 +96,6 @@ class Kernel extends HttpKernel
|
||||
StartFireflySession::class,
|
||||
ShareErrorsFromSession::class,
|
||||
VerifyCsrfToken::class,
|
||||
//SubstituteBindings::class,
|
||||
Binder::class,
|
||||
RedirectIfAuthenticated::class,
|
||||
],
|
||||
@@ -109,7 +109,6 @@ class Kernel extends HttpKernel
|
||||
StartFireflySession::class,
|
||||
ShareErrorsFromSession::class,
|
||||
VerifyCsrfToken::class,
|
||||
//SubstituteBindings::class,
|
||||
Binder::class,
|
||||
Authenticate::class,
|
||||
RedirectIfTwoFactorAuthenticated::class,
|
||||
@@ -125,7 +124,6 @@ class Kernel extends HttpKernel
|
||||
StartFireflySession::class,
|
||||
ShareErrorsFromSession::class,
|
||||
VerifyCsrfToken::class,
|
||||
//SubstituteBindings::class,
|
||||
Binder::class,
|
||||
Authenticate::class,
|
||||
],
|
||||
@@ -141,11 +139,11 @@ class Kernel extends HttpKernel
|
||||
StartFireflySession::class,
|
||||
ShareErrorsFromSession::class,
|
||||
VerifyCsrfToken::class,
|
||||
//SubstituteBindings::class,
|
||||
Authenticate::class,
|
||||
AuthenticateTwoFactor::class,
|
||||
Range::class,
|
||||
Binder::class,
|
||||
CreateFreshApiToken::class,
|
||||
],
|
||||
// MUST be logged in
|
||||
// MUST have 2fa
|
||||
@@ -159,12 +157,12 @@ class Kernel extends HttpKernel
|
||||
StartFireflySession::class,
|
||||
ShareErrorsFromSession::class,
|
||||
VerifyCsrfToken::class,
|
||||
//SubstituteBindings::class,
|
||||
Authenticate::class,
|
||||
AuthenticateTwoFactor::class,
|
||||
IsAdmin::class,
|
||||
Range::class,
|
||||
Binder::class,
|
||||
CreateFreshApiToken::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
* Authenticate.php
|
||||
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
||||
* Copyright (c) 2018 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
@@ -18,50 +18,94 @@
|
||||
* 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\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Session;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Contracts\Auth\Factory as Auth;
|
||||
|
||||
/**
|
||||
* Class Authenticate.
|
||||
* Class Authenticate
|
||||
*/
|
||||
class Authenticate
|
||||
{
|
||||
/**
|
||||
* The authentication factory instance.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Auth\Factory
|
||||
*/
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* Create a new middleware instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Auth\Factory $auth
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Auth $auth)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string|null $guard
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string[] ...$guards
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Illuminate\Auth\AuthenticationException
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, $guard = null)
|
||||
public function handle($request, Closure $next, ...$guards)
|
||||
{
|
||||
if (Auth::guard($guard)->guest()) {
|
||||
if ($request->ajax()) {
|
||||
return response('Unauthorized.', 401);
|
||||
}
|
||||
|
||||
return redirect()->guest('login');
|
||||
}
|
||||
if (1 === intval(auth()->user()->blocked)) {
|
||||
$message = strval(trans('firefly.block_account_logout'));
|
||||
if ('email_changed' === auth()->user()->blocked_code) {
|
||||
$message = strval(trans('firefly.email_changed_logout'));
|
||||
}
|
||||
|
||||
Session::flash('logoutMessage', $message);
|
||||
Auth::guard($guard)->logout();
|
||||
|
||||
return redirect()->guest('login');
|
||||
}
|
||||
$this->authenticate($guards);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user is logged in to any of the given guards.
|
||||
*
|
||||
* @param array $guards
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \Illuminate\Auth\AuthenticationException
|
||||
*/
|
||||
protected function authenticate(array $guards)
|
||||
{
|
||||
if (empty($guards)) {
|
||||
// go for default guard:
|
||||
if ($this->auth->check()) {
|
||||
// do an extra check on user object.
|
||||
$user = $this->auth->authenticate();
|
||||
if (1 === intval($user->blocked)) {
|
||||
$message = strval(trans('firefly.block_account_logout'));
|
||||
if ('email_changed' === $user->blocked_code) {
|
||||
$message = strval(trans('firefly.email_changed_logout'));
|
||||
}
|
||||
app('session')->flash('logoutMessage', $message);
|
||||
$this->auth->logout();
|
||||
|
||||
throw new AuthenticationException('Blocked account.', $guards);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->auth->authenticate();
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
foreach ($guards as $guard) {
|
||||
if ($this->auth->guard($guard)->check()) {
|
||||
return $this->auth->shouldUse($guard);
|
||||
}
|
||||
}
|
||||
|
||||
throw new AuthenticationException('Unauthenticated.', $guards);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,11 +23,8 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Contracts\Auth\Factory as Auth;
|
||||
use Log;
|
||||
use Preferences;
|
||||
use Session;
|
||||
|
||||
/**
|
||||
* Class AuthenticateTwoFactor.
|
||||
@@ -35,36 +32,41 @@ use Session;
|
||||
class AuthenticateTwoFactor
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
* The authentication factory instance.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string|null $guard
|
||||
*
|
||||
* @return mixed
|
||||
* @var \Illuminate\Contracts\Auth\Factory
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, $guard = null)
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* Create a new middleware instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Auth\Factory $auth
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Auth $auth)
|
||||
{
|
||||
// do the usual auth, again:
|
||||
if (Auth::guard($guard)->guest()) {
|
||||
if ($request->ajax()) {
|
||||
return response('Unauthorized.', 401);
|
||||
}
|
||||
$this->auth = $auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $request
|
||||
* @param Closure $next
|
||||
* @param array ...$guards
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|mixed
|
||||
* @throws \Illuminate\Container\EntryNotFoundException
|
||||
*/
|
||||
public function handle($request, Closure $next, ...$guards)
|
||||
{
|
||||
if ($this->auth->guest()) {
|
||||
return redirect()->guest('login');
|
||||
}
|
||||
|
||||
if (1 === intval(auth()->user()->blocked)) {
|
||||
Auth::guard($guard)->logout();
|
||||
Session::flash('logoutMessage', trans('firefly.block_account_logout'));
|
||||
|
||||
return redirect()->guest('login');
|
||||
}
|
||||
$is2faEnabled = Preferences::get('twoFactorAuthEnabled', false)->data;
|
||||
$has2faSecret = null !== Preferences::get('twoFactorAuthSecret');
|
||||
|
||||
// grab 2auth information from session.
|
||||
$is2faAuthed = 'true' === $request->cookie('twoFactorAuthenticated');
|
||||
$is2faEnabled = app('preferences')->get('twoFactorAuthEnabled', false)->data;
|
||||
$has2faSecret = null !== app('preferences')->get('twoFactorAuthSecret');
|
||||
$is2faAuthed = 'true' === $request->cookie('twoFactorAuthenticated');
|
||||
|
||||
if ($is2faEnabled && $has2faSecret && !$is2faAuthed) {
|
||||
Log::debug('Does not seem to be 2 factor authed, redirect.');
|
||||
@@ -74,4 +76,5 @@ class AuthenticateTwoFactor
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
* Binder.php
|
||||
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
||||
* Copyright (c) 2018 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
@@ -24,13 +24,20 @@ namespace FireflyIII\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use FireflyIII\Support\Domain;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Contracts\Auth\Factory as Auth;
|
||||
use Illuminate\Routing\Route;
|
||||
|
||||
/**
|
||||
* Class Binder.
|
||||
* Class HttpBinder
|
||||
*/
|
||||
class Binder
|
||||
{
|
||||
/**
|
||||
* The authentication factory instance.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Auth\Factory
|
||||
*/
|
||||
protected $auth;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@@ -38,21 +45,27 @@ class Binder
|
||||
|
||||
/**
|
||||
* Binder constructor.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Auth\Factory $auth
|
||||
*/
|
||||
public function __construct()
|
||||
public function __construct(Auth $auth)
|
||||
{
|
||||
$this->binders = Domain::getBindables();
|
||||
$this->auth = $auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string[] ...$guards
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Illuminate\Auth\AuthenticationException
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
public function handle($request, Closure $next, ...$guards)
|
||||
{
|
||||
foreach ($request->route()->parameters() as $key => $value) {
|
||||
if (isset($this->binders[$key])) {
|
||||
@@ -71,7 +84,7 @@ class Binder
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function performBinding($key, $value, $route)
|
||||
private function performBinding(string $key, string $value, Route $route)
|
||||
{
|
||||
$class = $this->binders[$key];
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user