mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-12-21 10:41:22 +00:00
Compare commits
928 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
636ae193a7 | ||
|
|
aff8493204 | ||
|
|
8f2d4494d5 | ||
|
|
7a2bb4eb96 | ||
|
|
4916c06797 | ||
|
|
a818ab0942 | ||
|
|
9aa89393c0 | ||
|
|
216b304fe1 | ||
|
|
cf8a687b4d | ||
|
|
d1d6ba4114 | ||
|
|
a5ac713310 | ||
|
|
6fa8c33672 | ||
|
|
dd2b019d3c | ||
|
|
3a12ad192f | ||
|
|
d669b75352 | ||
|
|
746d50d459 | ||
|
|
f741552d91 | ||
|
|
c38e4b86b4 | ||
|
|
b044d85e90 | ||
|
|
24d111f026 | ||
|
|
f50058e3c2 | ||
|
|
ce9445168c | ||
|
|
27b8b4b35a | ||
|
|
9ccb67db8a | ||
|
|
3d82afd4e6 | ||
|
|
5f9cb61160 | ||
|
|
7f54b70c24 | ||
|
|
a156dce281 | ||
|
|
c0b0e58720 | ||
|
|
170ffbae04 | ||
|
|
6755ec7eb0 | ||
|
|
c8bc9a096a | ||
|
|
dc7f5a562b | ||
|
|
3b5b51578d | ||
|
|
41188a1bd6 | ||
|
|
d9bafe34eb | ||
|
|
43dbba5378 | ||
|
|
168ed5ac56 | ||
|
|
e5b4a55d8e | ||
|
|
167c057e8a | ||
|
|
71bf054ab1 | ||
|
|
111fcd77c4 | ||
|
|
287c1c4ffa | ||
|
|
3d69dc786d | ||
|
|
57a3f20c13 | ||
|
|
eab2c57594 | ||
|
|
b019962f34 | ||
|
|
5c59c819b6 | ||
|
|
93b97b8d72 | ||
|
|
f1f922031a | ||
|
|
390cace775 | ||
|
|
28fdad9426 | ||
|
|
9155c13e08 | ||
|
|
e8776d44c5 | ||
|
|
5ee8d04800 | ||
|
|
eb31934fb7 | ||
|
|
000f86d318 | ||
|
|
d9b3ebc82f | ||
|
|
91b3ca047a | ||
|
|
08131e42af | ||
|
|
a013553a6c | ||
|
|
7b2fe8eb4a | ||
|
|
610f782054 | ||
|
|
94700f4064 | ||
|
|
0f12ebb31c | ||
|
|
00a8a9ac0e | ||
|
|
92616c6ae3 | ||
|
|
97db618cd8 | ||
|
|
2832d308f1 | ||
|
|
a7ecdf715f | ||
|
|
935dc3ff9f | ||
|
|
99d14e8cbe | ||
|
|
664fde2344 | ||
|
|
27c45eface | ||
|
|
f83bc3c8b3 | ||
|
|
0d5efb8d27 | ||
|
|
bf9c1c1875 | ||
|
|
049d866f62 | ||
|
|
d672f0c2ad | ||
|
|
7b040e8583 | ||
|
|
e1cf285272 | ||
|
|
53f7f13362 | ||
|
|
f710677cdc | ||
|
|
b5fbc8b632 | ||
|
|
1fdc0a196c | ||
|
|
559f429f5e | ||
|
|
ae4b198d3d | ||
|
|
e088ecbbad | ||
|
|
f6b7bd5b44 | ||
|
|
f5a21f64c0 | ||
|
|
f5cbed7c0c | ||
|
|
59fff8928b | ||
|
|
8743b49a17 | ||
|
|
19dc91937e | ||
|
|
4846cb7b14 | ||
|
|
54e0ac816b | ||
|
|
9ce960b3d7 | ||
|
|
cc9d1c4bfd | ||
|
|
d211555beb | ||
|
|
9350557d10 | ||
|
|
ea6c54cad0 | ||
|
|
81287602d7 | ||
|
|
441ba79ec7 | ||
|
|
c9e4a09da6 | ||
|
|
c84f4e2bc0 | ||
|
|
6938f8ab89 | ||
|
|
4fdd2c851f | ||
|
|
78b56712fc | ||
|
|
6dcdb0f9a0 | ||
|
|
cfab837b96 | ||
|
|
b8461cdeb6 | ||
|
|
e07d65ef6b | ||
|
|
80cbc35c89 | ||
|
|
2faf84780f | ||
|
|
ebdb1a8836 | ||
|
|
2dfa51652a | ||
|
|
1cf1ea230f | ||
|
|
86e127ebff | ||
|
|
a313265785 | ||
|
|
37693ad08d | ||
|
|
e8180d252b | ||
|
|
9957c95c68 | ||
|
|
3354d53a48 | ||
|
|
ddaa53b940 | ||
|
|
c2e04a11bf | ||
|
|
00d059b8df | ||
|
|
f3fff6f1c5 | ||
|
|
9360eb6a70 | ||
|
|
26fb03e6c8 | ||
|
|
eed3d021d9 | ||
|
|
48728e8418 | ||
|
|
f39f25760a | ||
|
|
10ea60daaf | ||
|
|
f2fa5e140b | ||
|
|
be5ff35b13 | ||
|
|
bd5d73d1e6 | ||
|
|
cc712b0c75 | ||
|
|
a5ac84e1b9 | ||
|
|
cbe1f762ca | ||
|
|
e6db49c20c | ||
|
|
4909fcc8b4 | ||
|
|
68cdfd00b0 | ||
|
|
323c16ebe1 | ||
|
|
c20a38b7b0 | ||
|
|
20993342a2 | ||
|
|
1b3198c143 | ||
|
|
173d0290ea | ||
|
|
ae4cd8da12 | ||
|
|
a494398332 | ||
|
|
c82bdd1f5a | ||
|
|
e6ac3ccfad | ||
|
|
b13b58a2b2 | ||
|
|
2b6790f83f | ||
|
|
fe30850568 | ||
|
|
493c71b89e | ||
|
|
deaebc373c | ||
|
|
e834489206 | ||
|
|
1b316e462e | ||
|
|
7bf75128a8 | ||
|
|
3857e8d49f | ||
|
|
b16149b842 | ||
|
|
32ed8a4d8d | ||
|
|
f949d2476b | ||
|
|
0620830b10 | ||
|
|
7ee25693aa | ||
|
|
7cb86add64 | ||
|
|
fbc9720f7a | ||
|
|
ffdd37ddd5 | ||
|
|
51062bc80b | ||
|
|
31533b5ef8 | ||
|
|
3649b595df | ||
|
|
83b7c9aa32 | ||
|
|
4e3c59a2da | ||
|
|
dcbfe90cf7 | ||
|
|
f69be86c74 | ||
|
|
820722f44e | ||
|
|
2ff806dedc | ||
|
|
a755badd5f | ||
|
|
4df1e5393b | ||
|
|
8c55bd179f | ||
|
|
ccfc5ece66 | ||
|
|
6c12f1bc86 | ||
|
|
88839e9610 | ||
|
|
4f6a733238 | ||
|
|
dc9083a764 | ||
|
|
6f231b840b | ||
|
|
61a703e605 | ||
|
|
8e0e9734a5 | ||
|
|
2afb455bac | ||
|
|
01792f91e2 | ||
|
|
651dff0750 | ||
|
|
9cbfbd41dc | ||
|
|
168d6f403c | ||
|
|
41f200e630 | ||
|
|
0809cfdc6d | ||
|
|
466d739da8 | ||
|
|
daf65cb387 | ||
|
|
2605f60983 | ||
|
|
fcf6cdb134 | ||
|
|
209258b507 | ||
|
|
f80bc214f9 | ||
|
|
f76990bb9b | ||
|
|
eef68c9b31 | ||
|
|
006c2ae186 | ||
|
|
8b66ff6afe | ||
|
|
4cc6d57e6e | ||
|
|
af1c6b22bb | ||
|
|
5958990ed5 | ||
|
|
cd4cbdc197 | ||
|
|
1f538be16e | ||
|
|
9703439a4c | ||
|
|
42203ba872 | ||
|
|
cd60b852a1 | ||
|
|
5b1d9e1a0d | ||
|
|
e02657a7c7 | ||
|
|
c352eb0c74 | ||
|
|
81b9d5da09 | ||
|
|
b9b0413510 | ||
|
|
97770da619 | ||
|
|
521623797e | ||
|
|
15d3414443 | ||
|
|
245f06c93a | ||
|
|
33899f0e2f | ||
|
|
4697fbdeef | ||
|
|
06174d6afb | ||
|
|
b2bb16c1e0 | ||
|
|
27aae279e6 | ||
|
|
e9ee93beb7 | ||
|
|
20941dedd3 | ||
|
|
5ac88623ed | ||
|
|
768508dd4b | ||
|
|
668633e764 | ||
|
|
3dbb1f034d | ||
|
|
1270e5d15c | ||
|
|
afec8480fb | ||
|
|
8720511046 | ||
|
|
a7a00ecf40 | ||
|
|
88bbafd3e8 | ||
|
|
ae3258b449 | ||
|
|
cf4d7cfeef | ||
|
|
46ee2a0568 | ||
|
|
ce250c85fc | ||
|
|
1087b9f5df | ||
|
|
0dc74d9d14 | ||
|
|
98b272383f | ||
|
|
e722daafd0 | ||
|
|
670ab059d1 | ||
|
|
09f826ceba | ||
|
|
df5e3c9be9 | ||
|
|
31c336b3b1 | ||
|
|
b3a419b2f3 | ||
|
|
11f63fa6ce | ||
|
|
4b35059140 | ||
|
|
7836feba63 | ||
|
|
1e54366f1f | ||
|
|
d039bb0e03 | ||
|
|
166d32f073 | ||
|
|
321890992e | ||
|
|
d644403d8a | ||
|
|
44e76f9518 | ||
|
|
f9703eca4c | ||
|
|
48551e8bf5 | ||
|
|
74b538805b | ||
|
|
35f493beff | ||
|
|
06d11c9133 | ||
|
|
da7eb615db | ||
|
|
5da5024ad3 | ||
|
|
c1346d4c86 | ||
|
|
c38a4a15ff | ||
|
|
bdec94ead6 | ||
|
|
69bebf202f | ||
|
|
38895afea6 | ||
|
|
70ed4188b6 | ||
|
|
d889094863 | ||
|
|
c96eb8753e | ||
|
|
5a7607f6c6 | ||
|
|
71bb88529a | ||
|
|
b26164a168 | ||
|
|
19444551e4 | ||
|
|
e0b2a6e627 | ||
|
|
2973765866 | ||
|
|
2a08a25064 | ||
|
|
a55e291908 | ||
|
|
2003d37a9a | ||
|
|
29145bf6cf | ||
|
|
caa1ff120a | ||
|
|
ef4e964c94 | ||
|
|
1f263f60a7 | ||
|
|
5fc7cafcbe | ||
|
|
397c4926eb | ||
|
|
a14544398b | ||
|
|
d439dceac1 | ||
|
|
af29b31ea8 | ||
|
|
b311ef70bc | ||
|
|
9f9d744406 | ||
|
|
e094871bc9 | ||
|
|
dfc95cee45 | ||
|
|
fdca234721 | ||
|
|
cf5cc626d7 | ||
|
|
358d9aac7d | ||
|
|
681bc580c4 | ||
|
|
4a2768f8d1 | ||
|
|
05f8773fa0 | ||
|
|
5e5fdfdd51 | ||
|
|
5e744390c0 | ||
|
|
cb5fa401cb | ||
|
|
2980860377 | ||
|
|
9ff0b282f3 | ||
|
|
4bc1c032bd | ||
|
|
9fcb00f10b | ||
|
|
723e461559 | ||
|
|
9b24e6d448 | ||
|
|
e622774775 | ||
|
|
84ce9bc94b | ||
|
|
a3a1bc30b1 | ||
|
|
b4c9a7698e | ||
|
|
8b2d7fc32f | ||
|
|
90e66cbd94 | ||
|
|
fd9a7080ea | ||
|
|
c0fad106f0 | ||
|
|
78c8243184 | ||
|
|
780abecd53 | ||
|
|
ea6896816d | ||
|
|
95a456860a | ||
|
|
b1b2fda155 | ||
|
|
5847f534c3 | ||
|
|
e8e8163fa7 | ||
|
|
6a21d82dcf | ||
|
|
d6b47656bc | ||
|
|
8c37ef3a95 | ||
|
|
35deed1d10 | ||
|
|
ba32a665f1 | ||
|
|
bbd19be554 | ||
|
|
c360cc6db6 | ||
|
|
7e4b9af315 | ||
|
|
9b03e6b124 | ||
|
|
013e16e15f | ||
|
|
180ec52798 | ||
|
|
c7b47b4453 | ||
|
|
4b00db7662 | ||
|
|
78a7b995d2 | ||
|
|
f7c50a123a | ||
|
|
9617d17aca | ||
|
|
d9884ddf73 | ||
|
|
8157f0a958 | ||
|
|
b7580a5f83 | ||
|
|
66703b30b3 | ||
|
|
53677e3c64 | ||
|
|
feef6a1756 | ||
|
|
5f299b895b | ||
|
|
4e1bb5fbac | ||
|
|
47ccc513ad | ||
|
|
cce1a01936 | ||
|
|
6f2b1a6a76 | ||
|
|
8526907f50 | ||
|
|
bc192a8e54 | ||
|
|
9ff6f8fc52 | ||
|
|
6573bd6b4b | ||
|
|
9dc3f614af | ||
|
|
3888b8cceb | ||
|
|
294df4a2b3 | ||
|
|
265dd37212 | ||
|
|
eb7c79ad27 | ||
|
|
de111c7100 | ||
|
|
e892c9a824 | ||
|
|
5eb0e18cae | ||
|
|
27cabb398e | ||
|
|
64dbb14241 | ||
|
|
bb4e2be9eb | ||
|
|
7d1de0da17 | ||
|
|
bf16c9a42b | ||
|
|
1a7b1ce499 | ||
|
|
efc9bc71a7 | ||
|
|
fc5b315af0 | ||
|
|
7a4a78628d | ||
|
|
d16fb30a62 | ||
|
|
2d177e660e | ||
|
|
2f131dc170 | ||
|
|
94810e371a | ||
|
|
59731878f6 | ||
|
|
54ede8aa18 | ||
|
|
b415b6b043 | ||
|
|
70c922cdc5 | ||
|
|
068fc32cb2 | ||
|
|
3dcdacc3b8 | ||
|
|
a6594358d8 | ||
|
|
f98921da46 | ||
|
|
25747fbcf2 | ||
|
|
aac5c2b13c | ||
|
|
cc810a5b6f | ||
|
|
1b3592d959 | ||
|
|
d75614e9a7 | ||
|
|
08703e282f | ||
|
|
2904baf44e | ||
|
|
f99e46bf75 | ||
|
|
9f87890ead | ||
|
|
638184cf66 | ||
|
|
03babfe75c | ||
|
|
238ed3c788 | ||
|
|
6a9d931ba3 | ||
|
|
a3d2a9e00b | ||
|
|
39b88e8207 | ||
|
|
449c6dfde5 | ||
|
|
7cc47ca0b1 | ||
|
|
95f4a83f41 | ||
|
|
35154dc7a3 | ||
|
|
0fd0d7d080 | ||
|
|
658265c938 | ||
|
|
38fe9e7e1c | ||
|
|
77056dcf8d | ||
|
|
026683a8e1 | ||
|
|
6ab6dd6ac3 | ||
|
|
83de3482ce | ||
|
|
919a35aed3 | ||
|
|
ad3defb071 | ||
|
|
9c929ecd1b | ||
|
|
f79c9f7cf1 | ||
|
|
8e75c345d9 | ||
|
|
44886d9aad | ||
|
|
c2d444347d | ||
|
|
5cb497596d | ||
|
|
1857469d2f | ||
|
|
ea71b4843d | ||
|
|
97727e2e3d | ||
|
|
f81e7da8bb | ||
|
|
8e827bf83b | ||
|
|
9e1fa284ca | ||
|
|
3bf800be6e | ||
|
|
635b9f9dba | ||
|
|
52a0d7cf7b | ||
|
|
a34516932b | ||
|
|
929a2a30a2 | ||
|
|
ffa88eeb08 | ||
|
|
51b45b4ed4 | ||
|
|
f263844793 | ||
|
|
18c46df9aa | ||
|
|
15846e157b | ||
|
|
bc59f2db0d | ||
|
|
cd2be8c1a4 | ||
|
|
f958115c50 | ||
|
|
e7d677bfb6 | ||
|
|
3e80ffc52b | ||
|
|
d0c7a5c076 | ||
|
|
f3f4e6b354 | ||
|
|
5a45b25614 | ||
|
|
0b5ee1edfc | ||
|
|
da3dc599f9 | ||
|
|
f013b435ab | ||
|
|
5f6975a113 | ||
|
|
c5dee29e4b | ||
|
|
633ee02f13 | ||
|
|
6b750c909a | ||
|
|
5f8b6640a9 | ||
|
|
dd42d8437c | ||
|
|
67a178591d | ||
|
|
f5e5659c1f | ||
|
|
8b0f0fb615 | ||
|
|
209116e766 | ||
|
|
79392ab656 | ||
|
|
3ca1207231 | ||
|
|
cec1b147f2 | ||
|
|
46cfcfa3e7 | ||
|
|
b833e8dfa2 | ||
|
|
77b843efd8 | ||
|
|
db72ad7c60 | ||
|
|
eadc630fcb | ||
|
|
170c1793cc | ||
|
|
9f7c6c2d0c | ||
|
|
72d054c55c | ||
|
|
524edfe7c2 | ||
|
|
c25c5623d2 | ||
|
|
4f38b77ef6 | ||
|
|
5862803434 | ||
|
|
5b3beded39 | ||
|
|
c61fb7a598 | ||
|
|
33d9148029 | ||
|
|
63969f5a33 | ||
|
|
edde18aeef | ||
|
|
657116d361 | ||
|
|
e16269daa8 | ||
|
|
c07591ff5c | ||
|
|
75a478ad54 | ||
|
|
8dae8b1a7f | ||
|
|
15fd8cf486 | ||
|
|
55333156ac | ||
|
|
8cdcba3231 | ||
|
|
8bab9e84e2 | ||
|
|
2faae83912 | ||
|
|
5a61a11a61 | ||
|
|
a6d71988f2 | ||
|
|
7069e242ae | ||
|
|
56ee830558 | ||
|
|
6dd12729e6 | ||
|
|
14a48303cb | ||
|
|
72cf6c9c0f | ||
|
|
144ee6b8ca | ||
|
|
8967d86da6 | ||
|
|
18c6edbb5d | ||
|
|
53de3c4717 | ||
|
|
ad577e4e81 | ||
|
|
44811a3e7c | ||
|
|
1ab3f05b3a | ||
|
|
5e76488ae7 | ||
|
|
32771fe7e1 | ||
|
|
9b40cc6881 | ||
|
|
2e35260bbb | ||
|
|
a067704277 | ||
|
|
de281818ac | ||
|
|
c49bfad38d | ||
|
|
c1ba591b26 | ||
|
|
719af38a61 | ||
|
|
ac61dfae6b | ||
|
|
813fb679a7 | ||
|
|
e7562781f7 | ||
|
|
56d36b7f53 | ||
|
|
53b3f7f821 | ||
|
|
08a53156bd | ||
|
|
8985cd6309 | ||
|
|
3833da7410 | ||
|
|
4210cd10db | ||
|
|
a7bd1c6892 | ||
|
|
52b0111afa | ||
|
|
7921d128e4 | ||
|
|
d7e838701a | ||
|
|
289bcb22aa | ||
|
|
3fe57b7983 | ||
|
|
32e92c2a16 | ||
|
|
1b3d208540 | ||
|
|
6a8bf0aa62 | ||
|
|
56715556ed | ||
|
|
838330b909 | ||
|
|
69553b138b | ||
|
|
36d7a02994 | ||
|
|
301528e2d2 | ||
|
|
0303b45707 | ||
|
|
ba722e8ed5 | ||
|
|
289e5a5442 | ||
|
|
fdad96e2bc | ||
|
|
af994e4dae | ||
|
|
006d68e279 | ||
|
|
29dc122ad3 | ||
|
|
cf4a8c6204 | ||
|
|
3c73fe92bf | ||
|
|
6637590797 | ||
|
|
b8bab11acd | ||
|
|
a2f600feac | ||
|
|
80dd62ef0a | ||
|
|
827b1c9cd8 | ||
|
|
2e4fcf803d | ||
|
|
d00d95fc6f | ||
|
|
3e3ab9bd25 | ||
|
|
6eecc7722d | ||
|
|
ada4aaf69a | ||
|
|
93244c1f78 | ||
|
|
be056cea6b | ||
|
|
659ca8be14 | ||
|
|
ea9af8366d | ||
|
|
80edd47d36 | ||
|
|
d7746b3649 | ||
|
|
c4c4fbc34c | ||
|
|
59f57c96e9 | ||
|
|
a2f852fecf | ||
|
|
ad114ed329 | ||
|
|
c4c3d0f07f | ||
|
|
6cf8102de5 | ||
|
|
e7e4aa2218 | ||
|
|
6d84f4b6c1 | ||
|
|
ce3e9ffd11 | ||
|
|
3ada260e0e | ||
|
|
8fdd0cb795 | ||
|
|
913e05a2e6 | ||
|
|
fa1f703ef6 | ||
|
|
4004c53e1b | ||
|
|
4838670649 | ||
|
|
a985e09282 | ||
|
|
9bd1503cb4 | ||
|
|
a2ccbf7844 | ||
|
|
61bbe8a905 | ||
|
|
59bc5d22d1 | ||
|
|
1423d5b314 | ||
|
|
152d0eb1d0 | ||
|
|
6426d1df06 | ||
|
|
9284eb3fe9 | ||
|
|
afdae8bc1e | ||
|
|
2a7085e593 | ||
|
|
2408fb3ed4 | ||
|
|
8316afb176 | ||
|
|
e59fd098a3 | ||
|
|
e044199693 | ||
|
|
8f8e29fc22 | ||
|
|
8de5384158 | ||
|
|
216c659335 | ||
|
|
041ca8a5d3 | ||
|
|
fe4f1b306d | ||
|
|
a0972d99fb | ||
|
|
e332bfef7c | ||
|
|
cba5e226d8 | ||
|
|
5aff0c4943 | ||
|
|
cb49c00f4d | ||
|
|
e26d797d57 | ||
|
|
938581527e | ||
|
|
c38ae09735 | ||
|
|
28c3cfe084 | ||
|
|
4a2823bcba | ||
|
|
18eba02026 | ||
|
|
d4690ce580 | ||
|
|
a785c450b1 | ||
|
|
7480dc4a19 | ||
|
|
ad01891a67 | ||
|
|
67fe35d564 | ||
|
|
7f19b6957a | ||
|
|
0a54caf202 | ||
|
|
4b4c1c7f8f | ||
|
|
d071f3947e | ||
|
|
b3d99cd210 | ||
|
|
90e696f82c | ||
|
|
958fcd1cfa | ||
|
|
8f57c7dcb3 | ||
|
|
77262f52a4 | ||
|
|
16bfbc8a12 | ||
|
|
1fd375b875 | ||
|
|
46131ad39d | ||
|
|
0b5c5b2ae9 | ||
|
|
55be174037 | ||
|
|
a17b7025f1 | ||
|
|
170cf7fd77 | ||
|
|
23cdb4d326 | ||
|
|
cbbe529572 | ||
|
|
0b382426e9 | ||
|
|
1cbbf9baa4 | ||
|
|
8d41ff7b79 | ||
|
|
e3b6057bf8 | ||
|
|
66a4042cad | ||
|
|
56c08d8302 | ||
|
|
d4e759754d | ||
|
|
a96e171cbf | ||
|
|
bd4a8c8397 | ||
|
|
04f71b3b43 | ||
|
|
d124de51db | ||
|
|
d87d12a0f5 | ||
|
|
f2b08346d0 | ||
|
|
d3682a6727 | ||
|
|
371bbd9508 | ||
|
|
a8a28f442f | ||
|
|
65ddd8a736 | ||
|
|
8bb27de233 | ||
|
|
37e2f097ba | ||
|
|
1966d87ce6 | ||
|
|
7b8c86e1e3 | ||
|
|
de634da513 | ||
|
|
96836e2d6c | ||
|
|
8a9d576f61 | ||
|
|
791d12fbb4 | ||
|
|
d1329be2fa | ||
|
|
3ed6561702 | ||
|
|
7a0587f433 | ||
|
|
0fe682bfe6 | ||
|
|
0f685e8789 | ||
|
|
420771c233 | ||
|
|
5e3e9271ca | ||
|
|
1e603c0833 | ||
|
|
03e1673e92 | ||
|
|
9f992f003d | ||
|
|
f50244a41f | ||
|
|
8b9607f9b5 | ||
|
|
af107ad5e8 | ||
|
|
8926d62165 | ||
|
|
45344ee347 | ||
|
|
baf9ebab15 | ||
|
|
b7dab817f2 | ||
|
|
fb2fa54480 | ||
|
|
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 | ||
|
|
3de57c668f |
@@ -3,6 +3,7 @@ languages:
|
||||
JavaScript: true
|
||||
PHP: true
|
||||
exclude_paths:
|
||||
- "gulpfile.js"
|
||||
- "public/packages/maximebf/php-debugbar/debugbar.js"
|
||||
- "public/packages/maximebf/php-debugbar/widgets.js"
|
||||
- "public/packages/maximebf/php-debugbar/openhandler.js"
|
||||
|
||||
28
.env.example
Normal file → Executable file
28
.env.example
Normal file → Executable file
@@ -1,6 +1,7 @@
|
||||
APP_ENV=production
|
||||
APP_DEBUG=false
|
||||
APP_KEY=SomeRandomString
|
||||
APP_KEY=SomeRandomStringOf32CharsExactly
|
||||
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=localhost
|
||||
@@ -10,12 +11,27 @@ DB_PASSWORD=secret
|
||||
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
QUEUE_DRIVER=sync
|
||||
|
||||
DEFAULT_CURRENCY=EUR
|
||||
DEFAULT_LANGUAGE=en_US
|
||||
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_HOST=mailtrap.io
|
||||
MAIL_PORT=2525
|
||||
MAIL_FROM=enter_your_email_here
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
|
||||
SHOW_INCOMPLETE_TRANSLATIONS=false
|
||||
|
||||
EMAIL_SMTP=
|
||||
EMAIL_DRIVER=smtp
|
||||
EMAIL_USERNAME=
|
||||
EMAIL_PASSWORD=
|
||||
ANALYTICS_ID=
|
||||
EMAIL_PRETEND=false
|
||||
RUNCLEANUP=true
|
||||
SITE_OWNER=mail@example.com
|
||||
|
||||
BLOCKED_DOMAINS=
|
||||
31
.env.testing
Normal file → Executable file
31
.env.testing
Normal file → Executable file
@@ -1,6 +1,7 @@
|
||||
APP_ENV=testing
|
||||
APP_DEBUG=true
|
||||
APP_KEY=SomeRandomString
|
||||
APP_KEY=SomeRandomStringOf32CharsExactly
|
||||
|
||||
|
||||
DB_CONNECTION=sqlite
|
||||
DB_HOST=localhost
|
||||
@@ -10,9 +11,27 @@ DB_PASSWORD=secret
|
||||
|
||||
CACHE_DRIVER=array
|
||||
SESSION_DRIVER=array
|
||||
QUEUE_DRIVER=array
|
||||
|
||||
EMAIL_SMTP=
|
||||
EMAIL_USERNAME=
|
||||
EMAIL_PASSWORD=
|
||||
ANALYTICS_ID=ABC
|
||||
EMAIL_PRETEND=true
|
||||
DEFAULT_CURRENCY=EUR
|
||||
DEFAULT_LANGUAGE=en_US
|
||||
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_DRIVER=log
|
||||
MAIL_HOST=mailtrap.io
|
||||
MAIL_PORT=2525
|
||||
MAIL_FROM=your_address_here@example.com
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
|
||||
SHOW_INCOMPLETE_TRANSLATIONS=false
|
||||
|
||||
ANALYTICS_ID=abcde
|
||||
RUNCLEANUP=false
|
||||
SITE_OWNER=your_address_here@example.com
|
||||
|
||||
BLOCKED_DOMAINS=
|
||||
2
.gitattributes
vendored
Normal file → Executable file
2
.gitattributes
vendored
Normal file → Executable file
@@ -1 +1,3 @@
|
||||
* text=auto
|
||||
*.css linguist-vendored
|
||||
*.less linguist-vendored
|
||||
|
||||
45
.gitignore
vendored
45
.gitignore
vendored
@@ -1,36 +1,17 @@
|
||||
/bootstrap/compiled.php
|
||||
/vendor
|
||||
composer.phar
|
||||
Thumbs.db
|
||||
.idea/
|
||||
tests/_output/*
|
||||
_ide_helper.php
|
||||
/build/logs/clover.xml
|
||||
index.html*
|
||||
app/storage/firefly-export*
|
||||
.vagrant
|
||||
firefly-iii-import-*.json
|
||||
tests/_output/*
|
||||
testing.sqlite
|
||||
_ide_helper_models.php
|
||||
clean.sqlite
|
||||
tests/acceptance/AcceptanceTester.php
|
||||
tests/functional/FunctionalTester.php
|
||||
tests/unit/UnitTester.php
|
||||
pi.php
|
||||
tests/_data/db.sqlite
|
||||
tests/_data/dump.sql
|
||||
db.sqlite_snapshot
|
||||
c3.php
|
||||
db.sqlite-journal
|
||||
tests/_output/*
|
||||
/node_modules
|
||||
Homestead.yaml
|
||||
Homestead.json
|
||||
.env
|
||||
clover.xml
|
||||
node_modules/
|
||||
addNewLines.php
|
||||
_ide_helper.php
|
||||
_ide_helper_models.php
|
||||
.phpstorm.meta.php
|
||||
.env.backup
|
||||
.env.local
|
||||
|
||||
tests/_output/*
|
||||
tests/_output/*
|
||||
storage/
|
||||
|
||||
# Eclipse project files
|
||||
.buildpath
|
||||
.project
|
||||
.settings/
|
||||
|
||||
.env.local
|
||||
|
||||
@@ -3,13 +3,18 @@ sudo: false
|
||||
|
||||
|
||||
php:
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7
|
||||
|
||||
install:
|
||||
- composer update
|
||||
- composer install
|
||||
- php artisan env
|
||||
- mv -v .env.testing .env
|
||||
- php artisan env
|
||||
- touch storage/upload/at-1.data
|
||||
- touch storage/upload/at-2.data
|
||||
- touch storage/database/testing.db
|
||||
- php artisan migrate --seed
|
||||
|
||||
script:
|
||||
- phpunit
|
||||
|
||||
7
LICENSE
Normal file
7
LICENSE
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright (C) 2016 Sander Dorigo
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
108
README.md
108
README.md
@@ -2,114 +2,26 @@
|
||||
|
||||
[](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)
|
||||
[](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102)
|
||||
[](https://codeclimate.com/github/JC5/firefly-iii)
|
||||
[](http://stillmaintained.com/JC5/firefly-iii)
|
||||
|
||||
## 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
|
||||
household accounts and savings accounts! It's pretty fancy. You should use it to save and organise money.
|
||||
|
||||
_Firefly is a system you'll have install yourself on webhosting of your choosing._
|
||||
|
||||
Personal financial management is pretty difficult, and everybody has their own approach to it. Some people
|
||||
make budgets, other people limit their cashflow by throwing away their credit cards, others try to increase
|
||||
their current cashflow. There are tons of ways to save and earn money.
|
||||
Personal financial management is pretty difficult, and everybody has their own approach to it. Some people make budgets, other people limit their cashflow by throwing away their credit cards, others try to increase their current cashflow. There are tons of ways to save and earn money.
|
||||
|
||||
Firefly works on the principle that if you know where you're money is going, you can stop it from going there.
|
||||
|
||||
#### Some advantages of using Firefly
|
||||
|
||||
To get to know Firefly, and to see if it fits you, check out these resources:
|
||||
- Firefly can import any CSV file, so migrating from other systems is easy.
|
||||
- Firefly runs on your own server, so you are fully in control of your data. Remember, there is no such thing as "the cloud", it’s just somebody else’s computer!
|
||||
- Firefly has lots of features without becoming fancy or bloated.
|
||||
- If you feel you're missing something you can just ask me and I'll add it!
|
||||
|
||||
- The screenshots below on this very page.
|
||||
- The featurelist below, also on this very page.
|
||||
- The [full description](https://github.com/JC5/firefly-iii/wiki/full-description), which will tell you how Firefly works,
|
||||
and the philosophy behind it.
|
||||
|
||||
|
||||
#### 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.
|
||||
|
||||
## Current features
|
||||
|
||||
- [A double-entry bookkeeping system](https://en.wikipedia.org/wiki/Double-entry_bookkeeping_system);
|
||||
- You can store, edit and remove [withdrawals, deposits and transfers](https://en.wikipedia.org/wiki/Financial_transaction). This allows you full financial management;
|
||||
- You can manage different types of accounts;
|
||||
- [Asset](https://en.wikipedia.org/wiki/Asset) accounts
|
||||
- Shared [asset accounts](https://en.wikipedia.org/wiki/Asset) ([household accounts](https://en.wikipedia.org/wiki/Household))
|
||||
- Saving accounts
|
||||
- Credit cards
|
||||
- It's possible to create, change and manage money using _[budgets](https://en.wikipedia.org/wiki/Envelope_system)_;
|
||||
- Organize transactions using categories;
|
||||
- Save towards a goal using [piggy banks](https://en.wikipedia.org/wiki/Piggy_bank);
|
||||
- Predict and anticipate [bills](https://en.wikipedia.org/wiki/Invoice);
|
||||
- View income / expense [reports](https://en.wikipedia.org/wiki/Financial_statement);
|
||||
- Organize expenses using tags;
|
||||
- Lots of help text in case you don't get it.
|
||||
|
||||
Everything is organised:
|
||||
|
||||
- Clear views that should show you how you're doing;
|
||||
- Easy navigation through your records;
|
||||
- Browse back and forth to see previous months or even years;
|
||||
- Lots of charts because we all love them;
|
||||
- Financial reporting showing you how well you are doing.
|
||||
|
||||
## Screenshots
|
||||
|
||||
_Please note that everything in these screenshots is fictional and may not be realistic._
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Running and installing
|
||||
|
||||
If you're still interested please read [the installation guide](https://github.com/JC5/firefly-iii/wiki/Installation),
|
||||
[the upgrade guide](https://github.com/JC5/firefly-iii/wiki/Upgrade-instructions) (if applicable)
|
||||
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.
|
||||
|
||||
## 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.
|
||||
* [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. 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)
|
||||

|
||||
Firefly is pretty awesome. [You can read more about Firefly III, and its features, on the Github Pages](https://jc5.github.io/firefly-iii/).
|
||||
|
||||
27
_development/codesniffer/ruleset.xml
Normal file
27
_development/codesniffer/ruleset.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset name="JamesStandard">
|
||||
<rule ref="Zend">
|
||||
<exclude name="Zend.NamingConventions.ValidVariableName" />
|
||||
<exclude name="PEAR.WhiteSpace.ScopeClosingBrace" />
|
||||
<!--<exclude name="PEAR.Whitespace.ScopeIndent"/>-->
|
||||
<exclude name="PEAR.WhiteSpace.ScopeClosingBrace"/>
|
||||
<exclude name="Generic.Formatting.MultipleStatementAlignment.Incorrect" />
|
||||
<exclude name="PEAR.Functions.FunctionCallSignature" />
|
||||
|
||||
</rule>
|
||||
<!--
|
||||
Here we change two messages from the same sniff. Note how the
|
||||
codes are slightly different because the sniff developer has
|
||||
defined both a MaxExceeded message and a TooLong message. In the
|
||||
case of this sniff, one is used for warnings and one is used
|
||||
for errors.
|
||||
-->
|
||||
<rule ref="Generic.Files.LineLength">
|
||||
<properties>
|
||||
<property name="lineLimit" value="160"/>
|
||||
<property name="absoluteLineLimit" value="160"/>
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
|
||||
</ruleset>
|
||||
322
_development/codestyle.xml
Normal file
322
_development/codestyle.xml
Normal file
@@ -0,0 +1,322 @@
|
||||
<code_scheme name="Use This One">
|
||||
<option name="RIGHT_MARGIN" value="160" />
|
||||
<CoffeeScriptCodeStyleSettings>
|
||||
<option name="SPACE_BEFORE_PROPERTY_COLON" value="true" />
|
||||
</CoffeeScriptCodeStyleSettings>
|
||||
<JSCodeStyleSettings>
|
||||
<option name="ALIGN_MULTILINE_VAR_DECLARATION" value="true" />
|
||||
</JSCodeStyleSettings>
|
||||
<PHPCodeStyleSettings>
|
||||
<option name="ALIGN_KEY_VALUE_PAIRS" value="true" />
|
||||
<option name="ALIGN_PHPDOC_PARAM_NAMES" value="true" />
|
||||
<option name="ALIGN_PHPDOC_COMMENTS" value="true" />
|
||||
<option name="ALIGN_ASSIGNMENTS" value="true" />
|
||||
<option name="COMMA_AFTER_LAST_ARRAY_ELEMENT" value="true" />
|
||||
<option name="PHPDOC_BLANK_LINE_BEFORE_TAGS" value="true" />
|
||||
<option name="PHPDOC_BLANK_LINES_AROUND_PARAMETERS" value="true" />
|
||||
<option name="PHPDOC_WRAP_LONG_LINES" value="true" />
|
||||
<option name="LOWER_CASE_BOOLEAN_CONST" value="true" />
|
||||
<option name="LOWER_CASE_NULL_CONST" value="true" />
|
||||
<option name="BLANK_LINE_BEFORE_RETURN_STATEMENT" value="true" />
|
||||
<option name="KEEP_RPAREN_AND_LBRACE_ON_ONE_LINE" value="true" />
|
||||
<option name="ALIGN_CLASS_CONSTANTS" value="true" />
|
||||
<option name="FORCE_SHORT_DECLARATION_ARRAY_STYLE" value="true" />
|
||||
</PHPCodeStyleSettings>
|
||||
<XML>
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||
</XML>
|
||||
<codeStyleSettings language="CoffeeScript">
|
||||
<option name="KEEP_LINE_BREAKS" value="false" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
|
||||
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="PHP">
|
||||
<option name="RIGHT_MARGIN" value="160" />
|
||||
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
|
||||
<option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
|
||||
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" />
|
||||
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
|
||||
<option name="ALIGN_GROUP_FIELD_DECLARATIONS" value="true" />
|
||||
<option name="CALL_PARAMETERS_WRAP" value="1" />
|
||||
<option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="METHOD_PARAMETERS_WRAP" value="1" />
|
||||
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="EXTENDS_LIST_WRAP" value="1" />
|
||||
<option name="EXTENDS_KEYWORD_WRAP" value="1" />
|
||||
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
|
||||
<option name="BINARY_OPERATION_WRAP" value="1" />
|
||||
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
|
||||
<option name="TERNARY_OPERATION_WRAP" value="1" />
|
||||
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
|
||||
<option name="FOR_STATEMENT_WRAP" value="1" />
|
||||
<option name="FOR_STATEMENT_LPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="FOR_STATEMENT_RPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
|
||||
<option name="ASSIGNMENT_WRAP" value="1" />
|
||||
<option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||
<option name="WHILE_BRACE_FORCE" value="3" />
|
||||
<option name="FOR_BRACE_FORCE" value="3" />
|
||||
<arrangement>
|
||||
<tokens>
|
||||
<token id="modifiers" name="modifiers">
|
||||
<rules>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<PUBLIC>true</PUBLIC>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<PROTECTED>true</PROTECTED>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<PRIVATE>true</PRIVATE>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
<rule>
|
||||
<match>
|
||||
<PUBLIC>true</PUBLIC>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
<rule>
|
||||
<match>
|
||||
<PROTECTED>true</PROTECTED>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
<rule>
|
||||
<match>
|
||||
<PRIVATE>true</PRIVATE>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</rules>
|
||||
</token>
|
||||
<token id="visibility" name="visibility">
|
||||
<rules>
|
||||
<rule>
|
||||
<match>
|
||||
<PUBLIC>true</PUBLIC>
|
||||
</match>
|
||||
</rule>
|
||||
<rule>
|
||||
<match>
|
||||
<PROTECTED>true</PROTECTED>
|
||||
</match>
|
||||
</rule>
|
||||
<rule>
|
||||
<match>
|
||||
<PRIVATE>true</PRIVATE>
|
||||
</match>
|
||||
</rule>
|
||||
</rules>
|
||||
</token>
|
||||
</tokens>
|
||||
<groups>
|
||||
<group>
|
||||
<type>GETTERS_AND_SETTERS</type>
|
||||
<order>KEEP</order>
|
||||
</group>
|
||||
</groups>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<CONST />
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD />
|
||||
<PUBLIC />
|
||||
<STATIC />
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD />
|
||||
<PROTECTED />
|
||||
<STATIC />
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD />
|
||||
<PRIVATE />
|
||||
<STATIC />
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD />
|
||||
<PUBLIC />
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD />
|
||||
<PROTECTED />
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD />
|
||||
<PRIVATE />
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<CONSTRUCTOR />
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<METHOD />
|
||||
<PUBLIC />
|
||||
<STATIC />
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<METHOD />
|
||||
<PROTECTED />
|
||||
<STATIC />
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<METHOD />
|
||||
<PRIVATE />
|
||||
<STATIC />
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<METHOD />
|
||||
<PUBLIC />
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<METHOD />
|
||||
<PROTECTED />
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<METHOD />
|
||||
<PRIVATE />
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<TRAIT />
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<INTERFACE />
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<CLASS />
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
36
_development/phpmd/phpmd.xml
Normal file
36
_development/phpmd/phpmd.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ruleset name="pcsg-generated-ruleset"
|
||||
xmlns="http://pmd.sf.net/ruleset/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
|
||||
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
|
||||
<description>Created with the PHP Coding Standard Generator. http://edorian.github.com/php-coding-standard-generator/
|
||||
</description>
|
||||
<rule ref="rulesets/codesize.xml/CyclomaticComplexity">
|
||||
<properties>
|
||||
<property name="reportLevel" value="5"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="rulesets/codesize.xml/NPathComplexity">
|
||||
<properties>
|
||||
<property name="minimum" value="128"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="rulesets/codesize.xml/ExcessiveMethodLength">
|
||||
<properties>
|
||||
<property name="minimum" value="40"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="rulesets/codesize.xml/ExcessiveParameterList">
|
||||
<properties>
|
||||
<property name="minimum" value="5"/>
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
<!-- Import rule set and exclude rules -->
|
||||
<rule ref="rulesets/controversial.xml">
|
||||
<exclude name="CamelCasePropertyName" />
|
||||
</rule>
|
||||
|
||||
|
||||
</ruleset>
|
||||
1
_development/readme.txt
Normal file
1
_development/readme.txt
Normal file
@@ -0,0 +1 @@
|
||||
These are some of the files I use for code formatting, PHPMD and PHPCS.
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace FireflyIII\Commands;
|
||||
/**
|
||||
* Class Command
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @package FireflyIII\Commands
|
||||
*/
|
||||
abstract class Command
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
67
app/Console/Commands/UpgradeFireflyInstructions.php
Normal file
67
app/Console/Commands/UpgradeFireflyInstructions.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Console\Commands;
|
||||
|
||||
use Config;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class UpgradeFireflyInstructions
|
||||
*
|
||||
* @package FireflyIII\Console\Commands
|
||||
*/
|
||||
class UpgradeFireflyInstructions extends Command
|
||||
{
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command description';
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly:upgrade-instructions';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
//
|
||||
$version = Config::get('firefly.version');
|
||||
$config = Config::get('upgrade.text');
|
||||
$text = isset($config[$version]) ? $config[$version] : null;
|
||||
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
$this->line('');
|
||||
|
||||
if (is_null($text)) {
|
||||
$this->line('Thank you for installing Firefly III, v' . $version);
|
||||
$this->line('If you are upgrading from a previous version,');
|
||||
$this->info('there are no extra upgrade instructions.');
|
||||
$this->line('Firefly III should be ready for use.');
|
||||
} else {
|
||||
$this->line('Thank you for installing Firefly III, v' . $version);
|
||||
$this->line('If you are upgrading from a previous version,');
|
||||
$this->line('please follow these upgrade instructions carefully:');
|
||||
$this->info(wordwrap($text));
|
||||
}
|
||||
|
||||
$this->line('');
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,25 @@
|
||||
<?php namespace FireflyIII\Console;
|
||||
<?php
|
||||
/**
|
||||
* Kernel.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Console;
|
||||
|
||||
use FireflyIII\Console\Commands\UpgradeFireflyInstructions;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
/**
|
||||
* Class Kernel
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @package FireflyIII\Console
|
||||
*/
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
|
||||
/**
|
||||
* The Artisan commands provided by your application.
|
||||
*
|
||||
@@ -19,6 +27,7 @@ class Kernel extends ConsoleKernel
|
||||
*/
|
||||
protected $commands
|
||||
= [
|
||||
UpgradeFireflyInstructions::class,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -26,12 +35,11 @@ class Kernel extends ConsoleKernel
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameters)
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<?php namespace FireflyIII\Events;
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Events;
|
||||
|
||||
/**
|
||||
* Class Event
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @package FireflyIII\Events
|
||||
*/
|
||||
abstract class Event
|
||||
{
|
||||
|
||||
//
|
||||
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php namespace FireflyIII\Events;
|
||||
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class JournalDeleted
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @package FireflyIII\Events
|
||||
*/
|
||||
class JournalDeleted extends Event
|
||||
{
|
||||
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +1,24 @@
|
||||
<?php namespace FireflyIII\Events;
|
||||
<?php
|
||||
/**
|
||||
* TransactionJournalStored.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Events;
|
||||
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class JournalCreated
|
||||
* Class TransactionJournalStored
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @package FireflyIII\Events
|
||||
*/
|
||||
class JournalCreated extends Event
|
||||
class TransactionJournalStored extends Event
|
||||
{
|
||||
|
||||
use SerializesModels;
|
||||
@@ -4,12 +4,12 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class JournalSaved
|
||||
* Class TransactionJournalUpdated
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @package FireflyIII\Events
|
||||
*/
|
||||
class JournalSaved extends Event
|
||||
class TransactionJournalUpdated extends Event
|
||||
{
|
||||
|
||||
use SerializesModels;
|
||||
@@ -1,18 +1,20 @@
|
||||
<?php namespace FireflyIII\Exceptions;
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
/**
|
||||
* Class Handler
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @package FireflyIII\Exceptions
|
||||
*/
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of the exception types that should not be reported.
|
||||
*
|
||||
@@ -20,40 +22,35 @@ class Handler extends ExceptionHandler
|
||||
*/
|
||||
protected $dontReport
|
||||
= [
|
||||
'Symfony\Component\HttpKernel\Exception\HttpException'
|
||||
AuthorizationException::class,
|
||||
HttpException::class,
|
||||
ModelNotFoundException::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Exception $e
|
||||
* @SuppressWarnings(PHPMD.ShortVariable)
|
||||
* @param \Exception $exception
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function render($request, Exception $e)
|
||||
public function render($request, Exception $exception)
|
||||
{
|
||||
if ($e instanceof HttpException) {
|
||||
return $this->renderHttpException($e);
|
||||
} else {
|
||||
return parent::render($request, $e);
|
||||
}
|
||||
return parent::render($request, $exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Report or log an exception.
|
||||
*
|
||||
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
|
||||
* @SuppressWarnings(PHPMD.ShortVariable)
|
||||
*
|
||||
* @param \Exception $e
|
||||
* @param Exception $exception
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function report(Exception $e)
|
||||
public function report(Exception $exception)
|
||||
{
|
||||
parent::report($e);
|
||||
parent::report($exception);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* AccountChartGeneratorInterface.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Account;
|
||||
|
||||
@@ -7,11 +14,11 @@ use FireflyIII\Models\Account;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface AccountChartGenerator
|
||||
* Interface AccountChartGeneratorInterface
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Account
|
||||
*/
|
||||
interface AccountChartGenerator
|
||||
interface AccountChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
@@ -21,7 +28,7 @@ interface AccountChartGenerator
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all(Collection $accounts, Carbon $start, Carbon $end);
|
||||
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
@@ -3,10 +3,8 @@
|
||||
namespace FireflyIII\Generator\Chart\Account;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Config;
|
||||
use FireflyIII\Models\Account;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
use Steam;
|
||||
|
||||
/**
|
||||
@@ -14,22 +12,54 @@ use Steam;
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Account
|
||||
*/
|
||||
class ChartJsAccountChartGenerator implements AccountChartGenerator
|
||||
class ChartJsAccountChartGenerator implements AccountChartGeneratorInterface
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all(Collection $accounts, Carbon $start, Carbon $end)
|
||||
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
return $this->frontpage($accounts, $start, $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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,13 +72,8 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
|
||||
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' => [],
|
||||
];
|
||||
$format = (string)trans('config.month_and_day');
|
||||
$data = ['count' => 0, 'labels' => [], 'datasets' => [],];
|
||||
$current = clone $start;
|
||||
while ($current <= $end) {
|
||||
$data['labels'][] = $current->formatLocalized($format);
|
||||
@@ -67,8 +92,14 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
|
||||
'data' => [],
|
||||
];
|
||||
$current = clone $start;
|
||||
$range = Steam::balanceInRange($account, $start, clone $end);
|
||||
$previous = round(array_values($range)[0], 2);
|
||||
while ($current <= $end) {
|
||||
$set['data'][] = Steam::balance($account, $current);
|
||||
$format = $current->format('Y-m-d');
|
||||
$balance = isset($range[$format]) ? round($range[$format], 2) : $previous;
|
||||
|
||||
$set['data'][] = $balance;
|
||||
$previous = $balance;
|
||||
$current->addDay();
|
||||
}
|
||||
$data['datasets'][] = $set;
|
||||
@@ -88,8 +119,7 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
|
||||
public function single(Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.monthAndDay.' . $language);
|
||||
$format = (string)trans('config.month_and_day');
|
||||
|
||||
$data = [
|
||||
'count' => 1,
|
||||
@@ -97,19 +127,55 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => $account->name,
|
||||
'data' => []
|
||||
]
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$range = Steam::balanceInRange($account, $start, $end);
|
||||
$current = clone $start;
|
||||
$previous = array_values($range)[0];
|
||||
|
||||
while ($end >= $current) {
|
||||
$theDate = $current->format('Y-m-d');
|
||||
$balance = isset($range[$theDate]) ? $range[$theDate] : $previous;
|
||||
|
||||
$data['labels'][] = $current->formatLocalized($format);
|
||||
$data['datasets'][0]['data'][] = Steam::balance($account, $current);
|
||||
$data['datasets'][0]['data'][] = $balance;
|
||||
$previous = $balance;
|
||||
$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);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $array
|
||||
* @param $entryId
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function isInArray($array, $entryId)
|
||||
{
|
||||
if (isset($array[$entryId])) {
|
||||
return $array[$entryId];
|
||||
}
|
||||
|
||||
return '0';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Account;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Account;
|
||||
use Grumpydictator\Gchart\GChart;
|
||||
use Illuminate\Support\Collection;
|
||||
use Steam;
|
||||
|
||||
|
||||
/**
|
||||
* Class GoogleAccountChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Account
|
||||
*/
|
||||
class GoogleAccountChartGenerator implements AccountChartGenerator
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
// make chart (original code):
|
||||
$chart = new GChart;
|
||||
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
|
||||
$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 $chart->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
return $this->all($accounts, $start, $end);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function single(Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
$current = clone $start;
|
||||
$today = new Carbon;
|
||||
$chart = new GChart;
|
||||
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
|
||||
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
|
||||
$chart->addCertainty(1);
|
||||
|
||||
while ($end >= $current) {
|
||||
$certain = $current < $today;
|
||||
$chart->addRow(clone $current, Steam::balance($account, $current), $certain);
|
||||
$current->addDay();
|
||||
}
|
||||
|
||||
|
||||
$chart->generate();
|
||||
|
||||
return $chart->getData();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,12 @@
|
||||
<?php
|
||||
/**
|
||||
* BillChartGeneratorInterface.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Bill;
|
||||
|
||||
|
||||
@@ -6,20 +14,20 @@ use FireflyIII\Models\Bill;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface BillChartGenerator
|
||||
* Interface BillChartGeneratorInterface
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Bill
|
||||
*/
|
||||
interface BillChartGenerator
|
||||
interface BillChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $paid
|
||||
* @param Collection $unpaid
|
||||
* @param string $paid
|
||||
* @param string $unpaid
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $paid, Collection $unpaid);
|
||||
public function frontpage($paid, $unpaid);
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
@@ -1,62 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* ChartJsBillChartGenerator.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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
|
||||
class ChartJsBillChartGenerator implements BillChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $paid
|
||||
* @param Collection $unpaid
|
||||
* @param string $paid
|
||||
* @param string $unpaid
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $paid, Collection $unpaid)
|
||||
public function frontpage($paid, $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);
|
||||
}
|
||||
/** @var Bill $entry */
|
||||
foreach ($unpaid as $entry) { // loop unpaid:
|
||||
$description = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')';
|
||||
$amount = ($entry[0]->amount_max + $entry[0]->amount_min) / 2;
|
||||
$unpaidDescriptions[] = $description;
|
||||
$unpaidAmount = bcadd($unpaidAmount, $amount);
|
||||
unset($amount, $description);
|
||||
}
|
||||
|
||||
$data = [
|
||||
[
|
||||
'value' => $unpaidAmount,
|
||||
'value' => round($unpaid, 2),
|
||||
'color' => 'rgba(53, 124, 165,0.7)',
|
||||
'highlight' => 'rgba(53, 124, 165,0.9)',
|
||||
'label' => trans('firefly.unpaid'),
|
||||
],
|
||||
[
|
||||
'value' => $paidAmount,
|
||||
'value' => round($paid * -1, 2), // paid is negative, must be positive.
|
||||
'color' => 'rgba(0, 141, 76, 0.7)',
|
||||
'highlight' => 'rgba(0, 141, 76, 0.9)',
|
||||
'label' => trans('firefly.paid'),
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
return $data;
|
||||
@@ -70,28 +56,24 @@ class ChartJsBillChartGenerator implements BillChartGenerator
|
||||
*/
|
||||
public function single(Bill $bill, Collection $entries)
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.month.' . $language);
|
||||
|
||||
$format = (string)trans('config.month');
|
||||
$data = [
|
||||
'count' => 3,
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
|
||||
// dataset: max amount
|
||||
// dataset: min amount
|
||||
// dataset: actual amount
|
||||
|
||||
$minAmount = [];
|
||||
$maxAmount = [];
|
||||
$actualAmount = [];
|
||||
/** @var TransactionJournal $entry */
|
||||
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, 2);
|
||||
/*
|
||||
* journalAmount has been collected in BillRepository::getJournals
|
||||
*/
|
||||
$actualAmount[] = round(($entry->journalAmount * -1), 2);
|
||||
}
|
||||
|
||||
$data['datasets'][] = [
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Bill;
|
||||
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Grumpydictator\Gchart\GChart;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class GoogleBillChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Bill
|
||||
*/
|
||||
class GoogleBillChartGenerator implements BillChartGenerator
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @param Collection $paid
|
||||
* @param Collection $unpaid
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $paid, Collection $unpaid)
|
||||
{
|
||||
// loop paid and create single entry:
|
||||
$paidDescriptions = [];
|
||||
$paidAmount = 0;
|
||||
$unpaidDescriptions = [];
|
||||
$unpaidAmount = 0;
|
||||
|
||||
bcscale(2);
|
||||
|
||||
|
||||
/** @var TransactionJournal $entry */
|
||||
foreach ($paid as $entry) {
|
||||
|
||||
$paidDescriptions[] = $entry->description;
|
||||
$paidAmount = bcadd($paidAmount, $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 = bcadd($unpaidAmount, $amount);
|
||||
unset($amount, $description);
|
||||
}
|
||||
|
||||
$chart = new GChart;
|
||||
$chart->addColumn(trans('firefly.name'), 'string');
|
||||
$chart->addColumn(trans('firefly.amount'), 'number');
|
||||
|
||||
$chart->addRow(trans('firefly.unpaid') . ': ' . join(', ', $unpaidDescriptions), $unpaidAmount);
|
||||
$chart->addRow(trans('firefly.paid') . ': ' . join(', ', $paidDescriptions), $paidAmount);
|
||||
|
||||
$chart->generate();
|
||||
|
||||
return $chart->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function single(Bill $bill, Collection $entries)
|
||||
{
|
||||
// make chart:
|
||||
$chart = new GChart;
|
||||
$chart->addColumn(trans('firefly.date'), 'date');
|
||||
$chart->addColumn(trans('firefly.maxAmount'), 'number');
|
||||
$chart->addColumn(trans('firefly.minAmount'), 'number');
|
||||
$chart->addColumn(trans('firefly.billEntry'), 'number');
|
||||
|
||||
/** @var TransactionJournal $result */
|
||||
foreach ($entries as $result) {
|
||||
$chart->addRow(
|
||||
clone $result->date,
|
||||
floatval($bill->amount_max),
|
||||
floatval($bill->amount_min),
|
||||
floatval($result->amount));
|
||||
}
|
||||
|
||||
$chart->generate();
|
||||
|
||||
return $chart->getData();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* BudgetChartGeneratorInterface.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Budget;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface BudgetChartGenerator
|
||||
* Interface BudgetChartGeneratorInterface
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Budget
|
||||
*/
|
||||
interface BudgetChartGenerator
|
||||
interface BudgetChartGeneratorInterface
|
||||
{
|
||||
/**
|
||||
* @param Collection $entries
|
||||
@@ -32,6 +39,13 @@ interface BudgetChartGenerator
|
||||
*/
|
||||
public function frontpage(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
* @param Collection $entries
|
||||
@@ -12,7 +12,7 @@ use Preferences;
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Budget
|
||||
*/
|
||||
class ChartJsBudgetChartGenerator implements BudgetChartGenerator
|
||||
class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
@@ -24,7 +24,7 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
|
||||
public function budget(Collection $entries, $dateFormat = 'month')
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$language = Preferences::get('language', env('DEFAULT_LANGUAGE', 'en_US'))->data;
|
||||
$format = Config::get('firefly.' . $dateFormat . '.' . $language);
|
||||
|
||||
$data = [
|
||||
@@ -33,7 +33,7 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
|
||||
[
|
||||
'label' => 'Amount',
|
||||
'data' => [],
|
||||
]
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -73,19 +73,19 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
|
||||
'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) {
|
||||
$filtered = $entries->filter(
|
||||
function ($entry) {
|
||||
return ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0);
|
||||
}
|
||||
);
|
||||
foreach ($filtered as $entry) {
|
||||
$data['labels'][] = $entry[0];
|
||||
$left[] = round($entry[1], 2);
|
||||
$spent[] = round($entry[2], 2);
|
||||
$overspent[] = round($entry[3], 2);
|
||||
}
|
||||
$spent[] = round($entry[2] * -1, 2); // spent is coming in negative, must be positive
|
||||
$overspent[] = round($entry[3] * -1, 2); // same
|
||||
}
|
||||
|
||||
$data['datasets'][] = [
|
||||
@@ -106,6 +106,40 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries)
|
||||
{
|
||||
// dataset:
|
||||
$data = [
|
||||
'count' => 0,
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
// get labels from one of the budgets (assuming there's at least one):
|
||||
$first = $entries->first();
|
||||
$keys = array_keys($first['budgeted']);
|
||||
foreach ($keys as $year) {
|
||||
$data['labels'][] = strval($year);
|
||||
}
|
||||
|
||||
// then, loop all entries and create datasets:
|
||||
foreach ($entries as $entry) {
|
||||
$name = $entry['name'];
|
||||
$spent = $entry['spent'];
|
||||
$budgeted = $entry['budgeted'];
|
||||
$data['datasets'][] = ['label' => 'Spent on ' . $name, 'data' => array_values($spent)];
|
||||
$data['datasets'][] = ['label' => 'Budgeted for ' . $name, 'data' => array_values($budgeted)];
|
||||
}
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
* @param Collection $entries
|
||||
@@ -115,8 +149,7 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
|
||||
public function year(Collection $budgets, Collection $entries)
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.month.' . $language);
|
||||
$format = (string)trans('config.month');
|
||||
|
||||
$data = [
|
||||
'labels' => [],
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Budget;
|
||||
|
||||
use Grumpydictator\Gchart\GChart;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class GoogleBudgetChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Budget
|
||||
*/
|
||||
class GoogleBudgetChartGenerator implements BudgetChartGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function budget(Collection $entries)
|
||||
{
|
||||
|
||||
$chart = new GChart;
|
||||
$chart->addColumn(trans('firefly.period'), 'date');
|
||||
$chart->addColumn(trans('firefly.spent'), 'number');
|
||||
|
||||
/** @var array $entry */
|
||||
foreach ($entries as $entry) {
|
||||
$chart->addRow($entry[0], $entry[1]);
|
||||
}
|
||||
|
||||
$chart->generate();
|
||||
|
||||
return $chart->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function budgetLimit(Collection $entries)
|
||||
{
|
||||
return $this->budget($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $entries)
|
||||
{
|
||||
$chart = new GChart;
|
||||
$chart->addColumn(trans('firefly.budget'), 'string');
|
||||
$chart->addColumn(trans('firefly.left'), 'number');
|
||||
$chart->addColumn(trans('firefly.spent'), 'number');
|
||||
$chart->addColumn(trans('firefly.overspent'), 'number');
|
||||
|
||||
/** @var array $entry */
|
||||
foreach ($entries as $entry) {
|
||||
if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) {
|
||||
$chart->addRow($entry[0], $entry[1], $entry[2], $entry[3]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$chart->generate();
|
||||
|
||||
return $chart->getData();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function year(Collection $budgets, Collection $entries)
|
||||
{
|
||||
$chart = new GChart;
|
||||
// add columns:
|
||||
$chart->addColumn(trans('firefly.month'), 'date');
|
||||
foreach ($budgets as $budget) {
|
||||
$chart->addColumn($budget->name, 'number');
|
||||
}
|
||||
|
||||
/** @var array $entry */
|
||||
foreach ($entries as $entry) {
|
||||
|
||||
$chart->addRowArray($entry);
|
||||
}
|
||||
|
||||
$chart->generate();
|
||||
|
||||
return $chart->getData();
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<?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 month(Collection $entries);
|
||||
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function year(Collection $categories, Collection $entries);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* CategoryChartGeneratorInterface.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Category;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface CategoryChartGeneratorInterface
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Category
|
||||
*/
|
||||
interface CategoryChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function earnedInPeriod(Collection $categories, Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function period(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function spentInPeriod(Collection $categories, Collection $entries);
|
||||
}
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Category;
|
||||
|
||||
use Config;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
|
||||
|
||||
/**
|
||||
@@ -12,41 +10,78 @@ use Preferences;
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Category
|
||||
*/
|
||||
class ChartJsCategoryChartGenerator implements CategoryChartGenerator
|
||||
class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
* @param string $dateFormat
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all(Collection $entries, $dateFormat = 'month')
|
||||
public function all(Collection $entries)
|
||||
{
|
||||
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.' . $dateFormat . '.' . $language);
|
||||
|
||||
$data = [
|
||||
'count' => 1,
|
||||
'count' => 2,
|
||||
'labels' => [],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => trans('firefly.spent'),
|
||||
'data' => []
|
||||
]
|
||||
'data' => [],
|
||||
],
|
||||
[
|
||||
'label' => trans('firefly.earned'),
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$data['labels'][] = $entry[0]->formatLocalized($format);
|
||||
$data['datasets'][0]['data'][] = round($entry[1], 2);
|
||||
$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 $categories
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function earnedInPeriod(Collection $categories, Collection $entries)
|
||||
{
|
||||
|
||||
// language:
|
||||
$format = (string)trans('config.month');
|
||||
|
||||
$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 $entries
|
||||
*
|
||||
@@ -60,20 +95,58 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => trans('firefly.spent'),
|
||||
'data' => []
|
||||
]
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
foreach ($entries as $entry) {
|
||||
if ($entry['sum'] != 0) {
|
||||
$data['labels'][] = $entry['name'];
|
||||
$data['datasets'][0]['data'][] = round($entry['sum'], 2);
|
||||
if ($entry->spent != 0) {
|
||||
$data['labels'][] = $entry->name;
|
||||
$data['datasets'][0]['data'][] = round(($entry->spent * -1), 2);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries)
|
||||
{
|
||||
// dataset:
|
||||
$data = [
|
||||
'count' => 0,
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
// get labels from one of the categories (assuming there's at least one):
|
||||
$first = $entries->first();
|
||||
$keys = array_keys($first['spent']);
|
||||
foreach ($keys as $year) {
|
||||
$data['labels'][] = strval($year);
|
||||
}
|
||||
|
||||
// then, loop all entries and create datasets:
|
||||
foreach ($entries as $entry) {
|
||||
$name = $entry['name'];
|
||||
$spent = $entry['spent'];
|
||||
$earned = $entry['earned'];
|
||||
if (array_sum(array_values($spent)) != 0) {
|
||||
$data['datasets'][] = ['label' => 'Spent in category ' . $name, 'data' => array_values($spent)];
|
||||
}
|
||||
if (array_sum(array_values($earned)) != 0) {
|
||||
$data['datasets'][] = ['label' => 'Earned in category ' . $name, 'data' => array_values($earned)];
|
||||
}
|
||||
}
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
@@ -81,9 +154,9 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function month(Collection $entries)
|
||||
public function period(Collection $entries)
|
||||
{
|
||||
return $this->all($entries, 'monthAndDay');
|
||||
return $this->all($entries);
|
||||
|
||||
}
|
||||
|
||||
@@ -93,12 +166,11 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function year(Collection $categories, Collection $entries)
|
||||
public function spentInPeriod(Collection $categories, Collection $entries)
|
||||
{
|
||||
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.month.' . $language);
|
||||
$format = (string)trans('config.month');
|
||||
|
||||
$data = [
|
||||
'count' => 0,
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Category;
|
||||
|
||||
use Grumpydictator\Gchart\GChart;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class GoogleCategoryChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Category
|
||||
*/
|
||||
class GoogleCategoryChartGenerator implements CategoryChartGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all(Collection $entries)
|
||||
{
|
||||
$chart = new GChart;
|
||||
$chart->addColumn(trans('firefly.period'), 'date');
|
||||
$chart->addColumn(trans('firefly.spent'), 'number');
|
||||
|
||||
/** @var array $entry */
|
||||
foreach ($entries as $entry) {
|
||||
$chart->addRow($entry[0], $entry[1]);
|
||||
}
|
||||
|
||||
$chart->generate();
|
||||
|
||||
return $chart->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $entries)
|
||||
{
|
||||
$chart = new GChart;
|
||||
$chart->addColumn(trans('firefly.category'), 'string');
|
||||
$chart->addColumn(trans('firefly.spent'), 'number');
|
||||
|
||||
|
||||
/** @var array $entry */
|
||||
foreach ($entries as $entry) {
|
||||
$sum = $entry['sum'];
|
||||
if ($sum != 0) {
|
||||
$chart->addRow($entry['name'], $sum);
|
||||
}
|
||||
}
|
||||
|
||||
$chart->generate();
|
||||
|
||||
return $chart->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function month(Collection $entries)
|
||||
{
|
||||
$chart = new GChart;
|
||||
|
||||
$chart->addColumn(trans('firefly.period'), 'date');
|
||||
$chart->addColumn(trans('firefly.spent'), 'number');
|
||||
|
||||
/** @var array $entry */
|
||||
foreach ($entries as $entry) {
|
||||
$chart->addRow($entry[0], $entry[1]);
|
||||
}
|
||||
$chart->generate();
|
||||
|
||||
return $chart->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function year(Collection $categories, Collection $entries)
|
||||
{
|
||||
$chart = new GChart;
|
||||
|
||||
$chart->addColumn(trans('firefly.month'), 'date');
|
||||
foreach ($categories as $category) {
|
||||
$chart->addColumn($category->name, 'number');
|
||||
}
|
||||
/** @var array $entry */
|
||||
foreach ($entries as $entry) {
|
||||
$chart->addRowArray($entry);
|
||||
}
|
||||
$chart->generate();
|
||||
|
||||
return $chart->getData();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,17 +3,15 @@
|
||||
namespace FireflyIII\Generator\Chart\PiggyBank;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Config;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
|
||||
|
||||
/**
|
||||
* Class GooglePiggyBankChartGenerator
|
||||
* Class ChartJsPiggyBankChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\PiggyBank
|
||||
*/
|
||||
class ChartJsPiggyBankChartGenerator implements PiggyBankChartGenerator
|
||||
class ChartJsPiggyBankChartGenerator implements PiggyBankChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
@@ -25,8 +23,7 @@ class ChartJsPiggyBankChartGenerator implements PiggyBankChartGenerator
|
||||
{
|
||||
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.month.' . $language);
|
||||
$format = (string)trans('config.month_and_day');
|
||||
|
||||
$data = [
|
||||
'count' => 1,
|
||||
@@ -34,8 +31,8 @@ class ChartJsPiggyBankChartGenerator implements PiggyBankChartGenerator
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => 'Diff',
|
||||
'data' => []
|
||||
]
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
$sum = '0';
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\PiggyBank;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Grumpydictator\Gchart\GChart;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
|
||||
/**
|
||||
* Class GooglePiggyBankChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\PiggyBank
|
||||
*/
|
||||
class GooglePiggyBankChartGenerator implements PiggyBankChartGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $set
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function history(Collection $set)
|
||||
{
|
||||
$chart = new GChart;
|
||||
$chart->addColumn(trans('firefly.date'), 'date');
|
||||
$chart->addColumn(trans('firefly.balance'), 'number');
|
||||
|
||||
$sum = '0';
|
||||
bcscale(2);
|
||||
|
||||
foreach ($set as $entry) {
|
||||
$sum = bcadd($sum, $entry->sum);
|
||||
$chart->addRow(new Carbon($entry->date), $sum);
|
||||
}
|
||||
|
||||
$chart->generate();
|
||||
|
||||
return $chart->getData();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* PiggyBankChartGenerator.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Generator\Chart\PiggyBank;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface PiggyBankChartGeneratorInterface
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\PiggyBank
|
||||
*/
|
||||
interface PiggyBankChartGeneratorInterface
|
||||
{
|
||||
/**
|
||||
* @param Collection $set
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function history(Collection $set);
|
||||
}
|
||||
@@ -2,18 +2,80 @@
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Report;
|
||||
|
||||
use Config;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
|
||||
/**
|
||||
* Class GoogleReportChartGenerator
|
||||
* Class ChartJsReportChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Report
|
||||
*/
|
||||
class ChartJsReportChartGenerator implements ReportChartGenerator
|
||||
class ChartJsReportChartGenerator implements ReportChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Same as above but other translations.
|
||||
*
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYearInOut(Collection $entries)
|
||||
{
|
||||
$data = [
|
||||
'count' => 2,
|
||||
'labels' => [],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => trans('firefly.income'),
|
||||
'data' => [],
|
||||
],
|
||||
[
|
||||
'label' => trans('firefly.expenses'),
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$data['labels'][] = $entry[0]->formatLocalized('%Y');
|
||||
$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 multiYearInOutSummarized($income, $expense, $count)
|
||||
{
|
||||
$data = [
|
||||
'count' => 2,
|
||||
'labels' => [trans('firefly.sum_of_years'), trans('firefly.average_of_years')],
|
||||
'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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
@@ -22,8 +84,7 @@ class ChartJsReportChartGenerator implements ReportChartGenerator
|
||||
public function yearInOut(Collection $entries)
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.month.' . $language);
|
||||
$format = (string)trans('config.month');
|
||||
|
||||
$data = [
|
||||
'count' => 2,
|
||||
@@ -31,12 +92,12 @@ class ChartJsReportChartGenerator implements ReportChartGenerator
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => trans('firefly.income'),
|
||||
'data' => []
|
||||
'data' => [],
|
||||
],
|
||||
[
|
||||
'label' => trans('firefly.expenses'),
|
||||
'data' => []
|
||||
]
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -61,16 +122,16 @@ class ChartJsReportChartGenerator implements ReportChartGenerator
|
||||
|
||||
$data = [
|
||||
'count' => 2,
|
||||
'labels' => [],
|
||||
'labels' => [trans('firefly.sum_of_year'), trans('firefly.average_of_year')],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => trans('firefly.income'),
|
||||
'data' => []
|
||||
'data' => [],
|
||||
],
|
||||
[
|
||||
'label' => trans('firefly.expenses'),
|
||||
'data' => []
|
||||
]
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
$data['datasets'][0]['data'][] = round($income, 2);
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Report;
|
||||
|
||||
use Grumpydictator\Gchart\GChart;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class GoogleReportChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Report
|
||||
*/
|
||||
class GoogleReportChartGenerator implements ReportChartGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function yearInOut(Collection $entries)
|
||||
{
|
||||
$chart = new GChart;
|
||||
$chart->addColumn(trans('firefly.month'), 'date');
|
||||
$chart->addColumn(trans('firefly.income'), 'number');
|
||||
$chart->addColumn(trans('firefly.expenses'), 'number');
|
||||
|
||||
/** @var array $entry */
|
||||
foreach ($entries as $entry) {
|
||||
$chart->addRowArray($entry);
|
||||
}
|
||||
$chart->generate();
|
||||
|
||||
return $chart->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $income
|
||||
* @param string $expense
|
||||
* @param int $count
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function yearInOutSummarized($income, $expense, $count)
|
||||
{
|
||||
$chart = new GChart;
|
||||
|
||||
$chart->addColumn(trans('firefly.summary'), 'string');
|
||||
$chart->addColumn(trans('firefly.income'), 'number');
|
||||
$chart->addColumn(trans('firefly.expenses'), 'number');
|
||||
$chart->addRow(trans('firefly.sum'), $income, $expense);
|
||||
$chart->addRow(trans('firefly.average'), ($income / $count), ($expense / $count));
|
||||
|
||||
$chart->generate();
|
||||
|
||||
return $chart->getData();
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<?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);
|
||||
|
||||
}
|
||||
54
app/Generator/Chart/Report/ReportChartGeneratorInterface.php
Normal file
54
app/Generator/Chart/Report/ReportChartGeneratorInterface.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* ReportChartGenerator.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Report;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface ReportChartGeneratorInterface
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Report
|
||||
*/
|
||||
interface ReportChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYearInOut(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param string $income
|
||||
* @param string $expense
|
||||
* @param int $count
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYearInOutSummarized($income, $expense, $count);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Events\JournalCreated;
|
||||
use FireflyIII\Events\TransactionJournalStored;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
@@ -26,15 +26,13 @@ class ConnectJournalToPiggyBank
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event when journal is saved.
|
||||
* Connect a new transaction journal to any related piggy banks.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
*
|
||||
* @param JournalCreated $event
|
||||
* @param TransactionJournalStored $event
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function handle(JournalCreated $event)
|
||||
public function handle(TransactionJournalStored $event)
|
||||
{
|
||||
/** @var TransactionJournal $journal */
|
||||
$journal = $event->journal;
|
||||
@@ -44,16 +42,16 @@ class ConnectJournalToPiggyBank
|
||||
$piggyBank = Auth::user()->piggybanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
|
||||
|
||||
if (is_null($piggyBank)) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
// update piggy bank rep for date of transaction journal.
|
||||
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
|
||||
if (is_null($repetition)) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bcscale(2);
|
||||
|
||||
$amount = $journal->actual_amount;
|
||||
$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;
|
||||
|
||||
77
app/Handlers/Events/FireRulesForStore.php
Normal file
77
app/Handlers/Events/FireRulesForStore.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* FireRulesForStore.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
|
||||
|
||||
use FireflyIII\Events\TransactionJournalStored;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Rules\Processor;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class FireRulesForStore
|
||||
*
|
||||
* @package FireflyIII\Handlers\Events
|
||||
*/
|
||||
class FireRulesForStore
|
||||
{
|
||||
/**
|
||||
* Create the event handler.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect a new transaction journal to any related piggy banks.
|
||||
*
|
||||
* @param TransactionJournalStored $event
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function handle(TransactionJournalStored $event)
|
||||
{
|
||||
// get all the user's rule groups, with the rules, order by 'order'.
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
$groups = $user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
|
||||
//
|
||||
/** @var RuleGroup $group */
|
||||
foreach ($groups as $group) {
|
||||
Log::debug('Now processing group "' . $group->title . '".');
|
||||
$rules = $group->rules()
|
||||
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
|
||||
->where('rule_triggers.trigger_type', 'user_action')
|
||||
->where('rule_triggers.trigger_value', 'store-journal')
|
||||
->where('rules.active', 1)
|
||||
->get(['rules.*']);
|
||||
/** @var Rule $rule */
|
||||
foreach ($rules as $rule) {
|
||||
Log::debug('Now handling rule #' . $rule->id . ' (' . $rule->title . ')');
|
||||
$processor = new Processor($rule, $event->journal);
|
||||
|
||||
// get some return out of this?
|
||||
$processor->handle();
|
||||
|
||||
if ($rule->stop_processing) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
app/Handlers/Events/FireRulesForUpdate.php
Normal file
74
app/Handlers/Events/FireRulesForUpdate.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* FireRulesForUpdate.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Events\TransactionJournalUpdated;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Rules\Processor;
|
||||
use FireflyIII\User;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class FireRulesForUpdate
|
||||
*
|
||||
* @package FireflyIII\Handlers\Events
|
||||
*/
|
||||
class FireRulesForUpdate
|
||||
{
|
||||
/**
|
||||
* Create the event handler.
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param TransactionJournalUpdated $event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(TransactionJournalUpdated $event)
|
||||
{
|
||||
// get all the user's rule groups, with the rules, order by 'order'.
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
$groups = $user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
|
||||
//
|
||||
/** @var RuleGroup $group */
|
||||
foreach ($groups as $group) {
|
||||
Log::debug('Now processing group "' . $group->title . '".');
|
||||
$rules = $group->rules()
|
||||
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
|
||||
->where('rule_triggers.trigger_type', 'user_action')
|
||||
->where('rule_triggers.trigger_value', 'update-journal')
|
||||
->where('rules.active', 1)
|
||||
->get(['rules.*']);
|
||||
/** @var Rule $rule */
|
||||
foreach ($rules as $rule) {
|
||||
Log::debug('Now handling rule #' . $rule->id . ' (' . $rule->title . ')');
|
||||
$processor = new Processor($rule, $event->journal);
|
||||
|
||||
// get some return out of this?
|
||||
$processor->handle();
|
||||
|
||||
if ($rule->stop_processing) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
<?php namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use App;
|
||||
use FireflyIII\Events\JournalSaved;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class RescanJournal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @package FireflyIII\Handlers\Events
|
||||
*/
|
||||
class RescanJournal
|
||||
{
|
||||
|
||||
/**
|
||||
* Create the event handler.
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param JournalSaved $event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(JournalSaved $event)
|
||||
{
|
||||
$journal = $event->journal;
|
||||
|
||||
Log::debug('Triggered saved event for journal #' . $journal->id . ' (' . $journal->description . ')');
|
||||
|
||||
/** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */
|
||||
$repository = App::make('FireflyIII\Repositories\Bill\BillRepositoryInterface');
|
||||
$list = $journal->user->bills()->where('active', 1)->where('automatch', 1)->get();
|
||||
|
||||
Log::debug('Found ' . $list->count() . ' bills to check.');
|
||||
|
||||
/** @var \FireflyIII\Models\Bill $bill */
|
||||
foreach ($list as $bill) {
|
||||
Log::debug('Now calling bill #' . $bill->id . ' (' . $bill->name . ')');
|
||||
$repository->scan($bill, $journal);
|
||||
}
|
||||
|
||||
Log::debug('Done!');
|
||||
}
|
||||
|
||||
}
|
||||
47
app/Handlers/Events/ScanForBillsAfterStore.php
Normal file
47
app/Handlers/Events/ScanForBillsAfterStore.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* RescanJournalAfterStore.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use FireflyIII\Events\TransactionJournalStored;
|
||||
use FireflyIII\Support\Events\BillScanner;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class RescanJournal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @package FireflyIII\Handlers\Events
|
||||
*/
|
||||
class ScanForBillsAfterStore
|
||||
{
|
||||
|
||||
/**
|
||||
* Create the event handler.
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan a transaction journal for possible links to bills, right after storing.
|
||||
*
|
||||
* @param TransactionJournalStored $event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(TransactionJournalStored $event)
|
||||
{
|
||||
$journal = $event->journal;
|
||||
BillScanner::scan($journal);
|
||||
}
|
||||
|
||||
}
|
||||
47
app/Handlers/Events/ScanForBillsAfterUpdate.php
Normal file
47
app/Handlers/Events/ScanForBillsAfterUpdate.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* ScanForBillsAfterUpdate.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use FireflyIII\Events\TransactionJournalUpdated;
|
||||
use FireflyIII\Support\Events\BillScanner;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class RescanJournal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @package FireflyIII\Handlers\Events
|
||||
*/
|
||||
class ScanForBillsAfterUpdate
|
||||
{
|
||||
|
||||
/**
|
||||
* Create the event handler.
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan a transaction journal for possibly related bills after it has been updated.
|
||||
*
|
||||
* @param TransactionJournalUpdated $event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(TransactionJournalUpdated $event)
|
||||
{
|
||||
$journal = $event->journal;
|
||||
BillScanner::scan($journal);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use FireflyIII\Events\JournalSaved;
|
||||
use FireflyIII\Events\TransactionJournalUpdated;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Models\PiggyBankRepetition;
|
||||
|
||||
@@ -25,11 +25,11 @@ class UpdateJournalConnection
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param JournalSaved $event
|
||||
* @param TransactionJournalUpdated $event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(JournalSaved $event)
|
||||
public function handle(TransactionJournalUpdated $event)
|
||||
{
|
||||
$journal = $event->journal;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
|
||||
224
app/Helpers/Attachments/AttachmentHelper.php
Normal file
224
app/Helpers/Attachments/AttachmentHelper.php
Normal file
@@ -0,0 +1,224 @@
|
||||
<?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 MessageBag */
|
||||
public $errors;
|
||||
/** @var MessageBag */
|
||||
public $messages;
|
||||
/** @var array */
|
||||
protected $allowedMimes;
|
||||
/** @var int */
|
||||
protected $maxUploadSize;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MessageBag
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MessageBag
|
||||
*/
|
||||
public function getMessages()
|
||||
{
|
||||
return $this->messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
if (!is_null($files)) {
|
||||
$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 = (string)trans('validation.file_already_attached', ['name' => $name]);
|
||||
$this->errors->add('attachments', $msg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 = (string)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 = (string)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 = (string)trans('validation.file_too_large', ['name' => $name]);
|
||||
$this->errors->add('attachments', $msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
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 Attachment $attachment
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAttachmentLocation(Attachment $attachment);
|
||||
|
||||
/**
|
||||
* @return MessageBag
|
||||
*/
|
||||
public function getErrors();
|
||||
|
||||
/**
|
||||
* @return MessageBag
|
||||
*/
|
||||
public function getMessages();
|
||||
|
||||
/**
|
||||
* @param Model $model
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveAttachmentsForModel(Model $model);
|
||||
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace FireflyIII\Helpers\Collection;
|
||||
|
||||
use FireflyIII\Models\Budget as BudgetModel;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@@ -26,9 +25,6 @@ class BalanceLine
|
||||
/** @var BudgetModel */
|
||||
protected $budget;
|
||||
|
||||
/** @var LimitRepetition */
|
||||
protected $repetition;
|
||||
|
||||
protected $role = self::ROLE_DEFAULTROLE;
|
||||
|
||||
/**
|
||||
@@ -48,24 +44,19 @@ class BalanceLine
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @return Collection
|
||||
*/
|
||||
public function getTitle()
|
||||
public function getBalanceEntries()
|
||||
{
|
||||
if ($this->getBudget() instanceof BudgetModel) {
|
||||
return $this->getBudget()->name;
|
||||
}
|
||||
if ($this->getRole() == self::ROLE_DEFAULTROLE) {
|
||||
return trans('firefly.noBudget');
|
||||
}
|
||||
if ($this->getRole() == self::ROLE_TAGROLE) {
|
||||
return trans('firefly.coveredWithTags');
|
||||
}
|
||||
if ($this->getRole() == self::ROLE_DIFFROLE) {
|
||||
return trans('firefly.leftUnbalanced');
|
||||
return $this->balanceEntries;
|
||||
}
|
||||
|
||||
return '';
|
||||
/**
|
||||
* @param Collection $balanceEntries
|
||||
*/
|
||||
public function setBalanceEntries($balanceEntries)
|
||||
{
|
||||
$this->balanceEntries = $balanceEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,6 +91,27 @@ class BalanceLine
|
||||
$this->role = $role;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
if ($this->getBudget() instanceof BudgetModel) {
|
||||
return $this->getBudget()->name;
|
||||
}
|
||||
if ($this->getRole() == self::ROLE_DEFAULTROLE) {
|
||||
return trans('firefly.noBudget');
|
||||
}
|
||||
if ($this->getRole() == self::ROLE_TAGROLE) {
|
||||
return trans('firefly.coveredWithTags');
|
||||
}
|
||||
if ($this->getRole() == self::ROLE_DIFFROLE) {
|
||||
return trans('firefly.leftUnbalanced');
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* If a BalanceLine has a budget/repetition, each BalanceEntry in this BalanceLine
|
||||
* should have a "spent" value, which is the amount of money that has been spent
|
||||
@@ -110,7 +122,7 @@ class BalanceLine
|
||||
*/
|
||||
public function leftOfRepetition()
|
||||
{
|
||||
$start = $this->getRepetition() ? $this->getRepetition()->amount : 0;
|
||||
$start = isset($this->budget->amount) ? $this->budget->amount : 0;
|
||||
/** @var BalanceEntry $balanceEntry */
|
||||
foreach ($this->getBalanceEntries() as $balanceEntry) {
|
||||
$start += $balanceEntry->getSpent();
|
||||
@@ -118,56 +130,4 @@ class BalanceLine
|
||||
|
||||
return $start;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LimitRepetition
|
||||
*/
|
||||
public function getRepetition()
|
||||
{
|
||||
return $this->repetition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LimitRepetition $repetition
|
||||
*/
|
||||
public function setRepetition($repetition)
|
||||
{
|
||||
$this->repetition = $repetition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getBalanceEntries()
|
||||
{
|
||||
return $this->balanceEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $balanceEntries
|
||||
*/
|
||||
public function setBalanceEntries($balanceEntries)
|
||||
{
|
||||
$this->balanceEntries = $balanceEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the BalanceEntries for a BalanceLine have a "left" value, the amount
|
||||
* of money left in the entire BalanceLine is returned here:
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function sumOfLeft()
|
||||
{
|
||||
$sum = '0';
|
||||
bcscale(2);
|
||||
/** @var BalanceEntry $balanceEntry */
|
||||
foreach ($this->getBalanceEntries() as $balanceEntry) {
|
||||
$sum = bcadd($sum, $balanceEntry->getLeft());
|
||||
}
|
||||
|
||||
return $sum;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -34,8 +34,10 @@ 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);
|
||||
$this->addTotal($category->spent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +56,7 @@ class Category
|
||||
*/
|
||||
public function getCategories()
|
||||
{
|
||||
$set = $this->categories->sortByDesc(
|
||||
$set = $this->categories->sortBy(
|
||||
function (CategoryModel $category) {
|
||||
return $category->spent;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace FireflyIII\Helpers\Collection;
|
||||
|
||||
use Crypt;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Support\Collection;
|
||||
use stdClass;
|
||||
@@ -33,19 +34,24 @@ class Expense
|
||||
*/
|
||||
public function addOrCreateExpense(TransactionJournal $entry)
|
||||
{
|
||||
bcscale(2);
|
||||
|
||||
$accountId = $entry->account_id;
|
||||
$amount = strval(round($entry->journalAmount, 2));
|
||||
if (bccomp('0', $amount) === -1) {
|
||||
$amount = bcmul($amount, '-1');
|
||||
}
|
||||
|
||||
if (!$this->expenses->has($accountId)) {
|
||||
$newObject = new stdClass;
|
||||
$newObject->amount = strval(round($entry->amount, 2));
|
||||
$newObject->name = $entry->name;
|
||||
$newObject->amount = $amount;
|
||||
$newObject->name = Crypt::decrypt($entry->account_name);
|
||||
$newObject->count = 1;
|
||||
$newObject->id = $accountId;
|
||||
$this->expenses->put($accountId, $newObject);
|
||||
} else {
|
||||
bcscale(2);
|
||||
$existing = $this->expenses->get($accountId);
|
||||
$existing->amount = bcadd($existing->amount, $entry->amount);
|
||||
$existing->amount = bcadd($existing->amount, $amount);
|
||||
$existing->count++;
|
||||
$this->expenses->put($accountId, $existing);
|
||||
}
|
||||
@@ -56,8 +62,18 @@ class Expense
|
||||
*/
|
||||
public function addToTotal($add)
|
||||
{
|
||||
$add = strval(round($add, 2));
|
||||
bcscale(2);
|
||||
|
||||
|
||||
$add = strval(round($add, 2));
|
||||
if (bccomp('0', $add) === -1) {
|
||||
$add = bcmul($add, '-1');
|
||||
}
|
||||
|
||||
// if amount is positive, the original transaction
|
||||
// was a transfer. But since this is an expense report,
|
||||
// that amount must be negative.
|
||||
|
||||
$this->total = bcadd($this->total, $add);
|
||||
}
|
||||
|
||||
@@ -66,7 +82,7 @@ class Expense
|
||||
*/
|
||||
public function getExpenses()
|
||||
{
|
||||
$set = $this->expenses->sortByDesc(
|
||||
$set = $this->expenses->sortBy(
|
||||
function (stdClass $object) {
|
||||
return $object->amount;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace FireflyIII\Helpers\Collection;
|
||||
|
||||
use Crypt;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Support\Collection;
|
||||
use stdClass;
|
||||
@@ -38,15 +39,15 @@ class Income
|
||||
$accountId = $entry->account_id;
|
||||
if (!$this->incomes->has($accountId)) {
|
||||
$newObject = new stdClass;
|
||||
$newObject->amount = strval(round($entry->amount, 2));
|
||||
$newObject->name = $entry->name;
|
||||
$newObject->amount = strval(round($entry->journalAmount, 2));
|
||||
$newObject->name = Crypt::decrypt($entry->account_name);
|
||||
$newObject->count = 1;
|
||||
$newObject->id = $accountId;
|
||||
$this->incomes->put($accountId, $newObject);
|
||||
} else {
|
||||
bcscale(2);
|
||||
$existing = $this->incomes->get($accountId);
|
||||
$existing->amount = bcadd($existing->amount, $entry->amount);
|
||||
$existing->amount = bcadd($existing->amount, $entry->journalAmount);
|
||||
$existing->count++;
|
||||
$this->incomes->put($accountId, $existing);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Account;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class AccountId
|
||||
@@ -19,9 +20,17 @@ class AccountId extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
// 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;
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
|
||||
/**
|
||||
* Class Amount
|
||||
*
|
||||
@@ -13,7 +11,7 @@ class Amount extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
* @return string|int
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
|
||||
28
app/Helpers/Csv/Converter/AmountComma.php
Normal file
28
app/Helpers/Csv/Converter/AmountComma.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
/**
|
||||
* Class AmountComma
|
||||
*
|
||||
* Parses the input as the amount with a comma as decimal separator
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
class AmountComma extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return float|int
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
$value = str_replace(',', '.', $this->value);
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return floatval($value);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,6 @@ class BasicConverter
|
||||
/** @var array */
|
||||
protected $mapped;
|
||||
/** @var string */
|
||||
protected $role;
|
||||
/** @var string */
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
@@ -86,22 +84,6 @@ class BasicConverter
|
||||
$this->mapped = $mapped;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRole()
|
||||
{
|
||||
return $this->role;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $role
|
||||
*/
|
||||
public function setRole($role)
|
||||
{
|
||||
$this->role = $role;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,7 @@ use Auth;
|
||||
use FireflyIII\Models\Budget;
|
||||
|
||||
/**
|
||||
* Class AccountId
|
||||
* Class BudgetId
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
|
||||
@@ -24,7 +24,7 @@ class CategoryName extends BasicConverter implements ConverterInterface
|
||||
$category = Category::firstOrCreateEncrypted(
|
||||
[
|
||||
'name' => $this->value,
|
||||
'user_id' => Auth::user()->id
|
||||
'user_id' => Auth::user()->id,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,11 +36,6 @@ interface ConverterInterface
|
||||
*/
|
||||
public function setMapped($mapped);
|
||||
|
||||
/**
|
||||
* @param string $role
|
||||
*/
|
||||
public function setRole($role);
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,7 @@ class CurrencyCode extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return mixed|static
|
||||
* @return TransactionCurrency
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ class CurrencyId extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return mixed|static
|
||||
* @return TransactionCurrency
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ class CurrencyName extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return mixed|static
|
||||
* @return TransactionCurrency
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ class CurrencySymbol extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return mixed|static
|
||||
* @return TransactionCurrency
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use InvalidArgumentException;
|
||||
use Log;
|
||||
use Session;
|
||||
|
||||
/**
|
||||
@@ -15,12 +18,21 @@ class Date extends BasicConverter implements ConverterInterface
|
||||
|
||||
/**
|
||||
* @return Carbon
|
||||
* @throws FireflyException
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class Description extends BasicConverter implements ConverterInterface
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* @return string
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
|
||||
/**
|
||||
* Class Amount
|
||||
*
|
||||
@@ -13,7 +11,7 @@ class Ignore extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
* @return null
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
|
||||
@@ -46,7 +46,7 @@ class OpposingAccountIban extends BasicConverter implements ConverterInterface
|
||||
/** @var Account $account */
|
||||
foreach ($set as $account) {
|
||||
if ($account->iban == $this->value) {
|
||||
Log::debug('OpposingAccountIban::convert found an Account (#' . $account->id . ': ' . $account->name . ') with IBAN ' . $this->value);
|
||||
Log::debug('OpposingAccountIban::convert found an Account (#' . $account->id . ': ******) with IBAN ******');
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use Auth;
|
||||
use FireflyIII\Models\Account;
|
||||
|
||||
/**
|
||||
* Class OpposingName
|
||||
* Class OpposingAccountId
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
|
||||
@@ -6,7 +6,7 @@ use Auth;
|
||||
use FireflyIII\Models\Account;
|
||||
|
||||
/**
|
||||
* Class OpposingName
|
||||
* Class OpposingAccountName
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Converter
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,7 @@ class RabobankDebetCredit extends BasicConverter implements ConverterInterface
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* @return int
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Tag;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
@@ -16,7 +15,7 @@ class TagsComma extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Bill
|
||||
* @return Collection
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace FireflyIII\Helpers\Csv\Converter;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Tag;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
@@ -16,7 +15,7 @@ class TagsSpace extends BasicConverter implements ConverterInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Bill
|
||||
* @return Collection
|
||||
*/
|
||||
public function convert()
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv;
|
||||
|
||||
use Crypt;
|
||||
@@ -16,28 +15,28 @@ class Data
|
||||
|
||||
/** @var string */
|
||||
protected $csvFileContent;
|
||||
|
||||
/** @var string */
|
||||
protected $csvFileLocation;
|
||||
/** @var string */
|
||||
protected $dateFormat;
|
||||
/** @var string */
|
||||
protected $delimiter;
|
||||
/** @var bool */
|
||||
protected $hasHeaders;
|
||||
|
||||
/** @var int */
|
||||
protected $importAccount = 0;
|
||||
/** @var array */
|
||||
protected $map;
|
||||
protected $map = [];
|
||||
/** @var array */
|
||||
protected $mapped;
|
||||
protected $mapped = [];
|
||||
/** @var Reader */
|
||||
protected $reader;
|
||||
/** @var array */
|
||||
protected $roles;
|
||||
|
||||
protected $roles = [];
|
||||
/** @var array */
|
||||
protected $specifix;
|
||||
protected $specifix = [];
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
@@ -48,58 +47,49 @@ class Data
|
||||
$this->sessionRoles();
|
||||
$this->sessionMapped();
|
||||
$this->sessionSpecifix();
|
||||
}
|
||||
|
||||
protected function sessionHasHeaders()
|
||||
{
|
||||
if (Session::has('csv-has-headers')) {
|
||||
$this->hasHeaders = (bool)Session::get('csv-has-headers');
|
||||
}
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
$this->sessionImportAccount();
|
||||
$this->sessionDelimiter();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCsvFileContent()
|
||||
{
|
||||
return $this->csvFileContent;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $csvFileContent
|
||||
*/
|
||||
public function setCsvFileContent($csvFileContent)
|
||||
{
|
||||
$this->csvFileContent = $csvFileContent;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @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 getDateFormat()
|
||||
@@ -108,6 +98,7 @@ class Data
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mixed $dateFormat
|
||||
*/
|
||||
public function setDateFormat($dateFormat)
|
||||
@@ -117,23 +108,26 @@ class Data
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function hasHeaders()
|
||||
public function getDelimiter()
|
||||
{
|
||||
return $this->hasHeaders;
|
||||
return $this->delimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $hasHeaders
|
||||
*
|
||||
* @param string $delimiter
|
||||
*/
|
||||
public function setHasHeaders($hasHeaders)
|
||||
public function setDelimiter($delimiter)
|
||||
{
|
||||
Session::put('csv-has-headers', $hasHeaders);
|
||||
$this->hasHeaders = $hasHeaders;
|
||||
Session::put('csv-delimiter', $delimiter);
|
||||
$this->delimiter = $delimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMap()
|
||||
@@ -142,6 +136,7 @@ class Data
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $map
|
||||
*/
|
||||
public function setMap(array $map)
|
||||
@@ -151,6 +146,7 @@ class Data
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMapped()
|
||||
@@ -159,6 +155,7 @@ class Data
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $mapped
|
||||
*/
|
||||
public function setMapped(array $mapped)
|
||||
@@ -168,22 +165,90 @@ class Data
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Reader
|
||||
*/
|
||||
public function getReader()
|
||||
{
|
||||
|
||||
if (strlen($this->csvFileContent) === 0) {
|
||||
$this->loadCsvFile();
|
||||
}
|
||||
|
||||
if (is_null($this->reader)) {
|
||||
$this->reader = Reader::createFromString($this->getCsvFileContent());
|
||||
$this->reader->setDelimiter($this->delimiter);
|
||||
}
|
||||
|
||||
return $this->reader;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @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(array $specifix)
|
||||
{
|
||||
Session::put('csv-specifix', $specifix);
|
||||
$this->specifix = $specifix;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasHeaders()
|
||||
{
|
||||
return $this->hasHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bool $hasHeaders
|
||||
*/
|
||||
public function setHasHeaders($hasHeaders)
|
||||
{
|
||||
Session::put('csv-has-headers', $hasHeaders);
|
||||
$this->hasHeaders = $hasHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int $importAccount
|
||||
*/
|
||||
public function setImportAccount($importAccount)
|
||||
{
|
||||
Session::put('csv-import-account', $importAccount);
|
||||
$this->importAccount = $importAccount;
|
||||
}
|
||||
|
||||
protected function loadCsvFile()
|
||||
{
|
||||
$file = $this->getCsvFileLocation();
|
||||
@@ -192,72 +257,66 @@ class Data
|
||||
$this->setCsvFileContent($contentDecrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCsvFileLocation()
|
||||
protected function sessionCsvFileLocation()
|
||||
{
|
||||
return $this->csvFileLocation;
|
||||
if (Session::has('csv-file')) {
|
||||
$this->csvFileLocation = (string)Session::get('csv-file');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $csvFileLocation
|
||||
*/
|
||||
public function setCsvFileLocation($csvFileLocation)
|
||||
protected function sessionDateFormat()
|
||||
{
|
||||
Session::put('csv-file', $csvFileLocation);
|
||||
$this->csvFileLocation = $csvFileLocation;
|
||||
if (Session::has('csv-date-format')) {
|
||||
$this->dateFormat = (string)Session::get('csv-date-format');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCsvFileContent()
|
||||
protected function sessionDelimiter()
|
||||
{
|
||||
return $this->csvFileContent;
|
||||
if (Session::has('csv-delimiter')) {
|
||||
$this->delimiter = Session::get('csv-delimiter');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $csvFileContent
|
||||
*/
|
||||
public function setCsvFileContent($csvFileContent)
|
||||
protected function sessionHasHeaders()
|
||||
{
|
||||
$this->csvFileContent = $csvFileContent;
|
||||
if (Session::has('csv-has-headers')) {
|
||||
$this->hasHeaders = (bool)Session::get('csv-has-headers');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRoles()
|
||||
protected function sessionImportAccount()
|
||||
{
|
||||
return $this->roles;
|
||||
if (Session::has('csv-import-account')) {
|
||||
$this->importAccount = intval(Session::get('csv-import-account'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $roles
|
||||
*/
|
||||
public function setRoles(array $roles)
|
||||
protected function sessionMap()
|
||||
{
|
||||
Session::put('csv-roles', $roles);
|
||||
$this->roles = $roles;
|
||||
if (Session::has('csv-map')) {
|
||||
$this->map = (array)Session::get('csv-map');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSpecifix()
|
||||
protected function sessionMapped()
|
||||
{
|
||||
return $this->specifix;
|
||||
if (Session::has('csv-mapped')) {
|
||||
$this->mapped = (array)Session::get('csv-mapped');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $specifix
|
||||
*/
|
||||
public function setSpecifix($specifix)
|
||||
protected function sessionRoles()
|
||||
{
|
||||
Session::put('csv-specifix', $specifix);
|
||||
$this->specifix = $specifix;
|
||||
if (Session::has('csv-roles')) {
|
||||
$this->roles = (array)Session::get('csv-roles');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function sessionSpecifix()
|
||||
{
|
||||
if (Session::has('csv-specifix')) {
|
||||
$this->specifix = (array)Session::get('csv-specifix');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,16 +2,22 @@
|
||||
|
||||
namespace FireflyIII\Helpers\Csv;
|
||||
|
||||
use App;
|
||||
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\Rule;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Rules\Processor;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\MessageBag;
|
||||
use Log;
|
||||
|
||||
@@ -33,6 +39,8 @@ class Importer
|
||||
protected $importRow;
|
||||
/** @var int */
|
||||
protected $imported = 0;
|
||||
/** @var Collection */
|
||||
protected $journals;
|
||||
/** @var array */
|
||||
protected $map;
|
||||
/** @var array */
|
||||
@@ -42,7 +50,7 @@ class Importer
|
||||
/** @var int */
|
||||
protected $rows = 0;
|
||||
/** @var array */
|
||||
protected $specifix;
|
||||
protected $specifix = [];
|
||||
|
||||
/**
|
||||
* Used by CsvController.
|
||||
@@ -64,6 +72,14 @@ class Importer
|
||||
return $this->imported;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getJournals()
|
||||
{
|
||||
return $this->journals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by CsvController
|
||||
*
|
||||
@@ -74,6 +90,14 @@ class Importer
|
||||
return $this->rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSpecifix()
|
||||
{
|
||||
return is_array($this->specifix) ? $this->specifix : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
@@ -81,6 +105,7 @@ class Importer
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
||||
$this->journals = new Collection;
|
||||
$this->map = $this->data->getMap();
|
||||
$this->roles = $this->data->getRoles();
|
||||
$this->mapped = $this->data->getMapped();
|
||||
@@ -88,143 +113,32 @@ class Importer
|
||||
|
||||
foreach ($this->data->getReader() as $index => $row) {
|
||||
if ($this->parseRow($index)) {
|
||||
Log::debug('--- Importing row ' . $index);
|
||||
$this->rows++;
|
||||
$result = $this->importRow($row);
|
||||
if (!($result === true)) {
|
||||
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');
|
||||
|
||||
/** @var ConverterInterface $converter */
|
||||
$converter = App::make('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();
|
||||
if ($journal instanceof TransactionJournal) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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['amount-modifier'] = '1';
|
||||
|
||||
return $filler;
|
||||
// once all journals have been imported (or not)
|
||||
// fire the rules.
|
||||
$this->fireRules();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Row denotes the original data.
|
||||
*
|
||||
* @return void
|
||||
* @param Data $data
|
||||
*/
|
||||
protected function postProcess()
|
||||
public function setData($data)
|
||||
{
|
||||
// do bank specific fixes (must be enabled but now all of them.
|
||||
|
||||
foreach ($this->getSpecifix() as $className) {
|
||||
/** @var SpecifixInterface $specifix */
|
||||
$specifix = App::make('FireflyIII\Helpers\Csv\Specifix\\' . $className);
|
||||
$specifix->setData($this->importData);
|
||||
$specifix->setRow($this->importRow);
|
||||
$this->importData = $specifix->fix();
|
||||
}
|
||||
|
||||
|
||||
$set = Config::get('csv.post_processors');
|
||||
foreach ($set as $className) {
|
||||
/** @var PostProcessorInterface $postProcessor */
|
||||
$postProcessor = App::make('FireflyIII\Helpers\Csv\PostProcessing\\' . $className);
|
||||
$postProcessor->setData($this->importData);
|
||||
$this->importData = $postProcessor->process();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSpecifix()
|
||||
{
|
||||
return $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';
|
||||
}
|
||||
|
||||
return true;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -238,6 +152,8 @@ class Importer
|
||||
if (is_null($this->importData['date'])) {
|
||||
$date = $this->importData['date-rent'];
|
||||
}
|
||||
|
||||
|
||||
$transactionType = $this->getTransactionType(); // defaults to deposit
|
||||
$errors = new MessageBag;
|
||||
$journal = TransactionJournal::create(
|
||||
@@ -245,10 +161,13 @@ class Importer
|
||||
'description' => $this->importData['description'], 'completed' => 0, 'date' => $date, 'bill_id' => $this->importData['bill-id'],]
|
||||
);
|
||||
if ($journal->getErrors()->count() == 0) {
|
||||
$accountId = $this->importData['asset-account']->id; // create first transaction:
|
||||
// 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]);
|
||||
@@ -266,6 +185,18 @@ class Importer
|
||||
$this->saveCategory($journal);
|
||||
$this->saveTags($journal);
|
||||
|
||||
// some debug info:
|
||||
$journalId = $journal->id;
|
||||
$type = $journal->getTransactionType();
|
||||
/** @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;
|
||||
}
|
||||
|
||||
@@ -274,18 +205,104 @@ class Importer
|
||||
*/
|
||||
protected function getTransactionType()
|
||||
{
|
||||
$transactionType = TransactionType::where('type', 'Deposit')->first();
|
||||
$transactionType = TransactionType::where('type', TransactionType::DEPOSIT)->first();
|
||||
if ($this->importData['amount'] < 0) {
|
||||
$transactionType = TransactionType::where('type', 'Withdrawal')->first();
|
||||
$transactionType = TransactionType::where('type', TransactionType::WITHDRAWAL)->first();
|
||||
}
|
||||
|
||||
if (in_array($this->importData['opposing-account-object']->accountType->type, ['Asset account', 'Default account'])) {
|
||||
$transactionType = TransactionType::where('type', 'Transfer')->first();
|
||||
$transactionType = TransactionType::where('type', TransactionType::TRANSFER)->first();
|
||||
}
|
||||
|
||||
return $transactionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 . ':');
|
||||
|
||||
// here would be the place where preprocessors would fire.
|
||||
|
||||
/** @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);
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $index
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function parseRow($index)
|
||||
{
|
||||
return (($this->data->hasHeaders() && $index >= 1) || !$this->data->hasHeaders());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
if ($specifix->getProcessorType() == SpecifixInterface::POST_PROCESSOR) {
|
||||
$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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*/
|
||||
@@ -321,11 +338,94 @@ class Importer
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Data $data
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
public function setData($data)
|
||||
protected function validateData()
|
||||
{
|
||||
$this->data = $data;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $groups
|
||||
* @param TransactionJournal $journal
|
||||
*/
|
||||
private function fireRule(Collection $groups, TransactionJournal $journal)
|
||||
{
|
||||
/** @var RuleGroup $group */
|
||||
foreach ($groups as $group) {
|
||||
|
||||
/** @var Rule $rule */
|
||||
foreach ($group->rules as $rule) {
|
||||
$processor = new Processor($rule, $journal);
|
||||
$processor->handle();
|
||||
if ($rule->stop_processing) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function fireRules()
|
||||
{
|
||||
// get all users rules.
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
$groups = $user
|
||||
->ruleGroups()
|
||||
->where('rule_groups.active', 1)
|
||||
->orderBy('order', 'ASC')
|
||||
->with(
|
||||
[
|
||||
'rules' => function (HasMany $q) {
|
||||
$q->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
|
||||
->where('rule_triggers.trigger_type', 'user_action')
|
||||
->where('rule_triggers.trigger_value', 'store-journal')
|
||||
->where('rules.active', 1)
|
||||
->orderBy('rules.order', 'ASC');
|
||||
},
|
||||
]
|
||||
)
|
||||
->get();
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($this->journals as $journal) {
|
||||
$this->fireRule($groups, $journal);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private 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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,7 +27,7 @@ class AnyAccount implements MapperInterface
|
||||
}
|
||||
asort($list);
|
||||
|
||||
array_unshift($list, trans('firefly.csv_do_not_map'));
|
||||
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class AssetAccount implements MapperInterface
|
||||
|
||||
asort($list);
|
||||
|
||||
array_unshift($list, trans('firefly.csv_do_not_map'));
|
||||
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class Bill implements MapperInterface
|
||||
}
|
||||
asort($list);
|
||||
|
||||
array_unshift($list, trans('firefly.csv_do_not_map'));
|
||||
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class Budget implements MapperInterface
|
||||
}
|
||||
asort($list);
|
||||
|
||||
array_unshift($list, trans('firefly.csv_do_not_map'));
|
||||
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class Category implements MapperInterface
|
||||
}
|
||||
asort($list);
|
||||
|
||||
array_unshift($list, trans('firefly.csv_do_not_map'));
|
||||
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class Tag implements MapperInterface
|
||||
}
|
||||
asort($list);
|
||||
|
||||
array_unshift($list, trans('firefly.csv_do_not_map'));
|
||||
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class TransactionCurrency implements MapperInterface
|
||||
|
||||
asort($list);
|
||||
|
||||
array_unshift($list, trans('firefly.csv_do_not_map'));
|
||||
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ class Currency implements PostProcessorInterface
|
||||
|
||||
// fix currency
|
||||
if (is_null($this->data['currency'])) {
|
||||
$currencyPreference = Preferences::get('currencyPreference', 'EUR');
|
||||
$currencyPreference = Preferences::get('currencyPreference', env('DEFAULT_CURRENCY', 'EUR'));
|
||||
$this->data['currency'] = TransactionCurrency::whereCode($currencyPreference->data)->first();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,10 @@ class OpposingAccount implements PostProcessorInterface
|
||||
*/
|
||||
public function process()
|
||||
{
|
||||
// three values:
|
||||
// opposing-account-id, opposing-account-iban, opposing-account-name
|
||||
|
||||
|
||||
$result = $this->checkIdNameObject();
|
||||
if (!is_null($result)) {
|
||||
return $result;
|
||||
@@ -56,11 +60,13 @@ class OpposingAccount implements PostProcessorInterface
|
||||
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;
|
||||
@@ -75,9 +81,12 @@ class OpposingAccount implements PostProcessorInterface
|
||||
protected function checkIbanString()
|
||||
{
|
||||
$rules = ['iban' => 'iban'];
|
||||
$check = ['iban' => $this->data['opposing-account-iban']];
|
||||
$iban = $this->data['opposing-account-iban'];
|
||||
$check = ['iban' => $iban];
|
||||
$validator = Validator::make($check, $rules);
|
||||
if (!$validator->fails()) {
|
||||
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;
|
||||
@@ -95,12 +104,14 @@ class OpposingAccount implements PostProcessorInterface
|
||||
$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;
|
||||
}
|
||||
|
||||
@@ -123,6 +134,7 @@ class OpposingAccount implements PostProcessorInterface
|
||||
'active' => true,
|
||||
]
|
||||
);
|
||||
Log::debug('OpposingAccountPostProcession: created a new account.');
|
||||
|
||||
return $account;
|
||||
}
|
||||
@@ -153,11 +165,13 @@ class OpposingAccount implements PostProcessorInterface
|
||||
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;
|
||||
@@ -175,7 +189,7 @@ class OpposingAccount implements PostProcessorInterface
|
||||
$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 . ': ' . $entry->name . ')');
|
||||
Log::debug('Found an account with this name (#' . $entry->id . ': ******)');
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: sander
|
||||
* Date: 05/07/15
|
||||
* Time: 19:20
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\PostProcessing;
|
||||
|
||||
|
||||
185
app/Helpers/Csv/Specifix/AbnAmroDescription.php
Normal file
185
app/Helpers/Csv/Specifix/AbnAmroDescription.php
Normal file
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Specifix;
|
||||
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Parses the description from txt files for ABN AMRO bank accounts.
|
||||
*
|
||||
* Based on the logic as described in the following Gist:
|
||||
* https://gist.github.com/vDorst/68d555a6a90f62fec004
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Specifix
|
||||
*/
|
||||
class AbnAmroDescription extends Specifix implements SpecifixInterface
|
||||
{
|
||||
/** @var array */
|
||||
protected $data;
|
||||
|
||||
/** @var array */
|
||||
protected $row;
|
||||
|
||||
/**
|
||||
* AbnAmroDescription constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->setProcessorType(self::POST_PROCESSOR);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function fix()
|
||||
{
|
||||
// Try to parse the description in known formats.
|
||||
$parsed = $this->parseSepaDescription() || $this->parseTRTPDescription() || $this->parseGEABEADescription() || $this->parseABNAMRODescription();
|
||||
|
||||
// If the description could not be parsed, specify an unknown opposing
|
||||
// account, as an opposing account is required
|
||||
if (!$parsed) {
|
||||
$this->data['opposing-account-name'] = trans('firefly.unknown');
|
||||
}
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $row
|
||||
*/
|
||||
public function setRow($row)
|
||||
{
|
||||
$this->row = $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the current description with costs from ABN AMRO itself
|
||||
*
|
||||
* @return boolean true if the description is GEA/BEA-format, false otherwise
|
||||
*/
|
||||
protected function parseABNAMRODescription()
|
||||
{
|
||||
// See if the current description is formatted in ABN AMRO format
|
||||
if (preg_match('/ABN AMRO.{24} (.*)/', $this->data['description'], $matches)) {
|
||||
Log::debug('AbnAmroSpecifix: Description is structured as costs from ABN AMRO itself.');
|
||||
|
||||
$this->data['opposing-account-name'] = 'ABN AMRO';
|
||||
$this->data['description'] = $matches[1];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the current description in GEA/BEA format
|
||||
*
|
||||
* @return boolean true if the description is GEA/BEAformat, false otherwise
|
||||
*/
|
||||
protected function parseGEABEADescription()
|
||||
{
|
||||
// See if the current description is formatted in GEA/BEA format
|
||||
if (preg_match('/([BG]EA) +(NR:[a-zA-Z:0-9]+) +([0-9.\/]+) +([^,]*)/', $this->data['description'], $matches)) {
|
||||
Log::debug('AbnAmroSpecifix: Description is structured as GEA or BEA format.');
|
||||
|
||||
// description and opposing account will be the same.
|
||||
$this->data['opposing-account-name'] = $matches[4];
|
||||
$this->data['description'] = $matches[4];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the current description in SEPA format
|
||||
*
|
||||
* @return boolean true if the description is SEPA format, false otherwise
|
||||
*/
|
||||
protected function parseSepaDescription()
|
||||
{
|
||||
// See if the current description is formatted as a SEPA plain description
|
||||
if (preg_match('/^SEPA(.{28})/', $this->data['description'], $matches)) {
|
||||
Log::debug('AbnAmroSpecifix: Description is structured as SEPA plain description.');
|
||||
|
||||
// SEPA plain descriptions contain several key-value pairs, split by a colon
|
||||
preg_match_all('/([A-Za-z]+(?=:\s)):\s([A-Za-z 0-9._#-]+(?=\s))/', $this->data['description'], $matches, PREG_SET_ORDER);
|
||||
|
||||
if (is_array($matches)) {
|
||||
foreach ($matches as $match) {
|
||||
$key = $match[1];
|
||||
$value = trim($match[2]);
|
||||
|
||||
switch (strtoupper($key)) {
|
||||
case 'OMSCHRIJVING':
|
||||
$this->data['description'] = $value;
|
||||
break;
|
||||
case 'NAAM':
|
||||
$this->data['opposing-account-name'] = $value;
|
||||
break;
|
||||
case 'IBAN':
|
||||
$this->data['opposing-account-iban'] = $value;
|
||||
break;
|
||||
default:
|
||||
// Ignore the rest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the current description in TRTP format
|
||||
*
|
||||
* @return boolean true if the description is TRTP format, false otherwise
|
||||
*/
|
||||
protected function parseTRTPDescription()
|
||||
{
|
||||
// See if the current description is formatted in TRTP format
|
||||
if (preg_match_all('!\/([A-Z]{3,4})\/([^/]*)!', $this->data['description'], $matches, PREG_SET_ORDER)) {
|
||||
Log::debug('AbnAmroSpecifix: Description is structured as TRTP format.');
|
||||
|
||||
if (is_array($matches)) {
|
||||
foreach ($matches as $match) {
|
||||
$key = $match[1];
|
||||
$value = trim($match[2]);
|
||||
|
||||
switch (strtoupper($key)) {
|
||||
case 'NAME':
|
||||
$this->data['opposing-account-name'] = $value;
|
||||
break;
|
||||
case 'REMI':
|
||||
$this->data['description'] = $value;
|
||||
break;
|
||||
case 'IBAN':
|
||||
$this->data['opposing-account-iban'] = $value;
|
||||
break;
|
||||
default:
|
||||
// Ignore the rest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace FireflyIII\Helpers\Csv\Specifix;
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Specifix
|
||||
*/
|
||||
class Dummy
|
||||
class Dummy extends Specifix implements SpecifixInterface
|
||||
{
|
||||
/** @var array */
|
||||
protected $data;
|
||||
@@ -15,6 +15,13 @@ class Dummy
|
||||
/** @var array */
|
||||
protected $row;
|
||||
|
||||
/**
|
||||
* Dummy constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->setProcessorType(self::POST_PROCESSOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Specifix;
|
||||
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class RabobankDescription
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Specifix
|
||||
*/
|
||||
class RabobankDescription
|
||||
class RabobankDescription extends Specifix implements SpecifixInterface
|
||||
{
|
||||
/** @var array */
|
||||
protected $data;
|
||||
@@ -15,6 +17,14 @@ class RabobankDescription
|
||||
/** @var array */
|
||||
protected $row;
|
||||
|
||||
/**
|
||||
* RabobankDescription constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->setProcessorType(self::POST_PROCESSOR);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
@@ -27,17 +37,6 @@ class RabobankDescription
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes Rabobank specific thing.
|
||||
*/
|
||||
protected function rabobankFixEmptyOpposing()
|
||||
{
|
||||
if (strlen($this->data['opposing-account-name']) == 0) {
|
||||
$this->data['opposing-account-name'] = $this->row[10];
|
||||
}
|
||||
$this->data['description'] = trim(str_replace($this->row[10], '', $this->data['description']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
@@ -54,5 +53,22 @@ class RabobankDescription
|
||||
$this->row = $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: "******".');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
44
app/Helpers/Csv/Specifix/Specifix.php
Normal file
44
app/Helpers/Csv/Specifix/Specifix.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* Specifix.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Helpers\Csv\Specifix;
|
||||
|
||||
/**
|
||||
* Class Specifix
|
||||
*
|
||||
* @package FireflyIII\Helpers\Csv\Specifix
|
||||
*/
|
||||
class Specifix
|
||||
{
|
||||
|
||||
/** @var int */
|
||||
protected $processorType;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getProcessorType()
|
||||
{
|
||||
return $this->processorType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $processorType
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setProcessorType($processorType)
|
||||
{
|
||||
$this->processorType = $processorType;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -8,16 +8,30 @@ namespace FireflyIII\Helpers\Csv\Specifix;
|
||||
*/
|
||||
interface SpecifixInterface
|
||||
{
|
||||
const PRE_PROCESSOR = 1;
|
||||
const POST_PROCESSOR = 2;
|
||||
/**
|
||||
* Implement bank and locale related fixes.
|
||||
*/
|
||||
public function fix();
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getProcessorType();
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData($data);
|
||||
|
||||
/**
|
||||
* @param int $processorType
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setProcessorType($processorType);
|
||||
|
||||
/**
|
||||
* @param array $row
|
||||
*/
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<?php
|
||||
namespace FireflyIII\Helpers\Csv;
|
||||
|
||||
use App;
|
||||
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;
|
||||
|
||||
@@ -45,9 +45,7 @@ class Wizard implements WizardInterface
|
||||
/*
|
||||
* Make each one unique.
|
||||
*/
|
||||
foreach ($values as $column => $found) {
|
||||
$values[$column] = array_unique($found);
|
||||
}
|
||||
$values = $this->uniqueRecursive($values);
|
||||
|
||||
return $values;
|
||||
}
|
||||
@@ -65,7 +63,8 @@ class Wizard implements WizardInterface
|
||||
|
||||
|
||||
if (is_array($map)) {
|
||||
foreach ($map as $index => $field) {
|
||||
$keys = array_keys($map);
|
||||
foreach ($keys as $index) {
|
||||
if (isset($roles[$index])) {
|
||||
$name = $roles[$index];
|
||||
if ($configRoles[$name]['mappable']) {
|
||||
@@ -112,6 +111,8 @@ class Wizard implements WizardInterface
|
||||
{
|
||||
foreach ($fields as $field) {
|
||||
if (!Session::has($field)) {
|
||||
Log::error('Session is missing field: ' . $field);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -137,7 +138,7 @@ class Wizard implements WizardInterface
|
||||
$class = 'FireflyIII\Helpers\Csv\Mapper\\' . $mapper;
|
||||
try {
|
||||
/** @var MapperInterface $mapObject */
|
||||
$mapObject = App::make($class);
|
||||
$mapObject = app($class);
|
||||
} catch (ReflectionException $e) {
|
||||
throw new FireflyException('Column "' . $columnRole . '" cannot be mapped because class ' . $mapper . ' does not exist.');
|
||||
}
|
||||
@@ -167,6 +168,20 @@ class Wizard implements WizardInterface
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function uniqueRecursive(array $array)
|
||||
{
|
||||
foreach ($array as $column => $found) {
|
||||
$array[$column] = array_unique($found);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $hasHeaders
|
||||
* @param int $index
|
||||
|
||||
78
app/Helpers/FiscalHelper.php
Normal file
78
app/Helpers/FiscalHelper.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Preferences;
|
||||
|
||||
/**
|
||||
* Class FiscalHelper
|
||||
*
|
||||
* @package FireflyIII\Helpers
|
||||
*/
|
||||
class FiscalHelper implements FiscalHelperInterface
|
||||
{
|
||||
|
||||
/** @var bool */
|
||||
protected $useCustomFiscalYear;
|
||||
|
||||
/**
|
||||
* FiscalHelper constructor.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (Preferences::get('customFiscalYear', 0)->data) {
|
||||
$this->useCustomFiscalYear = true;
|
||||
} else {
|
||||
$this->useCustomFiscalYear = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return Carbon date object
|
||||
*/
|
||||
public function startOfFiscalYear(Carbon $date)
|
||||
{
|
||||
// get start mm-dd. Then create a start date in the year passed.
|
||||
$startDate = clone $date;
|
||||
if ($this->useCustomFiscalYear === true) {
|
||||
$prefStartStr = Preferences::get('fiscalYearStart', '01-01')->data;
|
||||
list($mth, $day) = explode('-', $prefStartStr);
|
||||
$startDate->month(intval($mth))->day(intval($day));
|
||||
|
||||
// if start date is after passed date, sub 1 year.
|
||||
if ($startDate > $date) {
|
||||
$startDate->subYear();
|
||||
}
|
||||
} else {
|
||||
$startDate->startOfYear();
|
||||
}
|
||||
return $startDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return Carbon date object
|
||||
*/
|
||||
public function endOfFiscalYear(Carbon $date)
|
||||
{
|
||||
// get start of fiscal year for passed date
|
||||
$endDate = $this->startOfFiscalYear($date);
|
||||
if ($this->useCustomFiscalYear === true) {
|
||||
// add 1 year and sub 1 day
|
||||
$endDate->addYear();
|
||||
$endDate->subDay();
|
||||
} else {
|
||||
$endDate->endOfYear();
|
||||
}
|
||||
|
||||
|
||||
return $endDate;
|
||||
}
|
||||
}
|
||||
35
app/Helpers/FiscalHelperInterface.php
Normal file
35
app/Helpers/FiscalHelperInterface.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Helpers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* Interface FiscalHelperInterface
|
||||
*
|
||||
* @package FireflyIII\Helpers
|
||||
*/
|
||||
interface FiscalHelperInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* This method produces a clone of the Carbon date object passed, checks preferences
|
||||
* and calculates the first day of the fiscal year.
|
||||
*
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return Carbon date object
|
||||
*/
|
||||
public function startOfFiscalYear(Carbon $date);
|
||||
|
||||
/**
|
||||
* This method produces a clone of the Carbon date object passed, checks preferences
|
||||
* and calculates the last day of the fiscal year.
|
||||
*
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return Carbon date object
|
||||
*/
|
||||
public function endOfFiscalYear(Carbon $date);
|
||||
|
||||
}
|
||||
113
app/Helpers/Report/AccountReportHelper.php
Normal file
113
app/Helpers/Report/AccountReportHelper.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
/**
|
||||
* AccountReportHelper.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Helpers\Report;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use FireflyIII\Helpers\Collection\Account as AccountCollection;
|
||||
use FireflyIII\Models\Account;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
|
||||
/**
|
||||
* Class AccountReportHelper
|
||||
*
|
||||
* @package FireflyIII\Helpers\Report
|
||||
*/
|
||||
class AccountReportHelper implements AccountReportHelperInterface
|
||||
{
|
||||
/**
|
||||
* This method generates a full report for the given period on all
|
||||
* given accounts
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return AccountCollection
|
||||
*/
|
||||
public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$startAmount = '0';
|
||||
$endAmount = '0';
|
||||
$diff = '0';
|
||||
$ids = $accounts->pluck('id')->toArray();
|
||||
|
||||
$yesterday = clone $start;
|
||||
$yesterday->subDay();
|
||||
|
||||
bcscale(2);
|
||||
|
||||
// get balances for start.
|
||||
$startSet = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->whereIn('accounts.id', $ids)
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->whereNull('transactions.deleted_at')
|
||||
->where('transaction_journals.date', '<=', $yesterday->format('Y-m-d'))
|
||||
->groupBy('accounts.id')
|
||||
->get(['accounts.id', DB::Raw('SUM(`transactions`.`amount`) as `balance`')]);
|
||||
|
||||
// and end:
|
||||
$endSet = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->whereIn('accounts.id', $ids)
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->whereNull('transactions.deleted_at')
|
||||
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
|
||||
->groupBy('accounts.id')
|
||||
->get(['accounts.id', DB::Raw('SUM(`transactions`.`amount`) as `balance`')]);
|
||||
|
||||
|
||||
$accounts->each(
|
||||
function (Account $account) use ($startSet, $endSet) {
|
||||
/**
|
||||
* The balance for today always incorporates transactions
|
||||
* made on today. So to get todays "start" balance, we sub one
|
||||
* day.
|
||||
*/
|
||||
//
|
||||
$currentStart = $startSet->filter(
|
||||
function (Account $entry) use ($account) {
|
||||
return $account->id == $entry->id;
|
||||
}
|
||||
);
|
||||
if ($currentStart->first()) {
|
||||
$account->startBalance = $currentStart->first()->balance;
|
||||
}
|
||||
|
||||
$currentEnd = $endSet->filter(
|
||||
function (Account $entry) use ($account) {
|
||||
return $account->id == $entry->id;
|
||||
}
|
||||
);
|
||||
if ($currentEnd->first()) {
|
||||
$account->endBalance = $currentEnd->first()->balance;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// summarize:
|
||||
foreach ($accounts as $account) {
|
||||
$startAmount = bcadd($startAmount, $account->startBalance);
|
||||
$endAmount = bcadd($endAmount, $account->endBalance);
|
||||
$diff = bcadd($diff, bcsub($account->endBalance, $account->startBalance));
|
||||
}
|
||||
|
||||
$object = new AccountCollection;
|
||||
$object->setStart($startAmount);
|
||||
$object->setEnd($endAmount);
|
||||
$object->setDifference($diff);
|
||||
$object->setAccounts($accounts);
|
||||
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
36
app/Helpers/Report/AccountReportHelperInterface.php
Normal file
36
app/Helpers/Report/AccountReportHelperInterface.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* AccountReportHelperInterface.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Helpers\Report;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collection\Account as AccountCollection;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
|
||||
/**
|
||||
* Interface AccountReportHelperInterface
|
||||
*
|
||||
* @package FireflyIII\Helpers\Report
|
||||
*/
|
||||
interface AccountReportHelperInterface
|
||||
{
|
||||
/**
|
||||
* This method generates a full report for the given period on all
|
||||
* given accounts
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return AccountCollection
|
||||
*/
|
||||
public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts);
|
||||
|
||||
}
|
||||
241
app/Helpers/Report/BalanceReportHelper.php
Normal file
241
app/Helpers/Report/BalanceReportHelper.php
Normal file
@@ -0,0 +1,241 @@
|
||||
<?php
|
||||
/**
|
||||
* BalanceReportHelper.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Helpers\Report;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collection\Balance;
|
||||
use FireflyIII\Helpers\Collection\BalanceEntry;
|
||||
use FireflyIII\Helpers\Collection\BalanceHeader;
|
||||
use FireflyIII\Helpers\Collection\BalanceLine;
|
||||
use FireflyIII\Models\Budget as BudgetModel;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
|
||||
/**
|
||||
* Class BalanceReportHelper
|
||||
*
|
||||
* @package FireflyIII\Helpers\Report
|
||||
*/
|
||||
class BalanceReportHelper implements BalanceReportHelperInterface
|
||||
{
|
||||
|
||||
/** @var BudgetRepositoryInterface */
|
||||
protected $budgetRepository;
|
||||
/** @var TagRepositoryInterface */
|
||||
protected $tagRepository;
|
||||
|
||||
/**
|
||||
* ReportHelper constructor.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param BudgetRepositoryInterface $budgetRepository
|
||||
* @param TagRepositoryInterface $tagRepository
|
||||
*/
|
||||
public function __construct(BudgetRepositoryInterface $budgetRepository, TagRepositoryInterface $tagRepository)
|
||||
{
|
||||
$this->budgetRepository = $budgetRepository;
|
||||
$this->tagRepository = $tagRepository;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return Balance
|
||||
*/
|
||||
public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$balance = new Balance;
|
||||
|
||||
// build a balance header:
|
||||
$header = new BalanceHeader;
|
||||
$budgets = $this->budgetRepository->getBudgetsAndLimitsInRange($start, $end);
|
||||
$spentData = $this->budgetRepository->spentPerBudgetPerAccount($budgets, $accounts, $start, $end);
|
||||
foreach ($accounts as $account) {
|
||||
$header->addAccount($account);
|
||||
}
|
||||
|
||||
/** @var BudgetModel $budget */
|
||||
foreach ($budgets as $budget) {
|
||||
$balance->addBalanceLine($this->createBalanceLine($budget, $accounts, $spentData));
|
||||
}
|
||||
|
||||
$balance->addBalanceLine($this->createEmptyBalanceLine($accounts, $spentData));
|
||||
$balance->addBalanceLine($this->createTagsBalanceLine($accounts, $start, $end));
|
||||
$balance->addBalanceLine($this->createDifferenceBalanceLine($accounts, $spentData, $start, $end));
|
||||
|
||||
$balance->setBalanceHeader($header);
|
||||
|
||||
return $balance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
* @param Collection $accounts
|
||||
* @param Collection $spentData
|
||||
*
|
||||
* @return BalanceLine
|
||||
*/
|
||||
private function createBalanceLine(BudgetModel $budget, Collection $accounts, Collection $spentData)
|
||||
{
|
||||
$line = new BalanceLine;
|
||||
$line->setBudget($budget);
|
||||
|
||||
// loop accounts:
|
||||
foreach ($accounts as $account) {
|
||||
$balanceEntry = new BalanceEntry;
|
||||
$balanceEntry->setAccount($account);
|
||||
|
||||
// get spent:
|
||||
$entry = $spentData->filter(
|
||||
function (TransactionJournal $model) use ($budget, $account) {
|
||||
return $model->account_id == $account->id && $model->budget_id == $budget->id;
|
||||
}
|
||||
);
|
||||
$spent = 0;
|
||||
if (!is_null($entry->first())) {
|
||||
$spent = $entry->first()->spent;
|
||||
}
|
||||
$balanceEntry->setSpent($spent);
|
||||
$line->addBalanceEntry($balanceEntry);
|
||||
}
|
||||
|
||||
return $line;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Collection $spentData
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
*
|
||||
* @return BalanceLine
|
||||
*/
|
||||
private function createDifferenceBalanceLine(Collection $accounts, Collection $spentData, Carbon $start, Carbon $end)
|
||||
{
|
||||
$diff = new BalanceLine;
|
||||
$tagsLeft = $this->tagRepository->allCoveredByBalancingActs($accounts, $start, $end);
|
||||
|
||||
$diff->setRole(BalanceLine::ROLE_DIFFROLE);
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$entry = $spentData->filter(
|
||||
function (TransactionJournal $model) use ($account) {
|
||||
return $model->account_id == $account->id && is_null($model->budget_id);
|
||||
}
|
||||
);
|
||||
$spent = 0;
|
||||
if (!is_null($entry->first())) {
|
||||
$spent = $entry->first()->spent;
|
||||
}
|
||||
$leftEntry = $tagsLeft->filter(
|
||||
function (Tag $tag) use ($account) {
|
||||
return $tag->account_id == $account->id;
|
||||
}
|
||||
);
|
||||
$left = 0;
|
||||
if (!is_null($leftEntry->first())) {
|
||||
$left = $leftEntry->first()->sum;
|
||||
}
|
||||
bcscale(2);
|
||||
$diffValue = bcadd($spent, $left);
|
||||
|
||||
// difference:
|
||||
$diffEntry = new BalanceEntry;
|
||||
$diffEntry->setAccount($account);
|
||||
$diffEntry->setSpent($diffValue);
|
||||
$diff->addBalanceEntry($diffEntry);
|
||||
|
||||
}
|
||||
|
||||
return $diff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Collection $spentData
|
||||
*
|
||||
* @return BalanceLine
|
||||
*/
|
||||
private function createEmptyBalanceLine(Collection $accounts, Collection $spentData)
|
||||
{
|
||||
$empty = new BalanceLine;
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$entry = $spentData->filter(
|
||||
function (TransactionJournal $model) use ($account) {
|
||||
return $model->account_id == $account->id && is_null($model->budget_id);
|
||||
}
|
||||
);
|
||||
$spent = 0;
|
||||
if (!is_null($entry->first())) {
|
||||
$spent = $entry->first()->spent;
|
||||
}
|
||||
|
||||
// budget
|
||||
$budgetEntry = new BalanceEntry;
|
||||
$budgetEntry->setAccount($account);
|
||||
$budgetEntry->setSpent($spent);
|
||||
$empty->addBalanceEntry($budgetEntry);
|
||||
|
||||
}
|
||||
|
||||
return $empty;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return BalanceLine
|
||||
*/
|
||||
private function createTagsBalanceLine(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
$tags = new BalanceLine;
|
||||
$tagsLeft = $this->tagRepository->allCoveredByBalancingActs($accounts, $start, $end);
|
||||
|
||||
$tags->setRole(BalanceLine::ROLE_TAGROLE);
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$leftEntry = $tagsLeft->filter(
|
||||
function (Tag $tag) use ($account) {
|
||||
return $tag->account_id == $account->id;
|
||||
}
|
||||
);
|
||||
$left = 0;
|
||||
if (!is_null($leftEntry->first())) {
|
||||
$left = $leftEntry->first()->sum;
|
||||
}
|
||||
bcscale(2);
|
||||
|
||||
// balanced by tags
|
||||
$tagEntry = new BalanceEntry;
|
||||
$tagEntry->setAccount($account);
|
||||
$tagEntry->setLeft($left);
|
||||
$tags->addBalanceEntry($tagEntry);
|
||||
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
}
|
||||
32
app/Helpers/Report/BalanceReportHelperInterface.php
Normal file
32
app/Helpers/Report/BalanceReportHelperInterface.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* BalanceReportHelperInterface.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Helpers\Report;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collection\Balance;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
|
||||
/**
|
||||
* Interface BalanceReportHelperInterface
|
||||
*
|
||||
* @package FireflyIII\Helpers\Report
|
||||
*/
|
||||
interface BalanceReportHelperInterface
|
||||
{
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return Balance
|
||||
*/
|
||||
public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts);
|
||||
}
|
||||
133
app/Helpers/Report/BudgetReportHelper.php
Normal file
133
app/Helpers/Report/BudgetReportHelper.php
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/**
|
||||
* BudgetReportHelper.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Helpers\Report;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
|
||||
use FireflyIII\Helpers\Collection\BudgetLine;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class BudgetReportHelper
|
||||
*
|
||||
* @package FireflyIII\Helpers\Report
|
||||
*/
|
||||
class BudgetReportHelper implements BudgetReportHelperInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return BudgetCollection
|
||||
*/
|
||||
public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$object = new BudgetCollection;
|
||||
/** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */
|
||||
$repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
|
||||
$set = $repository->getBudgets();
|
||||
$allRepetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
|
||||
$allTotalSpent = $repository->spentAllPerDayForAccounts($accounts, $start, $end);
|
||||
bcscale(2);
|
||||
|
||||
foreach ($set as $budget) {
|
||||
|
||||
$repetitions = $allRepetitions->filter(
|
||||
function (LimitRepetition $rep) use ($budget) {
|
||||
return $rep->budget_id == $budget->id;
|
||||
}
|
||||
);
|
||||
$totalSpent = isset($allTotalSpent[$budget->id]) ? $allTotalSpent[$budget->id] : [];
|
||||
|
||||
// no repetition(s) for this budget:
|
||||
if ($repetitions->count() == 0) {
|
||||
$spent = array_sum($totalSpent);
|
||||
$budgetLine = new BudgetLine;
|
||||
$budgetLine->setBudget($budget);
|
||||
$budgetLine->setOverspent($spent);
|
||||
$object->addOverspent($spent);
|
||||
$object->addBudgetLine($budgetLine);
|
||||
continue;
|
||||
}
|
||||
|
||||
// one or more repetitions for budget:
|
||||
/** @var LimitRepetition $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$budgetLine = new BudgetLine;
|
||||
$budgetLine->setBudget($budget);
|
||||
$budgetLine->setRepetition($repetition);
|
||||
$expenses = $this->getSumOfRange($start, $end, $totalSpent);
|
||||
|
||||
// 200 en -100 is 100, vergeleken met 0 === 1
|
||||
// 200 en -200 is 0, vergeleken met 0 === 0
|
||||
// 200 en -300 is -100, vergeleken met 0 === -1
|
||||
|
||||
$left = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? bcadd($repetition->amount, $expenses) : 0;
|
||||
$spent = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? $expenses : '0';
|
||||
$overspent = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? '0' : bcadd($expenses, $repetition->amount);
|
||||
|
||||
$budgetLine->setLeft($left);
|
||||
$budgetLine->setSpent($expenses);
|
||||
$budgetLine->setOverspent($overspent);
|
||||
$budgetLine->setBudgeted($repetition->amount);
|
||||
|
||||
$object->addBudgeted($repetition->amount);
|
||||
$object->addSpent($spent);
|
||||
$object->addLeft($left);
|
||||
$object->addOverspent($overspent);
|
||||
$object->addBudgetLine($budgetLine);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// stuff outside of budgets:
|
||||
$noBudget = $repository->getWithoutBudgetSum($start, $end);
|
||||
$budgetLine = new BudgetLine;
|
||||
$budgetLine->setOverspent($noBudget);
|
||||
$budgetLine->setSpent($noBudget);
|
||||
$object->addOverspent($noBudget);
|
||||
$object->addBudgetLine($budgetLine);
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the array as returned by SingleCategoryRepositoryInterface::spentPerDay and SingleCategoryRepositoryInterface::earnedByDay
|
||||
* and sum up everything in the array in the given range.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param array $array
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getSumOfRange(Carbon $start, Carbon $end, array $array)
|
||||
{
|
||||
bcscale(2);
|
||||
$sum = '0';
|
||||
$currentStart = clone $start; // to not mess with the original one
|
||||
$currentEnd = clone $end; // to not mess with the original one
|
||||
|
||||
while ($currentStart <= $currentEnd) {
|
||||
$date = $currentStart->format('Y-m-d');
|
||||
if (isset($array[$date])) {
|
||||
$sum = bcadd($sum, $array[$date]);
|
||||
}
|
||||
$currentStart->addDay();
|
||||
}
|
||||
|
||||
return $sum;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user