mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-08-16 19:04:36 +00:00
Compare commits
595 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
8b9607f9b5 | ||
|
af107ad5e8 | ||
|
8926d62165 | ||
|
45344ee347 | ||
|
b7dab817f2 | ||
|
2c966a1234 | ||
|
143ea69c0d | ||
|
8a02ead013 | ||
|
930d5ab941 | ||
|
e54a56d3a8 | ||
|
f5216c0d85 | ||
|
ebc77540b9 | ||
|
28d2583c10 | ||
|
b0988a7b00 | ||
|
2c4920db2d | ||
|
8321663815 | ||
|
ac0280d460 | ||
|
ba6f4268f0 | ||
|
0bd18f94ac | ||
|
66fb63661f | ||
|
d9b16beb0a | ||
|
6a01a9bdfd | ||
|
b81b34a706 | ||
|
3750a00b5f | ||
|
05ddcc169d | ||
|
f571a5f1bd | ||
|
48b786adea | ||
|
d691fa9b4d | ||
|
da50f9e419 | ||
|
103de5e18a | ||
|
dac6efd98b | ||
|
46aa7f81b2 | ||
|
0683c7cd67 | ||
|
50d7aa7b6a | ||
|
b781215d0a | ||
|
866bc2f3bd | ||
|
49d4705014 | ||
|
9163fcfccb | ||
|
7e10641461 | ||
|
cdc0e3cfd8 | ||
|
466e81d56a | ||
|
5953f691d1 | ||
|
e2790ca6c1 | ||
|
66dbd48b76 | ||
|
73cfbbd2ba | ||
|
38bc38bf26 | ||
|
e12d13c838 | ||
|
fcc3af6136 | ||
|
491298e1cb | ||
|
72b5895217 | ||
|
0a8f4017bd | ||
|
cb985f5897 | ||
|
968ec0853f | ||
|
0bde72d3df | ||
|
45293fbd42 | ||
|
72997065f0 | ||
|
a838dc163d | ||
|
3d15a4ca6d | ||
|
51c7d4fb1b | ||
|
fa586dba7e | ||
|
8ad40389f2 | ||
|
b3333cc2d3 | ||
|
3699a7ba9a | ||
|
204e521ba4 | ||
|
c9bab3e5c3 | ||
|
3d00e20238 | ||
|
c3958ed3c4 | ||
|
e5b88be5fa | ||
|
425552988a | ||
|
a81dd8abe5 | ||
|
bac8154a5b | ||
|
737d15fa0e | ||
|
5f2317af7f | ||
|
2bd1f783e5 | ||
|
d6c0c9f963 | ||
|
21b6ad7a41 | ||
|
a65d609fdc | ||
|
04e676b936 | ||
|
be8eaaffdf | ||
|
28c753523f | ||
|
7fbd0b2ffc | ||
|
7ffb48a87a | ||
|
d485270e1f | ||
|
5fd688b266 | ||
|
3716668e0c | ||
|
ae7fd18c34 | ||
|
4f59b1d32f | ||
|
90cb3279df | ||
|
cf0c7ef6b2 | ||
|
47c23781d9 | ||
|
e258c050f7 | ||
|
57801b2f34 | ||
|
710e9c9423 | ||
|
deefef83bd | ||
|
51e30aed66 | ||
|
8d109a3cfe | ||
|
3424e019b5 | ||
|
c6b4bceb67 | ||
|
afb4155015 | ||
|
8d99baf38a | ||
|
b91cb60328 | ||
|
c0d62237fc | ||
|
223ea80860 | ||
|
5a77bef494 | ||
|
80c0efe821 | ||
|
8044d89557 | ||
|
4f0ed97410 | ||
|
af7952f204 | ||
|
d8dcae856b | ||
|
7296796ed9 | ||
|
a2c2bb4948 | ||
|
72ebfdc20e | ||
|
16b95ea78a | ||
|
c04f08dfd8 | ||
|
a30793e818 | ||
|
e39e1eaf21 | ||
|
ab22d2cbaa | ||
|
96ddbe7227 | ||
|
4d09235aef | ||
|
136b8975e3 | ||
|
e21b1eca17 | ||
|
244b90b1d4 | ||
|
b318f3f940 | ||
|
e211c9812e | ||
|
eef28d96f4 | ||
|
c8227e09ee | ||
|
3e05fd91d9 | ||
|
450baba56a | ||
|
17a8c4918c | ||
|
0e2419d61a | ||
|
79b1a2ca6d | ||
|
2213c68155 | ||
|
2492b1fa96 | ||
|
6c6598dac5 | ||
|
a137112e66 | ||
|
8642ae8180 | ||
|
894c4dc5a7 | ||
|
d96063ea6e | ||
|
c3dc193f3e | ||
|
3c2952009e | ||
|
f4ade470df | ||
|
2e33b43389 | ||
|
92799699bc | ||
|
7ab0508167 | ||
|
3c65c28936 | ||
|
43892da07e | ||
|
7c436920a4 | ||
|
89d565e63b | ||
|
150b6fe5b6 | ||
|
0e77574c26 | ||
|
df23863443 | ||
|
581bf11b21 | ||
|
d602d4b429 | ||
|
d1d4a52934 | ||
|
375d113769 | ||
|
9b83974bff | ||
|
3c68c99bd5 | ||
|
ec4b37c596 | ||
|
ba9601d21c | ||
|
50c13fd469 | ||
|
7af072b8fc | ||
|
faa128d41e | ||
|
868fe46932 | ||
|
e9e4307ce5 | ||
|
774d4844a9 | ||
|
586c53e670 | ||
|
68e073fbff | ||
|
8101dc37b1 | ||
|
63f16c458d | ||
|
821e007e95 | ||
|
1656a2f11a | ||
|
4dbc135dce | ||
|
fc886f6bc1 | ||
|
f93e480466 | ||
|
fe807e23f8 | ||
|
ecf61c31f1 | ||
|
4feff18af5 | ||
|
a07c52e0d8 | ||
|
7bb07d7f55 | ||
|
f12dfc8a14 | ||
|
be030f15c4 | ||
|
f5fb6c063b | ||
|
fb722f06b9 | ||
|
c0ea19e15e | ||
|
cdeb1ad87c | ||
|
0dbe4e94fa | ||
|
b5e2e8aa1d | ||
|
9502010248 | ||
|
fea0557b47 | ||
|
ed4fcc9011 | ||
|
ed12ea7cfb | ||
|
73e526645e | ||
|
72aeafb2b5 | ||
|
cc1af60cb4 | ||
|
359fab315f | ||
|
6a9574bab9 | ||
|
83d6158483 | ||
|
63ef89b6cc | ||
|
b0beab4cd3 | ||
|
a34782575f | ||
|
142bdc9430 | ||
|
14b79cb0a4 | ||
|
ce5beeaf2c | ||
|
31114a2ca5 | ||
|
32528094ad | ||
|
0a2a01c44c | ||
|
c1888dc3ac | ||
|
4d76afbe01 | ||
|
76d7a97f93 | ||
|
8b1366b20a | ||
|
e0f9685578 | ||
|
5235657954 | ||
|
a15fbc8094 | ||
|
546f1d9c50 | ||
|
74231f552a | ||
|
b250a10e3c | ||
|
a9f1b31dd6 | ||
|
7fe393acaf | ||
|
04faba4db5 | ||
|
91bba40c20 | ||
|
79e39f7de8 | ||
|
9c09353559 | ||
|
50752a5bfe | ||
|
d59879db7d | ||
|
aab125da27 | ||
|
74fc731f96 | ||
|
bd0050fec2 | ||
|
aa5e313b92 | ||
|
e89d613b7e | ||
|
8757929ead | ||
|
e0a9b19802 | ||
|
308da6dc6e | ||
|
b6960fb0e5 | ||
|
137208c3fd | ||
|
d7a9a62a1d | ||
|
075315bdaa | ||
|
3948fcd614 | ||
|
8e61e129ab | ||
|
20cffd0502 | ||
|
5df09dab09 | ||
|
7446b911e5 | ||
|
f15267c1ab | ||
|
9c9fc2b5dc | ||
|
28f601b54b | ||
|
18b8a05014 | ||
|
910c995ed8 | ||
|
498468aa2c | ||
|
637aebcb34 | ||
|
9afd5cb277 | ||
|
bc525e7272 | ||
|
a80180780d | ||
|
0d73086c37 | ||
|
02ae39238d | ||
|
43d6b51d42 | ||
|
84566310de | ||
|
6a6ec9fbe4 | ||
|
84a7f825d7 | ||
|
0372c1aaf1 | ||
|
c9fff197f7 | ||
|
6900392e43 | ||
|
c00bcd78cc | ||
|
b2b82124e6 | ||
|
3de57c668f | ||
|
43669648ce | ||
|
3b73b416d5 | ||
|
5153591c8f | ||
|
3172bc90da | ||
|
76a1b2cd51 | ||
|
bdf7eee72f | ||
|
2d4b148b2c | ||
|
d67db74ca2 | ||
|
516725456f | ||
|
001d72a484 | ||
|
c555e28988 | ||
|
af13d1943f | ||
|
52df2edc8f | ||
|
cd08484a13 | ||
|
f38d38f139 | ||
|
93b6c68938 | ||
|
a4cc25175a | ||
|
3fb14b4708 | ||
|
6bdb6db330 | ||
|
d05c165ace | ||
|
ab53cdb896 | ||
|
c1f142af78 | ||
|
6e261abb73 | ||
|
39af9e4414 | ||
|
5b50abb2c7 | ||
|
13bda0a264 | ||
|
1658c666ab | ||
|
170aebfe54 | ||
|
c4ef379d0e | ||
|
18b038d8ff | ||
|
12ee5da872 | ||
|
601f9f86bb | ||
|
d7329a5915 | ||
|
9e7b730002 | ||
|
2d59d845bc | ||
|
c2645894e0 | ||
|
3751106317 | ||
|
60bb639351 | ||
|
74c50930bd | ||
|
9105104303 | ||
|
540dde135e | ||
|
f8936210cf | ||
|
1dc6d8de40 | ||
|
1069db3c13 | ||
|
65122f0144 | ||
|
d2c018f7da | ||
|
46493c2af6 | ||
|
c303e03f76 | ||
|
d8b65f62e7 | ||
|
854368a8f3 | ||
|
7653a34aea | ||
|
ee50b58e00 | ||
|
1eb60ab100 | ||
|
4a20eef351 | ||
|
26c9b2c353 | ||
|
16374bce9b | ||
|
86011d4ea2 | ||
|
fcb8b02da9 | ||
|
571165c2bb | ||
|
c842113610 | ||
|
974a8b3b70 | ||
|
2e20c99ada | ||
|
aa88ff6f2c | ||
|
5e6aa63d03 | ||
|
ad6700c114 | ||
|
f08c6efb00 | ||
|
cc807ec132 | ||
|
24e7c68243 | ||
|
ab25edd37a | ||
|
be47fde6c2 | ||
|
1ffa8c5e72 | ||
|
d855ccb8a7 | ||
|
d88919474b | ||
|
e139664301 | ||
|
5adf5f6e3f | ||
|
aef2075c8e | ||
|
922b2962a3 | ||
|
b4a401700e | ||
|
e9601bb9c1 | ||
|
58af3dc6ea | ||
|
074295df61 | ||
|
ec349b31c7 | ||
|
5ae236e016 | ||
|
d7c5897aba | ||
|
5252e7efe7 | ||
|
fc7d65629a | ||
|
f28fdf8252 | ||
|
5d07c4a949 | ||
|
fdd9eaab4b | ||
|
e0d863a46f | ||
|
3aacb6f5f3 | ||
|
428e331b3e | ||
|
847e05e9a7 | ||
|
087eb5dbe6 | ||
|
f15932b2ac | ||
|
d3a4c3795d | ||
|
b15d55e1d9 | ||
|
4f5889cc5b | ||
|
bf2a104a4e | ||
|
0c6dd5cd16 | ||
|
5efb06a7aa | ||
|
b13acef272 | ||
|
cfa67d6c0f | ||
|
e70444f19a | ||
|
0258982e60 | ||
|
70eed5cb5e | ||
|
a650fa51f7 | ||
|
cb205580d8 | ||
|
f9329aac00 | ||
|
745f4a7523 | ||
|
60254dafd7 | ||
|
a8d60388ba | ||
|
83ec60254c | ||
|
c15c45f765 | ||
|
cbe52b5089 | ||
|
e4e2921f3e | ||
|
4673170531 | ||
|
2c2ed26c38 | ||
|
94be5244fe | ||
|
f137a08493 | ||
|
48624d0a34 | ||
|
4cceb3ddaa | ||
|
f728395603 | ||
|
3e82d43807 | ||
|
2194c4e0a9 | ||
|
c581080f3f | ||
|
f6b1ec27e5 | ||
|
368b183230 | ||
|
9028ad36ad | ||
|
6cc041cd39 | ||
|
63ff01e78d | ||
|
9e5484937e | ||
|
b8ed489b14 | ||
|
765152d04b | ||
|
14934367d8 | ||
|
04164500c8 | ||
|
5160f2c298 | ||
|
124c9303b9 | ||
|
cd27f0ad69 | ||
|
a7555bcce3 | ||
|
6b5c4fd3f4 | ||
|
cc55e2acee | ||
|
1511f75a80 | ||
|
f01bbefc1f | ||
|
1d1eb5ffa8 | ||
|
a465cb2191 | ||
|
42d13e02ef | ||
|
d00786c43f | ||
|
4b47f99829 | ||
|
35aaf40003 | ||
|
cc5b4a1e02 | ||
|
7079521e8c | ||
|
b5025560a5 | ||
|
3f4bdd7f0e | ||
|
e94bb9b549 | ||
|
1ddaacbef5 | ||
|
e8b40518e0 | ||
|
0f88cbb41b | ||
|
780d137b76 | ||
|
ad8a9717d1 | ||
|
9d6ea6b2f6 | ||
|
7559383089 | ||
|
f84381c927 | ||
|
cb0122a43f | ||
|
6776b20989 | ||
|
e98d556022 | ||
|
5bf18b69d7 | ||
|
ea17f045a7 | ||
|
526f565ea7 | ||
|
4aff9d6e73 | ||
|
bf516d4d21 | ||
|
ae92e409d9 | ||
|
4d017dc8a9 | ||
|
707f4e2965 | ||
|
1c3bffdc50 | ||
|
e54ddcb8b0 | ||
|
ddefb0debc | ||
|
92d8dde90d | ||
|
1bb0508ddf | ||
|
a280a326b9 | ||
|
683e9b7c2c | ||
|
a44e5da421 | ||
|
8cd2c90ad7 | ||
|
5e57a390a2 | ||
|
620848272e | ||
|
1e86794416 | ||
|
e36717259b | ||
|
75b9238b90 | ||
|
ce5b20027e | ||
|
0de1242c83 | ||
|
8bd445ab19 | ||
|
fdef0de163 | ||
|
b1b03a4325 | ||
|
0587d96474 | ||
|
c2241567e4 | ||
|
7ac24ba418 | ||
|
c933ffec66 | ||
|
e587d934b1 | ||
|
f354e90656 | ||
|
1b0bc7ec6e | ||
|
ee1acb9c00 | ||
|
06862a2812 | ||
|
5fa87e18db | ||
|
77989e2720 | ||
|
3a1102fa4e | ||
|
8a9974ce53 | ||
|
4be8f1ca03 | ||
|
1ec2970ee3 | ||
|
81b3a22606 | ||
|
f81a475cc9 | ||
|
d7ee03d4f9 | ||
|
c1c06410c2 | ||
|
657d16bb60 | ||
|
e65a4c1010 | ||
|
e23d3f5661 | ||
|
e13611f7af | ||
|
596cd09489 | ||
|
0be5b27d34 | ||
|
a27471ae55 | ||
|
e27e3622a8 | ||
|
e95273b72b | ||
|
583d4f3249 | ||
|
d6967c4516 | ||
|
40b3097374 | ||
|
1a1f127993 | ||
|
a0f34a7ce1 | ||
|
db020db34b | ||
|
681167bc1b | ||
|
40e49ffc37 | ||
|
834b1afb38 | ||
|
62d5a1da87 | ||
|
8d8308e557 | ||
|
e1aa63487a | ||
|
b7fbe110d4 | ||
|
58859eb35a | ||
|
4b7e1ae1c6 | ||
|
3a06a6ac07 | ||
|
db0f269dc8 | ||
|
3cabe6ca5a | ||
|
d483005219 | ||
|
fea9bc4e7e | ||
|
d579992c98 | ||
|
ad1c61d959 | ||
|
bb1da31830 | ||
|
a50949e554 | ||
|
14dce8a10b | ||
|
1240c8f685 | ||
|
cc7c2e952c | ||
|
409ec2e086 | ||
|
a7f6848e53 | ||
|
4b0b79199d | ||
|
d1d6c48d9b | ||
|
21631780bb | ||
|
b935e32340 | ||
|
72dd064932 | ||
|
2e75446665 | ||
|
be17e4481e | ||
|
616c849b1f | ||
|
71947c097f | ||
|
546787802d | ||
|
294d0e388a | ||
|
193a1b0325 | ||
|
12743217a2 | ||
|
b252b9da66 | ||
|
cdef9c3c7e | ||
|
71dcebb744 | ||
|
25f248c60a | ||
|
d5cbc17831 | ||
|
7a10217511 | ||
|
7559efab77 | ||
|
8254efbd03 | ||
|
4ae24225a5 | ||
|
67d9154563 | ||
|
ad0319c188 | ||
|
eb650ea3ec | ||
|
7eba33e805 | ||
|
e1cb9d387e | ||
|
2ace7c3ca0 | ||
|
58014f0592 | ||
|
1d4938bb09 | ||
|
bbf4007c3e | ||
|
4d5124fb4c | ||
|
14a7cd05b1 | ||
|
946be80eef | ||
|
9ad8b1a980 | ||
|
f733216fcb | ||
|
ffc6139e21 | ||
|
571cac6644 | ||
|
2738ac5a5c | ||
|
7dfde51b84 | ||
|
2d2f18e538 | ||
|
3af0dd2e3b | ||
|
349e077802 | ||
|
812aae358f | ||
|
c3c59d0627 | ||
|
89518b412d | ||
|
f43b026162 | ||
|
b806c70f52 | ||
|
10bff3c0b8 | ||
|
65c12fd0b2 | ||
|
50f71c4130 | ||
|
8e401a53dc | ||
|
64a289a47c | ||
|
8f2c37061b | ||
|
39f2de6b90 | ||
|
855ba8d4f3 | ||
|
74f098e718 | ||
|
56c8a84691 | ||
|
8bbf319032 | ||
|
afbca4ae65 | ||
|
0ef6d2f91a | ||
|
fbe4435599 | ||
|
34be565dd1 | ||
|
af838e4ed1 | ||
|
60fe8ce011 | ||
|
8ece341467 | ||
|
dfa6bdbcb8 | ||
|
fb2481ebaa | ||
|
4874c116cf | ||
|
e19c44efbd | ||
|
4b687b9bdc | ||
|
6af79ef601 | ||
|
352b996ad2 | ||
|
4a93bb35f8 | ||
|
8e1f493daf | ||
|
59ee153375 | ||
|
60f7f1fc16 | ||
|
b7433683d8 | ||
|
42799b9273 | ||
|
860a0f790e | ||
|
8daccbfbb4 | ||
|
285b77dcb7 |
@@ -1,3 +1 @@
|
||||
src_dir: .
|
||||
coverage_clover: storage/coverage/clover.xml
|
||||
json_path: storage/coverage/coveralls-upload.json
|
||||
|
@@ -1,6 +1,6 @@
|
||||
APP_ENV=production
|
||||
APP_DEBUG=false
|
||||
APP_KEY=SomeRandomString
|
||||
APP_KEY=SomeRandomStringOf32CharsExactly
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=localhost
|
||||
@@ -17,4 +17,8 @@ EMAIL_USERNAME=
|
||||
EMAIL_PASSWORD=
|
||||
ANALYTICS_ID=
|
||||
EMAIL_PRETEND=false
|
||||
RUNCLEANUP=true
|
||||
RUNCLEANUP=true
|
||||
SITE_OWNER=mail@example.com
|
||||
|
||||
SENDGRID_USERNAME=
|
||||
SENDGRID_PASSWORD=
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -31,3 +31,6 @@ addNewLines.php
|
||||
.phpstorm.meta.php
|
||||
.env.backup
|
||||
.env.local
|
||||
|
||||
tests/_output/*
|
||||
tests/_output/*
|
||||
|
22
.jshintrc
Normal file
22
.jshintrc
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"undef": true,
|
||||
"unused": false,
|
||||
"strict": true,
|
||||
"browser": true,
|
||||
"jquery": true,
|
||||
"devel": true,
|
||||
"globals": [
|
||||
"language",
|
||||
"token",
|
||||
"currencyCode",
|
||||
"$",
|
||||
"token",
|
||||
"accountID",
|
||||
"billID",
|
||||
"currentMonthName",
|
||||
"previousMonthName",
|
||||
"nextMonthName",
|
||||
"everything",
|
||||
"moment"
|
||||
]
|
||||
}
|
3
.scrutinizer.yml
Normal file
3
.scrutinizer.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
# .scrutinizer.yml
|
||||
tools:
|
||||
external_code_coverage: false
|
@@ -6,10 +6,6 @@ php:
|
||||
- 5.5
|
||||
- 5.6
|
||||
|
||||
addons:
|
||||
code_climate:
|
||||
repo_token: 26489f9e854fcdf7e7660ba29c1455694685465b1f90329a79f7d2bf448acb61
|
||||
|
||||
install:
|
||||
- composer update
|
||||
- php artisan env
|
||||
@@ -17,6 +13,3 @@ install:
|
||||
|
||||
script:
|
||||
- phpunit
|
||||
|
||||
after_script:
|
||||
- php vendor/bin/coveralls
|
||||
|
57
README.md
57
README.md
@@ -1,13 +1,11 @@
|
||||
# Firefly III
|
||||
#### v3.4.0.10
|
||||
|
||||
[](https://travis-ci.org/JC5/firefly-iii)
|
||||
[](http://stillmaintained.com/JC5/firefly-iii)
|
||||
[](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102)
|
||||
[](https://codeclimate.com/github/JC5/firefly-iii)
|
||||
[](https://coveralls.io/r/JC5/firefly-iii?branch=master)
|
||||
[](https://packagist.org/packages/grumpydictator/firefly-iii)
|
||||

|
||||
[](https://packagist.org/packages/grumpydictator/firefly-iii)
|
||||
[](https://packagist.org/packages/grumpydictator/firefly-iii)
|
||||
|
||||
[](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master)
|
||||
[](https://scrutinizer-ci.com/g/JC5/firefly-iii/build-status/master)
|
||||
|
||||
## About
|
||||
|
||||
"Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared
|
||||
@@ -30,7 +28,7 @@ To get to know Firefly, and to see if it fits you, check out these resources:
|
||||
and the philosophy behind it.
|
||||
|
||||
|
||||
#### About the name (if you care)
|
||||
#### About the name (should you care)
|
||||
|
||||
It's III, or 3, because [version 2](https://github.com/JC5/Firefly) and version 1 (not online) preceded it. It has been growing steadily ever since.
|
||||
|
||||
@@ -63,19 +61,19 @@ Everything is organised:
|
||||
|
||||
_Please note that everything in these screenshots is fictional and may not be realistic._
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
## Running and installing
|
||||
|
||||
@@ -84,14 +82,35 @@ If you're still interested please read [the installation guide](https://github.c
|
||||
and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)**.
|
||||
|
||||
If you want to try out Firefly III, you can do so on [this dedicated website](https://geld.nder.be/).
|
||||
This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site.
|
||||
This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site. Accounts on the demo sites will stop working after one week.
|
||||
|
||||
## Credits
|
||||
|
||||
Firefly III uses the following libraries and tools:
|
||||
|
||||
* The AdminLTE template by [Almsaseed Studio](https://almsaeedstudio.com/)
|
||||
* The [Google charts](https://developers.google.com/chart/) library.
|
||||
* [Chart.js](http://www.chartjs.org/)
|
||||
* [Bootstrap](http://getbootstrap.com/)
|
||||
* [Laravel](http://laravel.com/)
|
||||
* [Twig](http://twig.sensiolabs.org/)
|
||||
* For development, some of the excellent tools made by [Barry van den Heuvel](https://github.com/barryvdh)
|
||||
* [Bootstrap sortable](https://github.com/drvic10k/bootstrap-sortable) by [Matúš Brliť](https://github.com/drvic10k).
|
||||
* [Date range picker](https://github.com/dangrossman/bootstrap-daterangepicker/) by [Dan Grossman](https://github.com/dangrossman)
|
||||
* The [real favicon generator](http://realfavicongenerator.net/)
|
||||
* Various other open source components (see [composer.json](https://github.com/JC5/firefly-iii/blob/master/composer.json))
|
||||
|
||||
|
||||
## Current state
|
||||
|
||||
Firefly III is pretty much all grown up. Full test coverage (nerd alert!) is coming. One of the things on the todo-list
|
||||
is adding translations.
|
||||
Firefly III is pretty much all grown up. Full test coverage (nerd alert!) is coming. Translations are a work in progress.
|
||||
|
||||
Questions, ideas, bugs or other things to contribute? [Let me know](https://github.com/JC5/firefly-iii/issues/new)!
|
||||
|
||||
If you like this tool, feel free to [donate me some beer money](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2ZMV952UUSCLU&lc=NL&item_name=Development%20of%20Firefly¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted).
|
||||
|
||||
[](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102)
|
||||
[](https://codeclimate.com/github/JC5/firefly-iii)
|
||||
[](http://stillmaintained.com/JC5/firefly-iii)
|
||||
[](https://packagist.org/packages/grumpydictator/firefly-iii)
|
||||

|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
/**
|
||||
* Class Handler
|
||||
@@ -33,7 +34,7 @@ class Handler extends ExceptionHandler
|
||||
*/
|
||||
public function render($request, Exception $e)
|
||||
{
|
||||
if ($this->isHttpException($e)) {
|
||||
if ($e instanceof HttpException) {
|
||||
return $this->renderHttpException($e);
|
||||
} else {
|
||||
return parent::render($request, $e);
|
||||
|
52
app/Generator/Chart/Account/AccountChartGenerator.php
Normal file
52
app/Generator/Chart/Account/AccountChartGenerator.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Account;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Account;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface AccountChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Account
|
||||
*/
|
||||
interface AccountChartGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all(Collection $accounts, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $accounts, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function single(Account $account, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end);
|
||||
}
|
194
app/Generator/Chart/Account/ChartJsAccountChartGenerator.php
Normal file
194
app/Generator/Chart/Account/ChartJsAccountChartGenerator.php
Normal file
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Account;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Config;
|
||||
use FireflyIII\Models\Account;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
use Steam;
|
||||
|
||||
/**
|
||||
* Class ChartJsAccountChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Account
|
||||
*/
|
||||
class ChartJsAccountChartGenerator implements AccountChartGenerator
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
return $this->frontpage($accounts, $start, $end);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
$data = [
|
||||
'count' => 1,
|
||||
'labels' => [], 'datasets' => [[
|
||||
'label' => trans('firefly.spent'),
|
||||
'data' => []]]];
|
||||
|
||||
bcscale(2);
|
||||
$start->subDay();
|
||||
$ids = $this->getIdsFromCollection($accounts);
|
||||
$startBalances = Steam::balancesById($ids, $start);
|
||||
$endBalances = Steam::balancesById($ids, $end);
|
||||
|
||||
$accounts->each(
|
||||
function (Account $account) use ($startBalances, $endBalances) {
|
||||
$id = $account->id;
|
||||
$startBalance = $this->isInArray($startBalances, $id);
|
||||
$endBalance = $this->isInArray($endBalances, $id);
|
||||
$diff = bcsub($endBalance, $startBalance);
|
||||
$account->difference = round($diff, 2);
|
||||
}
|
||||
);
|
||||
|
||||
$accounts = $accounts->sortByDesc(
|
||||
function (Account $account) {
|
||||
return $account->difference;
|
||||
}
|
||||
);
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
if ($account->difference > 0) {
|
||||
$data['labels'][] = $account->name;
|
||||
$data['datasets'][0]['data'][] = $account->difference;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $array
|
||||
* @param $entryId
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function isInArray($array, $entryId)
|
||||
{
|
||||
if (isset($array[$entryId])) {
|
||||
return $array[$entryId];
|
||||
}
|
||||
|
||||
return '0';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.monthAndDay.' . $language);
|
||||
$data = [
|
||||
'count' => 0,
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
$current = clone $start;
|
||||
while ($current <= $end) {
|
||||
$data['labels'][] = $current->formatLocalized($format);
|
||||
$current->addDay();
|
||||
}
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$set = [
|
||||
'label' => $account->name,
|
||||
'fillColor' => 'rgba(220,220,220,0.2)',
|
||||
'strokeColor' => 'rgba(220,220,220,1)',
|
||||
'pointColor' => 'rgba(220,220,220,1)',
|
||||
'pointStrokeColor' => '#fff',
|
||||
'pointHighlightFill' => '#fff',
|
||||
'pointHighlightStroke' => 'rgba(220,220,220,1)',
|
||||
'data' => [],
|
||||
];
|
||||
$current = clone $start;
|
||||
while ($current <= $end) {
|
||||
$set['data'][] = Steam::balance($account, $current);
|
||||
$current->addDay();
|
||||
}
|
||||
$data['datasets'][] = $set;
|
||||
}
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function single(Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.monthAndDay.' . $language);
|
||||
|
||||
$data = [
|
||||
'count' => 1,
|
||||
'labels' => [],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => $account->name,
|
||||
'data' => []
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
$current = clone $start;
|
||||
|
||||
while ($end >= $current) {
|
||||
$data['labels'][] = $current->formatLocalized($format);
|
||||
$data['datasets'][0]['data'][] = Steam::balance($account, $current);
|
||||
$current->addDay();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $collection
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getIdsFromCollection(Collection $collection)
|
||||
{
|
||||
$ids = [];
|
||||
foreach ($collection as $entry) {
|
||||
$ids[] = $entry->id;
|
||||
}
|
||||
|
||||
return array_unique($ids);
|
||||
|
||||
}
|
||||
}
|
32
app/Generator/Chart/Bill/BillChartGenerator.php
Normal file
32
app/Generator/Chart/Bill/BillChartGenerator.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace FireflyIII\Generator\Chart\Bill;
|
||||
|
||||
|
||||
use FireflyIII\Models\Bill;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface BillChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Bill
|
||||
*/
|
||||
interface BillChartGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $paid
|
||||
* @param Collection $unpaid
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $paid, Collection $unpaid);
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function single(Bill $bill, Collection $entries);
|
||||
|
||||
}
|
114
app/Generator/Chart/Bill/ChartJsBillChartGenerator.php
Normal file
114
app/Generator/Chart/Bill/ChartJsBillChartGenerator.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Bill;
|
||||
|
||||
use Config;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
|
||||
/**
|
||||
* Class ChartJsBillChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Bill
|
||||
*/
|
||||
class ChartJsBillChartGenerator implements BillChartGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $paid
|
||||
* @param Collection $unpaid
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $paid, Collection $unpaid)
|
||||
{
|
||||
$paidDescriptions = [];
|
||||
$paidAmount = 0;
|
||||
$unpaidDescriptions = [];
|
||||
$unpaidAmount = 0;
|
||||
bcscale(2);
|
||||
|
||||
/** @var TransactionJournal $entry */
|
||||
foreach ($paid as $entry) { // loop paid and create single entry:
|
||||
$paidDescriptions[] = $entry->description;
|
||||
$paidAmount = bcadd($paidAmount, $entry->amount_positive);
|
||||
}
|
||||
/** @var Bill $entry */
|
||||
foreach ($unpaid as $entry) { // loop unpaid:
|
||||
$description = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')';
|
||||
$amount = bcdiv(bcadd($entry[0]->amount_max, $entry[0]->amount_min), 2);
|
||||
$unpaidDescriptions[] = $description;
|
||||
$unpaidAmount = bcadd($unpaidAmount, $amount);
|
||||
unset($amount, $description);
|
||||
}
|
||||
|
||||
$data = [
|
||||
[
|
||||
'value' => $unpaidAmount,
|
||||
'color' => 'rgba(53, 124, 165,0.7)',
|
||||
'highlight' => 'rgba(53, 124, 165,0.9)',
|
||||
'label' => trans('firefly.unpaid'),
|
||||
],
|
||||
[
|
||||
'value' => $paidAmount,
|
||||
'color' => 'rgba(0, 141, 76, 0.7)',
|
||||
'highlight' => 'rgba(0, 141, 76, 0.9)',
|
||||
'label' => trans('firefly.paid'),
|
||||
]
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function single(Bill $bill, Collection $entries)
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.month.' . $language);
|
||||
|
||||
$data = [
|
||||
'count' => 3,
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
|
||||
// dataset: max amount
|
||||
// dataset: min amount
|
||||
// dataset: actual amount
|
||||
|
||||
$minAmount = [];
|
||||
$maxAmount = [];
|
||||
$actualAmount = [];
|
||||
foreach ($entries as $entry) {
|
||||
$data['labels'][] = $entry->date->formatLocalized($format);
|
||||
$minAmount[] = round($bill->amount_min, 2);
|
||||
$maxAmount[] = round($bill->amount_max, 2);
|
||||
$actualAmount[] = round(($entry->amount * -1), 2);
|
||||
}
|
||||
|
||||
$data['datasets'][] = [
|
||||
'label' => trans('firefly.minAmount'),
|
||||
'data' => $minAmount,
|
||||
];
|
||||
$data['datasets'][] = [
|
||||
'label' => trans('firefly.billEntry'),
|
||||
'data' => $actualAmount,
|
||||
];
|
||||
$data['datasets'][] = [
|
||||
'label' => trans('firefly.maxAmount'),
|
||||
'data' => $maxAmount,
|
||||
];
|
||||
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
43
app/Generator/Chart/Budget/BudgetChartGenerator.php
Normal file
43
app/Generator/Chart/Budget/BudgetChartGenerator.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Budget;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface BudgetChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Budget
|
||||
*/
|
||||
interface BudgetChartGenerator
|
||||
{
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function budget(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function budgetLimit(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function year(Collection $budgets, Collection $entries);
|
||||
|
||||
}
|
144
app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php
Normal file
144
app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Budget;
|
||||
|
||||
|
||||
use Config;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
|
||||
/**
|
||||
* Class ChartJsBudgetChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Budget
|
||||
*/
|
||||
class ChartJsBudgetChartGenerator implements BudgetChartGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
* @param string $dateFormat
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function budget(Collection $entries, $dateFormat = 'month')
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.' . $dateFormat . '.' . $language);
|
||||
|
||||
$data = [
|
||||
'labels' => [],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => 'Amount',
|
||||
'data' => [],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
/** @var array $entry */
|
||||
foreach ($entries as $entry) {
|
||||
$data['labels'][] = $entry[0]->formatLocalized($format);
|
||||
$data['datasets'][0]['data'][] = $entry[1];
|
||||
|
||||
}
|
||||
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function budgetLimit(Collection $entries)
|
||||
{
|
||||
return $this->budget($entries, 'monthAndDay');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $entries)
|
||||
{
|
||||
$data = [
|
||||
'count' => 0,
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
// dataset: left
|
||||
// dataset: spent
|
||||
// dataset: overspent
|
||||
$left = [];
|
||||
$spent = [];
|
||||
$overspent = [];
|
||||
foreach ($entries as $entry) {
|
||||
if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) {
|
||||
$data['labels'][] = $entry[0];
|
||||
$left[] = round($entry[1], 2);
|
||||
$spent[] = round($entry[2], 2);
|
||||
$overspent[] = round($entry[3], 2);
|
||||
}
|
||||
}
|
||||
|
||||
$data['datasets'][] = [
|
||||
'label' => trans('firefly.left'),
|
||||
'data' => $left,
|
||||
];
|
||||
$data['datasets'][] = [
|
||||
'label' => trans('firefly.spent'),
|
||||
'data' => $spent,
|
||||
];
|
||||
$data['datasets'][] = [
|
||||
'label' => trans('firefly.overspent'),
|
||||
'data' => $overspent,
|
||||
];
|
||||
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function year(Collection $budgets, Collection $entries)
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.month.' . $language);
|
||||
|
||||
$data = [
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
|
||||
foreach ($budgets as $budget) {
|
||||
$data['labels'][] = $budget->name;
|
||||
}
|
||||
/** @var array $entry */
|
||||
foreach ($entries as $entry) {
|
||||
$array = [
|
||||
'label' => $entry[0]->formatLocalized($format),
|
||||
'data' => [],
|
||||
];
|
||||
array_shift($entry);
|
||||
$array['data'] = $entry;
|
||||
$data['datasets'][] = $array;
|
||||
|
||||
}
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
52
app/Generator/Chart/Category/CategoryChartGenerator.php
Normal file
52
app/Generator/Chart/Category/CategoryChartGenerator.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Category;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface CategoryChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Category
|
||||
*/
|
||||
interface CategoryChartGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function period(Collection $entries);
|
||||
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function spentInYear(Collection $categories, Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function earnedInYear(Collection $categories, Collection $entries);
|
||||
}
|
161
app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php
Normal file
161
app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php
Normal file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Category;
|
||||
|
||||
use Config;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
|
||||
|
||||
/**
|
||||
* Class ChartJsCategoryChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Category
|
||||
*/
|
||||
class ChartJsCategoryChartGenerator implements CategoryChartGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all(Collection $entries)
|
||||
{
|
||||
|
||||
|
||||
$data = [
|
||||
'count' => 2,
|
||||
'labels' => [],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => trans('firefly.spent'),
|
||||
'data' => []
|
||||
],
|
||||
[
|
||||
'label' => trans('firefly.earned'),
|
||||
'data' => []
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$data['labels'][] = $entry[1];
|
||||
$spent = round($entry[2], 2);
|
||||
$earned = round($entry[3], 2);
|
||||
|
||||
$data['datasets'][0]['data'][] = $spent == 0 ? null : $spent * -1;
|
||||
$data['datasets'][1]['data'][] = $earned == 0 ? null : $earned;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $entries)
|
||||
{
|
||||
$data = [
|
||||
'count' => 1,
|
||||
'labels' => [],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => trans('firefly.spent'),
|
||||
'data' => []
|
||||
]
|
||||
],
|
||||
];
|
||||
foreach ($entries as $entry) {
|
||||
if ($entry['sum'] != 0) {
|
||||
$data['labels'][] = $entry['name'];
|
||||
$data['datasets'][0]['data'][] = round(($entry['sum'] * -1), 2);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function period(Collection $entries)
|
||||
{
|
||||
return $this->all($entries);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function spentInYear(Collection $categories, Collection $entries)
|
||||
{
|
||||
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.month.' . $language);
|
||||
|
||||
$data = [
|
||||
'count' => 0,
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
|
||||
foreach ($categories as $category) {
|
||||
$data['labels'][] = $category->name;
|
||||
}
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$date = $entry[0]->formatLocalized($format);
|
||||
array_shift($entry);
|
||||
$data['count']++;
|
||||
$data['datasets'][] = ['label' => $date, 'data' => $entry];
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function earnedInYear(Collection $categories, Collection $entries)
|
||||
{
|
||||
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.month.' . $language);
|
||||
|
||||
$data = [
|
||||
'count' => 0,
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
|
||||
foreach ($categories as $category) {
|
||||
$data['labels'][] = $category->name;
|
||||
}
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$date = $entry[0]->formatLocalized($format);
|
||||
array_shift($entry);
|
||||
$data['count']++;
|
||||
$data['datasets'][] = ['label' => $date, 'data' => $entry];
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\PiggyBank;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Config;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
|
||||
|
||||
/**
|
||||
* Class ChartJsPiggyBankChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\PiggyBank
|
||||
*/
|
||||
class ChartJsPiggyBankChartGenerator implements PiggyBankChartGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $set
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function history(Collection $set)
|
||||
{
|
||||
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.monthAndDay.' . $language);
|
||||
|
||||
$data = [
|
||||
'count' => 1,
|
||||
'labels' => [],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => 'Diff',
|
||||
'data' => []
|
||||
]
|
||||
],
|
||||
];
|
||||
$sum = '0';
|
||||
bcscale(2);
|
||||
foreach ($set as $entry) {
|
||||
$date = new Carbon($entry->date);
|
||||
$sum = bcadd($sum, $entry->sum);
|
||||
$data['labels'][] = $date->formatLocalized($format);
|
||||
$data['datasets'][0]['data'][] = round($sum, 2);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
20
app/Generator/Chart/PiggyBank/PiggyBankChartGenerator.php
Normal file
20
app/Generator/Chart/PiggyBank/PiggyBankChartGenerator.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\PiggyBank;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface PiggyBankChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\PiggyBank
|
||||
*/
|
||||
interface PiggyBankChartGenerator
|
||||
{
|
||||
/**
|
||||
* @param Collection $set
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function history(Collection $set);
|
||||
}
|
83
app/Generator/Chart/Report/ChartJsReportChartGenerator.php
Normal file
83
app/Generator/Chart/Report/ChartJsReportChartGenerator.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Report;
|
||||
|
||||
use Config;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
|
||||
/**
|
||||
* Class ChartJsReportChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Report
|
||||
*/
|
||||
class ChartJsReportChartGenerator implements ReportChartGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function yearInOut(Collection $entries)
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.month.' . $language);
|
||||
|
||||
$data = [
|
||||
'count' => 2,
|
||||
'labels' => [],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => trans('firefly.income'),
|
||||
'data' => []
|
||||
],
|
||||
[
|
||||
'label' => trans('firefly.expenses'),
|
||||
'data' => []
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$data['labels'][] = $entry[0]->formatLocalized($format);
|
||||
$data['datasets'][0]['data'][] = round($entry[1], 2);
|
||||
$data['datasets'][1]['data'][] = round($entry[2], 2);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $income
|
||||
* @param string $expense
|
||||
* @param int $count
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function yearInOutSummarized($income, $expense, $count)
|
||||
{
|
||||
|
||||
$data = [
|
||||
'count' => 2,
|
||||
'labels' => [trans('firefly.sum_of_year'), trans('firefly.average_of_year')],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => trans('firefly.income'),
|
||||
'data' => []
|
||||
],
|
||||
[
|
||||
'label' => trans('firefly.expenses'),
|
||||
'data' => []
|
||||
]
|
||||
],
|
||||
];
|
||||
$data['datasets'][0]['data'][] = round($income, 2);
|
||||
$data['datasets'][1]['data'][] = round( $expense, 2);
|
||||
$data['datasets'][0]['data'][] = round(($income / $count), 2);
|
||||
$data['datasets'][1]['data'][] = round(( $expense / $count), 2);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
31
app/Generator/Chart/Report/ReportChartGenerator.php
Normal file
31
app/Generator/Chart/Report/ReportChartGenerator.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Report;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface ReportChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Report
|
||||
*/
|
||||
interface ReportChartGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function yearInOut(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param string $income
|
||||
* @param string $expense
|
||||
* @param int $count
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function yearInOutSummarized($income, $expense, $count);
|
||||
|
||||
}
|
@@ -4,7 +4,6 @@ use Auth;
|
||||
use FireflyIII\Events\JournalCreated;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
|
||||
/**
|
||||
@@ -52,18 +51,16 @@ class ConnectJournalToPiggyBank
|
||||
if (is_null($repetition)) {
|
||||
return false;
|
||||
}
|
||||
bcscale(2);
|
||||
|
||||
$amount = $journal->amount;
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($journal->transactions()->get() as $transaction) {
|
||||
if ($transaction->account_id == $piggyBank->account_id) {
|
||||
if ($transaction->amount < 0) {
|
||||
$amount = $transaction->amount * -1;
|
||||
}
|
||||
}
|
||||
$amount = $journal->amount_positive;
|
||||
// if piggy account matches source account, the amount is positive
|
||||
if ($piggyBank->account_id == $journal->source_account->id) {
|
||||
$amount = $amount * -1;
|
||||
}
|
||||
|
||||
$repetition->currentamount += $amount;
|
||||
|
||||
$repetition->currentamount = bcadd($repetition->currentamount, $amount);
|
||||
$repetition->save();
|
||||
|
||||
PiggyBankEvent::create(['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]);
|
||||
|
@@ -1,6 +1,5 @@
|
||||
<?php namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use App;
|
||||
use FireflyIII\Events\JournalSaved;
|
||||
use Log;
|
||||
|
||||
@@ -36,7 +35,7 @@ class RescanJournal
|
||||
Log::debug('Triggered saved event for journal #' . $journal->id . ' (' . $journal->description . ')');
|
||||
|
||||
/** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */
|
||||
$repository = App::make('FireflyIII\Repositories\Bill\BillRepositoryInterface');
|
||||
$repository = app('FireflyIII\Repositories\Bill\BillRepositoryInterface');
|
||||
$list = $journal->user->bills()->where('active', 1)->where('automatch', 1)->get();
|
||||
|
||||
Log::debug('Found ' . $list->count() . ' bills to check.');
|
||||
|
@@ -49,10 +49,12 @@ class UpdateJournalConnection
|
||||
if (is_null($repetition)) {
|
||||
return;
|
||||
}
|
||||
$amount = $journal->amount;
|
||||
$diff = $amount - $event->amount;// update current repetition
|
||||
bcscale(2);
|
||||
|
||||
$repetition->currentamount += $diff;
|
||||
$amount = $journal->amount;
|
||||
$diff = bcsub($amount, $event->amount); // update current repetition
|
||||
|
||||
$repetition->currentamount = bcadd($repetition->currentamount, $diff);
|
||||
$repetition->save();
|
||||
|
||||
|
||||
|
222
app/Helpers/Attachments/AttachmentHelper.php
Normal file
222
app/Helpers/Attachments/AttachmentHelper.php
Normal file
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Attachments;
|
||||
|
||||
use Auth;
|
||||
use Config;
|
||||
use Crypt;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\MessageBag;
|
||||
use Input;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
|
||||
/**
|
||||
* Class AttachmentHelper
|
||||
*
|
||||
* @package FireflyIII\Helpers\Attachments
|
||||
*/
|
||||
class AttachmentHelper implements AttachmentHelperInterface
|
||||
{
|
||||
|
||||
/** @var int */
|
||||
protected $maxUploadSize;
|
||||
/** @var array */
|
||||
protected $allowedMimes;
|
||||
/** @var MessageBag */
|
||||
public $errors;
|
||||
/** @var MessageBag */
|
||||
public $messages;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->maxUploadSize = Config::get('firefly.maxUploadSize');
|
||||
$this->allowedMimes = Config::get('firefly.allowedMimes');
|
||||
$this->errors = new MessageBag;
|
||||
$this->messages = new MessageBag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAttachmentLocation(Attachment $attachment)
|
||||
{
|
||||
$path = storage_path('upload') . DIRECTORY_SEPARATOR . 'at-' . $attachment->id . '.data';
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model $model
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveAttachmentsForModel(Model $model)
|
||||
{
|
||||
$files = Input::file('attachments');
|
||||
|
||||
if (is_array($files)) {
|
||||
foreach ($files as $entry) {
|
||||
if (!is_null($entry)) {
|
||||
$this->processFile($entry, $model);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->processFile($files, $model);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UploadedFile $file
|
||||
* @param Model $model
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasFile(UploadedFile $file, Model $model)
|
||||
{
|
||||
$md5 = md5_file($file->getRealPath());
|
||||
$name = $file->getClientOriginalName();
|
||||
$class = get_class($model);
|
||||
$count = Auth::user()->attachments()->where('md5', $md5)->where('attachable_id', $model->id)->where('attachable_type', $class)->count();
|
||||
|
||||
if ($count > 0) {
|
||||
$msg = trans('validation.file_already_attached', ['name' => $name]);
|
||||
$this->errors->add('attachments', $msg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UploadedFile $file
|
||||
* @param Model $model
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateUpload(UploadedFile $file, Model $model)
|
||||
{
|
||||
if (!$this->validMime($file)) {
|
||||
return false;
|
||||
}
|
||||
if (!$this->validSize($file)) {
|
||||
return false;
|
||||
}
|
||||
if ($this->hasFile($file, $model)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UploadedFile $file
|
||||
* @param Model $model
|
||||
*
|
||||
* @return bool|Attachment
|
||||
*/
|
||||
protected function processFile(UploadedFile $file, Model $model)
|
||||
{
|
||||
$validation = $this->validateUpload($file, $model);
|
||||
if ($validation === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$attachment = new Attachment; // create Attachment object.
|
||||
$attachment->user()->associate(Auth::user());
|
||||
$attachment->attachable()->associate($model);
|
||||
$attachment->md5 = md5_file($file->getRealPath());
|
||||
$attachment->filename = $file->getClientOriginalName();
|
||||
$attachment->mime = $file->getMimeType();
|
||||
$attachment->size = $file->getSize();
|
||||
$attachment->uploaded = 0;
|
||||
$attachment->save();
|
||||
|
||||
$path = $file->getRealPath(); // encrypt and move file to storage.
|
||||
$content = file_get_contents($path);
|
||||
$encrypted = Crypt::encrypt($content);
|
||||
|
||||
// store it:
|
||||
$upload = $this->getAttachmentLocation($attachment);
|
||||
if (is_writable(dirname($upload))) {
|
||||
file_put_contents($upload, $encrypted);
|
||||
}
|
||||
|
||||
$attachment->uploaded = 1; // update attachment
|
||||
$attachment->save();
|
||||
|
||||
$name = e($file->getClientOriginalName()); // add message:
|
||||
$msg = trans('validation.file_attached', ['name' => $name]);
|
||||
$this->messages->add('attachments', $msg);
|
||||
|
||||
// return it.
|
||||
return $attachment;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UploadedFile $file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validMime(UploadedFile $file)
|
||||
{
|
||||
$mime = e($file->getMimeType());
|
||||
$name = e($file->getClientOriginalName());
|
||||
|
||||
if (!in_array($mime, $this->allowedMimes)) {
|
||||
$msg = trans('validation.file_invalid_mime', ['name' => $name, 'mime' => $mime]);
|
||||
$this->errors->add('attachments', $msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UploadedFile $file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validSize(UploadedFile $file)
|
||||
{
|
||||
$size = $file->getSize();
|
||||
$name = e($file->getClientOriginalName());
|
||||
if ($size > $this->maxUploadSize) {
|
||||
$msg = trans('validation.file_too_large', ['name' => $name]);
|
||||
$this->errors->add('attachments', $msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MessageBag
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MessageBag
|
||||
*/
|
||||
public function getMessages()
|
||||
{
|
||||
return $this->messages;
|
||||
}
|
||||
|
||||
|
||||
}
|
41
app/Helpers/Attachments/AttachmentHelperInterface.php
Normal file
41
app/Helpers/Attachments/AttachmentHelperInterface.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Attachments;
|
||||
|
||||
use FireflyIII\Models\Attachment;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\MessageBag;
|
||||
|
||||
/**
|
||||
* Interface AttachmentHelperInterface
|
||||
*
|
||||
* @package FireflyIII\Helpers\Attachments
|
||||
*/
|
||||
interface AttachmentHelperInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Model $model
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveAttachmentsForModel(Model $model);
|
||||
|
||||
/**
|
||||
* @return MessageBag
|
||||
*/
|
||||
public function getErrors();
|
||||
|
||||
/**
|
||||
* @return MessageBag
|
||||
*/
|
||||
public function getMessages();
|
||||
|
||||
/**
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAttachmentLocation(Attachment $attachment);
|
||||
|
||||
}
|
@@ -64,6 +64,8 @@ class BalanceLine
|
||||
if ($this->getRole() == self::ROLE_DIFFROLE) {
|
||||
return trans('firefly.leftUnbalanced');
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,10 +159,11 @@ class BalanceLine
|
||||
*/
|
||||
public function sumOfLeft()
|
||||
{
|
||||
$sum = 0.0;
|
||||
$sum = '0';
|
||||
bcscale(2);
|
||||
/** @var BalanceEntry $balanceEntry */
|
||||
foreach ($this->getBalanceEntries() as $balanceEntry) {
|
||||
$sum += $balanceEntry->getLeft();
|
||||
$sum = bcadd($sum, $balanceEntry->getLeft());
|
||||
}
|
||||
|
||||
return $sum;
|
||||
|
@@ -40,7 +40,7 @@ class Bill
|
||||
*/
|
||||
public function getBills()
|
||||
{
|
||||
$this->bills->sortBy(
|
||||
$set = $this->bills->sortBy(
|
||||
function (BillLine $bill) {
|
||||
$active = intval($bill->getBill()->active) == 0 ? 1 : 0;
|
||||
$name = $bill->getBill()->name;
|
||||
@@ -50,7 +50,7 @@ class Bill
|
||||
);
|
||||
|
||||
|
||||
return $this->bills;
|
||||
return $set;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -16,19 +16,19 @@ class BillLine
|
||||
|
||||
/** @var bool */
|
||||
protected $active;
|
||||
/** @var float */
|
||||
/** @var string */
|
||||
protected $amount;
|
||||
/** @var BillModel */
|
||||
protected $bill;
|
||||
/** @var bool */
|
||||
protected $hit;
|
||||
/** @var float */
|
||||
/** @var string */
|
||||
protected $max;
|
||||
/** @var float */
|
||||
/** @var string */
|
||||
protected $min;
|
||||
|
||||
/**
|
||||
* @return float
|
||||
* @return string
|
||||
*/
|
||||
public function getAmount()
|
||||
{
|
||||
@@ -36,7 +36,7 @@ class BillLine
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $amount
|
||||
* @param string $amount
|
||||
*/
|
||||
public function setAmount($amount)
|
||||
{
|
||||
@@ -60,7 +60,7 @@ class BillLine
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
* @return string
|
||||
*/
|
||||
public function getMax()
|
||||
{
|
||||
@@ -68,7 +68,7 @@ class BillLine
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $max
|
||||
* @param string $max
|
||||
*/
|
||||
public function setMax($max)
|
||||
{
|
||||
@@ -76,7 +76,7 @@ class BillLine
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
* @return string
|
||||
*/
|
||||
public function getMin()
|
||||
{
|
||||
@@ -84,7 +84,7 @@ class BillLine
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $min
|
||||
* @param string $min
|
||||
*/
|
||||
public function setMin($min)
|
||||
{
|
||||
|
@@ -34,7 +34,8 @@ class Category
|
||||
*/
|
||||
public function addCategory(CategoryModel $category)
|
||||
{
|
||||
if ($category->spent > 0) {
|
||||
// spent is minus zero for an expense report:
|
||||
if ($category->spent < 0) {
|
||||
$this->categories->push($category);
|
||||
}
|
||||
}
|
||||
@@ -54,14 +55,14 @@ class Category
|
||||
*/
|
||||
public function getCategories()
|
||||
{
|
||||
$this->categories->sortByDesc(
|
||||
$set = $this->categories->sortByDesc(
|
||||
function (CategoryModel $category) {
|
||||
return $category->spent;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
return $this->categories;
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -33,11 +33,10 @@ class Expense
|
||||
*/
|
||||
public function addOrCreateExpense(TransactionJournal $entry)
|
||||
{
|
||||
|
||||
$accountId = $entry->account_id;
|
||||
if (!$this->expenses->has($accountId)) {
|
||||
$newObject = new stdClass;
|
||||
$newObject->amount = strval(round($entry->amount, 2));
|
||||
$newObject->amount = strval(round($entry->amount_positive, 2));
|
||||
$newObject->name = $entry->name;
|
||||
$newObject->count = 1;
|
||||
$newObject->id = $accountId;
|
||||
@@ -45,7 +44,7 @@ class Expense
|
||||
} else {
|
||||
bcscale(2);
|
||||
$existing = $this->expenses->get($accountId);
|
||||
$existing->amount = bcadd($existing->amount, $entry->amount);
|
||||
$existing->amount = bcadd($existing->amount, $entry->amount_positive);
|
||||
$existing->count++;
|
||||
$this->expenses->put($accountId, $existing);
|
||||
}
|
||||
@@ -66,13 +65,13 @@ class Expense
|
||||
*/
|
||||
public function getExpenses()
|
||||
{
|
||||
$this->expenses->sortByDesc(
|
||||
$set = $this->expenses->sortByDesc(
|
||||
function (stdClass $object) {
|
||||
return $object->amount;
|
||||
}
|
||||
);
|
||||
|
||||
return $this->expenses;
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -18,7 +18,7 @@ class Income
|
||||
|
||||
/** @var Collection */
|
||||
protected $incomes;
|
||||
/** @var float */
|
||||
/** @var string */
|
||||
protected $total;
|
||||
|
||||
/**
|
||||
@@ -38,7 +38,7 @@ class Income
|
||||
$accountId = $entry->account_id;
|
||||
if (!$this->incomes->has($accountId)) {
|
||||
$newObject = new stdClass;
|
||||
$newObject->amount = strval(round($entry->amount, 2));
|
||||
$newObject->amount = strval(round($entry->amount_positive, 2));
|
||||
$newObject->name = $entry->name;
|
||||
$newObject->count = 1;
|
||||
$newObject->id = $accountId;
|
||||
@@ -46,7 +46,7 @@ class Income
|
||||
} else {
|
||||
bcscale(2);
|
||||
$existing = $this->incomes->get($accountId);
|
||||
$existing->amount = bcadd($existing->amount, $entry->amount);
|
||||
$existing->amount = bcadd($existing->amount, $entry->amount_positive);
|
||||
$existing->count++;
|
||||
$this->incomes->put($accountId, $existing);
|
||||
}
|
||||
@@ -67,17 +67,17 @@ class Income
|
||||
*/
|
||||
public function getIncomes()
|
||||
{
|
||||
$this->incomes->sortByDesc(
|
||||
$set = $this->incomes->sortByDesc(
|
||||
function (stdClass $object) {
|
||||
return $object->amount;
|
||||
}
|
||||
);
|
||||
|
||||
return $this->incomes;
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
* @return string
|
||||
*/
|
||||
public function getTotal()
|
||||
{
|
||||
|
38
app/Helpers/Csv/Converter/AccountId.php
Normal file
38
app/Helpers/Csv/Converter/AccountId.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Account;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class AccountId
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class AccountId extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Account
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
// is mapped? Then it's easy!
|
||||
if (isset($this->mapped[$this->index][$this->value])) {
|
||||
|
||||
/** @var Account $account */
|
||||
$account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]);
|
||||
} else {
|
||||
|
||||
/** @var Account $account */
|
||||
$account = Auth::user()->accounts()->find($this->value);
|
||||
|
||||
if (!is_null($account)) {
|
||||
Log::debug('Found ' . $account->accountType->type . ' named "******" with ID: ' . $this->value . ' (not mapped) ');
|
||||
}
|
||||
}
|
||||
|
||||
return $account;
|
||||
}
|
||||
}
|
26
app/Helpers/Csv/Converter/Amount.php
Normal file
26
app/Helpers/Csv/Converter/Amount.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
|
||||
/**
|
||||
* Class Amount
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class Amount extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
if (is_numeric($this->value)) {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
68
app/Helpers/Csv/Converter/AssetAccountIban.php
Normal file
68
app/Helpers/Csv/Converter/AssetAccountIban.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
|
||||
/**
|
||||
* Class AssetAccountIban
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class AssetAccountIban extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
// is mapped? Then it's easy!
|
||||
if (isset($this->mapped[$this->index][$this->value])) {
|
||||
$account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]);
|
||||
|
||||
return $account;
|
||||
}
|
||||
if (strlen($this->value) > 0) {
|
||||
// find or create new account:
|
||||
$account = $this->findAccount();
|
||||
$accountType = AccountType::where('type', 'Asset account')->first();
|
||||
|
||||
if (is_null($account)) {
|
||||
// create it if doesn't exist.
|
||||
$account = Account::firstOrCreateEncrypted(
|
||||
[
|
||||
'name' => $this->value,
|
||||
'iban' => $this->value,
|
||||
'user_id' => Auth::user()->id,
|
||||
'account_type_id' => $accountType->id,
|
||||
'active' => 1,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
*/
|
||||
protected function findAccount()
|
||||
{
|
||||
$set = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']);
|
||||
/** @var Account $entry */
|
||||
foreach ($set as $entry) {
|
||||
if ($entry->iban == $this->value) {
|
||||
|
||||
return $entry;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
51
app/Helpers/Csv/Converter/AssetAccountName.php
Normal file
51
app/Helpers/Csv/Converter/AssetAccountName.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
|
||||
/**
|
||||
* Class AssetAccountName
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class AssetAccountName extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
// is mapped? Then it's easy!
|
||||
if (isset($this->mapped[$this->index][$this->value])) {
|
||||
$account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]);
|
||||
|
||||
return $account;
|
||||
}
|
||||
// find or create new account:
|
||||
$accountType = AccountType::where('type', 'Asset account')->first();
|
||||
$set = Auth::user()->accounts()->accountTypeIn(['Asset account', 'Default account'])->get();
|
||||
/** @var Account $entry */
|
||||
foreach ($set as $entry) {
|
||||
if ($entry->name == $this->value) {
|
||||
return $entry;
|
||||
}
|
||||
}
|
||||
|
||||
// create it if doesnt exist.
|
||||
$account = Account::firstOrCreateEncrypted(
|
||||
[
|
||||
'name' => $this->value,
|
||||
'iban' => '',
|
||||
'user_id' => Auth::user()->id,
|
||||
'account_type_id' => $accountType->id,
|
||||
'active' => 1,
|
||||
]
|
||||
);
|
||||
|
||||
return $account;
|
||||
}
|
||||
}
|
122
app/Helpers/Csv/Converter/BasicConverter.php
Normal file
122
app/Helpers/Csv/Converter/BasicConverter.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
/**
|
||||
* Class BasicConverter
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class BasicConverter
|
||||
{
|
||||
/** @var array */
|
||||
protected $data;
|
||||
/** @var string */
|
||||
protected $field;
|
||||
/** @var int */
|
||||
protected $index;
|
||||
/** @var array */
|
||||
protected $mapped;
|
||||
/** @var string */
|
||||
protected $role;
|
||||
/** @var string */
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getField()
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
*/
|
||||
public function setField($field)
|
||||
{
|
||||
$this->field = $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getIndex()
|
||||
{
|
||||
return $this->index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $index
|
||||
*/
|
||||
public function setIndex($index)
|
||||
{
|
||||
$this->index = $index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMapped()
|
||||
{
|
||||
return $this->mapped;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $mapped
|
||||
*/
|
||||
public function setMapped($mapped)
|
||||
{
|
||||
$this->mapped = $mapped;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRole()
|
||||
{
|
||||
return $this->role;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $role
|
||||
*/
|
||||
public function setRole($role)
|
||||
{
|
||||
$this->role = $role;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*/
|
||||
public function setValue($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
|
||||
}
|
30
app/Helpers/Csv/Converter/BillId.php
Normal file
30
app/Helpers/Csv/Converter/BillId.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Bill;
|
||||
|
||||
/**
|
||||
* Class BillId
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class BillId extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Bill
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
// is mapped? Then it's easy!
|
||||
if (isset($this->mapped[$this->index][$this->value])) {
|
||||
$bill = Auth::user()->bills()->find($this->mapped[$this->index][$this->value]);
|
||||
} else {
|
||||
$bill = Auth::user()->bills()->find($this->value);
|
||||
}
|
||||
|
||||
return $bill;
|
||||
}
|
||||
}
|
38
app/Helpers/Csv/Converter/BillName.php
Normal file
38
app/Helpers/Csv/Converter/BillName.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Bill;
|
||||
|
||||
/**
|
||||
* Class BillName
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class BillName extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Bill
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
$bill = null;
|
||||
// is mapped? Then it's easy!
|
||||
if (isset($this->mapped[$this->index][$this->value])) {
|
||||
$bill = Auth::user()->bills()->find($this->mapped[$this->index][$this->value]);
|
||||
} else {
|
||||
|
||||
$bills = Auth::user()->bills()->get();
|
||||
/** @var Bill $bill */
|
||||
foreach ($bills as $bill) {
|
||||
if ($bill->name == $this->value) {
|
||||
return $bill;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $bill;
|
||||
}
|
||||
}
|
29
app/Helpers/Csv/Converter/BudgetId.php
Normal file
29
app/Helpers/Csv/Converter/BudgetId.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Budget;
|
||||
|
||||
/**
|
||||
* Class AccountId
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class BudgetId extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Budget
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
// is mapped? Then it's easy!
|
||||
if (isset($this->mapped[$this->index][$this->value])) {
|
||||
$budget = Auth::user()->budgets()->find($this->mapped[$this->index][$this->value]);
|
||||
} else {
|
||||
$budget = Auth::user()->budgets()->find($this->value);
|
||||
}
|
||||
|
||||
return $budget;
|
||||
}
|
||||
}
|
35
app/Helpers/Csv/Converter/BudgetName.php
Normal file
35
app/Helpers/Csv/Converter/BudgetName.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Budget;
|
||||
|
||||
/**
|
||||
* Class BudgetName
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class BudgetName extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Budget
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
// is mapped? Then it's easy!
|
||||
if (isset($this->mapped[$this->index][$this->value])) {
|
||||
$budget = Auth::user()->budgets()->find($this->mapped[$this->index][$this->value]);
|
||||
} else {
|
||||
$budget = Budget::firstOrCreateEncrypted(
|
||||
[
|
||||
'name' => $this->value,
|
||||
'user_id' => Auth::user()->id,
|
||||
'active' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return $budget;
|
||||
}
|
||||
}
|
29
app/Helpers/Csv/Converter/CategoryId.php
Normal file
29
app/Helpers/Csv/Converter/CategoryId.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Category;
|
||||
|
||||
/**
|
||||
* Class CategoryId
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class CategoryId extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Category
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
// is mapped? Then it's easy!
|
||||
if (isset($this->mapped[$this->index][$this->value])) {
|
||||
$category = Auth::user()->categories()->find($this->mapped[$this->index][$this->value]);
|
||||
} else {
|
||||
$category = Auth::user()->categories()->find($this->value);
|
||||
}
|
||||
|
||||
return $category;
|
||||
}
|
||||
}
|
34
app/Helpers/Csv/Converter/CategoryName.php
Normal file
34
app/Helpers/Csv/Converter/CategoryName.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Category;
|
||||
|
||||
/**
|
||||
* Class CategoryName
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class CategoryName extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Category
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
// is mapped? Then it's easy!
|
||||
if (isset($this->mapped[$this->index][$this->value])) {
|
||||
$category = Auth::user()->categories()->find($this->mapped[$this->index][$this->value]);
|
||||
} else {
|
||||
$category = Category::firstOrCreateEncrypted(
|
||||
[
|
||||
'name' => $this->value,
|
||||
'user_id' => Auth::user()->id
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return $category;
|
||||
}
|
||||
}
|
49
app/Helpers/Csv/Converter/ConverterInterface.php
Normal file
49
app/Helpers/Csv/Converter/ConverterInterface.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
/**
|
||||
* Interface ConverterInterface
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
interface ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function convert();
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData(array $data);
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
*
|
||||
*/
|
||||
public function setField($field);
|
||||
|
||||
/**
|
||||
* @param int $index
|
||||
*/
|
||||
public function setIndex($index);
|
||||
|
||||
/**
|
||||
* @param array $mapped
|
||||
*/
|
||||
public function setMapped($mapped);
|
||||
|
||||
/**
|
||||
* @param string $role
|
||||
*/
|
||||
public function setRole($role);
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*/
|
||||
public function setValue($value);
|
||||
|
||||
}
|
28
app/Helpers/Csv/Converter/CurrencyCode.php
Normal file
28
app/Helpers/Csv/Converter/CurrencyCode.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
|
||||
/**
|
||||
* Class CurrencyCode
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class CurrencyCode extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return mixed|static
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
if (isset($this->mapped[$this->index][$this->value])) {
|
||||
$currency = TransactionCurrency::find($this->mapped[$this->index][$this->value]);
|
||||
} else {
|
||||
$currency = TransactionCurrency::whereCode($this->value)->first();
|
||||
}
|
||||
|
||||
return $currency;
|
||||
}
|
||||
}
|
28
app/Helpers/Csv/Converter/CurrencyId.php
Normal file
28
app/Helpers/Csv/Converter/CurrencyId.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
|
||||
/**
|
||||
* Class CurrencyId
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class CurrencyId extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return mixed|static
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
if (isset($this->mapped[$this->index][$this->value])) {
|
||||
$currency = TransactionCurrency::find($this->mapped[$this->index][$this->value]);
|
||||
} else {
|
||||
$currency = TransactionCurrency::find($this->value);
|
||||
}
|
||||
|
||||
return $currency;
|
||||
}
|
||||
}
|
28
app/Helpers/Csv/Converter/CurrencyName.php
Normal file
28
app/Helpers/Csv/Converter/CurrencyName.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
|
||||
/**
|
||||
* Class CurrencyName
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class CurrencyName extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return mixed|static
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
if (isset($this->mapped[$this->index][$this->value])) {
|
||||
$currency = TransactionCurrency::find($this->mapped[$this->index][$this->value]);
|
||||
} else {
|
||||
$currency = TransactionCurrency::whereName($this->value)->first();
|
||||
}
|
||||
|
||||
return $currency;
|
||||
}
|
||||
}
|
28
app/Helpers/Csv/Converter/CurrencySymbol.php
Normal file
28
app/Helpers/Csv/Converter/CurrencySymbol.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
|
||||
/**
|
||||
* Class CurrencySymbol
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class CurrencySymbol extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return mixed|static
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
if (isset($this->mapped[$this->index][$this->value])) {
|
||||
$currency = TransactionCurrency::find($this->mapped[$this->index][$this->value]);
|
||||
} else {
|
||||
$currency = TransactionCurrency::whereSymbol($this->value)->first();
|
||||
}
|
||||
|
||||
return $currency;
|
||||
}
|
||||
}
|
38
app/Helpers/Csv/Converter/Date.php
Normal file
38
app/Helpers/Csv/Converter/Date.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use InvalidArgumentException;
|
||||
use Log;
|
||||
use Session;
|
||||
|
||||
/**
|
||||
* Class Date
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class Date extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Carbon
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
$format = Session::get('csv-date-format');
|
||||
try {
|
||||
$date = Carbon::createFromFormat($format, $this->value);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
Log::error('Date conversion error: ' . $e->getMessage() . '. Value was "' . $this->value . '", format was "' . $format . '".');
|
||||
|
||||
$message = trans('firefly.csv_date_parse_error', ['format' => $format, 'value' => $this->value]);
|
||||
|
||||
throw new FireflyException($message);
|
||||
}
|
||||
|
||||
|
||||
return $date;
|
||||
}
|
||||
}
|
21
app/Helpers/Csv/Converter/Description.php
Normal file
21
app/Helpers/Csv/Converter/Description.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
/**
|
||||
* Class Description
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class Description extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
return trim($this->data['description'] . ' ' . $this->value);
|
||||
}
|
||||
}
|
22
app/Helpers/Csv/Converter/Ignore.php
Normal file
22
app/Helpers/Csv/Converter/Ignore.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
|
||||
/**
|
||||
* Class Amount
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class Ignore extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
57
app/Helpers/Csv/Converter/OpposingAccountIban.php
Normal file
57
app/Helpers/Csv/Converter/OpposingAccountIban.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Account;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class OpposingAccountIban
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class OpposingAccountIban extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* If mapped, return account. Otherwise, only return the name itself.
|
||||
*
|
||||
* @return Account|string
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
if (isset($this->mapped[$this->index][$this->value])) {
|
||||
$account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]);
|
||||
|
||||
return $account;
|
||||
} else {
|
||||
if (strlen($this->value) > 0) {
|
||||
$account = $this->findAccount();
|
||||
if (!is_null($account)) {
|
||||
return $account;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
*/
|
||||
protected function findAccount()
|
||||
{
|
||||
$set = Auth::user()->accounts()->get();
|
||||
/** @var Account $account */
|
||||
foreach ($set as $account) {
|
||||
if ($account->iban == $this->value) {
|
||||
Log::debug('OpposingAccountIban::convert found an Account (#' . $account->id . ': ******) with IBAN ******');
|
||||
|
||||
return $account;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
32
app/Helpers/Csv/Converter/OpposingAccountId.php
Normal file
32
app/Helpers/Csv/Converter/OpposingAccountId.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Account;
|
||||
|
||||
/**
|
||||
* Class OpposingName
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class OpposingAccountId extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @return Account
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
if (isset($this->mapped[$this->index][$this->value])) {
|
||||
$account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]);
|
||||
|
||||
} else {
|
||||
$account = Auth::user()->accounts()->find($this->value);
|
||||
}
|
||||
|
||||
return $account;
|
||||
|
||||
}
|
||||
}
|
31
app/Helpers/Csv/Converter/OpposingAccountName.php
Normal file
31
app/Helpers/Csv/Converter/OpposingAccountName.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Account;
|
||||
|
||||
/**
|
||||
* Class OpposingName
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class OpposingAccountName extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* If mapped, return account. Otherwise, only return the name itself.
|
||||
*
|
||||
* @return Account|string
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
if (isset($this->mapped[$this->index][$this->value])) {
|
||||
$account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]);
|
||||
|
||||
return $account;
|
||||
} else {
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
}
|
26
app/Helpers/Csv/Converter/RabobankDebetCredit.php
Normal file
26
app/Helpers/Csv/Converter/RabobankDebetCredit.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
|
||||
/**
|
||||
* Class RabobankDebetCredit
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class RabobankDebetCredit extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
if ($this->value == 'D') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
40
app/Helpers/Csv/Converter/TagsComma.php
Normal file
40
app/Helpers/Csv/Converter/TagsComma.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Tag;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class TagsComma
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class TagsComma extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Bill
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
$tags = new Collection;
|
||||
|
||||
$strings = explode(',', $this->value);
|
||||
foreach ($strings as $string) {
|
||||
$tag = Tag::firstOrCreateEncrypted(
|
||||
[
|
||||
'tag' => $string,
|
||||
'tagMode' => 'nothing',
|
||||
'user_id' => Auth::user()->id,
|
||||
]
|
||||
);
|
||||
$tags->push($tag);
|
||||
}
|
||||
$tags = $tags->merge($this->data['tags']);
|
||||
|
||||
return $tags;
|
||||
}
|
||||
}
|
40
app/Helpers/Csv/Converter/TagsSpace.php
Normal file
40
app/Helpers/Csv/Converter/TagsSpace.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Tag;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class TagsSpace
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class TagsSpace extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Bill
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
$tags = new Collection;
|
||||
|
||||
$strings = explode(' ', $this->value);
|
||||
foreach ($strings as $string) {
|
||||
$tag = Tag::firstOrCreateEncrypted(
|
||||
[
|
||||
'tag' => $string,
|
||||
'tagMode' => 'nothing',
|
||||
'user_id' => Auth::user()->id,
|
||||
]
|
||||
);
|
||||
$tags->push($tag);
|
||||
}
|
||||
$tags = $tags->merge($this->data['tags']);
|
||||
|
||||
return $tags;
|
||||
}
|
||||
}
|
283
app/Helpers/Csv/Data.php
Normal file
283
app/Helpers/Csv/Data.php
Normal file
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv;
|
||||
|
||||
use Crypt;
|
||||
use League\Csv\Reader;
|
||||
use Session;
|
||||
|
||||
/**
|
||||
* Class Data
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv
|
||||
*/
|
||||
class Data
|
||||
{
|
||||
|
||||
/** @var string */
|
||||
protected $csvFileContent;
|
||||
|
||||
/** @var string */
|
||||
protected $csvFileLocation;
|
||||
/** @var string */
|
||||
protected $dateFormat;
|
||||
/** @var bool */
|
||||
protected $hasHeaders;
|
||||
|
||||
/** @var array */
|
||||
protected $map = [];
|
||||
/** @var array */
|
||||
protected $mapped = [];
|
||||
/** @var Reader */
|
||||
protected $reader;
|
||||
/** @var array */
|
||||
protected $roles = [];
|
||||
|
||||
/** @var array */
|
||||
protected $specifix = [];
|
||||
|
||||
/** @var int */
|
||||
protected $importAccount = 0;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->sessionHasHeaders();
|
||||
$this->sessionDateFormat();
|
||||
$this->sessionCsvFileLocation();
|
||||
$this->sessionMap();
|
||||
$this->sessionRoles();
|
||||
$this->sessionMapped();
|
||||
$this->sessionSpecifix();
|
||||
$this->sessionImportAccount();
|
||||
}
|
||||
|
||||
protected function sessionHasHeaders()
|
||||
{
|
||||
if (Session::has('csv-has-headers')) {
|
||||
$this->hasHeaders = (bool)Session::get('csv-has-headers');
|
||||
}
|
||||
}
|
||||
|
||||
protected function sessionImportAccount()
|
||||
{
|
||||
if (Session::has('csv-import-account')) {
|
||||
$this->importAccount = intval(Session::get('csv-import-account'));
|
||||
}
|
||||
}
|
||||
|
||||
protected function sessionDateFormat()
|
||||
{
|
||||
if (Session::has('csv-date-format')) {
|
||||
$this->dateFormat = (string)Session::get('csv-date-format');
|
||||
}
|
||||
}
|
||||
|
||||
protected function sessionCsvFileLocation()
|
||||
{
|
||||
if (Session::has('csv-file')) {
|
||||
$this->csvFileLocation = (string)Session::get('csv-file');
|
||||
}
|
||||
}
|
||||
|
||||
protected function sessionMap()
|
||||
{
|
||||
if (Session::has('csv-map')) {
|
||||
$this->map = (array)Session::get('csv-map');
|
||||
}
|
||||
}
|
||||
|
||||
protected function sessionRoles()
|
||||
{
|
||||
if (Session::has('csv-roles')) {
|
||||
$this->roles = (array)Session::get('csv-roles');
|
||||
}
|
||||
}
|
||||
|
||||
protected function sessionMapped()
|
||||
{
|
||||
if (Session::has('csv-mapped')) {
|
||||
$this->mapped = (array)Session::get('csv-mapped');
|
||||
}
|
||||
}
|
||||
|
||||
protected function sessionSpecifix()
|
||||
{
|
||||
if (Session::has('csv-specifix')) {
|
||||
$this->specifix = (array)Session::get('csv-specifix');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDateFormat()
|
||||
{
|
||||
return $this->dateFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $dateFormat
|
||||
*/
|
||||
public function setDateFormat($dateFormat)
|
||||
{
|
||||
Session::put('csv-date-format', $dateFormat);
|
||||
$this->dateFormat = $dateFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $importAccount
|
||||
*/
|
||||
public function setImportAccount($importAccount)
|
||||
{
|
||||
Session::put('csv-import-account', $importAccount);
|
||||
$this->importAccount = $importAccount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasHeaders()
|
||||
{
|
||||
return $this->hasHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $hasHeaders
|
||||
*/
|
||||
public function setHasHeaders($hasHeaders)
|
||||
{
|
||||
Session::put('csv-has-headers', $hasHeaders);
|
||||
$this->hasHeaders = $hasHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMap()
|
||||
{
|
||||
return $this->map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $map
|
||||
*/
|
||||
public function setMap(array $map)
|
||||
{
|
||||
Session::put('csv-map', $map);
|
||||
$this->map = $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMapped()
|
||||
{
|
||||
return $this->mapped;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $mapped
|
||||
*/
|
||||
public function setMapped(array $mapped)
|
||||
{
|
||||
Session::put('csv-mapped', $mapped);
|
||||
$this->mapped = $mapped;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Reader
|
||||
*/
|
||||
public function getReader()
|
||||
{
|
||||
|
||||
if (strlen($this->csvFileContent) === 0) {
|
||||
$this->loadCsvFile();
|
||||
}
|
||||
|
||||
if (is_null($this->reader)) {
|
||||
$this->reader = Reader::createFromString($this->getCsvFileContent());
|
||||
}
|
||||
|
||||
return $this->reader;
|
||||
}
|
||||
|
||||
protected function loadCsvFile()
|
||||
{
|
||||
$file = $this->getCsvFileLocation();
|
||||
$content = file_get_contents($file);
|
||||
$contentDecrypted = Crypt::decrypt($content);
|
||||
$this->setCsvFileContent($contentDecrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCsvFileLocation()
|
||||
{
|
||||
return $this->csvFileLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $csvFileLocation
|
||||
*/
|
||||
public function setCsvFileLocation($csvFileLocation)
|
||||
{
|
||||
Session::put('csv-file', $csvFileLocation);
|
||||
$this->csvFileLocation = $csvFileLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCsvFileContent()
|
||||
{
|
||||
return $this->csvFileContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $csvFileContent
|
||||
*/
|
||||
public function setCsvFileContent($csvFileContent)
|
||||
{
|
||||
$this->csvFileContent = $csvFileContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRoles()
|
||||
{
|
||||
return $this->roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $roles
|
||||
*/
|
||||
public function setRoles(array $roles)
|
||||
{
|
||||
Session::put('csv-roles', $roles);
|
||||
$this->roles = $roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSpecifix()
|
||||
{
|
||||
return is_array($this->specifix) ? $this->specifix : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $specifix
|
||||
*/
|
||||
public function setSpecifix($specifix)
|
||||
{
|
||||
Session::put('csv-specifix', $specifix);
|
||||
$this->specifix = $specifix;
|
||||
}
|
||||
|
||||
|
||||
}
|
371
app/Helpers/Csv/Importer.php
Normal file
371
app/Helpers/Csv/Importer.php
Normal file
@@ -0,0 +1,371 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv;
|
||||
|
||||
use Auth;
|
||||
use Config;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Csv\Converter\ConverterInterface;
|
||||
use FireflyIII\Helpers\Csv\PostProcessing\PostProcessorInterface;
|
||||
use FireflyIII\Helpers\Csv\Specifix\SpecifixInterface;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\MessageBag;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class Importer
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv
|
||||
*/
|
||||
class Importer
|
||||
{
|
||||
|
||||
/** @var Data */
|
||||
protected $data;
|
||||
/** @var array */
|
||||
protected $errors;
|
||||
/** @var array */
|
||||
protected $importData;
|
||||
/** @var array */
|
||||
protected $importRow;
|
||||
/** @var int */
|
||||
protected $imported = 0;
|
||||
/** @var array */
|
||||
protected $map;
|
||||
/** @var array */
|
||||
protected $mapped;
|
||||
/** @var array */
|
||||
protected $roles;
|
||||
/** @var int */
|
||||
protected $rows = 0;
|
||||
/** @var array */
|
||||
protected $specifix = [];
|
||||
|
||||
/** @var Collection */
|
||||
protected $journals;
|
||||
|
||||
/**
|
||||
* Used by CsvController.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by CsvController
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getImported()
|
||||
{
|
||||
return $this->imported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by CsvController
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRows()
|
||||
{
|
||||
return $this->rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getJournals()
|
||||
{
|
||||
return $this->journals;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
||||
$this->journals = new Collection;
|
||||
$this->map = $this->data->getMap();
|
||||
$this->roles = $this->data->getRoles();
|
||||
$this->mapped = $this->data->getMapped();
|
||||
$this->specifix = $this->data->getSpecifix();
|
||||
|
||||
foreach ($this->data->getReader() as $index => $row) {
|
||||
if ($this->parseRow($index)) {
|
||||
Log::debug('--- Importing row ' . $index);
|
||||
$this->rows++;
|
||||
$result = $this->importRow($row);
|
||||
if (!($result instanceof TransactionJournal)) {
|
||||
Log::error('Caught error at row #' . $index . ': ' . $result);
|
||||
$this->errors[$index] = $result;
|
||||
} else {
|
||||
$this->imported++;
|
||||
$this->journals->push($result);
|
||||
}
|
||||
Log::debug('---');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $index
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function parseRow($index)
|
||||
{
|
||||
return (($this->data->hasHeaders() && $index >= 1) || !$this->data->hasHeaders());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $row
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @return string|bool
|
||||
*/
|
||||
protected function importRow($row)
|
||||
{
|
||||
|
||||
$data = $this->getFiller(); // These fields are necessary to create a new transaction journal. Some are optional
|
||||
foreach ($row as $index => $value) {
|
||||
$role = isset($this->roles[$index]) ? $this->roles[$index] : '_ignore';
|
||||
$class = Config::get('csv.roles.' . $role . '.converter');
|
||||
$field = Config::get('csv.roles.' . $role . '.field');
|
||||
|
||||
Log::debug('Column #' . $index . ' (role: ' . $role . ') : converter ' . $class . ' stores its data into field ' . $field . ':');
|
||||
|
||||
/** @var ConverterInterface $converter */
|
||||
$converter = app('FireflyIII\Helpers\Csv\Converter\\' . $class);
|
||||
$converter->setData($data); // the complete array so far.
|
||||
$converter->setField($field);
|
||||
$converter->setIndex($index);
|
||||
$converter->setMapped($this->mapped);
|
||||
$converter->setValue($value);
|
||||
$converter->setRole($role);
|
||||
$data[$field] = $converter->convert();
|
||||
}
|
||||
// move to class vars.
|
||||
$this->importData = $data;
|
||||
$this->importRow = $row;
|
||||
unset($data, $row);
|
||||
// post processing and validating.
|
||||
$this->postProcess();
|
||||
$result = $this->validateData();
|
||||
|
||||
if (!($result === true)) {
|
||||
return $result; // return error.
|
||||
}
|
||||
$journal = $this->createTransactionJournal();
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getFiller()
|
||||
{
|
||||
$filler = [];
|
||||
foreach (Config::get('csv.roles') as $role) {
|
||||
if (isset($role['field'])) {
|
||||
$fieldName = $role['field'];
|
||||
$filler[$fieldName] = null;
|
||||
}
|
||||
}
|
||||
// some extra's:
|
||||
$filler['bill-id'] = null;
|
||||
$filler['opposing-account-object'] = null;
|
||||
$filler['asset-account-object'] = null;
|
||||
$filler['amount-modifier'] = '1';
|
||||
|
||||
return $filler;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Row denotes the original data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function postProcess()
|
||||
{
|
||||
// do bank specific fixes (must be enabled but now all of them.
|
||||
|
||||
foreach ($this->getSpecifix() as $className) {
|
||||
/** @var SpecifixInterface $specifix */
|
||||
$specifix = app('FireflyIII\Helpers\Csv\Specifix\\' . $className);
|
||||
$specifix->setData($this->importData);
|
||||
$specifix->setRow($this->importRow);
|
||||
Log::debug('Now post-process specifix named ' . $className . ':');
|
||||
$this->importData = $specifix->fix();
|
||||
}
|
||||
|
||||
|
||||
$set = Config::get('csv.post_processors');
|
||||
foreach ($set as $className) {
|
||||
/** @var PostProcessorInterface $postProcessor */
|
||||
$postProcessor = app('FireflyIII\Helpers\Csv\PostProcessing\\' . $className);
|
||||
$postProcessor->setData($this->importData);
|
||||
Log::debug('Now post-process processor named ' . $className . ':');
|
||||
$this->importData = $postProcessor->process();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSpecifix()
|
||||
{
|
||||
return is_array($this->specifix) ? $this->specifix : [];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
protected function validateData()
|
||||
{
|
||||
if (is_null($this->importData['date']) && is_null($this->importData['date-rent'])) {
|
||||
return 'No date value for this row.';
|
||||
}
|
||||
if (is_null($this->importData['opposing-account-object'])) {
|
||||
return 'Opposing account is null';
|
||||
}
|
||||
|
||||
if (!($this->importData['asset-account-object'] instanceof Account)) {
|
||||
return 'No asset account to import into.';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return TransactionJournal|string
|
||||
*/
|
||||
protected function createTransactionJournal()
|
||||
{
|
||||
bcscale(2);
|
||||
$date = $this->importData['date'];
|
||||
if (is_null($this->importData['date'])) {
|
||||
$date = $this->importData['date-rent'];
|
||||
}
|
||||
|
||||
|
||||
$transactionType = $this->getTransactionType(); // defaults to deposit
|
||||
$errors = new MessageBag;
|
||||
$journal = TransactionJournal::create(
|
||||
['user_id' => Auth::user()->id, 'transaction_type_id' => $transactionType->id, 'transaction_currency_id' => $this->importData['currency']->id,
|
||||
'description' => $this->importData['description'], 'completed' => 0, 'date' => $date, 'bill_id' => $this->importData['bill-id'],]
|
||||
);
|
||||
if ($journal->getErrors()->count() == 0) {
|
||||
// first transaction
|
||||
$accountId = $this->importData['asset-account-object']->id; // create first transaction:
|
||||
$amount = $this->importData['amount'];
|
||||
$transaction = Transaction::create(['transaction_journal_id' => $journal->id, 'account_id' => $accountId, 'amount' => $amount]);
|
||||
$errors = $transaction->getErrors();
|
||||
|
||||
// second transaction
|
||||
$accountId = $this->importData['opposing-account-object']->id; // create second transaction:
|
||||
$amount = bcmul($this->importData['amount'], -1);
|
||||
$transaction = Transaction::create(['transaction_journal_id' => $journal->id, 'account_id' => $accountId, 'amount' => $amount]);
|
||||
$errors = $transaction->getErrors()->merge($errors);
|
||||
}
|
||||
if ($errors->count() == 0) {
|
||||
$journal->completed = 1;
|
||||
$journal->save();
|
||||
} else {
|
||||
$text = join(',', $errors->all());
|
||||
|
||||
return $text;
|
||||
}
|
||||
$this->saveBudget($journal);
|
||||
$this->saveCategory($journal);
|
||||
$this->saveTags($journal);
|
||||
|
||||
// some debug info:
|
||||
$journalId = $journal->id;
|
||||
$type = $journal->transactionType->type;
|
||||
/** @var Account $asset */
|
||||
$asset = $this->importData['asset-account-object'];
|
||||
/** @var Account $opposing */
|
||||
$opposing = $this->importData['opposing-account-object'];
|
||||
|
||||
Log::info('Created journal #' . $journalId . ' of type ' . $type . '!');
|
||||
Log::info('Asset account ****** (#' . $asset->id . ') lost/gained: ' . $this->importData['amount']);
|
||||
Log::info($opposing->accountType->type . ' ****** (#' . $opposing->id . ') lost/gained: ' . bcmul($this->importData['amount'], -1));
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TransactionType
|
||||
*/
|
||||
protected function getTransactionType()
|
||||
{
|
||||
$transactionType = TransactionType::where('type', 'Deposit')->first();
|
||||
if ($this->importData['amount'] < 0) {
|
||||
$transactionType = TransactionType::where('type', 'Withdrawal')->first();
|
||||
}
|
||||
|
||||
if (in_array($this->importData['opposing-account-object']->accountType->type, ['Asset account', 'Default account'])) {
|
||||
$transactionType = TransactionType::where('type', 'Transfer')->first();
|
||||
}
|
||||
|
||||
return $transactionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*/
|
||||
protected function saveBudget(TransactionJournal $journal)
|
||||
{
|
||||
// add budget:
|
||||
if (!is_null($this->importData['budget'])) {
|
||||
$journal->budgets()->save($this->importData['budget']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*/
|
||||
protected function saveCategory(TransactionJournal $journal)
|
||||
{
|
||||
// add category:
|
||||
if (!is_null($this->importData['category'])) {
|
||||
$journal->categories()->save($this->importData['category']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*/
|
||||
protected function saveTags(TransactionJournal $journal)
|
||||
{
|
||||
if (!is_null($this->importData['tags'])) {
|
||||
foreach ($this->importData['tags'] as $tag) {
|
||||
$journal->tags()->save($tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Data $data
|
||||
*/
|
||||
public function setData($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
}
|
34
app/Helpers/Csv/Mapper/AnyAccount.php
Normal file
34
app/Helpers/Csv/Mapper/AnyAccount.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Mapper;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Account;
|
||||
|
||||
/**
|
||||
* Class AnyAccount
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Mapper
|
||||
*/
|
||||
class AnyAccount implements MapperInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMap()
|
||||
{
|
||||
$result = Auth::user()->accounts()->with('accountType')->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
|
||||
|
||||
$list = [];
|
||||
/** @var Account $account */
|
||||
foreach ($result as $account) {
|
||||
$list[$account->id] = $account->name . ' (' . $account->accountType->type . ')';
|
||||
}
|
||||
asort($list);
|
||||
|
||||
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
45
app/Helpers/Csv/Mapper/AssetAccount.php
Normal file
45
app/Helpers/Csv/Mapper/AssetAccount.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Mapper;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Account;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* Class AssetAccount
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Mapper
|
||||
*/
|
||||
class AssetAccount implements MapperInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMap()
|
||||
{
|
||||
$result = Auth::user()->accounts()->with(
|
||||
['accountmeta' => function (HasMany $query) {
|
||||
$query->where('name', 'accountRole');
|
||||
}]
|
||||
)->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
|
||||
|
||||
$list = [];
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($result as $account) {
|
||||
$name = $account->name;
|
||||
if (strlen($account->iban) > 0) {
|
||||
$name .= ' (' . $account->iban . ')';
|
||||
}
|
||||
$list[$account->id] = $name;
|
||||
}
|
||||
|
||||
asort($list);
|
||||
|
||||
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
34
app/Helpers/Csv/Mapper/Bill.php
Normal file
34
app/Helpers/Csv/Mapper/Bill.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Mapper;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Bill as BillModel;
|
||||
|
||||
/**
|
||||
* Class Bill
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Mapper
|
||||
*/
|
||||
class Bill implements MapperInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMap()
|
||||
{
|
||||
$result = Auth::user()->bills()->get(['bills.*']);
|
||||
$list = [];
|
||||
|
||||
/** @var BillModel $bill */
|
||||
foreach ($result as $bill) {
|
||||
$list[$bill->id] = $bill->name . ' [' . $bill->match . ']';
|
||||
}
|
||||
asort($list);
|
||||
|
||||
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
34
app/Helpers/Csv/Mapper/Budget.php
Normal file
34
app/Helpers/Csv/Mapper/Budget.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Mapper;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Budget as BudgetModel;
|
||||
|
||||
/**
|
||||
* Class Budget
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Mapper
|
||||
*/
|
||||
class Budget implements MapperInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMap()
|
||||
{
|
||||
$result = Auth::user()->budgets()->get(['budgets.*']);
|
||||
$list = [];
|
||||
|
||||
/** @var BudgetModel $budget */
|
||||
foreach ($result as $budget) {
|
||||
$list[$budget->id] = $budget->name;
|
||||
}
|
||||
asort($list);
|
||||
|
||||
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
34
app/Helpers/Csv/Mapper/Category.php
Normal file
34
app/Helpers/Csv/Mapper/Category.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Mapper;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Category as CategoryModel;
|
||||
|
||||
/**
|
||||
* Class Category
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Mapper
|
||||
*/
|
||||
class Category implements MapperInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMap()
|
||||
{
|
||||
$result = Auth::user()->categories()->get(['categories.*']);
|
||||
$list = [];
|
||||
|
||||
/** @var CategoryModel $category */
|
||||
foreach ($result as $category) {
|
||||
$list[$category->id] = $category->name;
|
||||
}
|
||||
asort($list);
|
||||
|
||||
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
16
app/Helpers/Csv/Mapper/MapperInterface.php
Normal file
16
app/Helpers/Csv/Mapper/MapperInterface.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Mapper;
|
||||
|
||||
/**
|
||||
* Interface MapperInterface
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Mapper
|
||||
*/
|
||||
interface MapperInterface
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMap();
|
||||
}
|
34
app/Helpers/Csv/Mapper/Tag.php
Normal file
34
app/Helpers/Csv/Mapper/Tag.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Mapper;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Tag as TagModel;
|
||||
|
||||
/**
|
||||
* Class Tag
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Mapper
|
||||
*/
|
||||
class Tag implements MapperInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMap()
|
||||
{
|
||||
$result = Auth::user()->budgets()->get(['tags.*']);
|
||||
$list = [];
|
||||
|
||||
/** @var TagModel $tag */
|
||||
foreach ($result as $tag) {
|
||||
$list[$tag->id] = $tag->tag;
|
||||
}
|
||||
asort($list);
|
||||
|
||||
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
32
app/Helpers/Csv/Mapper/TransactionCurrency.php
Normal file
32
app/Helpers/Csv/Mapper/TransactionCurrency.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Mapper;
|
||||
|
||||
use FireflyIII\Models\TransactionCurrency as TC;
|
||||
|
||||
/**
|
||||
* Class TransactionCurrency
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Mapper
|
||||
*/
|
||||
class TransactionCurrency implements MapperInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMap()
|
||||
{
|
||||
$currencies = TC::get();
|
||||
$list = [];
|
||||
foreach ($currencies as $currency) {
|
||||
$list[$currency->id] = $currency->name . ' (' . $currency->code . ')';
|
||||
}
|
||||
|
||||
asort($list);
|
||||
|
||||
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
35
app/Helpers/Csv/PostProcessing/Amount.php
Normal file
35
app/Helpers/Csv/PostProcessing/Amount.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\PostProcessing;
|
||||
|
||||
/**
|
||||
* Class Amount
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\PostProcessing
|
||||
*/
|
||||
class Amount implements PostProcessorInterface
|
||||
{
|
||||
|
||||
/** @var array */
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function process()
|
||||
{
|
||||
bcscale(2);
|
||||
$this->data['amount'] = bcmul($this->data['amount'], $this->data['amount-modifier']);
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
185
app/Helpers/Csv/PostProcessing/AssetAccount.php
Normal file
185
app/Helpers/Csv/PostProcessing/AssetAccount.php
Normal file
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\PostProcessing;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use Log;
|
||||
use Validator;
|
||||
|
||||
/**
|
||||
* Class AssetAccount
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\PostProcessing
|
||||
*/
|
||||
class AssetAccount implements PostProcessorInterface
|
||||
{
|
||||
|
||||
/** @var array */
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function process()
|
||||
{
|
||||
$result = $this->checkIdNameObject(); // has object in ID or Name?
|
||||
if (!is_null($result)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result = $this->checkIbanString();
|
||||
if (!is_null($result)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result = $this->checkNameString();
|
||||
if (!is_null($result)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function checkIdNameObject()
|
||||
{
|
||||
if ($this->data['asset-account-id'] instanceof Account) { // first priority. try to find the account based on ID, if any
|
||||
$this->data['asset-account-object'] = $this->data['asset-account-id'];
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
if ($this->data['asset-account-iban'] instanceof Account) { // second: try to find the account based on IBAN, if any.
|
||||
$this->data['asset-account-object'] = $this->data['asset-account-iban'];
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
*/
|
||||
protected function checkIbanString()
|
||||
{
|
||||
$rules = ['iban' => 'iban'];
|
||||
$check = ['iban' => $this->data['asset-account-iban']];
|
||||
$validator = Validator::make($check, $rules);
|
||||
if (!$validator->fails()) {
|
||||
$this->data['asset-account-object'] = $this->parseIbanString();
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
*/
|
||||
protected function parseIbanString()
|
||||
{
|
||||
// create by name and/or iban.
|
||||
$accounts = Auth::user()->accounts()->get();
|
||||
foreach ($accounts as $entry) {
|
||||
if ($entry->iban == $this->data['asset-account-iban']) {
|
||||
|
||||
return $entry;
|
||||
}
|
||||
}
|
||||
$account = $this->createAccount();
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
*/
|
||||
protected function createAccount()
|
||||
{
|
||||
$accountType = $this->getAccountType();
|
||||
|
||||
// create if not exists:
|
||||
$name = is_string($this->data['asset-account-name']) && strlen($this->data['asset-account-name']) > 0 ? $this->data['asset-account-name']
|
||||
: $this->data['asset-account-iban'];
|
||||
$account = Account::firstOrCreateEncrypted(
|
||||
[
|
||||
'user_id' => Auth::user()->id,
|
||||
'account_type_id' => $accountType->id,
|
||||
'name' => $name,
|
||||
'iban' => $this->data['asset-account-iban'],
|
||||
'active' => true,
|
||||
]
|
||||
);
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return AccountType
|
||||
*/
|
||||
protected function getAccountType()
|
||||
{
|
||||
return AccountType::where('type', 'Asset account')->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
*/
|
||||
protected function checkNameString()
|
||||
{
|
||||
if ($this->data['asset-account-name'] instanceof Account) { // third: try to find account based on name, if any.
|
||||
$this->data['asset-account-object'] = $this->data['asset-account-name'];
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
if (is_string($this->data['asset-account-name'])) {
|
||||
$this->data['asset-account-object'] = $this->parseNameString();
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
*/
|
||||
protected function parseNameString()
|
||||
{
|
||||
$accountType = $this->getAccountType();
|
||||
$accounts = Auth::user()->accounts()->where('account_type_id', $accountType->id)->get();
|
||||
foreach ($accounts as $entry) {
|
||||
if ($entry->name == $this->data['asset-account-name']) {
|
||||
Log::debug('Found an asset account with this name (#' . $entry->id . ': ******)');
|
||||
|
||||
return $entry;
|
||||
}
|
||||
}
|
||||
// create if not exists:
|
||||
$account = Account::firstOrCreateEncrypted(
|
||||
[
|
||||
'user_id' => Auth::user()->id,
|
||||
'account_type_id' => $accountType->id,
|
||||
'name' => $this->data['asset-account-name'],
|
||||
'iban' => '',
|
||||
'active' => true,
|
||||
]
|
||||
);
|
||||
|
||||
return $account;
|
||||
}
|
||||
}
|
37
app/Helpers/Csv/PostProcessing/Bill.php
Normal file
37
app/Helpers/Csv/PostProcessing/Bill.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\PostProcessing;
|
||||
|
||||
/**
|
||||
* Class Bill
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\PostProcessing
|
||||
*/
|
||||
class Bill implements PostProcessorInterface
|
||||
{
|
||||
|
||||
/** @var array */
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function process()
|
||||
{
|
||||
|
||||
// get bill id.
|
||||
if (!is_null($this->data['bill'])) {
|
||||
$this->data['bill-id'] = $this->data['bill']->id;
|
||||
}
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
41
app/Helpers/Csv/PostProcessing/Currency.php
Normal file
41
app/Helpers/Csv/PostProcessing/Currency.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\PostProcessing;
|
||||
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use Preferences;
|
||||
|
||||
/**
|
||||
* Class Currency
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\PostProcessing
|
||||
*/
|
||||
class Currency implements PostProcessorInterface
|
||||
{
|
||||
|
||||
/** @var array */
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function process()
|
||||
{
|
||||
|
||||
// fix currency
|
||||
if (is_null($this->data['currency'])) {
|
||||
$currencyPreference = Preferences::get('currencyPreference', 'EUR');
|
||||
$this->data['currency'] = TransactionCurrency::whereCode($currencyPreference->data)->first();
|
||||
}
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
38
app/Helpers/Csv/PostProcessing/Description.php
Normal file
38
app/Helpers/Csv/PostProcessing/Description.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\PostProcessing;
|
||||
|
||||
/**
|
||||
* Class Description
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\PostProcessing
|
||||
*/
|
||||
class Description implements PostProcessorInterface
|
||||
{
|
||||
|
||||
/** @var array */
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function process()
|
||||
{
|
||||
$this->data['description'] = trim($this->data['description']);
|
||||
if (strlen($this->data['description']) == 0) {
|
||||
$this->data['description'] = trans('firefly.csv_empty_description');
|
||||
}
|
||||
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData(array $data)
|
||||
{
|
||||
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
210
app/Helpers/Csv/PostProcessing/OpposingAccount.php
Normal file
210
app/Helpers/Csv/PostProcessing/OpposingAccount.php
Normal file
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\PostProcessing;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use Log;
|
||||
use Validator;
|
||||
|
||||
/**
|
||||
* Class OpposingAccount
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\PostProcessing
|
||||
*/
|
||||
class OpposingAccount implements PostProcessorInterface
|
||||
{
|
||||
|
||||
/** @var array */
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function process()
|
||||
{
|
||||
// three values:
|
||||
// opposing-account-id, opposing-account-iban, opposing-account-name
|
||||
|
||||
|
||||
$result = $this->checkIdNameObject();
|
||||
if (!is_null($result)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result = $this->checkIbanString();
|
||||
if (!is_null($result)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result = $this->checkNameString();
|
||||
if (!is_null($result)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function checkIdNameObject()
|
||||
{
|
||||
if ($this->data['opposing-account-id'] instanceof Account) { // first priority. try to find the account based on ID, if any
|
||||
Log::debug('OpposingAccountPostProcession: opposing-account-id is an Account.');
|
||||
$this->data['opposing-account-object'] = $this->data['opposing-account-id'];
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
if ($this->data['opposing-account-iban'] instanceof Account) { // second: try to find the account based on IBAN, if any.
|
||||
Log::debug('OpposingAccountPostProcession: opposing-account-iban is an Account.');
|
||||
$this->data['opposing-account-object'] = $this->data['opposing-account-iban'];
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
*/
|
||||
protected function checkIbanString()
|
||||
{
|
||||
$rules = ['iban' => 'iban'];
|
||||
$iban = $this->data['opposing-account-iban'];
|
||||
$check = ['iban' => $iban];
|
||||
$validator = Validator::make($check, $rules);
|
||||
if (is_string($iban) && strlen($iban) > 0 && !$validator->fails()) {
|
||||
|
||||
Log::debug('OpposingAccountPostProcession: opposing-account-iban is a string (******).');
|
||||
$this->data['opposing-account-object'] = $this->parseIbanString();
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
*/
|
||||
protected function parseIbanString()
|
||||
{
|
||||
// create by name and/or iban.
|
||||
$accounts = Auth::user()->accounts()->get();
|
||||
foreach ($accounts as $entry) {
|
||||
if ($entry->iban == $this->data['opposing-account-iban']) {
|
||||
Log::debug('OpposingAccountPostProcession: opposing-account-iban matches an Account.');
|
||||
|
||||
return $entry;
|
||||
}
|
||||
}
|
||||
$account = $this->createAccount();
|
||||
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
*/
|
||||
protected function createAccount()
|
||||
{
|
||||
$accountType = $this->getAccountType();
|
||||
|
||||
// create if not exists:
|
||||
$name = is_string($this->data['opposing-account-name']) && strlen($this->data['opposing-account-name']) > 0 ? $this->data['opposing-account-name']
|
||||
: $this->data['opposing-account-iban'];
|
||||
$account = Account::firstOrCreateEncrypted(
|
||||
[
|
||||
'user_id' => Auth::user()->id,
|
||||
'account_type_id' => $accountType->id,
|
||||
'name' => $name,
|
||||
'iban' => $this->data['opposing-account-iban'],
|
||||
'active' => true,
|
||||
]
|
||||
);
|
||||
Log::debug('OpposingAccountPostProcession: created a new account.');
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return AccountType
|
||||
*/
|
||||
protected function getAccountType()
|
||||
{
|
||||
// opposing account type:
|
||||
if ($this->data['amount'] < 0) {
|
||||
// create expense account:
|
||||
|
||||
return AccountType::where('type', 'Expense account')->first();
|
||||
} else {
|
||||
// create revenue account:
|
||||
|
||||
return AccountType::where('type', 'Revenue account')->first();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
*/
|
||||
protected function checkNameString()
|
||||
{
|
||||
if ($this->data['opposing-account-name'] instanceof Account) { // third: try to find account based on name, if any.
|
||||
Log::debug('OpposingAccountPostProcession: opposing-account-name is an Account.');
|
||||
$this->data['opposing-account-object'] = $this->data['opposing-account-name'];
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
if (is_string($this->data['opposing-account-name'])) {
|
||||
|
||||
$this->data['opposing-account-object'] = $this->parseNameString();
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
*/
|
||||
protected function parseNameString()
|
||||
{
|
||||
$accountType = $this->getAccountType();
|
||||
$accounts = Auth::user()->accounts()->where('account_type_id', $accountType->id)->get();
|
||||
foreach ($accounts as $entry) {
|
||||
if ($entry->name == $this->data['opposing-account-name']) {
|
||||
Log::debug('Found an account with this name (#' . $entry->id . ': ******)');
|
||||
|
||||
return $entry;
|
||||
}
|
||||
}
|
||||
// create if not exists:
|
||||
$account = Account::firstOrCreateEncrypted(
|
||||
[
|
||||
'user_id' => Auth::user()->id,
|
||||
'account_type_id' => $accountType->id,
|
||||
'name' => $this->data['opposing-account-name'],
|
||||
'iban' => '',
|
||||
'active' => true,
|
||||
]
|
||||
);
|
||||
|
||||
return $account;
|
||||
}
|
||||
}
|
29
app/Helpers/Csv/PostProcessing/PostProcessorInterface.php
Normal file
29
app/Helpers/Csv/PostProcessing/PostProcessorInterface.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: sander
|
||||
* Date: 05/07/15
|
||||
* Time: 19:20
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\PostProcessing;
|
||||
|
||||
|
||||
/**
|
||||
* Interface PostProcessorInterface
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\PostProcessing
|
||||
*/
|
||||
interface PostProcessorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function process();
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData(array $data);
|
||||
}
|
45
app/Helpers/Csv/Specifix/Dummy.php
Normal file
45
app/Helpers/Csv/Specifix/Dummy.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Specifix;
|
||||
|
||||
/**
|
||||
* Class Dummy
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Specifix
|
||||
*/
|
||||
class Dummy
|
||||
{
|
||||
/** @var array */
|
||||
protected $data;
|
||||
|
||||
/** @var array */
|
||||
protected $row;
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function fix()
|
||||
{
|
||||
return $this->data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $row
|
||||
*/
|
||||
public function setRow($row)
|
||||
{
|
||||
$this->row = $row;
|
||||
}
|
||||
|
||||
|
||||
}
|
66
app/Helpers/Csv/Specifix/RabobankDescription.php
Normal file
66
app/Helpers/Csv/Specifix/RabobankDescription.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Specifix;
|
||||
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class RabobankDescription
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Specifix
|
||||
*/
|
||||
class RabobankDescription
|
||||
{
|
||||
/** @var array */
|
||||
protected $data;
|
||||
|
||||
/** @var array */
|
||||
protected $row;
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function fix()
|
||||
{
|
||||
$this->rabobankFixEmptyOpposing();
|
||||
|
||||
return $this->data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes Rabobank specific thing.
|
||||
*/
|
||||
protected function rabobankFixEmptyOpposing()
|
||||
{
|
||||
Log::debug('RaboSpecifix: Opposing account name is "******".');
|
||||
if (is_string($this->data['opposing-account-name']) && strlen($this->data['opposing-account-name']) == 0) {
|
||||
Log::debug('RaboSpecifix: opp-name is zero length, changed to: "******"');
|
||||
$this->data['opposing-account-name'] = $this->row[10];
|
||||
|
||||
Log::debug('Description was: "******".');
|
||||
$this->data['description'] = trim(str_replace($this->row[10], '', $this->data['description']));
|
||||
Log::debug('Description is now: "******".');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $row
|
||||
*/
|
||||
public function setRow($row)
|
||||
{
|
||||
$this->row = $row;
|
||||
}
|
||||
|
||||
|
||||
}
|
25
app/Helpers/Csv/Specifix/SpecifixInterface.php
Normal file
25
app/Helpers/Csv/Specifix/SpecifixInterface.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
namespace FireflyIII\Helpers\Csv\Specifix;
|
||||
|
||||
/**
|
||||
* Interface SpecifixInterface
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Specifix
|
||||
*/
|
||||
interface SpecifixInterface
|
||||
{
|
||||
/**
|
||||
* Implement bank and locale related fixes.
|
||||
*/
|
||||
public function fix();
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData($data);
|
||||
|
||||
/**
|
||||
* @param array $row
|
||||
*/
|
||||
public function setRow($row);
|
||||
}
|
194
app/Helpers/Csv/Wizard.php
Normal file
194
app/Helpers/Csv/Wizard.php
Normal file
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
namespace FireflyIII\Helpers\Csv;
|
||||
|
||||
use Auth;
|
||||
use Config;
|
||||
use Crypt;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Csv\Mapper\MapperInterface;
|
||||
use League\Csv\Reader;
|
||||
use Log;
|
||||
use ReflectionException;
|
||||
use Session;
|
||||
|
||||
/**
|
||||
* Class Wizard
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv
|
||||
*/
|
||||
class Wizard implements WizardInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Reader $reader
|
||||
* @param array $map
|
||||
* @param bool $hasHeaders
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMappableValues($reader, array $map, $hasHeaders)
|
||||
{
|
||||
$values = [];
|
||||
/*
|
||||
* Loop over the CSV and collect mappable data:
|
||||
*/
|
||||
$keys = array_keys($map);
|
||||
foreach ($reader as $index => $row) {
|
||||
if ($this->useRow($hasHeaders, $index)) {
|
||||
// collect all map values
|
||||
|
||||
foreach ($keys as $column) {
|
||||
$values[$column][] = $row[$column];
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Make each one unique.
|
||||
*/
|
||||
$values = $this->uniqueRecursive($values);
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $roles
|
||||
* @param mixed $map
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function processSelectedMapping(array $roles, $map)
|
||||
{
|
||||
$configRoles = Config::get('csv.roles');
|
||||
$maps = [];
|
||||
|
||||
|
||||
if (is_array($map)) {
|
||||
foreach ($map as $index => $field) {
|
||||
if (isset($roles[$index])) {
|
||||
$name = $roles[$index];
|
||||
if ($configRoles[$name]['mappable']) {
|
||||
$maps[$index] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $maps;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $input
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function processSelectedRoles($input)
|
||||
{
|
||||
$roles = [];
|
||||
|
||||
|
||||
/*
|
||||
* Store all rows for each column:
|
||||
*/
|
||||
if (is_array($input)) {
|
||||
foreach ($input as $index => $role) {
|
||||
if ($role != '_ignore') {
|
||||
$roles[$index] = $role;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $fields
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function sessionHasValues(array $fields)
|
||||
{
|
||||
foreach ($fields as $field) {
|
||||
if (!Session::has($field)) {
|
||||
Log::error('Session is missing field: ' . $field);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $map
|
||||
*
|
||||
* @return array
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function showOptions(array $map)
|
||||
{
|
||||
$options = [];
|
||||
foreach ($map as $index => $columnRole) {
|
||||
|
||||
$mapper = Config::get('csv.roles.' . $columnRole . '.mapper');
|
||||
if (is_null($mapper)) {
|
||||
throw new FireflyException('Cannot map field of type "' . $columnRole . '".');
|
||||
}
|
||||
$class = 'FireflyIII\Helpers\Csv\Mapper\\' . $mapper;
|
||||
try {
|
||||
/** @var MapperInterface $mapObject */
|
||||
$mapObject = app($class);
|
||||
} catch (ReflectionException $e) {
|
||||
throw new FireflyException('Column "' . $columnRole . '" cannot be mapped because class ' . $mapper . ' does not exist.');
|
||||
}
|
||||
$set = $mapObject->getMap();
|
||||
$options[$index] = $set;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function storeCsvFile($path)
|
||||
{
|
||||
$time = str_replace(' ', '-', microtime());
|
||||
$fileName = 'csv-upload-' . Auth::user()->id . '-' . $time . '.csv.encrypted';
|
||||
$fullPath = storage_path('upload') . DIRECTORY_SEPARATOR . $fileName;
|
||||
$content = file_get_contents($path);
|
||||
$contentEncrypted = Crypt::encrypt($content);
|
||||
file_put_contents($fullPath, $contentEncrypted);
|
||||
|
||||
return $fullPath;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $hasHeaders
|
||||
* @param int $index
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function useRow($hasHeaders, $index)
|
||||
{
|
||||
return ($hasHeaders && $index > 1) || !$hasHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function uniqueRecursive(array $array)
|
||||
{
|
||||
foreach ($array as $column => $found) {
|
||||
$array[$column] = array_unique($found);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
59
app/Helpers/Csv/WizardInterface.php
Normal file
59
app/Helpers/Csv/WizardInterface.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv;
|
||||
|
||||
use League\Csv\Reader;
|
||||
|
||||
/**
|
||||
* Interface WizardInterface
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv
|
||||
*/
|
||||
interface WizardInterface
|
||||
{
|
||||
/**
|
||||
* @param Reader $reader
|
||||
* @param array $map
|
||||
* @param bool $hasHeaders
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMappableValues($reader, array $map, $hasHeaders);
|
||||
|
||||
/**
|
||||
* @param array $roles
|
||||
* @param mixed $map
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function processSelectedMapping(array $roles, $map);
|
||||
|
||||
/**
|
||||
* @param mixed $input
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function processSelectedRoles($input);
|
||||
|
||||
/**
|
||||
* @param array $fields
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function sessionHasValues(array $fields);
|
||||
|
||||
/**
|
||||
* @param array $map
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function showOptions(array $map);
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function storeCsvFile($path);
|
||||
|
||||
}
|
@@ -37,10 +37,12 @@ class Help implements HelpInterface
|
||||
*/
|
||||
public function getFromGithub($route)
|
||||
{
|
||||
$uri = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/en/' . e($route) . '.md';
|
||||
$content = [
|
||||
$uri = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/en/' . e($route) . '.md';
|
||||
$routeIndex = str_replace('.', '-', $route);
|
||||
$title = trans('help.' . $routeIndex);
|
||||
$content = [
|
||||
'text' => '<p>There is no help for this route!</p>',
|
||||
'title' => $route,
|
||||
'title' => $title,
|
||||
];
|
||||
try {
|
||||
$content['text'] = file_get_contents($uri);
|
||||
@@ -69,6 +71,18 @@ class Help implements HelpInterface
|
||||
return Route::has($route);
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param $route
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function inCache($route)
|
||||
{
|
||||
return Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text');
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
@@ -82,16 +96,4 @@ class Help implements HelpInterface
|
||||
Cache::put('help.' . $route . '.text', $content['text'], 10080); // a week.
|
||||
Cache::put('help.' . $route . '.title', $content['title'], 10080);
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param $route
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function inCache($route)
|
||||
{
|
||||
return Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text');
|
||||
}
|
||||
}
|
||||
|
@@ -1,167 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Reminders;
|
||||
|
||||
use Amount;
|
||||
use Auth;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\Reminder;
|
||||
use Navigation;
|
||||
|
||||
/**
|
||||
* Class ReminderHelper
|
||||
*
|
||||
* @package FireflyIII\Helpers\Reminders
|
||||
*/
|
||||
class ReminderHelper implements ReminderHelperInterface
|
||||
{
|
||||
/**
|
||||
* @param PiggyBank $piggyBank
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Reminder
|
||||
*/
|
||||
public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end)
|
||||
{
|
||||
$reminder = Auth::user()->reminders()->where('remindersable_id', $piggyBank->id)->onDates($start, $end)->first();
|
||||
if (is_null($reminder)) {
|
||||
|
||||
if (!is_null($piggyBank->targetdate)) {
|
||||
// get ranges again, but now for the start date
|
||||
$ranges = $this->getReminderRanges($piggyBank, $start);
|
||||
$currentRep = $piggyBank->currentRelevantRep();
|
||||
$left = $piggyBank->targetamount - $currentRep->currentamount;
|
||||
$perReminder = count($ranges) == 0 ? $left : $left / count($ranges);
|
||||
} else {
|
||||
$perReminder = null;
|
||||
$ranges = [];
|
||||
$left = 0;
|
||||
}
|
||||
$metaData = [
|
||||
'perReminder' => $perReminder,
|
||||
'rangesCount' => count($ranges),
|
||||
'ranges' => $ranges,
|
||||
'leftToSave' => $left,
|
||||
];
|
||||
|
||||
$reminder = new Reminder;
|
||||
$reminder->user()->associate(Auth::user());
|
||||
$reminder->startdate = $start;
|
||||
$reminder->enddate = $end;
|
||||
$reminder->active = true;
|
||||
$reminder->metadata = $metaData;
|
||||
$reminder->notnow = false;
|
||||
$reminder->remindersable()->associate($piggyBank);
|
||||
$reminder->save();
|
||||
|
||||
return $reminder;
|
||||
|
||||
} else {
|
||||
return $reminder;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all reminders for a piggy bank for a given date.
|
||||
*
|
||||
* @param PiggyBank $piggyBank
|
||||
*
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function createReminders(PiggyBank $piggyBank, Carbon $date)
|
||||
{
|
||||
$ranges = $this->getReminderRanges($piggyBank);
|
||||
|
||||
foreach ($ranges as $range) {
|
||||
if ($date < $range['end'] && $date > $range['start']) {
|
||||
// create a reminder here!
|
||||
$this->createReminder($piggyBank, $range['start'], $range['end']);
|
||||
// stop looping, we're done.
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine will return an array consisting of two dates which indicate the start
|
||||
* and end date for each reminder that this piggy bank will have, if the piggy bank has
|
||||
* any reminders. For example:
|
||||
*
|
||||
* [12 mar - 15 mar]
|
||||
* [15 mar - 18 mar]
|
||||
*
|
||||
* etcetera.
|
||||
*
|
||||
* Array is filled with tiny arrays with Carbon objects in them.
|
||||
*
|
||||
* @param PiggyBank $piggyBank
|
||||
* @param Carbon $date ;
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getReminderRanges(PiggyBank $piggyBank, Carbon $date = null)
|
||||
{
|
||||
$ranges = [];
|
||||
if (is_null($date)) {
|
||||
$date = new Carbon;
|
||||
}
|
||||
|
||||
if ($piggyBank->remind_me === false) {
|
||||
return $ranges;
|
||||
}
|
||||
|
||||
if (!is_null($piggyBank->targetdate)) {
|
||||
// count back until now.
|
||||
$start = $piggyBank->targetdate;
|
||||
$end = $piggyBank->startdate;
|
||||
|
||||
while ($start > $end) {
|
||||
$currentEnd = clone $start;
|
||||
$start = Navigation::subtractPeriod($start, $piggyBank->reminder, 1);
|
||||
$currentStart = clone $start;
|
||||
$ranges[] = ['start' => clone $currentStart, 'end' => clone $currentEnd];
|
||||
}
|
||||
} else {
|
||||
$start = clone $piggyBank->startdate;
|
||||
while ($start < $date) {
|
||||
$currentStart = clone $start;
|
||||
$start = Navigation::addPeriod($start, $piggyBank->reminder, 0);
|
||||
$currentEnd = clone $start;
|
||||
$ranges[] = ['start' => clone $currentStart, 'end' => clone $currentEnd];
|
||||
}
|
||||
}
|
||||
|
||||
return $ranges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a reminder, finds the piggy bank and tells you what to do now.
|
||||
* Aka how much money to put in.
|
||||
*
|
||||
*
|
||||
* @param Reminder $reminder
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getReminderText(Reminder $reminder)
|
||||
{
|
||||
/** @var PiggyBank $piggyBank */
|
||||
$piggyBank = $reminder->remindersable;
|
||||
|
||||
if (is_null($piggyBank)) {
|
||||
return 'Piggy bank no longer exists.';
|
||||
}
|
||||
|
||||
if (is_null($piggyBank->targetdate)) {
|
||||
return 'Add money to this piggy bank to reach your target of ' . Amount::format($piggyBank->targetamount);
|
||||
}
|
||||
|
||||
return 'Add ' . Amount::format($reminder->metadata->perReminder) . ' to fill this piggy bank on ' . $piggyBank->targetdate->format('jS F Y');
|
||||
|
||||
}
|
||||
}
|
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Reminders;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\Reminder;
|
||||
|
||||
/**
|
||||
* Interface ReminderHelperInterface
|
||||
*
|
||||
* @package FireflyIII\Helpers\Reminders
|
||||
*/
|
||||
interface ReminderHelperInterface
|
||||
{
|
||||
/**
|
||||
* Takes a reminder, finds the piggy bank and tells you what to do now.
|
||||
* Aka how much money to put in.
|
||||
*
|
||||
* @param Reminder $reminder
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getReminderText(Reminder $reminder);
|
||||
|
||||
/**
|
||||
* This routine will return an array consisting of two dates which indicate the start
|
||||
* and end date for each reminder that this piggy bank will have, if the piggy bank has
|
||||
* any reminders. For example:
|
||||
*
|
||||
* [12 mar - 15 mar]
|
||||
* [15 mar - 18 mar]
|
||||
*
|
||||
* etcetera.
|
||||
*
|
||||
* Array is filled with tiny arrays with Carbon objects in them.
|
||||
*
|
||||
* @param PiggyBank $piggyBank
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getReminderRanges(PiggyBank $piggyBank);
|
||||
|
||||
/**
|
||||
* @param PiggyBank $piggyBank
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Reminder
|
||||
*/
|
||||
public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* Create all reminders for a piggy bank for a given date.
|
||||
*
|
||||
* @param PiggyBank $piggyBank
|
||||
*
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function createReminders(PiggyBank $piggyBank, Carbon $date);
|
||||
}
|
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace FireflyIII\Helpers\Report;
|
||||
|
||||
use App;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collection\Account as AccountCollection;
|
||||
use FireflyIII\Helpers\Collection\Balance;
|
||||
@@ -53,16 +52,17 @@ class ReportHelper implements ReportHelperInterface
|
||||
* @param Carbon $end
|
||||
* @param $shared
|
||||
*
|
||||
* @return Account
|
||||
* @return AccountCollection
|
||||
*/
|
||||
public function getAccountReport(Carbon $date, Carbon $end, $shared)
|
||||
{
|
||||
|
||||
|
||||
$accounts = $this->query->getAllAccounts($date, $end, $shared);
|
||||
$start = 0;
|
||||
$end = 0;
|
||||
$diff = 0;
|
||||
$start = '0';
|
||||
$end = '0';
|
||||
$diff = '0';
|
||||
bcscale(2);
|
||||
|
||||
// remove cash account, if any:
|
||||
$accounts = $accounts->filter(
|
||||
@@ -70,14 +70,16 @@ class ReportHelper implements ReportHelperInterface
|
||||
if ($account->accountType->type != 'Cash account') {
|
||||
return $account;
|
||||
}
|
||||
} // @codeCoverageIgnore
|
||||
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
// summarize:
|
||||
foreach ($accounts as $account) {
|
||||
$start += $account->startBalance;
|
||||
$end += $account->endBalance;
|
||||
$diff += ($account->endBalance - $account->startBalance);
|
||||
$start = bcadd($start, $account->startBalance);
|
||||
$end = bcadd($end, $account->endBalance);
|
||||
$diff = bcadd($diff, bcsub($account->endBalance, $account->startBalance));
|
||||
}
|
||||
|
||||
$object = new AccountCollection;
|
||||
@@ -112,8 +114,8 @@ class ReportHelper implements ReportHelperInterface
|
||||
*/
|
||||
public function getBalanceReport(Carbon $start, Carbon $end, $shared)
|
||||
{
|
||||
$repository = App::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
|
||||
$tagRepository = App::make('FireflyIII\Repositories\Tag\TagRepositoryInterface');
|
||||
$repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
|
||||
$tagRepository = app('FireflyIII\Repositories\Tag\TagRepositoryInterface');
|
||||
$balance = new Balance;
|
||||
|
||||
// build a balance header:
|
||||
@@ -131,7 +133,7 @@ class ReportHelper implements ReportHelperInterface
|
||||
$line->setBudget($budget);
|
||||
|
||||
// get budget amount for current period:
|
||||
$rep = $repository->getCurrentRepetition($budget, $start);
|
||||
$rep = $repository->getCurrentRepetition($budget, $start, $end);
|
||||
$line->setRepetition($rep);
|
||||
|
||||
// loop accounts:
|
||||
@@ -161,7 +163,8 @@ class ReportHelper implements ReportHelperInterface
|
||||
foreach ($accounts as $account) {
|
||||
$spent = $this->query->spentNoBudget($account, $start, $end);
|
||||
$left = $tagRepository->coveredByBalancingActs($account, $start, $end);
|
||||
$diff = $spent + $left;
|
||||
bcscale(2);
|
||||
$diff = bcsub($spent, $left);
|
||||
|
||||
// budget
|
||||
$budgetEntry = new BalanceEntry;
|
||||
@@ -196,16 +199,15 @@ class ReportHelper implements ReportHelperInterface
|
||||
* This method generates a full report for the given period on all
|
||||
* the users bills and their payments.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param boolean $shared
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return BillCollection
|
||||
*/
|
||||
public function getBillReport(Carbon $start, Carbon $end, $shared)
|
||||
public function getBillReport(Carbon $start, Carbon $end)
|
||||
{
|
||||
/** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */
|
||||
$repository = App::make('FireflyIII\Repositories\Bill\BillRepositoryInterface');
|
||||
$repository = app('FireflyIII\Repositories\Bill\BillRepositoryInterface');
|
||||
$bills = $repository->getBills();
|
||||
$collection = new BillCollection;
|
||||
|
||||
@@ -214,18 +216,19 @@ class ReportHelper implements ReportHelperInterface
|
||||
$billLine = new BillLine;
|
||||
$billLine->setBill($bill);
|
||||
$billLine->setActive(intval($bill->active) == 1);
|
||||
$billLine->setMin(floatval($bill->amount_min));
|
||||
$billLine->setMax(floatval($bill->amount_max));
|
||||
$billLine->setMin($bill->amount_min);
|
||||
$billLine->setMax($bill->amount_max);
|
||||
|
||||
// is hit in period?
|
||||
bcscale(2);
|
||||
$set = $repository->getJournalsInRange($bill, $start, $end);
|
||||
if ($set->count() == 0) {
|
||||
$billLine->setHit(false);
|
||||
} else {
|
||||
$billLine->setHit(true);
|
||||
$amount = 0;
|
||||
$amount = '0';
|
||||
foreach ($set as $entry) {
|
||||
$amount += $entry->amount;
|
||||
$amount = bcadd($amount, $entry->amount);
|
||||
}
|
||||
$billLine->setAmount($amount);
|
||||
}
|
||||
@@ -249,16 +252,18 @@ class ReportHelper implements ReportHelperInterface
|
||||
{
|
||||
$object = new BudgetCollection;
|
||||
/** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */
|
||||
$repository = App::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
|
||||
$repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
|
||||
$set = $repository->getBudgets();
|
||||
|
||||
bcscale(2);
|
||||
|
||||
foreach ($set as $budget) {
|
||||
|
||||
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
|
||||
|
||||
// no repetition(s) for this budget:
|
||||
if ($repetitions->count() == 0) {
|
||||
$spent = $repository->spentInPeriodCorrected($budget, $start, $end, $shared);
|
||||
$spent = $repository->balanceInPeriod($budget, $start, $end, $shared);
|
||||
$budgetLine = new BudgetLine;
|
||||
$budgetLine->setBudget($budget);
|
||||
$budgetLine->setOverspent($spent);
|
||||
@@ -273,10 +278,11 @@ class ReportHelper implements ReportHelperInterface
|
||||
$budgetLine = new BudgetLine;
|
||||
$budgetLine->setBudget($budget);
|
||||
$budgetLine->setRepetition($repetition);
|
||||
$expenses = $repository->spentInPeriodCorrected($budget, $repetition->startdate, $repetition->enddate, $shared);
|
||||
$left = $expenses < floatval($repetition->amount) ? floatval($repetition->amount) - $expenses : 0;
|
||||
$spent = $expenses > floatval($repetition->amount) ? 0 : $expenses;
|
||||
$overspent = $expenses > floatval($repetition->amount) ? $expenses - floatval($repetition->amount) : 0;
|
||||
$expenses = $repository->balanceInPeriod($budget, $repetition->startdate, $repetition->enddate, $shared);
|
||||
$expenses = $expenses * -1;
|
||||
$left = $expenses < $repetition->amount ? bcsub($repetition->amount, $expenses) : 0;
|
||||
$spent = $expenses > $repetition->amount ? 0 : $expenses;
|
||||
$overspent = $expenses > $repetition->amount ? bcsub($expenses, $repetition->amount) : 0;
|
||||
|
||||
$budgetLine->setLeft($left);
|
||||
$budgetLine->setSpent($spent);
|
||||
@@ -319,10 +325,10 @@ class ReportHelper implements ReportHelperInterface
|
||||
* GET CATEGORIES:
|
||||
*/
|
||||
/** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */
|
||||
$repository = App::make('FireflyIII\Repositories\Category\CategoryRepositoryInterface');
|
||||
$repository = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface');
|
||||
$set = $repository->getCategories();
|
||||
foreach ($set as $category) {
|
||||
$spent = $repository->spentInPeriodCorrected($category, $start, $end, $shared);
|
||||
$spent = $repository->balanceInPeriod($category, $start, $end, $shared);
|
||||
$category->spent = $spent;
|
||||
$object->addCategory($category);
|
||||
$object->addTotal($spent);
|
||||
@@ -345,7 +351,7 @@ class ReportHelper implements ReportHelperInterface
|
||||
$object = new Expense;
|
||||
$set = $this->query->expenseInPeriodCorrected($start, $end, $shared);
|
||||
foreach ($set as $entry) {
|
||||
$object->addToTotal($entry->amount);
|
||||
$object->addToTotal($entry->amount_positive);
|
||||
$object->addOrCreateExpense($entry);
|
||||
}
|
||||
|
||||
@@ -366,7 +372,7 @@ class ReportHelper implements ReportHelperInterface
|
||||
$object = new Income;
|
||||
$set = $this->query->incomeInPeriodCorrected($start, $end, $shared);
|
||||
foreach ($set as $entry) {
|
||||
$object->addToTotal($entry->amount);
|
||||
$object->addToTotal($entry->amount_positive);
|
||||
$object->addOrCreateIncome($entry);
|
||||
}
|
||||
|
||||
|
@@ -3,8 +3,9 @@
|
||||
namespace FireflyIII\Helpers\Report;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collection\Account;
|
||||
use FireflyIII\Helpers\Collection\Account as AccountCollection;
|
||||
use FireflyIII\Helpers\Collection\Balance;
|
||||
use FireflyIII\Helpers\Collection\Bill as BillCollection;
|
||||
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
|
||||
use FireflyIII\Helpers\Collection\Category as CategoryCollection;
|
||||
use FireflyIII\Helpers\Collection\Expense;
|
||||
@@ -26,7 +27,7 @@ interface ReportHelperInterface
|
||||
* @param Carbon $end
|
||||
* @param boolean $shared
|
||||
*
|
||||
* @return Account
|
||||
* @return AccountCollection
|
||||
*/
|
||||
public function getAccountReport(Carbon $date, Carbon $end, $shared);
|
||||
|
||||
@@ -34,13 +35,12 @@ interface ReportHelperInterface
|
||||
* This method generates a full report for the given period on all
|
||||
* the users bills and their payments.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param boolean $shared
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Account
|
||||
* @return BillCollection
|
||||
*/
|
||||
public function getBillReport(Carbon $start, Carbon $end, $shared);
|
||||
public function getBillReport(Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
|
@@ -21,7 +21,11 @@ use Steam;
|
||||
class ReportQuery implements ReportQueryInterface
|
||||
{
|
||||
/**
|
||||
* See ReportQueryInterface::incomeInPeriodCorrected
|
||||
* See ReportQueryInterface::incomeInPeriodCorrected.
|
||||
*
|
||||
* This method's length is caused mainly by the query build stuff. Therefor:
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
@@ -46,6 +50,7 @@ class ReportQuery implements ReportQueryInterface
|
||||
function (Builder $q) { // and transfers from a shared account.
|
||||
$q->where('transaction_types.type', 'Transfer');
|
||||
$q->where('acm_to.data', '=', '"sharedAsset"');
|
||||
$q->where('acm_from.data', '!=', '"sharedAsset"');
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -54,9 +59,7 @@ class ReportQuery implements ReportQueryInterface
|
||||
$query->where('transaction_types.type', 'Withdrawal'); // any withdrawal is fine.
|
||||
}
|
||||
$query->orderBy('transaction_journals.date');
|
||||
|
||||
// get everything
|
||||
$data = $query->get(
|
||||
$data = $query->get( // get everything
|
||||
['transaction_journals.*', 'transaction_types.type', 'ac_to.name as name', 'ac_to.id as account_id', 'ac_to.encrypted as account_encrypted']
|
||||
);
|
||||
|
||||
@@ -67,13 +70,6 @@ class ReportQuery implements ReportQueryInterface
|
||||
}
|
||||
}
|
||||
);
|
||||
$data = $data->filter(
|
||||
function (TransactionJournal $journal) {
|
||||
if ($journal->amount != 0) {
|
||||
return $journal;
|
||||
}
|
||||
} // @codeCoverageIgnore
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
@@ -97,7 +93,6 @@ class ReportQuery implements ReportQueryInterface
|
||||
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
|
||||
}
|
||||
)
|
||||
->orderBy('accounts.name', 'ASC')
|
||||
->where(
|
||||
function (Builder $query) {
|
||||
|
||||
@@ -161,6 +156,7 @@ class ReportQuery implements ReportQueryInterface
|
||||
function (Builder $q) {
|
||||
$q->where('transaction_types.type', 'Transfer');
|
||||
$q->where('acm_from.data', '=', '"sharedAsset"');
|
||||
$q->where('acm_to.data','!=','"sharedAsset"');
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -188,7 +184,9 @@ class ReportQuery implements ReportQueryInterface
|
||||
if ($journal->amount != 0) {
|
||||
return $journal;
|
||||
}
|
||||
} // @codeCoverageIgnore
|
||||
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
return $data;
|
||||
@@ -207,30 +205,9 @@ class ReportQuery implements ReportQueryInterface
|
||||
public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end)
|
||||
{
|
||||
|
||||
return floatval(
|
||||
Auth::user()->transactionjournals()
|
||||
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->transactionTypes(['Withdrawal'])
|
||||
->where('transactions.account_id', $account->id)
|
||||
->before($end)
|
||||
->after($start)
|
||||
->where('budget_transaction_journal.budget_id', $budget->id)
|
||||
->get(['transaction_journals.*'])->sum('amount')
|
||||
) * -1;
|
||||
}
|
||||
bcscale(2);
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param bool $shared
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function spentNoBudget(Account $account, Carbon $start, Carbon $end, $shared = false)
|
||||
{
|
||||
return floatval(
|
||||
return bcmul(
|
||||
Auth::user()->transactionjournals()
|
||||
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
@@ -238,10 +215,31 @@ class ReportQuery implements ReportQueryInterface
|
||||
->where('transactions.account_id', $account->id)
|
||||
->before($end)
|
||||
->after($start)
|
||||
->whereNull('budget_transaction_journal.budget_id')->get(['transaction_journals.*'])->sum('amount')
|
||||
->where('budget_transaction_journal.budget_id', $budget->id)
|
||||
->get(['transaction_journals.*'])->sum('amount'), -1
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function spentNoBudget(Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
return
|
||||
Auth::user()->transactionjournals()
|
||||
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->transactionTypes(['Withdrawal'])
|
||||
->where('transactions.account_id', $account->id)
|
||||
->before($end)
|
||||
->after($start)
|
||||
->whereNull('budget_transaction_journal.budget_id')->get(['transaction_journals.*'])->sum('amount');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
|
@@ -71,11 +71,10 @@ interface ReportQueryInterface
|
||||
* @param Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param bool $shared
|
||||
*
|
||||
* @return float
|
||||
* @return string
|
||||
*/
|
||||
public function spentNoBudget(Account $account, Carbon $start, Carbon $end, $shared = false);
|
||||
public function spentNoBudget(Account $account, Carbon $start, Carbon $end);
|
||||
|
||||
|
||||
}
|
||||
|
@@ -3,12 +3,12 @@
|
||||
use Auth;
|
||||
use Carbon\Carbon;
|
||||
use Config;
|
||||
use FireflyIII\Http\Requests;
|
||||
use ExpandedForm;
|
||||
use FireflyIII\Http\Requests\AccountFormRequest;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use Input;
|
||||
use Redirect;
|
||||
use Preferences;
|
||||
use Session;
|
||||
use Steam;
|
||||
use URL;
|
||||
@@ -46,6 +46,8 @@ class AccountController extends Controller
|
||||
Session::put('accounts.create.url', URL::previous());
|
||||
}
|
||||
Session::forget('accounts.create.fromStore');
|
||||
Session::flash('gaEventCategory', 'accounts');
|
||||
Session::flash('gaEventAction', 'create-' . $what);
|
||||
|
||||
return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle'));
|
||||
|
||||
@@ -56,15 +58,19 @@ class AccountController extends Controller
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function delete(Account $account)
|
||||
public function delete(AccountRepositoryInterface $repository, Account $account)
|
||||
{
|
||||
$typeName = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
|
||||
$subTitle = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]);
|
||||
$typeName = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
|
||||
$subTitle = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]);
|
||||
$accountList = Expandedform::makeSelectList($repository->getAccounts([$account->accountType->type]), true);
|
||||
unset($accountList[$account->id]);
|
||||
|
||||
// put previous url in session
|
||||
Session::put('accounts.delete.url', URL::previous());
|
||||
Session::flash('gaEventCategory', 'accounts');
|
||||
Session::flash('gaEventAction', 'delete-' . $typeName);
|
||||
|
||||
return view('accounts.delete', compact('account', 'subTitle'));
|
||||
return view('accounts.delete', compact('account', 'subTitle', 'accountList'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,23 +81,24 @@ class AccountController extends Controller
|
||||
*/
|
||||
public function destroy(AccountRepositoryInterface $repository, Account $account)
|
||||
{
|
||||
|
||||
$type = $account->accountType->type;
|
||||
$typeName = Config::get('firefly.shortNamesByFullName.' . $type);
|
||||
$name = $account->name;
|
||||
$moveTo = Auth::user()->accounts()->find(intval(Input::get('move_account_before_delete')));
|
||||
|
||||
$repository->destroy($account);
|
||||
$repository->destroy($account, $moveTo);
|
||||
|
||||
Session::flash('success', trans('firefly.' . $typeName . '_deleted', ['name' => $name]));
|
||||
Preferences::mark();
|
||||
|
||||
return Redirect::to(Session::get('accounts.delete.url'));
|
||||
return redirect(Session::get('accounts.delete.url'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccountRepositoryInterface $repository
|
||||
* @param Account $account
|
||||
*
|
||||
* @return View
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function edit(AccountRepositoryInterface $repository, Account $account)
|
||||
{
|
||||
@@ -123,9 +130,11 @@ class AccountController extends Controller
|
||||
'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'),
|
||||
'openingBalanceDate' => $openingBalance ? $openingBalance->date->format('Y-m-d') : null,
|
||||
'openingBalance' => $openingBalanceAmount,
|
||||
'virtualBalance' => floatval($account->virtual_balance)
|
||||
'virtualBalance' => round($account->virtual_balance, 2)
|
||||
];
|
||||
Session::flash('preFilled', $preFilled);
|
||||
Session::flash('gaEventCategory', 'accounts');
|
||||
Session::flash('gaEventAction', 'edit-' . $what);
|
||||
|
||||
return view('accounts.edit', compact('account', 'subTitle', 'subTitleIcon', 'openingBalance', 'what'));
|
||||
}
|
||||
@@ -134,7 +143,7 @@ class AccountController extends Controller
|
||||
* @param AccountRepositoryInterface $repository
|
||||
* @param $what
|
||||
*
|
||||
* @return View
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function index(AccountRepositoryInterface $repository, $what)
|
||||
{
|
||||
@@ -147,12 +156,24 @@ class AccountController extends Controller
|
||||
* HERE WE ARE
|
||||
*/
|
||||
$start = clone Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = clone Session::get('end', Carbon::now()->endOfMonth());
|
||||
$start->subDay();
|
||||
|
||||
// start balances:
|
||||
$ids = [];
|
||||
foreach ($accounts as $account) {
|
||||
$ids[] = $account->id;
|
||||
}
|
||||
|
||||
$startBalances = Steam::balancesById($ids, $start);
|
||||
$endBalances = Steam::balancesById($ids, $end);
|
||||
$activities = Steam::getLastActivities($ids);
|
||||
|
||||
$accounts->each(
|
||||
function (Account $account) use ($start, $repository) {
|
||||
$account->lastActivityDate = $repository->getLastActivity($account);
|
||||
$account->startBalance = Steam::balance($account, $start);
|
||||
$account->endBalance = Steam::balance($account, clone Session::get('end', Carbon::now()->endOfMonth()));
|
||||
function (Account $account) use ($activities, $startBalances, $endBalances) {
|
||||
$account->lastActivityDate = isset($activities[$account->id]) ? $activities[$account->id] : null;
|
||||
$account->startBalance = isset($startBalances[$account->id]) ? $startBalances[$account->id] : null;
|
||||
$account->endBalance = isset($endBalances[$account->id]) ? $endBalances[$account->id] : null;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -163,7 +184,7 @@ class AccountController extends Controller
|
||||
* @param AccountRepositoryInterface $repository
|
||||
* @param Account $account
|
||||
*
|
||||
* @return View
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function show(AccountRepositoryInterface $repository, Account $account)
|
||||
{
|
||||
@@ -189,30 +210,31 @@ class AccountController extends Controller
|
||||
$accountData = [
|
||||
'name' => $request->input('name'),
|
||||
'accountType' => $request->input('what'),
|
||||
'virtualBalance' => floatval($request->input('virtualBalance')),
|
||||
'virtualBalance' => round($request->input('virtualBalance'), 2),
|
||||
'active' => true,
|
||||
'user' => Auth::user()->id,
|
||||
'iban' => $request->input('iban'),
|
||||
'accountRole' => $request->input('accountRole'),
|
||||
'openingBalance' => floatval($request->input('openingBalance')),
|
||||
'openingBalanceDate' => new Carbon($request->input('openingBalanceDate')),
|
||||
'openingBalance' => round($request->input('openingBalance'), 2),
|
||||
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
|
||||
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
|
||||
|
||||
];
|
||||
$account = $repository->store($accountData);
|
||||
|
||||
$account = $repository->store($accountData);
|
||||
|
||||
Session::flash('success', 'New account "' . $account->name . '" stored!');
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('create_another')) === 1) {
|
||||
// set value so create routine will not overwrite URL:
|
||||
Session::put('accounts.create.fromStore', true);
|
||||
|
||||
return Redirect::route('accounts.create')->withInput();
|
||||
return redirect(route('accounts.create', [$request->input('what')]))->withInput();
|
||||
}
|
||||
|
||||
// redirect to previous URL.
|
||||
return Redirect::to(Session::get('accounts.create.url'));
|
||||
|
||||
|
||||
return redirect(Session::get('accounts.create.url'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,7 +242,7 @@ class AccountController extends Controller
|
||||
* @param AccountRepositoryInterface $repository
|
||||
* @param Account $account
|
||||
*
|
||||
* @return $this|\Illuminate\Http\RedirectResponse
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(AccountFormRequest $request, AccountRepositoryInterface $repository, Account $account)
|
||||
{
|
||||
@@ -229,28 +251,31 @@ class AccountController extends Controller
|
||||
'name' => $request->input('name'),
|
||||
'active' => $request->input('active'),
|
||||
'user' => Auth::user()->id,
|
||||
'iban' => $request->input('iban'),
|
||||
'accountRole' => $request->input('accountRole'),
|
||||
'virtualBalance' => floatval($request->input('virtualBalance')),
|
||||
'openingBalance' => floatval($request->input('openingBalance')),
|
||||
'openingBalanceDate' => new Carbon($request->input('openingBalanceDate')),
|
||||
'virtualBalance' => round($request->input('virtualBalance'), 2),
|
||||
'openingBalance' => round($request->input('openingBalance'), 2),
|
||||
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
|
||||
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
|
||||
'ccType' => $request->input('ccType'),
|
||||
'ccMonthlyPaymentDate' => $request->input('ccMonthlyPaymentDate'),
|
||||
];
|
||||
|
||||
|
||||
$repository->update($account, $accountData);
|
||||
|
||||
Session::flash('success', 'Account "' . $account->name . '" updated.');
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('return_to_edit')) === 1) {
|
||||
// set value so edit routine will not overwrite URL:
|
||||
Session::put('accounts.edit.fromUpdate', true);
|
||||
|
||||
return Redirect::route('accounts.edit', $account->id)->withInput(['return_to_edit' => 1]);
|
||||
return redirect(route('accounts.edit', [$account->id]))->withInput(['return_to_edit' => 1]);
|
||||
}
|
||||
|
||||
// redirect to previous URL.
|
||||
return Redirect::to(Session::get('accounts.edit.url'));
|
||||
return redirect(Session::get('accounts.edit.url'));
|
||||
|
||||
}
|
||||
|
||||
|
173
app/Http/Controllers/AttachmentController.php
Normal file
173
app/Http/Controllers/AttachmentController.php
Normal file
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
|
||||
use Crypt;
|
||||
use File;
|
||||
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
|
||||
use FireflyIII\Http\Requests\AttachmentFormRequest;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
|
||||
use Input;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use Session;
|
||||
use URL;
|
||||
use View;
|
||||
|
||||
/**
|
||||
* Class AttachmentController
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers
|
||||
*/
|
||||
class AttachmentController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
View::share('mainTitleIcon', 'fa-paperclip');
|
||||
View::share('title', trans('firefly.attachments'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function edit(Attachment $attachment)
|
||||
{
|
||||
$subTitleIcon = 'fa-pencil';
|
||||
$subTitle = trans('firefly.edit_attachment', ['name' => $attachment->filename]);
|
||||
|
||||
// put previous url in session if not redirect from store (not "return_to_edit").
|
||||
if (Session::get('attachments.edit.fromUpdate') !== true) {
|
||||
Session::put('attachments.edit.url', URL::previous());
|
||||
}
|
||||
Session::forget('attachments.edit.fromUpdate');
|
||||
|
||||
return view('attachments.edit', compact('attachment', 'subTitleIcon', 'subTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function delete(Attachment $attachment)
|
||||
{
|
||||
$subTitle = trans('firefly.delete_attachment', ['name' => $attachment->filename]);
|
||||
|
||||
// put previous url in session
|
||||
Session::put('attachments.delete.url', URL::previous());
|
||||
Session::flash('gaEventCategory', 'attachments');
|
||||
Session::flash('gaEventAction', 'delete-attachment');
|
||||
|
||||
return view('attachments.delete', compact('attachment', 'subTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AttachmentRepositoryInterface $repository
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy(AttachmentRepositoryInterface $repository, Attachment $attachment)
|
||||
{
|
||||
$name = $attachment->filename;
|
||||
|
||||
$repository->destroy($attachment);
|
||||
|
||||
Session::flash('success', trans('firefly.attachment_deleted', ['name' => $name]));
|
||||
Preferences::mark();
|
||||
|
||||
return redirect(Session::get('attachments.delete.url'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Attachment $attachment
|
||||
*/
|
||||
public function download(Attachment $attachment, AttachmentHelperInterface $helper)
|
||||
{
|
||||
|
||||
$file = $helper->getAttachmentLocation($attachment);
|
||||
if (file_exists($file)) {
|
||||
|
||||
$quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\'));
|
||||
|
||||
header('Content-Description: File Transfer');
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Content-Disposition: attachment; filename=' . $quoted);
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
header('Connection: Keep-Alive');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header('Pragma: public');
|
||||
header('Content-Length: ' . $attachment->size);
|
||||
|
||||
echo Crypt::decrypt(file_get_contents($file));
|
||||
|
||||
} else {
|
||||
abort(404);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function preview(Attachment $attachment)
|
||||
{
|
||||
if ($attachment->mime == 'application/pdf') {
|
||||
$file = public_path('images/page_white_acrobat.png');
|
||||
} else {
|
||||
$file = public_path('images/page_green.png');
|
||||
}
|
||||
|
||||
$response = Response::make(File::get($file));
|
||||
$response->header('Content-Type', 'image/png');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param AttachmentFormRequest $request
|
||||
* @param AttachmentRepositoryInterface $repository
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(AttachmentFormRequest $request, AttachmentRepositoryInterface $repository, Attachment $attachment)
|
||||
{
|
||||
|
||||
$attachmentData = [
|
||||
'title' => $request->input('title'),
|
||||
'description' => $request->input('description'),
|
||||
'notes' => $request->input('notes'),
|
||||
];
|
||||
|
||||
$repository->update($attachment, $attachmentData);
|
||||
|
||||
Session::flash('success', 'Attachment "' . $attachment->filename . '" updated.');
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('return_to_edit')) === 1) {
|
||||
// set value so edit routine will not overwrite URL:
|
||||
Session::put('attachments.edit.fromUpdate', true);
|
||||
|
||||
return redirect(route('attachments.edit', [$attachment->id]))->withInput(['return_to_edit' => 1]);
|
||||
}
|
||||
|
||||
// redirect to previous URL.
|
||||
return redirect(Session::get('attachments.edit.url'));
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -1,14 +1,18 @@
|
||||
<?php namespace FireflyIII\Http\Controllers\Auth;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\Auth\Guard;
|
||||
use Illuminate\Contracts\Auth\Registrar;
|
||||
use FireflyIII\Models\Role;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
|
||||
use Illuminate\Foundation\Auth\ThrottlesLogins;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Mail\Message;
|
||||
use Mail;
|
||||
use Request as Rq;
|
||||
use Session;
|
||||
use Twig;
|
||||
use Validator;
|
||||
|
||||
/**
|
||||
* Class AuthController
|
||||
@@ -17,35 +21,93 @@ use Twig;
|
||||
*/
|
||||
class AuthController extends Controller
|
||||
{
|
||||
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Registration & Login Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller handles the registration of new users, as well as the
|
||||
| authentication of existing users. By default, this controller uses
|
||||
| a simple trait to add these behaviors. Why don't you explore it?
|
||||
|
|
||||
*/
|
||||
/**
|
||||
* Show the application registration form.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function getRegister()
|
||||
{
|
||||
$host = Rq::getHttpHost();
|
||||
|
||||
return view('auth.register', compact('host'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a login request to the application.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function postLogin(Request $request)
|
||||
{
|
||||
$this->validate(
|
||||
$request, [
|
||||
$this->loginUsername() => 'required', 'password' => 'required',
|
||||
]
|
||||
);
|
||||
|
||||
// If the class is using the ThrottlesLogins trait, we can automatically throttle
|
||||
// the login attempts for this application. We'll key this by the username and
|
||||
// the IP address of the client making these requests into this application.
|
||||
$throttles = $this->isUsingThrottlesLoginsTrait();
|
||||
|
||||
if ($throttles && $this->hasTooManyLoginAttempts($request)) {
|
||||
return $this->sendLockoutResponse($request);
|
||||
}
|
||||
|
||||
$credentials = $this->getCredentials($request);
|
||||
$credentials['blocked'] = 0; // most not be blocked.
|
||||
|
||||
if (Auth::attempt($credentials, $request->has('remember'))) {
|
||||
return $this->handleUserWasAuthenticated($request, $throttles);
|
||||
}
|
||||
|
||||
// default error message:
|
||||
$message = $this->getFailedLoginMessage();
|
||||
|
||||
// try to find a blocked user with this email address.
|
||||
/** @var User $foundUser */
|
||||
$foundUser = User::where('email', $credentials['email'])->where('blocked', 1)->first();
|
||||
if (!is_null($foundUser)) {
|
||||
// if it exists, show message:
|
||||
$code = $foundUser->blocked_code;
|
||||
$message = trans('firefly.' . $code . '_error', ['email' => $credentials['email']]);
|
||||
}
|
||||
|
||||
// try
|
||||
|
||||
// If the login attempt was unsuccessful we will increment the number of attempts
|
||||
// to login and redirect the user back to the login form. Of course, when this
|
||||
// user surpasses their maximum number of attempts they will get locked out.
|
||||
if ($throttles) {
|
||||
$this->incrementLoginAttempts($request);
|
||||
}
|
||||
|
||||
return redirect($this->loginPath())
|
||||
->withInput($request->only($this->loginUsername(), 'remember'))
|
||||
->withErrors(
|
||||
[
|
||||
$this->loginUsername() => $message,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
use AuthenticatesAndRegistersUsers;
|
||||
|
||||
public $redirectTo = '/';
|
||||
|
||||
/**
|
||||
* Create a new authentication controller instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Auth\Guard $auth
|
||||
* @param \Illuminate\Contracts\Auth\Registrar $registrar
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
*/
|
||||
public function __construct(Guard $auth, Registrar $registrar)
|
||||
public function __construct()
|
||||
{
|
||||
$this->auth = $auth;
|
||||
$this->registrar = $registrar;
|
||||
parent::__construct();
|
||||
|
||||
$this->middleware('guest', ['except' => 'getLogout']);
|
||||
}
|
||||
@@ -67,11 +129,11 @@ class AuthController extends Controller
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postRegister(Request $request)
|
||||
{
|
||||
$validator = $this->registrar->validator($request->all());
|
||||
$validator = $this->validator($request->all());
|
||||
|
||||
if ($validator->fails()) {
|
||||
$this->throwValidationException(
|
||||
@@ -84,25 +146,71 @@ class AuthController extends Controller
|
||||
$data = $request->all();
|
||||
$data['password'] = bcrypt($data['password']);
|
||||
|
||||
$this->auth->login($this->registrar->create($data));
|
||||
Auth::login($this->create($data));
|
||||
|
||||
// get the email address
|
||||
$email = $this->auth->user()->email;
|
||||
if (Auth::user() instanceof User) {
|
||||
$email = Auth::user()->email;
|
||||
$address = route('index');
|
||||
// send email.
|
||||
Mail::send(
|
||||
['emails.registered-html', 'emails.registered'], ['address' => $address], function (Message $message) use ($email) {
|
||||
$message->to($email, $email)->subject('Welcome to Firefly III! ');
|
||||
}
|
||||
);
|
||||
|
||||
// send email.
|
||||
Mail::send(
|
||||
'emails.registered', [], function (Message $message) use ($email) {
|
||||
$message->to($email, $email)->subject('Welcome to Firefly III!');
|
||||
// set flash message
|
||||
Session::flash('success', 'You have registered successfully!');
|
||||
Session::flash('gaEventCategory', 'user');
|
||||
Session::flash('gaEventAction', 'new-registration');
|
||||
|
||||
// first user ever?
|
||||
if (User::count() == 1) {
|
||||
$admin = Role::where('name', 'owner')->first();
|
||||
Auth::user()->attachRole($admin);
|
||||
}
|
||||
|
||||
|
||||
return redirect($this->redirectPath());
|
||||
}
|
||||
);
|
||||
// @codeCoverageIgnoreStart
|
||||
abort(500, 'Not a user!');
|
||||
|
||||
// set flash message
|
||||
Session::flash('success', 'You have registered successfully!');
|
||||
Session::flash('gaEventCategory', 'user');
|
||||
Session::flash('gaEventAction', 'new-registration');
|
||||
|
||||
|
||||
return redirect($this->redirectPath());
|
||||
return redirect('/');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a validator for an incoming registration request.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return \Illuminate\Contracts\Validation\Validator
|
||||
*/
|
||||
public function validator(array $data)
|
||||
{
|
||||
return Validator::make(
|
||||
$data, [
|
||||
'email' => 'required|email|max:255|unique:users',
|
||||
'password' => 'required|confirmed|min:6',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new user instance after a valid registration.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function create(array $data)
|
||||
{
|
||||
return User::create(
|
||||
[
|
||||
'email' => $data['email'],
|
||||
'password' => $data['password'],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,6 @@
|
||||
<?php namespace FireflyIII\Http\Controllers\Auth;
|
||||
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\Auth\Guard;
|
||||
use Illuminate\Contracts\Auth\PasswordBroker;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
|
||||
/**
|
||||
@@ -33,17 +31,12 @@ class PasswordController extends Controller
|
||||
/**
|
||||
* Create a new password controller instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Auth\Guard $auth
|
||||
* @param \Illuminate\Contracts\Auth\PasswordBroker $passwords
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
*/
|
||||
public function __construct(Guard $auth, PasswordBroker $passwords)
|
||||
public function __construct()
|
||||
{
|
||||
$this->auth = $auth;
|
||||
$this->passwords = $passwords;
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
@@ -1,14 +1,12 @@
|
||||
<?php namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Config;
|
||||
use FireflyIII\Http\Requests;
|
||||
use FireflyIII\Http\Requests\BillFormRequest;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use Input;
|
||||
use Redirect;
|
||||
use Preferences;
|
||||
use Session;
|
||||
use URL;
|
||||
use View;
|
||||
@@ -32,18 +30,21 @@ class BillController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$periods = Config::get('firefly.periods_to_text');
|
||||
$periods = Config::get('firefly.periods_to_text');
|
||||
$subTitle = trans('firefly.create_new_bill');
|
||||
|
||||
|
||||
// put previous url in session if not redirect from store (not "create another").
|
||||
if (Session::get('bills.create.fromStore') !== true) {
|
||||
Session::put('bills.create.url', URL::previous());
|
||||
}
|
||||
Session::forget('bills.create.fromStore');
|
||||
$subTitle = 'Create new bill';
|
||||
Session::flash('gaEventCategory', 'bills');
|
||||
Session::flash('gaEventAction', 'create');
|
||||
|
||||
return view('bills.create', compact('periods', 'subTitle'));
|
||||
}
|
||||
@@ -51,13 +52,15 @@ class BillController extends Controller
|
||||
/**
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @return $this
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function delete(Bill $bill)
|
||||
{
|
||||
// put previous url in session
|
||||
Session::put('bills.delete.url', URL::previous());
|
||||
$subTitle = 'Delete "' . e($bill->name) . '"';
|
||||
Session::flash('gaEventCategory', 'bills');
|
||||
Session::flash('gaEventAction', 'delete');
|
||||
$subTitle = trans('firefly.delete_bill', ['name' => $bill->name]);
|
||||
|
||||
return view('bills.delete', compact('bill', 'subTitle'));
|
||||
}
|
||||
@@ -73,26 +76,28 @@ class BillController extends Controller
|
||||
$repository->destroy($bill);
|
||||
|
||||
Session::flash('success', 'The bill was deleted.');
|
||||
Preferences::mark();
|
||||
|
||||
return Redirect::to(Session::get('bills.delete.url'));
|
||||
|
||||
return redirect(Session::get('bills.delete.url'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @return $this
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function edit(Bill $bill)
|
||||
{
|
||||
$periods = Config::get('firefly.periods_to_text');
|
||||
$subTitle = 'Edit "' . e($bill->name) . '"';
|
||||
$subTitle = trans('firefly.edit_bill', ['name' => $bill->name]);
|
||||
|
||||
// put previous url in session if not redirect from store (not "return_to_edit").
|
||||
if (Session::get('bills.edit.fromUpdate') !== true) {
|
||||
Session::put('bills.edit.url', URL::previous());
|
||||
}
|
||||
Session::forget('bills.edit.fromUpdate');
|
||||
Session::flash('gaEventCategory', 'bills');
|
||||
Session::flash('gaEventAction', 'edit');
|
||||
|
||||
return view('bills.edit', compact('subTitle', 'periods', 'bill'));
|
||||
}
|
||||
@@ -126,7 +131,7 @@ class BillController extends Controller
|
||||
if (intval($bill->active) == 0) {
|
||||
Session::flash('warning', 'Inactive bills cannot be scanned.');
|
||||
|
||||
return Redirect::to(URL::previous());
|
||||
return redirect(URL::previous());
|
||||
}
|
||||
|
||||
$journals = $repository->getPossiblyRelatedJournals($bill);
|
||||
@@ -137,15 +142,16 @@ class BillController extends Controller
|
||||
|
||||
|
||||
Session::flash('success', 'Rescanned everything.');
|
||||
Preferences::mark();
|
||||
|
||||
return Redirect::to(URL::previous());
|
||||
return redirect(URL::previous());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BillRepositoryInterface $repository
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @return mixed
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function show(BillRepositoryInterface $repository, Bill $bill)
|
||||
{
|
||||
@@ -161,23 +167,24 @@ class BillController extends Controller
|
||||
* @param BillFormRequest $request
|
||||
* @param BillRepositoryInterface $repository
|
||||
*
|
||||
* @return $this|\Illuminate\Http\RedirectResponse
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(BillFormRequest $request, BillRepositoryInterface $repository)
|
||||
{
|
||||
$billData = $request->getBillData();
|
||||
$bill = $repository->store($billData);
|
||||
Session::flash('success', 'Bill "' . e($bill->name) . '" stored.');
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('create_another')) === 1) {
|
||||
// set value so create routine will not overwrite URL:
|
||||
Session::put('bills.create.fromStore', true);
|
||||
|
||||
return Redirect::route('bills.create')->withInput();
|
||||
return redirect(route('bills.create'))->withInput();
|
||||
}
|
||||
|
||||
// redirect to previous URL.
|
||||
return Redirect::to(Session::get('bills.create.url'));
|
||||
return redirect(Session::get('bills.create.url'));
|
||||
|
||||
}
|
||||
|
||||
@@ -186,7 +193,7 @@ class BillController extends Controller
|
||||
* @param BillRepositoryInterface $repository
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @return $this|\Illuminate\Http\RedirectResponse
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(BillFormRequest $request, BillRepositoryInterface $repository, Bill $bill)
|
||||
{
|
||||
@@ -194,16 +201,17 @@ class BillController extends Controller
|
||||
$bill = $repository->update($bill, $billData);
|
||||
|
||||
Session::flash('success', 'Bill "' . e($bill->name) . '" updated.');
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('return_to_edit')) === 1) {
|
||||
// set value so edit routine will not overwrite URL:
|
||||
Session::put('bills.edit.fromUpdate', true);
|
||||
|
||||
return Redirect::route('bills.edit', $bill->id)->withInput(['return_to_edit' => 1]);
|
||||
return redirect(route('bills.edit', [$bill->id]))->withInput(['return_to_edit' => 1]);
|
||||
}
|
||||
|
||||
// redirect to previous URL.
|
||||
return Redirect::to(Session::get('bills.edit.url'));
|
||||
return redirect(Session::get('bills.edit.url'));
|
||||
|
||||
}
|
||||
|
||||
|
@@ -1,15 +1,15 @@
|
||||
<?php namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Amount;
|
||||
use Auth;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Http\Requests;
|
||||
use FireflyIII\Http\Requests\BudgetFormRequest;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use Input;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
use Redirect;
|
||||
use Response;
|
||||
use Session;
|
||||
use URL;
|
||||
@@ -19,6 +19,7 @@ use View;
|
||||
* Class BudgetController
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers
|
||||
* @SuppressWarnings(PHPMD.TooManyMethods)
|
||||
*/
|
||||
class BudgetController extends Controller
|
||||
{
|
||||
@@ -45,13 +46,17 @@ class BudgetController extends Controller
|
||||
$amount = intval(Input::get('amount'));
|
||||
$date = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$limitRepetition = $repository->updateLimitAmount($budget, $date, $amount);
|
||||
if ($amount == 0) {
|
||||
$limitRepetition = null;
|
||||
}
|
||||
Preferences::mark();
|
||||
|
||||
return Response::json(['name' => $budget->name, 'repetition' => $limitRepetition ? $limitRepetition->id : 0]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
@@ -60,6 +65,8 @@ class BudgetController extends Controller
|
||||
Session::put('budgets.create.url', URL::previous());
|
||||
}
|
||||
Session::forget('budgets.create.fromStore');
|
||||
Session::flash('gaEventCategory', 'budgets');
|
||||
Session::flash('gaEventAction', 'create');
|
||||
$subTitle = trans('firefly.create_new_budget');
|
||||
|
||||
return view('budgets.create', compact('subTitle'));
|
||||
@@ -72,10 +79,12 @@ class BudgetController extends Controller
|
||||
*/
|
||||
public function delete(Budget $budget)
|
||||
{
|
||||
$subTitle = 'Delete budget' . e($budget->name) . '"';
|
||||
$subTitle = trans('firefly.delete_budget', ['name' => $budget->name]);
|
||||
|
||||
// put previous url in session
|
||||
Session::put('budgets.delete.url', URL::previous());
|
||||
Session::flash('gaEventCategory', 'budgets');
|
||||
Session::flash('gaEventAction', 'delete');
|
||||
|
||||
return view('budgets.delete', compact('budget', 'subTitle'));
|
||||
}
|
||||
@@ -94,24 +103,28 @@ class BudgetController extends Controller
|
||||
|
||||
|
||||
Session::flash('success', 'The budget "' . e($name) . '" was deleted.');
|
||||
Preferences::mark();
|
||||
|
||||
return Redirect::to(Session::get('budgets.delete.url'));
|
||||
|
||||
return redirect(Session::get('budgets.delete.url'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return $this
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function edit(Budget $budget)
|
||||
{
|
||||
$subTitle = 'Edit budget "' . e($budget->name) . '"';
|
||||
$subTitle = trans('firefly.edit_budget', ['name' => $budget->name]);
|
||||
|
||||
// put previous url in session if not redirect from store (not "return_to_edit").
|
||||
if (Session::get('budgets.edit.fromUpdate') !== true) {
|
||||
Session::put('budgets.edit.url', URL::previous());
|
||||
}
|
||||
Session::forget('budgets.edit.fromUpdate');
|
||||
Session::flash('gaEventCategory', 'budgets');
|
||||
Session::flash('gaEventAction', 'edit');
|
||||
|
||||
return view('budgets.edit', compact('budget', 'subTitle'));
|
||||
|
||||
@@ -120,51 +133,62 @@ class BudgetController extends Controller
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
*
|
||||
* @return View
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function index(BudgetRepositoryInterface $repository)
|
||||
{
|
||||
$budgets = $repository->getActiveBudgets();
|
||||
$inactive = $repository->getInactiveBudgets();
|
||||
|
||||
$budgets = $repository->getActiveBudgets();
|
||||
$inactive = $repository->getInactiveBudgets();
|
||||
$spent = '0';
|
||||
$budgeted = '0';
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod(Session::get('start', new Carbon), $range);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
|
||||
$budgetIncomeTotal = Preferences::get($key, 1000)->data;
|
||||
$period = Navigation::periodShow($start, $range);
|
||||
bcscale(2);
|
||||
/**
|
||||
* Do some cleanup:
|
||||
*/
|
||||
$repository->cleanupBudgets();
|
||||
|
||||
|
||||
// loop the budgets:
|
||||
$budgets->each(
|
||||
function (Budget $budget) use ($repository) {
|
||||
$date = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->endOfMonth());
|
||||
$budget->spent = $repository->spentInPeriodCorrected($budget, $date, $end);
|
||||
$budget->currentRep = $repository->getCurrentRepetition($budget, $date);
|
||||
/** @var Budget $budget */
|
||||
foreach ($budgets as $budget) {
|
||||
$budget->spent = $repository->balanceInPeriod($budget, $start, $end);
|
||||
$budget->currentRep = $repository->getCurrentRepetition($budget, $start, $end);
|
||||
if ($budget->currentRep) {
|
||||
$budgeted = bcadd($budgeted, $budget->currentRep->amount);
|
||||
}
|
||||
$spent = bcadd($spent, $budget->spent);
|
||||
|
||||
}
|
||||
|
||||
|
||||
$budgetMaximum = Preferences::get('budgetMaximum', 1000)->data;
|
||||
$defaultCurrency = Amount::getDefaultCurrency();
|
||||
|
||||
return view(
|
||||
'budgets.index', compact('budgetMaximum','period', 'range', 'budgetIncomeTotal', 'defaultCurrency', 'inactive', 'budgets', 'spent', 'budgeted')
|
||||
);
|
||||
|
||||
$dateAsString = Session::get('start', Carbon::now()->startOfMonth())->format('FY');
|
||||
$spent = $budgets->sum('spent');
|
||||
$amount = Preferences::get('budgetIncomeTotal' . $dateAsString, 1000)->data;
|
||||
$overspent = $spent > $amount;
|
||||
$spentPCT = $overspent ? ceil($amount / $spent * 100) : ceil($spent / $amount * 100);
|
||||
$budgetMax = Preferences::get('budgetMaximum', 1000);
|
||||
$budgetMaximum = $budgetMax->data;
|
||||
|
||||
return view('budgets.index', compact('budgetMaximum', 'inactive', 'budgets', 'spent', 'spentPCT', 'overspent', 'amount'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
*
|
||||
* @return View
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function noBudget(BudgetRepositoryInterface $repository)
|
||||
{
|
||||
$start = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->startOfMonth());
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod(Session::get('start', new Carbon), $range);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
$list = $repository->getWithoutBudget($start, $end);
|
||||
$subTitle = 'Transactions without a budget between ' . $start->format('jS F Y') . ' and ' . $end->format('jS F Y');
|
||||
$subTitle = trans(
|
||||
'firefly.without_budget_between',
|
||||
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
|
||||
return view('budgets.noBudget', compact('list', 'subTitle'));
|
||||
}
|
||||
@@ -174,11 +198,15 @@ class BudgetController extends Controller
|
||||
*/
|
||||
public function postUpdateIncome()
|
||||
{
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod(Session::get('start', new Carbon), $range);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
|
||||
|
||||
$date = Session::get('start', Carbon::now()->startOfMonth())->format('FY');
|
||||
Preferences::set('budgetIncomeTotal' . $date, intval(Input::get('amount')));
|
||||
Preferences::set($key, intval(Input::get('amount')));
|
||||
Preferences::mark();
|
||||
|
||||
return Redirect::route('budgets.index');
|
||||
return redirect(route('budgets.index'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,7 +214,7 @@ class BudgetController extends Controller
|
||||
* @param Budget $budget
|
||||
* @param LimitRepetition $repetition
|
||||
*
|
||||
* @return View
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function show(BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition = null)
|
||||
{
|
||||
@@ -197,8 +225,15 @@ class BudgetController extends Controller
|
||||
}
|
||||
|
||||
$journals = $repository->getJournals($budget, $repetition);
|
||||
$limits = !is_null($repetition->id) ? [$repetition->budgetLimit] : $repository->getBudgetLimits($budget);
|
||||
$subTitle = !is_null($repetition->id) ? e($budget->name) . ' in ' . $repetition->startdate->format('F Y') : e($budget->name);
|
||||
|
||||
if (is_null($repetition->id)) {
|
||||
$limits = $repository->getBudgetLimits($budget);
|
||||
$subTitle = e($budget->name);
|
||||
} else {
|
||||
$limits = [$repetition->budgetLimit];
|
||||
$subTitle = trans('firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]);
|
||||
}
|
||||
|
||||
$journals->setPath('/budgets/show/' . $budget->id);
|
||||
|
||||
return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle'));
|
||||
@@ -219,16 +254,17 @@ class BudgetController extends Controller
|
||||
$budget = $repository->store($budgetData);
|
||||
|
||||
Session::flash('success', 'New budget "' . $budget->name . '" stored!');
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('create_another')) === 1) {
|
||||
// set value so create routine will not overwrite URL:
|
||||
Session::put('budgets.create.fromStore', true);
|
||||
|
||||
return Redirect::route('budgets.create')->withInput();
|
||||
return redirect(route('budgets.create'))->withInput();
|
||||
}
|
||||
|
||||
// redirect to previous URL.
|
||||
return Redirect::to(Session::get('budgets.create.url'));
|
||||
return redirect(Session::get('budgets.create.url'));
|
||||
|
||||
}
|
||||
|
||||
@@ -237,7 +273,7 @@ class BudgetController extends Controller
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return $this|\Illuminate\Http\RedirectResponse
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(BudgetFormRequest $request, BudgetRepositoryInterface $repository, Budget $budget)
|
||||
{
|
||||
@@ -249,26 +285,30 @@ class BudgetController extends Controller
|
||||
$repository->update($budget, $budgetData);
|
||||
|
||||
Session::flash('success', 'Budget "' . $budget->name . '" updated.');
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('return_to_edit')) === 1) {
|
||||
// set value so edit routine will not overwrite URL:
|
||||
Session::put('budgets.edit.fromUpdate', true);
|
||||
|
||||
return Redirect::route('budgets.edit', $budget->id)->withInput(['return_to_edit' => 1]);
|
||||
return redirect(route('budgets.edit', [$budget->id]))->withInput(['return_to_edit' => 1]);
|
||||
}
|
||||
|
||||
// redirect to previous URL.
|
||||
return Redirect::to(Session::get('budgets.edit.url'));
|
||||
return redirect(Session::get('budgets.edit.url'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return View
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function updateIncome()
|
||||
{
|
||||
$date = Session::get('start', Carbon::now()->startOfMonth())->format('FY');
|
||||
$amount = Preferences::get('budgetIncomeTotal' . $date, 1000);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod(Session::get('start', new Carbon), $range);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
|
||||
$amount = Preferences::get($key, 1000);
|
||||
|
||||
return view('budgets.income', compact('amount'));
|
||||
}
|
||||
|
@@ -5,9 +5,12 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Http\Requests\CategoryFormRequest;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
use Redirect;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
use Session;
|
||||
use URL;
|
||||
use View;
|
||||
@@ -31,7 +34,7 @@ class CategoryController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
@@ -40,7 +43,9 @@ class CategoryController extends Controller
|
||||
Session::put('categories.create.url', URL::previous());
|
||||
}
|
||||
Session::forget('categories.create.fromStore');
|
||||
$subTitle = 'Create a new category';
|
||||
Session::flash('gaEventCategory', 'categories');
|
||||
Session::flash('gaEventAction', 'create');
|
||||
$subTitle = trans('firefly.create_new_category');
|
||||
|
||||
return view('categories.create', compact('subTitle'));
|
||||
}
|
||||
@@ -52,10 +57,12 @@ class CategoryController extends Controller
|
||||
*/
|
||||
public function delete(Category $category)
|
||||
{
|
||||
$subTitle = 'Delete category "' . e($category->name) . '"';
|
||||
$subTitle = trans('firefly.delete_category', ['name' => $category->name]);
|
||||
|
||||
// put previous url in session
|
||||
Session::put('categories.delete.url', URL::previous());
|
||||
Session::flash('gaEventCategory', 'categories');
|
||||
Session::flash('gaEventAction', 'delete');
|
||||
|
||||
return view('categories.delete', compact('category', 'subTitle'));
|
||||
}
|
||||
@@ -73,24 +80,27 @@ class CategoryController extends Controller
|
||||
$repository->destroy($category);
|
||||
|
||||
Session::flash('success', 'The category "' . e($name) . '" was deleted.');
|
||||
Preferences::mark();
|
||||
|
||||
return Redirect::to(Session::get('categories.delete.url'));
|
||||
return redirect(Session::get('categories.delete.url'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
*
|
||||
* @return $this
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function edit(Category $category)
|
||||
{
|
||||
$subTitle = 'Edit category "' . e($category->name) . '"';
|
||||
$subTitle = trans('firefly.edit_category', ['name' => $category->name]);
|
||||
|
||||
// put previous url in session if not redirect from store (not "return_to_edit").
|
||||
if (Session::get('categories.edit.fromUpdate') !== true) {
|
||||
Session::put('categories.edit.url', URL::previous());
|
||||
}
|
||||
Session::forget('categories.edit.fromUpdate');
|
||||
Session::flash('gaEventCategory', 'categories');
|
||||
Session::flash('gaEventAction', 'edit');
|
||||
|
||||
return view('categories.edit', compact('category', 'subTitle'));
|
||||
|
||||
@@ -99,7 +109,7 @@ class CategoryController extends Controller
|
||||
/**
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
*
|
||||
* @return $this
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function index(CategoryRepositoryInterface $repository)
|
||||
{
|
||||
@@ -124,7 +134,10 @@ class CategoryController extends Controller
|
||||
$start = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->startOfMonth());
|
||||
$list = $repository->getWithoutCategory($start, $end);
|
||||
$subTitle = 'Transactions without a category between ' . $start->format('jS F Y') . ' and ' . $end->format('jS F Y');
|
||||
$subTitle = trans(
|
||||
'firefly.without_category_between',
|
||||
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
|
||||
return view('categories.noCategory', compact('list', 'subTitle'));
|
||||
}
|
||||
@@ -133,7 +146,32 @@ class CategoryController extends Controller
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param Category $category
|
||||
*
|
||||
* @return View
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function showWithDate(CategoryRepositoryInterface $repository, Category $category, $date)
|
||||
{
|
||||
$carbon = new Carbon($date);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($carbon, $range);
|
||||
$end = Navigation::endOfPeriod($carbon, $range);
|
||||
$subTitle = $category->name;
|
||||
|
||||
$hideCategory = true; // used in list.
|
||||
$page = intval(Input::get('page'));
|
||||
|
||||
$set = $repository->getJournalsInRange($category, $page, $start, $end);
|
||||
$count = $repository->countJournalsInRange($category, $start, $end);
|
||||
$journals = new LengthAwarePaginator($set, $count, 50, $page);
|
||||
$journals->setPath('categories/show/' . $category->id . '/' . $date);
|
||||
|
||||
return view('categories.show_with_date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param Category $category
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function show(CategoryRepositoryInterface $repository, Category $category)
|
||||
{
|
||||
@@ -141,17 +179,54 @@ class CategoryController extends Controller
|
||||
$page = intval(Input::get('page'));
|
||||
$set = $repository->getJournals($category, $page);
|
||||
$count = $repository->countJournals($category);
|
||||
$subTitle = $category->name;
|
||||
$journals = new LengthAwarePaginator($set, $count, 50, $page);
|
||||
$journals->setPath('categories/show/' . $category->id);
|
||||
|
||||
return view('categories.show', compact('category', 'journals', 'hideCategory'));
|
||||
// list of ranges for list of periods:
|
||||
|
||||
// oldest transaction in category:
|
||||
$start = $repository->getFirstActivityDate($category);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = Navigation::endOfX(new Carbon, $range);
|
||||
$entries = new Collection;
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('category-show');
|
||||
$cache->addProperty($category->id);
|
||||
if ($cache->has()) {
|
||||
$entries = $cache->get();
|
||||
} else {
|
||||
|
||||
while ($end >= $start) {
|
||||
$end = Navigation::startOfPeriod($end, $range);
|
||||
$currentEnd = Navigation::endOfPeriod($end, $range);
|
||||
|
||||
// here do something.
|
||||
$spent = $repository->spentInPeriod($category, $end, $currentEnd);
|
||||
$earned = $repository->earnedInPeriod($category, $end, $currentEnd);
|
||||
$dateStr = $end->format('Y-m-d');
|
||||
$dateName = Navigation::periodShow($end, $range);
|
||||
$entries->push([$dateStr, $dateName, $spent, $earned]);
|
||||
|
||||
$end = Navigation::subtractPeriod($end, $range, 1);
|
||||
|
||||
}
|
||||
$cache->store($entries);
|
||||
}
|
||||
|
||||
return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CategoryFormRequest $request
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
*
|
||||
* @return mixed
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(CategoryFormRequest $request, CategoryRepositoryInterface $repository)
|
||||
{
|
||||
@@ -162,14 +237,15 @@ class CategoryController extends Controller
|
||||
$category = $repository->store($categoryData);
|
||||
|
||||
Session::flash('success', 'New category "' . $category->name . '" stored!');
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('create_another')) === 1) {
|
||||
Session::put('categories.create.fromStore', true);
|
||||
|
||||
return Redirect::route('categories.create')->withInput();
|
||||
return redirect(route('categories.create'))->withInput();
|
||||
}
|
||||
|
||||
return Redirect::route('categories.index');
|
||||
return redirect(route('categories.index'));
|
||||
}
|
||||
|
||||
|
||||
@@ -189,15 +265,16 @@ class CategoryController extends Controller
|
||||
$repository->update($category, $categoryData);
|
||||
|
||||
Session::flash('success', 'Category "' . $category->name . '" updated.');
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('return_to_edit')) === 1) {
|
||||
Session::put('categories.edit.fromUpdate', true);
|
||||
|
||||
return Redirect::route('categories.edit', $category->id);
|
||||
return redirect(route('categories.edit', [$category->id]));
|
||||
}
|
||||
|
||||
// redirect to previous URL.
|
||||
return Redirect::to(Session::get('categories.edit.url'));
|
||||
return redirect(Session::get('categories.edit.url'));
|
||||
|
||||
}
|
||||
|
||||
|
@@ -6,12 +6,11 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use Grumpydictator\Gchart\GChart;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use Session;
|
||||
use Steam;
|
||||
|
||||
/**
|
||||
* Class AccountController
|
||||
@@ -20,25 +19,51 @@ use Steam;
|
||||
*/
|
||||
class AccountController extends Controller
|
||||
{
|
||||
|
||||
/** @var \FireflyIII\Generator\Chart\Account\AccountChartGenerator */
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
// create chart generator:
|
||||
$this->generator = app('FireflyIII\Generator\Chart\Account\AccountChartGenerator');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shows the balances for all the user's accounts.
|
||||
*
|
||||
* @param GChart $chart
|
||||
* @param AccountRepositoryInterface $repository
|
||||
*
|
||||
* @param $year
|
||||
* @param $month
|
||||
* @param bool $shared
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function all(GChart $chart, AccountRepositoryInterface $repository, $year, $month, $shared = false)
|
||||
public function all(AccountRepositoryInterface $repository, $year, $month, $shared = false)
|
||||
{
|
||||
$start = new Carbon($year . '-' . $month . '-01');
|
||||
$end = clone $start;
|
||||
$end->endOfMonth();
|
||||
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('all');
|
||||
$cache->addProperty('accounts');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
/** @var Collection $accounts */
|
||||
$accounts = $repository->getAccounts(['Default account', 'Asset account']);
|
||||
if ($shared === false) {
|
||||
// remove the shared accounts from the collection:
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $index => $account) {
|
||||
if ($account->getMeta('accountRole') == 'sharedAsset') {
|
||||
@@ -47,104 +72,102 @@ class AccountController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
// make chart:
|
||||
$data = $this->generator->all($accounts, $start, $end);
|
||||
$cache->store($data);
|
||||
|
||||
$index = 1;
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
|
||||
$chart->addCertainty($index);
|
||||
$index++;
|
||||
}
|
||||
$current = clone $start;
|
||||
$current->subDay();
|
||||
$today = Carbon::now();
|
||||
while ($end >= $current) {
|
||||
$row = [clone $current];
|
||||
$certain = $current < $today;
|
||||
foreach ($accounts as $account) {
|
||||
$row[] = Steam::balance($account, $current);
|
||||
$row[] = $certain;
|
||||
}
|
||||
$chart->addRowArray($row);
|
||||
$current->addDay();
|
||||
}
|
||||
$chart->generate();
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
return Response::json($chart->getData());
|
||||
/**
|
||||
* Shows the balances for all the user's expense accounts.
|
||||
*
|
||||
* @param AccountRepositoryInterface $repository
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function expenseAccounts(AccountRepositoryInterface $repository)
|
||||
{
|
||||
$start = clone Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = clone Session::get('end', Carbon::now()->endOfMonth());
|
||||
$accounts = $repository->getAccounts(['Expense account', 'Beneficiary account']);
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('expenseAccounts');
|
||||
$cache->addProperty('accounts');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$data = $this->generator->expenseAccounts($accounts, $start, $end);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the balances for all the user's frontpage accounts.
|
||||
*
|
||||
* @param GChart $chart
|
||||
* @param AccountRepositoryInterface $repository
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function frontpage(GChart $chart, AccountRepositoryInterface $repository)
|
||||
public function frontpage(AccountRepositoryInterface $repository)
|
||||
{
|
||||
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
|
||||
|
||||
$frontPage = Preferences::get('frontPageAccounts', []);
|
||||
$start = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->endOfMonth());
|
||||
$start = clone Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = clone Session::get('end', Carbon::now()->endOfMonth());
|
||||
$accounts = $repository->getFrontpageAccounts($frontPage);
|
||||
|
||||
$index = 1;
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
|
||||
$chart->addCertainty($index);
|
||||
$index++;
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('frontpage');
|
||||
$cache->addProperty('accounts');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
$current = clone $start;
|
||||
$current->subDay();
|
||||
$today = Carbon::now();
|
||||
while ($end >= $current) {
|
||||
$row = [clone $current];
|
||||
$certain = $current < $today;
|
||||
foreach ($accounts as $account) {
|
||||
$row[] = Steam::balance($account, $current);
|
||||
$row[] = $certain;
|
||||
}
|
||||
$chart->addRowArray($row);
|
||||
$current->addDay();
|
||||
}
|
||||
$chart->generate();
|
||||
|
||||
return Response::json($chart->getData());
|
||||
$data = $this->generator->frontpage($accounts, $start, $end);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows an account's balance for a single month.
|
||||
*
|
||||
* @param GChart $chart
|
||||
* @param Account $account
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function single(GChart $chart, Account $account)
|
||||
public function single(Account $account)
|
||||
{
|
||||
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
|
||||
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
|
||||
$chart->addCertainty(1);
|
||||
|
||||
$start = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->endOfMonth());
|
||||
$current = clone $start;
|
||||
$today = new Carbon;
|
||||
|
||||
while ($end >= $current) {
|
||||
$certain = $current < $today;
|
||||
$chart->addRow(clone $current, Steam::balance($account, $current), $certain);
|
||||
$current->addDay();
|
||||
$start = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->endOfMonth());
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('frontpage');
|
||||
$cache->addProperty('single');
|
||||
$cache->addProperty($account->id);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$data = $this->generator->single($account, $start, $end);
|
||||
$cache->store($data);
|
||||
|
||||
$chart->generate();
|
||||
|
||||
return Response::json($chart->getData());
|
||||
return Response::json($data);
|
||||
}
|
||||
}
|
||||
|
@@ -6,13 +6,10 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use Grumpydictator\Gchart\GChart;
|
||||
use Illuminate\Support\Collection;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Response;
|
||||
use Session;
|
||||
use Steam;
|
||||
|
||||
/**
|
||||
* Class BillController
|
||||
@@ -21,120 +18,86 @@ use Steam;
|
||||
*/
|
||||
class BillController extends Controller
|
||||
{
|
||||
|
||||
/** @var \FireflyIII\Generator\Chart\Bill\BillChartGenerator */
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
* Shows the overview for a bill. The min/max amount and matched journals.
|
||||
*
|
||||
* @param GChart $chart
|
||||
* @param BillRepositoryInterface $repository
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function single(GChart $chart, BillRepositoryInterface $repository, Bill $bill)
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
$chart->addColumn(trans('firefly.date'), 'date');
|
||||
$chart->addColumn(trans('firefly.maxAmount'), 'number');
|
||||
$chart->addColumn(trans('firefly.minAmount'), 'number');
|
||||
$chart->addColumn(trans('firefly.billEntry'), 'number');
|
||||
|
||||
// get first transaction or today for start:
|
||||
$results = $repository->getJournals($bill);
|
||||
/** @var TransactionJournal $result */
|
||||
foreach ($results as $result) {
|
||||
$chart->addRow(clone $result->date, floatval($bill->amount_max), floatval($bill->amount_min), floatval($result->amount));
|
||||
}
|
||||
|
||||
$chart->generate();
|
||||
|
||||
return Response::json($chart->getData());
|
||||
|
||||
parent::__construct();
|
||||
// create chart generator:
|
||||
$this->generator = app('FireflyIII\Generator\Chart\Bill\BillChartGenerator');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows all bills and whether or not theyve been paid this month (pie chart).
|
||||
*
|
||||
* @param GChart $chart
|
||||
*
|
||||
* @param BillRepositoryInterface $repository
|
||||
* @param AccountRepositoryInterface $accounts
|
||||
* @param BillRepositoryInterface $repository
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function frontpage(GChart $chart, BillRepositoryInterface $repository, AccountRepositoryInterface $accounts)
|
||||
public function frontpage(BillRepositoryInterface $repository)
|
||||
{
|
||||
$chart->addColumn(trans('firefly.name'), 'string');
|
||||
$chart->addColumn(trans('firefly.amount'), 'number');
|
||||
$start = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->endOfMonth());
|
||||
$cache = new CacheProperties(); // chart properties for cache:
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('bills');
|
||||
$cache->addProperty('frontpage');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$start = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->endOfMonth());
|
||||
$bills = $repository->getActiveBills();
|
||||
$paid = new Collection; // journals.
|
||||
$unpaid = new Collection; // bills
|
||||
// loop paid and create single entry:
|
||||
$paidDescriptions = [];
|
||||
$paidAmount = 0;
|
||||
$unpaidDescriptions = [];
|
||||
$unpaidAmount = 0;
|
||||
$set = $repository->getBillsForChart($start, $end);
|
||||
|
||||
/** @var Bill $bill */
|
||||
foreach ($bills as $bill) {
|
||||
$ranges = $repository->getRanges($bill, $start, $end);
|
||||
// optionally expand this set with credit card data
|
||||
$set = $repository->getCreditCardInfoForChart($set, $start, $end);
|
||||
$paid = $set->get('paid');
|
||||
$unpaid = $set->get('unpaid');
|
||||
|
||||
foreach ($ranges as $range) {
|
||||
// paid a bill in this range?
|
||||
$journals = $repository->getJournalsInRange($bill, $range['start'], $range['end']);
|
||||
if ($journals->count() == 0) {
|
||||
$unpaid->push([$bill, $range['start']]);
|
||||
} else {
|
||||
$paid = $paid->merge($journals);
|
||||
}
|
||||
|
||||
// build chart:
|
||||
$data = $this->generator->frontpage($paid, $unpaid);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the overview for a bill. The min/max amount and matched journals.
|
||||
*
|
||||
* @param BillRepositoryInterface $repository
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function single(BillRepositoryInterface $repository, Bill $bill)
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('single');
|
||||
$cache->addProperty('bill');
|
||||
$cache->addProperty($bill->id);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
// get first transaction or today for start:
|
||||
$results = $repository->getJournals($bill);
|
||||
|
||||
// resort:
|
||||
$results = $results->sortBy(
|
||||
function (TransactionJournal $journal) {
|
||||
return $journal->date->format('U');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$creditCards = $accounts->getCreditCards();
|
||||
foreach ($creditCards as $creditCard) {
|
||||
$balance = Steam::balance($creditCard, $end, true);
|
||||
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
|
||||
if ($balance < 0) {
|
||||
// unpaid! create a fake bill that matches the amount.
|
||||
$description = $creditCard->name;
|
||||
$amount = $balance * -1;
|
||||
$fakeBill = $repository->createFakeBill($description, $date, $amount);
|
||||
unset($description, $amount);
|
||||
$unpaid->push([$fakeBill, $date]);
|
||||
}
|
||||
if ($balance == 0) {
|
||||
// find transfer(s) TO the credit card which should account for
|
||||
// anything paid. If not, the CC is not yet used.
|
||||
$journals = $accounts->getTransfersInRange($creditCard, $start, $end);
|
||||
$paid = $paid->merge($journals);
|
||||
}
|
||||
}
|
||||
$data = $this->generator->single($bill, $results);
|
||||
$cache->store($data);
|
||||
|
||||
|
||||
/** @var TransactionJournal $entry */
|
||||
foreach ($paid as $entry) {
|
||||
|
||||
$paidDescriptions[] = $entry->description;
|
||||
$paidAmount += floatval($entry->amount);
|
||||
}
|
||||
|
||||
// loop unpaid:
|
||||
/** @var Bill $entry */
|
||||
foreach ($unpaid as $entry) {
|
||||
$description = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')';
|
||||
$amount = ($entry[0]->amount_max + $entry[0]->amount_min) / 2;
|
||||
$unpaidDescriptions[] = $description;
|
||||
$unpaidAmount += $amount;
|
||||
unset($amount, $description);
|
||||
}
|
||||
|
||||
$chart->addRow(trans('firefly.unpaid') . ': ' . join(', ', $unpaidDescriptions), $unpaidAmount);
|
||||
$chart->addRow(trans('firefly.paid') . ': ' . join(', ', $paidDescriptions), $paidAmount);
|
||||
$chart->generate();
|
||||
|
||||
return Response::json($chart->getData());
|
||||
return Response::json($data);
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use Grumpydictator\Gchart\GChart;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
@@ -21,17 +21,30 @@ use Session;
|
||||
*/
|
||||
class BudgetController extends Controller
|
||||
{
|
||||
|
||||
/** @var \FireflyIII\Generator\Chart\Budget\BudgetChartGenerator */
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
// create chart generator:
|
||||
$this->generator = app('FireflyIII\Generator\Chart\Budget\BudgetChartGenerator');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GChart $chart
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function budget(GChart $chart, BudgetRepositoryInterface $repository, Budget $budget)
|
||||
public function budget(BudgetRepositoryInterface $repository, Budget $budget)
|
||||
{
|
||||
$chart->addColumn(trans('firefly.period'), 'date');
|
||||
$chart->addColumn(trans('firefly.spent'), 'number');
|
||||
|
||||
|
||||
// dates and times
|
||||
$first = $repository->getFirstBudgetLimitDate($budget);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$last = Session::get('end', new Carbon);
|
||||
@@ -39,157 +52,198 @@ class BudgetController extends Controller
|
||||
$final->addYears(2);
|
||||
$last = Navigation::endOfX($last, $range, $final);
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($first);
|
||||
$cache->addProperty($last);
|
||||
$cache->addProperty('budget');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$entries = new Collection;
|
||||
|
||||
while ($first < $last) {
|
||||
$end = Navigation::addPeriod($first, $range, 0);
|
||||
|
||||
$spent = $repository->spentInPeriodCorrected($budget, $first, $end);
|
||||
$chart->addRow($end, $spent);
|
||||
|
||||
|
||||
$end->subDay();
|
||||
$chartDate = clone $end;
|
||||
$chartDate->startOfMonth();
|
||||
$spent = $repository->balanceInPeriod($budget, $first, $end) * -1;
|
||||
$entries->push([$chartDate, $spent]);
|
||||
$first = Navigation::addPeriod($first, $range, 0);
|
||||
}
|
||||
|
||||
$chart->generate();
|
||||
$data = $this->generator->budget($entries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($chart->getData());
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the amount left in a specific budget limit.
|
||||
*
|
||||
* @param GChart $chart
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param Budget $budget
|
||||
* @param LimitRepetition $repetition
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function budgetLimit(GChart $chart, BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition)
|
||||
public function budgetLimit(BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition)
|
||||
{
|
||||
$start = clone $repetition->startdate;
|
||||
$end = $repetition->enddate;
|
||||
bcscale(2);
|
||||
|
||||
$chart->addColumn(trans('firefly.day'), 'date');
|
||||
$chart->addColumn(trans('firefly.left'), 'number');
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('budget');
|
||||
$cache->addProperty('limit');
|
||||
$cache->addProperty($budget->id);
|
||||
$cache->addProperty($repetition->id);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
|
||||
$amount = $repetition->amount;
|
||||
$entries = new Collection;
|
||||
$amount = $repetition->amount;
|
||||
|
||||
while ($start <= $end) {
|
||||
/*
|
||||
* Sum of expenses on this day:
|
||||
*/
|
||||
$sum = $repository->expensesOnDayCorrected($budget, $start);
|
||||
$amount += $sum;
|
||||
$chart->addRow(clone $start, $amount);
|
||||
$sum = $repository->expensesOnDayCorrected($budget, $start);
|
||||
$amount = bcadd($amount, $sum);
|
||||
$entries->push([clone $start, $amount]);
|
||||
$start->addDay();
|
||||
}
|
||||
$chart->generate();
|
||||
|
||||
return Response::json($chart->getData());
|
||||
$data = $this->generator->budgetLimit($entries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a budget list with spent/left/overspent.
|
||||
*
|
||||
* @param GChart $chart
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function frontpage(GChart $chart, BudgetRepositoryInterface $repository)
|
||||
public function frontpage(BudgetRepositoryInterface $repository)
|
||||
{
|
||||
$chart->addColumn(trans('firefly.budget'), 'string');
|
||||
$chart->addColumn(trans('firefly.left'), 'number');
|
||||
$chart->addColumn(trans('firefly.spent'), 'number');
|
||||
$chart->addColumn(trans('firefly.overspent'), 'number');
|
||||
|
||||
$budgets = $repository->getBudgets();
|
||||
$start = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->endOfMonth());
|
||||
$allEntries = new Collection;
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('budget');
|
||||
$cache->addProperty('all');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
bcscale(2);
|
||||
|
||||
/** @var Budget $budget */
|
||||
foreach ($budgets as $budget) {
|
||||
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
|
||||
if ($repetitions->count() == 0) {
|
||||
$expenses = $repository->spentInPeriodCorrected($budget, $start, $end, true);
|
||||
$allEntries->push([$budget->name, 0, 0, $expenses]);
|
||||
$expenses = $repository->balanceInPeriod($budget, $start, $end, true) * -1;
|
||||
$allEntries->push([$budget->name, 0, 0, $expenses, 0, 0]);
|
||||
continue;
|
||||
}
|
||||
/** @var LimitRepetition $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$expenses = $repository->spentInPeriodCorrected($budget, $repetition->startdate, $repetition->enddate, true);
|
||||
$left = $expenses < floatval($repetition->amount) ? floatval($repetition->amount) - $expenses : 0;
|
||||
$spent = $expenses > floatval($repetition->amount) ? floatval($repetition->amount) : $expenses;
|
||||
$overspent = $expenses > floatval($repetition->amount) ? $expenses - floatval($repetition->amount) : 0;
|
||||
$allEntries->push(
|
||||
[$budget->name . ' (' . $repetition->startdate->formatLocalized($this->monthAndDayFormat) . ')',
|
||||
$left,
|
||||
$spent,
|
||||
$overspent
|
||||
]
|
||||
);
|
||||
$expenses = $repository->balanceInPeriod($budget, $repetition->startdate, $repetition->enddate, true) * -1;
|
||||
// $left can be less than zero.
|
||||
// $overspent can be more than zero ( = overspending)
|
||||
|
||||
$left = max(bcsub($repetition->amount, $expenses), 0); // limited at zero.
|
||||
$overspent = max(bcsub($expenses, $repetition->amount), 0); // limited at zero.
|
||||
$name = $budget->name;
|
||||
|
||||
// $spent is maxed to the repetition amount:
|
||||
$spent = $expenses > $repetition->amount ? $repetition->amount : $expenses;
|
||||
|
||||
|
||||
$allEntries->push([$name, $left, $spent, $overspent, $repetition->amount, $expenses]);
|
||||
}
|
||||
}
|
||||
|
||||
$noBudgetExpenses = $repository->getWithoutBudgetSum($start, $end);
|
||||
$allEntries->push([trans('firefly.noBudget'), 0, 0, $noBudgetExpenses]);
|
||||
$noBudgetExpenses = $repository->getWithoutBudgetSum($start, $end) * -1;
|
||||
$allEntries->push([trans('firefly.noBudget'), 0, 0, $noBudgetExpenses, 0, 0]);
|
||||
|
||||
foreach ($allEntries as $entry) {
|
||||
if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) {
|
||||
$chart->addRow($entry[0], $entry[1], $entry[2], $entry[3]);
|
||||
}
|
||||
}
|
||||
$data = $this->generator->frontpage($allEntries);
|
||||
$cache->store($data);
|
||||
|
||||
$chart->generate();
|
||||
|
||||
return Response::json($chart->getData());
|
||||
return Response::json($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a yearly overview for a budget.
|
||||
*
|
||||
* @param GChart $chart
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param $year
|
||||
* @param bool $shared
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function year(GChart $chart, BudgetRepositoryInterface $repository, $year, $shared = false)
|
||||
public function year(BudgetRepositoryInterface $repository, $year, $shared = false)
|
||||
{
|
||||
$start = new Carbon($year . '-01-01');
|
||||
$end = new Carbon($year . '-12-31');
|
||||
$shared = $shared == 'shared' ? true : false;
|
||||
$budgets = $repository->getBudgets();
|
||||
$start = new Carbon($year . '-01-01');
|
||||
$end = new Carbon($year . '-12-31');
|
||||
$shared = $shared == 'shared' ? true : false;
|
||||
$allBudgets = $repository->getBudgets();
|
||||
$budgets = new Collection;
|
||||
|
||||
// add columns:
|
||||
$chart->addColumn(trans('firefly.month'), 'date');
|
||||
foreach ($budgets as $budget) {
|
||||
$chart->addColumn($budget->name, 'number');
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('budget');
|
||||
$cache->addProperty('year');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
// filter empty budgets:
|
||||
|
||||
foreach ($allBudgets as $budget) {
|
||||
$spent = $repository->balanceInPeriod($budget, $start, $end, $shared);
|
||||
if ($spent != 0) {
|
||||
$budgets->push($budget);
|
||||
}
|
||||
}
|
||||
|
||||
$entries = new Collection;
|
||||
|
||||
while ($start < $end) {
|
||||
// month is the current end of the period:
|
||||
$month = clone $start;
|
||||
$month->endOfMonth();
|
||||
// make a row:
|
||||
$row = [clone $start];
|
||||
|
||||
// each budget, fill the row:
|
||||
foreach ($budgets as $budget) {
|
||||
$spent = $repository->spentInPeriodCorrected($budget, $start, $month, $shared);
|
||||
$row[] = $spent;
|
||||
$spent = $repository->balanceInPeriod($budget, $start, $month, $shared);
|
||||
$row[] = $spent * -1;
|
||||
}
|
||||
$chart->addRowArray($row);
|
||||
|
||||
$start->addMonth();
|
||||
$entries->push($row);
|
||||
$start->endOfMonth()->addDay();
|
||||
}
|
||||
|
||||
$chart->generate();
|
||||
$data = $this->generator->year($allBudgets, $entries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($chart->getData());
|
||||
return Response::json($data);
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,8 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use Grumpydictator\Gchart\GChart;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
use Response;
|
||||
@@ -20,42 +21,65 @@ use Session;
|
||||
*/
|
||||
class CategoryController extends Controller
|
||||
{
|
||||
/** @var \FireflyIII\Generator\Chart\Category\CategoryChartGenerator */
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
// create chart generator:
|
||||
$this->generator = app('FireflyIII\Generator\Chart\Category\CategoryChartGenerator');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show an overview for a category for all time, per month/week/year.
|
||||
*
|
||||
* @param GChart $chart
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param Category $category
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function all(GChart $chart, CategoryRepositoryInterface $repository, Category $category)
|
||||
public function all(CategoryRepositoryInterface $repository, Category $category)
|
||||
{
|
||||
// oldest transaction in category:
|
||||
$start = $repository->getFirstActivityDate($category);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
// jump to start of week / month / year / etc
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
|
||||
$chart->addColumn(trans('firefly.period'), 'date');
|
||||
$chart->addColumn(trans('firefly.spent'), 'number');
|
||||
$start = $repository->getFirstActivityDate($category);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = new Carbon;
|
||||
$entries = new Collection;
|
||||
|
||||
|
||||
$end = new Carbon;
|
||||
while ($start <= $end) {
|
||||
|
||||
$currentEnd = Navigation::endOfPeriod($start, $range);
|
||||
$spent = $repository->spentInPeriodCorrected($category, $start, $currentEnd);
|
||||
$chart->addRow(clone $start, $spent);
|
||||
|
||||
$start = Navigation::addPeriod($start, $range, 0);
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('all');
|
||||
$cache->addProperty('categories');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$chart->generate();
|
||||
while ($start <= $end) {
|
||||
$currentEnd = Navigation::endOfPeriod($start, $range);
|
||||
$spent = $repository->spentInPeriod($category, $start, $currentEnd);
|
||||
$earned = $repository->earnedInPeriod($category, $start, $currentEnd);
|
||||
$date = Navigation::periodShow($start, $range);
|
||||
$entries->push([clone $start, $date, $spent, $earned]);
|
||||
$start = Navigation::addPeriod($start, $range, 0);
|
||||
}
|
||||
// limit the set to the last 40:
|
||||
$entries = $entries->reverse();
|
||||
$entries = $entries->slice(0, 48);
|
||||
$entries = $entries->reverse();
|
||||
|
||||
return Response::json($chart->getData());
|
||||
$data = $this->generator->all($entries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
|
||||
}
|
||||
@@ -63,70 +87,124 @@ class CategoryController extends Controller
|
||||
/**
|
||||
* Show this month's category overview.
|
||||
*
|
||||
* @param GChart $chart
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function frontpage(GChart $chart, CategoryRepositoryInterface $repository)
|
||||
public function frontpage(CategoryRepositoryInterface $repository)
|
||||
{
|
||||
$chart->addColumn(trans('firefly.category'), 'string');
|
||||
$chart->addColumn(trans('firefly.spent'), 'number');
|
||||
|
||||
$start = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->endOfMonth());
|
||||
$set = $repository->getCategoriesAndExpensesCorrected($start, $end);
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('category');
|
||||
$cache->addProperty('frontpage');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$array = $repository->getCategoriesAndExpensesCorrected($start, $end);
|
||||
// sort by callback:
|
||||
uasort(
|
||||
$set,
|
||||
$array,
|
||||
function ($left, $right) {
|
||||
if ($left['sum'] == $right['sum']) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ($left['sum'] < $right['sum']) ? 1 : -1;
|
||||
return ($left['sum'] < $right['sum']) ? -1 : 1;
|
||||
}
|
||||
);
|
||||
$set = new Collection($array);
|
||||
$data = $this->generator->frontpage($set);
|
||||
|
||||
|
||||
foreach ($set as $entry) {
|
||||
$sum = floatval($entry['sum']);
|
||||
if ($sum != 0) {
|
||||
$chart->addRow($entry['name'], $sum);
|
||||
}
|
||||
}
|
||||
|
||||
$chart->generate();
|
||||
|
||||
return Response::json($chart->getData());
|
||||
return Response::json($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GChart $chart
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param Category $category
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function month(GChart $chart, CategoryRepositoryInterface $repository, Category $category)
|
||||
public function currentPeriod(CategoryRepositoryInterface $repository, Category $category)
|
||||
{
|
||||
$start = clone Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->endOfMonth());
|
||||
|
||||
$chart->addColumn(trans('firefly.period'), 'date');
|
||||
$chart->addColumn(trans('firefly.spent'), 'number');
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($category->id);
|
||||
$cache->addProperty('category');
|
||||
$cache->addProperty('currentPeriod');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
$entries = new Collection;
|
||||
|
||||
|
||||
while ($start <= $end) {
|
||||
$spent = $repository->spentOnDaySumCorrected($category, $start);
|
||||
$chart->addRow(clone $start, $spent);
|
||||
$spent = $repository->spentOnDaySumCorrected($category, $start);
|
||||
$earned = $repository->earnedOnDaySumCorrected($category, $start);
|
||||
$date = Navigation::periodShow($start, '1D');
|
||||
$entries->push([clone $start, $date, $spent, $earned]);
|
||||
$start->addDay();
|
||||
}
|
||||
|
||||
$chart->generate();
|
||||
$data = $this->generator->period($entries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($chart->getData());
|
||||
return Response::json($data);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param Category $category
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function specificPeriod(CategoryRepositoryInterface $repository, Category $category, $date)
|
||||
{
|
||||
$carbon = new Carbon($date);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($carbon, $range);
|
||||
$end = Navigation::endOfPeriod($carbon, $range);
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($category->id);
|
||||
$cache->addProperty('category');
|
||||
$cache->addProperty('specificPeriod');
|
||||
$cache->addProperty($date);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
$entries = new Collection;
|
||||
|
||||
|
||||
while ($start <= $end) {
|
||||
$spent = $repository->spentOnDaySumCorrected($category, $start);
|
||||
$earned = $repository->earnedOnDaySumCorrected($category, $start);
|
||||
$theDate = Navigation::periodShow($start, '1D');
|
||||
$entries->push([clone $start, $theDate, $spent, $earned]);
|
||||
$start->addDay();
|
||||
}
|
||||
|
||||
$data = $this->generator->period($entries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
|
||||
}
|
||||
@@ -134,45 +212,118 @@ class CategoryController extends Controller
|
||||
/**
|
||||
* This chart will only show expenses.
|
||||
*
|
||||
* @param GChart $chart
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param $year
|
||||
* @param bool $shared
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function year(GChart $chart, CategoryRepositoryInterface $repository, $year, $shared = false)
|
||||
public function spentInYear(CategoryRepositoryInterface $repository, $year, $shared = false)
|
||||
{
|
||||
$start = new Carbon($year . '-01-01');
|
||||
$end = new Carbon($year . '-12-31');
|
||||
$shared = $shared == 'shared' ? true : false;
|
||||
$categories = $repository->getCategories();
|
||||
$start = new Carbon($year . '-01-01');
|
||||
$end = new Carbon($year . '-12-31');
|
||||
|
||||
// add columns:
|
||||
$chart->addColumn(trans('firefly.month'), 'date');
|
||||
foreach ($categories as $category) {
|
||||
$chart->addColumn($category->name, 'number');
|
||||
$cache = new CacheProperties; // chart properties for cache:
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('category');
|
||||
$cache->addProperty('spent-in-year');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$shared = $shared == 'shared' ? true : false;
|
||||
$allCategories = $repository->getCategories();
|
||||
$entries = new Collection;
|
||||
$categories = $allCategories->filter(
|
||||
function (Category $category) use ($repository, $start, $end, $shared) {
|
||||
$spent = $repository->balanceInPeriod($category, $start, $end, $shared);
|
||||
if ($spent < 0) {
|
||||
return $category;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
while ($start < $end) {
|
||||
// month is the current end of the period:
|
||||
$month = clone $start;
|
||||
$month = clone $start; // month is the current end of the period
|
||||
$month->endOfMonth();
|
||||
// make a row:
|
||||
$row = [clone $start];
|
||||
$row = [clone $start]; // make a row:
|
||||
|
||||
// each budget, fill the row:
|
||||
foreach ($categories as $category) {
|
||||
$spent = $repository->spentInPeriodCorrected($category, $start, $month, $shared);
|
||||
$row[] = $spent;
|
||||
foreach ($categories as $category) { // each budget, fill the row
|
||||
$spent = $repository->balanceInPeriod($category, $start, $month, $shared);
|
||||
if ($spent < 0) {
|
||||
$row[] = $spent * -1;
|
||||
} else {
|
||||
$row[] = 0;
|
||||
}
|
||||
}
|
||||
$chart->addRowArray($row);
|
||||
|
||||
$entries->push($row);
|
||||
$start->addMonth();
|
||||
}
|
||||
$data = $this->generator->spentInYear($categories, $entries);
|
||||
$cache->store($data);
|
||||
|
||||
$chart->generate();
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
return Response::json($chart->getData());
|
||||
/**
|
||||
* This chart will only show income.
|
||||
*
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param $year
|
||||
* @param bool $shared
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function earnedInYear(CategoryRepositoryInterface $repository, $year, $shared = false)
|
||||
{
|
||||
$start = new Carbon($year . '-01-01');
|
||||
$end = new Carbon($year . '-12-31');
|
||||
|
||||
$cache = new CacheProperties; // chart properties for cache:
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('category');
|
||||
$cache->addProperty('earned-in-year');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$shared = $shared == 'shared' ? true : false;
|
||||
$allCategories = $repository->getCategories();
|
||||
$allEntries = new Collection;
|
||||
$categories = $allCategories->filter(
|
||||
function (Category $category) use ($repository, $start, $end, $shared) {
|
||||
$spent = $repository->balanceInPeriod($category, $start, $end, $shared);
|
||||
if ($spent > 0) {
|
||||
return $category;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
while ($start < $end) {
|
||||
$month = clone $start; // month is the current end of the period
|
||||
$month->endOfMonth();
|
||||
$row = [clone $start]; // make a row:
|
||||
|
||||
foreach ($categories as $category) { // each budget, fill the row
|
||||
$spent = $repository->balanceInPeriod($category, $start, $month, $shared);
|
||||
if ($spent > 0) {
|
||||
$row[] = $spent;
|
||||
} else {
|
||||
$row[] = 0;
|
||||
}
|
||||
}
|
||||
$allEntries->push($row);
|
||||
$start->addMonth();
|
||||
}
|
||||
$data = $this->generator->earnedInYear($categories, $allEntries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
}
|
||||
|
@@ -2,11 +2,10 @@
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Chart;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use Grumpydictator\Gchart\GChart;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
use Response;
|
||||
|
||||
@@ -18,32 +17,44 @@ use Response;
|
||||
*/
|
||||
class PiggyBankController extends Controller
|
||||
{
|
||||
|
||||
/** @var \FireflyIII\Generator\Chart\PiggyBank\PiggyBankChartGenerator */
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
// create chart generator:
|
||||
$this->generator = app('FireflyIII\Generator\Chart\PiggyBank\PiggyBankChartGenerator');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the piggy bank history.
|
||||
*
|
||||
* @param GChart $chart
|
||||
* @param PiggyBankRepositoryInterface $repository
|
||||
* @param PiggyBank $piggyBank
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function history(GChart $chart, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
|
||||
public function history(PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
|
||||
{
|
||||
$chart->addColumn(trans('firefly.date'), 'date');
|
||||
$chart->addColumn(trans('firefly.balance'), 'number');
|
||||
|
||||
/** @var Collection $set */
|
||||
$set = $repository->getEventSummarySet($piggyBank);
|
||||
$sum = 0;
|
||||
|
||||
foreach ($set as $entry) {
|
||||
$sum += floatval($entry->sum);
|
||||
$chart->addRow(new Carbon($entry->date), $sum);
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('piggy-history');
|
||||
$cache->addProperty($piggyBank->id);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$chart->generate();
|
||||
/** @var Collection $set */
|
||||
$set = new Collection($repository->getEventSummarySet($piggyBank));
|
||||
$data = $this->generator->history($set);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($chart->getData());
|
||||
return Response::json($data);
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -6,8 +6,10 @@ namespace FireflyIII\Http\Controllers\Chart;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Report\ReportQueryInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use Grumpydictator\Gchart\GChart;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
use Response;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class ReportController
|
||||
@@ -17,85 +19,118 @@ use Response;
|
||||
class ReportController extends Controller
|
||||
{
|
||||
|
||||
/** @var \FireflyIII\Generator\Chart\Report\ReportChartGenerator */
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
// create chart generator:
|
||||
$this->generator = app('FireflyIII\Generator\Chart\Report\ReportChartGenerator');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Summarizes all income and expenses, per month, for a given year.
|
||||
*
|
||||
* @param GChart $chart
|
||||
* @param ReportQueryInterface $query
|
||||
* @param $year
|
||||
* @param bool $shared
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function yearInOut(GChart $chart, ReportQueryInterface $query, $year, $shared = false)
|
||||
public function yearInOut(ReportQueryInterface $query, $year, $shared = false)
|
||||
{
|
||||
// get start and end of year
|
||||
$start = new Carbon($year . '-01-01');
|
||||
$end = new Carbon($year . '-12-31');
|
||||
$shared = $shared == 'shared' ? true : false;
|
||||
|
||||
$chart->addColumn(trans('firefly.month'), 'date');
|
||||
$chart->addColumn(trans('firefly.income'), 'number');
|
||||
$chart->addColumn(trans('firefly.expenses'), 'number');
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('yearInOut');
|
||||
$cache->addProperty($year);
|
||||
$cache->addProperty($shared);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$entries = new Collection;
|
||||
while ($start < $end) {
|
||||
$month = clone $start;
|
||||
$month->endOfMonth();
|
||||
// total income and total expenses:
|
||||
$incomeSum = floatval($query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount'));
|
||||
$expenseSum = floatval($query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount'));
|
||||
$incomeSum = $query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount_positive');
|
||||
$expenseSum = $query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount_positive');
|
||||
|
||||
$chart->addRow(clone $start, $incomeSum, $expenseSum);
|
||||
$entries->push([clone $start, $incomeSum, $expenseSum]);
|
||||
$start->addMonth();
|
||||
}
|
||||
$chart->generate();
|
||||
|
||||
return Response::json($chart->getData());
|
||||
$data = $this->generator->yearInOut($entries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Summarizes all income and expenses for a given year. Gives a total and an average.
|
||||
*
|
||||
* @param GChart $chart
|
||||
* @param ReportQueryInterface $query
|
||||
* @param $year
|
||||
* @param bool $shared
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function yearInOutSummarized(GChart $chart, ReportQueryInterface $query, $year, $shared = false)
|
||||
public function yearInOutSummarized(ReportQueryInterface $query, $year, $shared = false)
|
||||
{
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('yearInOutSummarized');
|
||||
$cache->addProperty($year);
|
||||
$cache->addProperty($shared);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$start = new Carbon($year . '-01-01');
|
||||
$end = new Carbon($year . '-12-31');
|
||||
$shared = $shared == 'shared' ? true : false;
|
||||
$income = 0;
|
||||
$expense = 0;
|
||||
$income = '0';
|
||||
$expense = '0';
|
||||
$count = 0;
|
||||
|
||||
$chart->addColumn(trans('firefly.summary'), 'string');
|
||||
$chart->addColumn(trans('firefly.income'), 'number');
|
||||
$chart->addColumn(trans('firefly.expenses'), 'number');
|
||||
bcscale(2);
|
||||
|
||||
while ($start < $end) {
|
||||
$month = clone $start;
|
||||
$month->endOfMonth();
|
||||
// total income and total expenses:
|
||||
$income += floatval($query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount'));
|
||||
$expense += floatval($query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount'));
|
||||
$currentIncome = $query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount_positive');
|
||||
$currentExpense = $query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount_positive');
|
||||
|
||||
Log::debug('Date ['.$month->format('M Y').']: income = ['.$income.' + '.$currentIncome.'], out = ['.$expense.' + '.$currentExpense.']');
|
||||
|
||||
$income = bcadd($income, $currentIncome);
|
||||
$expense = bcadd($expense, $currentExpense);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$count++;
|
||||
$start->addMonth();
|
||||
}
|
||||
|
||||
// add total + average:
|
||||
$chart->addRow(trans('firefly.sum'), $income, $expense);
|
||||
$count = $count > 0 ? $count : 1;
|
||||
$chart->addRow(trans('firefly.average'), ($income / $count), ($expense / $count));
|
||||
$data = $this->generator->yearInOutSummarized($income, $expense, $count);
|
||||
$cache->store($data);
|
||||
|
||||
$chart->generate();
|
||||
|
||||
return Response::json($chart->getData());
|
||||
return Response::json($data);
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
use Auth;
|
||||
use Config;
|
||||
use Illuminate\Foundation\Bus\DispatchesCommands;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Preferences;
|
||||
@@ -16,7 +16,7 @@ use View;
|
||||
abstract class Controller extends BaseController
|
||||
{
|
||||
|
||||
use DispatchesCommands, ValidatesRequests;
|
||||
use DispatchesJobs, ValidatesRequests;
|
||||
|
||||
/** @var string */
|
||||
protected $monthAndDayFormat;
|
||||
|
74
app/Http/Controllers/CronController.php
Normal file
74
app/Http/Controllers/CronController.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use FireflyIII\User;
|
||||
|
||||
/**
|
||||
* Class WebhookController
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers
|
||||
*/
|
||||
class CronController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Firefly doesn't have anything that should be in the a cron job, except maybe this one, and it's fairly exceptional.
|
||||
*
|
||||
* If you use SendGrid like I do, you can detect bounces and thereby check if users gave an invalid address. If they did,
|
||||
* it's easy to block them and change their password. Optionally, you could notify yourself about it and send them a message.
|
||||
*
|
||||
* But thats something not supported right now.
|
||||
*/
|
||||
public function sendgrid()
|
||||
{
|
||||
|
||||
if (strlen(env('SENDGRID_USERNAME')) > 0 && strlen(env('SENDGRID_PASSWORD')) > 0) {
|
||||
|
||||
$set = [
|
||||
'blocks' => 'https://api.sendgrid.com/api/blocks.get.json',
|
||||
'bounces' => 'https://api.sendgrid.com/api/bounces.get.json',
|
||||
'invalids' => 'https://api.sendgrid.com/api/invalidemails.get.json',
|
||||
|
||||
];
|
||||
echo '<pre>';
|
||||
foreach ($set as $name => $URL) {
|
||||
|
||||
|
||||
$parameters = [
|
||||
'api_user' => env('SENDGRID_USERNAME'),
|
||||
'api_key' => env('SENDGRID_PASSWORD'),
|
||||
'date' => 1,
|
||||
'days' => 7
|
||||
];
|
||||
$fullURL = $URL . '?' . http_build_query($parameters);
|
||||
$data = json_decode(file_get_contents($fullURL));
|
||||
|
||||
/*
|
||||
* Loop the result, if any.
|
||||
*/
|
||||
if (is_array($data)) {
|
||||
echo 'Found ' . count($data) . ' entries in the SendGrid ' . $name . ' list.' . "\n";
|
||||
foreach ($data as $entry) {
|
||||
$address = $entry->email;
|
||||
$user = User::where('email', $address)->where('blocked', 0)->first();
|
||||
if (!is_null($user)) {
|
||||
echo 'Found a user: ' . $address . ', who is now blocked.' . "\n";
|
||||
$user->blocked = 1;
|
||||
$user->blocked_code = 'bounced';
|
||||
$user->password = 'bounced';
|
||||
$user->save();
|
||||
} else {
|
||||
echo 'Found no user: ' . $address . ', did nothing.' . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
echo 'Done!' . "\n";
|
||||
} else {
|
||||
echo 'Please fill in SendGrid details.';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user