mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-12-21 10:41:22 +00:00
Compare commits
858 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fca6eb810 | ||
|
|
5a0ae8530c | ||
|
|
7949c9ad74 | ||
|
|
6fb9362f7e | ||
|
|
3481d364cc | ||
|
|
373b9cdd9f | ||
|
|
75af63e6ac | ||
|
|
5aa62a1be4 | ||
|
|
aede8bf0e0 | ||
|
|
9ab7abcb95 | ||
|
|
f87b28afd9 | ||
|
|
8661f6d1ac | ||
|
|
4536b4b2b4 | ||
|
|
655f03940b | ||
|
|
4122de7823 | ||
|
|
0f4c67d24e | ||
|
|
20e8c45819 | ||
|
|
2b8b844fb2 | ||
|
|
3284b8764f | ||
|
|
d19946336e | ||
|
|
770a220808 | ||
|
|
78b71e72f1 | ||
|
|
19990f49b0 | ||
|
|
8208d44466 | ||
|
|
002b2b6dee | ||
|
|
c207167b14 | ||
|
|
cfc066e911 | ||
|
|
3a1d011841 | ||
|
|
7d05c0da9c | ||
|
|
1d7f2ca9e4 | ||
|
|
ea2e0d7546 | ||
|
|
64b79ee64c | ||
|
|
8a00101470 | ||
|
|
01aba73f5b | ||
|
|
71e31346e8 | ||
|
|
483cce9880 | ||
|
|
c8db39a91e | ||
|
|
6d398a2edf | ||
|
|
bd3c8119ba | ||
|
|
16aa78d13c | ||
|
|
3be5cca60a | ||
|
|
bc3dfb96fd | ||
|
|
e78e98a6cf | ||
|
|
9db0e48f63 | ||
|
|
3de52b6bc1 | ||
|
|
be52abbe3b | ||
|
|
ac55b0fafb | ||
|
|
887b6789fc | ||
|
|
ff50fec112 | ||
|
|
4538ef3cf9 | ||
|
|
a872cf7061 | ||
|
|
2d8ca363db | ||
|
|
8e8b011587 | ||
|
|
4241ae035e | ||
|
|
3ef569d280 | ||
|
|
6fe28b15df | ||
|
|
a609a47138 | ||
|
|
b575b87f77 | ||
|
|
7c5ee8a67d | ||
|
|
452c14bece | ||
|
|
57f63ba752 | ||
|
|
5f153b8a01 | ||
|
|
1be49876df | ||
|
|
a79b2a7773 | ||
|
|
cdf6e5a487 | ||
|
|
7c82f45604 | ||
|
|
4d49701203 | ||
|
|
d48cc69898 | ||
|
|
af466a1d75 | ||
|
|
b9599d3aa1 | ||
|
|
dbebfe7c07 | ||
|
|
ddf54fdb83 | ||
|
|
619138d294 | ||
|
|
126b19bf2d | ||
|
|
cc76adf7b6 | ||
|
|
83bcb56a6a | ||
|
|
6e88a70661 | ||
|
|
6755a9878b | ||
|
|
b8ef7593ee | ||
|
|
602cc26f0f | ||
|
|
62271fe064 | ||
|
|
83f5f776a6 | ||
|
|
2a5566a820 | ||
|
|
398e547d06 | ||
|
|
ba957196da | ||
|
|
b5c4a24133 | ||
|
|
cc688dc112 | ||
|
|
91b5eaff80 | ||
|
|
4a52503cb3 | ||
|
|
bcd7e7ea94 | ||
|
|
ba9ae54fbb | ||
|
|
39e05c9991 | ||
|
|
8962f90bcc | ||
|
|
daf3a95db0 | ||
|
|
1c9ebafe2b | ||
|
|
00b3df4455 | ||
|
|
600c3e75bb | ||
|
|
6349fccd0f | ||
|
|
6ececdad26 | ||
|
|
c67f1a7b93 | ||
|
|
8617ea760a | ||
|
|
41a2406f07 | ||
|
|
adae8e45a9 | ||
|
|
e346ae533d | ||
|
|
31789255c9 | ||
|
|
dbe6edd133 | ||
|
|
7cfbcec56e | ||
|
|
9f9a055f64 | ||
|
|
d3614d3505 | ||
|
|
800f67908e | ||
|
|
e2c613c422 | ||
|
|
457037ed99 | ||
|
|
f9f21efd36 | ||
|
|
2d59b6718d | ||
|
|
0c6d213296 | ||
|
|
c34fb7f037 | ||
|
|
796be319b7 | ||
|
|
d28fcdc6a5 | ||
|
|
d0afcb6cfa | ||
|
|
7bd4de937a | ||
|
|
3025693178 | ||
|
|
c9cc3bf3ff | ||
|
|
1f670f7a05 | ||
|
|
48d735b53b | ||
|
|
b91f6c7ce6 | ||
|
|
ad116d1959 | ||
|
|
a0de10870d | ||
|
|
eb0c00896f | ||
|
|
deccd4e9fe | ||
|
|
8be4ec08ad | ||
|
|
59ad0624f2 | ||
|
|
f0c69ca84f | ||
|
|
3ba1c07f68 | ||
|
|
14cd4aaac8 | ||
|
|
8a1fae5d9d | ||
|
|
e323f5a2d5 | ||
|
|
c5c1cbd66f | ||
|
|
4cc9dbbe6a | ||
|
|
3649991ad6 | ||
|
|
1d25691aa2 | ||
|
|
235076b465 | ||
|
|
c2670fa379 | ||
|
|
a769a5391d | ||
|
|
1f58c46f67 | ||
|
|
f4c9f2d0e7 | ||
|
|
851b9136fe | ||
|
|
0fe10e470d | ||
|
|
8c8ea17fac | ||
|
|
7c546b8d3a | ||
|
|
63334a61ad | ||
|
|
f61e65cf54 | ||
|
|
05bf752629 | ||
|
|
5096a90e34 | ||
|
|
03792b3905 | ||
|
|
995b049a5f | ||
|
|
bde37ec2c7 | ||
|
|
d6b3fe7e1b | ||
|
|
954b394987 | ||
|
|
97dae6dde5 | ||
|
|
fe039500d6 | ||
|
|
6952957794 | ||
|
|
01cc97ad55 | ||
|
|
b5c8e005e2 | ||
|
|
1c2602438f | ||
|
|
33da756a2f | ||
|
|
488d4a38b8 | ||
|
|
e60f60b0f8 | ||
|
|
8aa2e3d2f5 | ||
|
|
d5f65e5d07 | ||
|
|
c8511a6e2a | ||
|
|
379b15be1d | ||
|
|
2ee1fea293 | ||
|
|
4385d71c6f | ||
|
|
cf6ea64aba | ||
|
|
101317cb16 | ||
|
|
5990a21c46 | ||
|
|
a9bc007327 | ||
|
|
0c71770b1d | ||
|
|
5bae7e9bdb | ||
|
|
1818a596fe | ||
|
|
8f7541b841 | ||
|
|
090546cda3 | ||
|
|
dcd89d38e7 | ||
|
|
800478d437 | ||
|
|
f797344106 | ||
|
|
9352ee3e25 | ||
|
|
811026dc4a | ||
|
|
479a4dac7b | ||
|
|
499fbbeb17 | ||
|
|
a35bcf6415 | ||
|
|
818ffdfc85 | ||
|
|
d5e19c7ac0 | ||
|
|
37639b0ff4 | ||
|
|
740d89dce6 | ||
|
|
4a7b08fc4e | ||
|
|
48a5f83f00 | ||
|
|
48819c928d | ||
|
|
45a6866dd0 | ||
|
|
6690586406 | ||
|
|
909e54845c | ||
|
|
a7204eb9fa | ||
|
|
6463166c00 | ||
|
|
f8268a864b | ||
|
|
721fff29b3 | ||
|
|
4cf312d3d4 | ||
|
|
36f1b6a834 | ||
|
|
050d7e8f00 | ||
|
|
7c5bed2bb5 | ||
|
|
87316cf7c1 | ||
|
|
f7d61e5a9b | ||
|
|
b2030a72a0 | ||
|
|
533797fc9e | ||
|
|
5688234b9d | ||
|
|
9335789362 | ||
|
|
9e6a2a3fa4 | ||
|
|
122fc77357 | ||
|
|
c978e7965f | ||
|
|
b0e4e24603 | ||
|
|
56de307a3e | ||
|
|
e1dd9ed41b | ||
|
|
17a64764d3 | ||
|
|
3cd0540474 | ||
|
|
27cd9fac8a | ||
|
|
1d2012cc23 | ||
|
|
1b00835dd1 | ||
|
|
413dcf8164 | ||
|
|
7f17e8fb2f | ||
|
|
254d8994d0 | ||
|
|
4f72519ad9 | ||
|
|
900b246183 | ||
|
|
abddb29f37 | ||
|
|
8d429ef753 | ||
|
|
b7679b5c60 | ||
|
|
49982d6eb1 | ||
|
|
3191a6c12b | ||
|
|
32f8747f2e | ||
|
|
38e45a62cf | ||
|
|
c0e2e78780 | ||
|
|
3fe3ddbc49 | ||
|
|
5ca532a54a | ||
|
|
a120df090a | ||
|
|
22d359503a | ||
|
|
e8d84abe43 | ||
|
|
98937cedaa | ||
|
|
d592d6cd7a | ||
|
|
0341a04ee3 | ||
|
|
540fc4f924 | ||
|
|
04290bf9b6 | ||
|
|
ecbc0c1778 | ||
|
|
44b8e48c3a | ||
|
|
a5036c86dc | ||
|
|
ac86e75233 | ||
|
|
9ec3febbfa | ||
|
|
1c5dc6ab6d | ||
|
|
abb8eafec2 | ||
|
|
eb8f5512c5 | ||
|
|
d146476c91 | ||
|
|
7a57670925 | ||
|
|
8a49e98246 | ||
|
|
cf0845d190 | ||
|
|
02bbdcc251 | ||
|
|
13f6bd759b | ||
|
|
497400587d | ||
|
|
a58cd83ea7 | ||
|
|
3f802fe27a | ||
|
|
6a13dd317d | ||
|
|
a442d3d952 | ||
|
|
0d4febff85 | ||
|
|
ba222eaf77 | ||
|
|
b14719464c | ||
|
|
c756b80962 | ||
|
|
a54a886bf0 | ||
|
|
dbe9628cc5 | ||
|
|
7a3b39886e | ||
|
|
fab511cc53 | ||
|
|
4979d9d0bf | ||
|
|
45914b2e9e | ||
|
|
1e9aaf2d2a | ||
|
|
de56c18c6e | ||
|
|
eaefb7136a | ||
|
|
fe9344cd0a | ||
|
|
f010c17ae6 | ||
|
|
f63cd74965 | ||
|
|
9e8f8f76a4 | ||
|
|
d88c6a82d0 | ||
|
|
a8fdf7ffad | ||
|
|
245389d74f | ||
|
|
26933637dd | ||
|
|
98312ac554 | ||
|
|
1ba03088c9 | ||
|
|
c0dfc554b3 | ||
|
|
5c691491e8 | ||
|
|
9731b59174 | ||
|
|
52bf358978 | ||
|
|
c92a56c980 | ||
|
|
3142151fc3 | ||
|
|
fb555f5b96 | ||
|
|
8f1c693d3d | ||
|
|
b8a8becd0c | ||
|
|
b71abd3f6a | ||
|
|
9ae74b4278 | ||
|
|
bdbf434006 | ||
|
|
1a5e93c739 | ||
|
|
8e42ba74c6 | ||
|
|
42bb083e99 | ||
|
|
ae4eecc7f2 | ||
|
|
c4f25b6191 | ||
|
|
29b200040f | ||
|
|
7cb1598fb1 | ||
|
|
65b6f162d8 | ||
|
|
c56d2e08f4 | ||
|
|
ca0a0886b1 | ||
|
|
e9822ae1a3 | ||
|
|
04b284f030 | ||
|
|
9ef24c0a43 | ||
|
|
7ee650ba7a | ||
|
|
96cafed154 | ||
|
|
f65c2ff4fb | ||
|
|
121deec62f | ||
|
|
838d0808c0 | ||
|
|
974fbe9e5b | ||
|
|
f26f94ad3b | ||
|
|
7410f1944c | ||
|
|
c34947f657 | ||
|
|
54092118e1 | ||
|
|
866a7d7401 | ||
|
|
32ab916707 | ||
|
|
1a245f1303 | ||
|
|
a23c61ee3c | ||
|
|
f44336f7aa | ||
|
|
98d4bc48b6 | ||
|
|
a3f1b72bac | ||
|
|
a37f70947b | ||
|
|
71195aa789 | ||
|
|
f6511bed32 | ||
|
|
619500ca64 | ||
|
|
986d290434 | ||
|
|
878f8c58bb | ||
|
|
e067da1fe9 | ||
|
|
f340c636fe | ||
|
|
ce260a1a1e | ||
|
|
a21c9f15e3 | ||
|
|
e64b778d13 | ||
|
|
a1f139f62a | ||
|
|
8ae1d1c963 | ||
|
|
8f8016179b | ||
|
|
2e32e994c3 | ||
|
|
1575e3b045 | ||
|
|
9ab5f68601 | ||
|
|
7fcb806dfe | ||
|
|
5ae736c7cc | ||
|
|
d77ba9970b | ||
|
|
49f97a2c7b | ||
|
|
659ff89062 | ||
|
|
5529641bea | ||
|
|
b38f1d7b2a | ||
|
|
53ba202b14 | ||
|
|
11cc333de7 | ||
|
|
70e47ab4d0 | ||
|
|
1582b35ae2 | ||
|
|
62c27cee6c | ||
|
|
81b8bc9e93 | ||
|
|
49758c4e72 | ||
|
|
001ef4fe1c | ||
|
|
94d0401f4e | ||
|
|
2dbd9bd0b1 | ||
|
|
9168c97eb6 | ||
|
|
758953b6e3 | ||
|
|
8ccdf9ea83 | ||
|
|
9c6a3e4ad5 | ||
|
|
6151d4a0ec | ||
|
|
61014d45f4 | ||
|
|
05a93a2426 | ||
|
|
a4c7412220 | ||
|
|
94e51952f4 | ||
|
|
ebdd64f46f | ||
|
|
2dc70ece44 | ||
|
|
c23ea5ea76 | ||
|
|
6521a7c604 | ||
|
|
02e792148c | ||
|
|
69c350dcca | ||
|
|
1aee3d8e2c | ||
|
|
02695d852c | ||
|
|
7405138489 | ||
|
|
4804257fd1 | ||
|
|
67f2e3a32a | ||
|
|
8d709f9cf4 | ||
|
|
4d3132f1c9 | ||
|
|
36b44f1814 | ||
|
|
32761aeda0 | ||
|
|
851b05c110 | ||
|
|
997e951aca | ||
|
|
448dc6b7c6 | ||
|
|
84458fa46f | ||
|
|
50bb8a0d91 | ||
|
|
997b3c3061 | ||
|
|
4f240c004c | ||
|
|
597a8d36af | ||
|
|
cf52a4c5c2 | ||
|
|
c29180a094 | ||
|
|
10f4304559 | ||
|
|
30447bcf70 | ||
|
|
9ff9385c47 | ||
|
|
6c5499e848 | ||
|
|
3bacbe8536 | ||
|
|
09c7a69050 | ||
|
|
5dc727580f | ||
|
|
248a4ed527 | ||
|
|
db95185eee | ||
|
|
85dae15a0d | ||
|
|
3e61a1e12b | ||
|
|
6cd4186ac9 | ||
|
|
cbbadc3d6d | ||
|
|
fc0024faa2 | ||
|
|
0f3d4062d7 | ||
|
|
7ba8a88989 | ||
|
|
349d254193 | ||
|
|
be201e811d | ||
|
|
84a032fbb4 | ||
|
|
4815602558 | ||
|
|
e4b83392be | ||
|
|
0658c17adb | ||
|
|
bdc72aee42 | ||
|
|
689d91e30f | ||
|
|
6b785e4318 | ||
|
|
f46cf55912 | ||
|
|
d520849ce1 | ||
|
|
50661bbb3b | ||
|
|
d2d5b1ac76 | ||
|
|
244972e0f8 | ||
|
|
f80e6c2efa | ||
|
|
e9e32eda3c | ||
|
|
73844e223f | ||
|
|
6583a6d9c6 | ||
|
|
ca4824adcd | ||
|
|
80b5cc08bb | ||
|
|
afbcc79a06 | ||
|
|
3371bd2e04 | ||
|
|
5efdf53c06 | ||
|
|
c9112de8ba | ||
|
|
fd4b589a13 | ||
|
|
df813dbac9 | ||
|
|
004fb362ec | ||
|
|
3cd749753a | ||
|
|
c7964f7693 | ||
|
|
57bba2fd3f | ||
|
|
04c9b2a7a8 | ||
|
|
b9d142c2b7 | ||
|
|
6ab52e282f | ||
|
|
b14adf8c3f | ||
|
|
4e0b162f5f | ||
|
|
62d47ff7f0 | ||
|
|
7f025380f0 | ||
|
|
7d1e981bca | ||
|
|
a08103f996 | ||
|
|
dd4991a4f8 | ||
|
|
5442292d23 | ||
|
|
3f050d3d03 | ||
|
|
ad1e9c27e9 | ||
|
|
ab761696bf | ||
|
|
0713273a99 | ||
|
|
5668a3271b | ||
|
|
1eca105a91 | ||
|
|
3883b99c24 | ||
|
|
d6adbc697a | ||
|
|
a5789b1085 | ||
|
|
a6ccbcb795 | ||
|
|
1a6067f7ae | ||
|
|
cb735b18a9 | ||
|
|
909bd11147 | ||
|
|
1a76c606ed | ||
|
|
8c9b6796a1 | ||
|
|
844ab608d4 | ||
|
|
dc39094975 | ||
|
|
b32184d525 | ||
|
|
d95ae53ce2 | ||
|
|
5e3147ddeb | ||
|
|
9e594c6075 | ||
|
|
c0058c51ea | ||
|
|
b0b68d4243 | ||
|
|
22eb90212d | ||
|
|
94e264b6ce | ||
|
|
7ea15761a6 | ||
|
|
1ced4a089d | ||
|
|
648e63628c | ||
|
|
2847e2aff5 | ||
|
|
9dfaabb5d0 | ||
|
|
6a21f98ea4 | ||
|
|
4d5f4cc1c0 | ||
|
|
970ce6cb0d | ||
|
|
31cad5de00 | ||
|
|
e06db9e620 | ||
|
|
f57ac64dc2 | ||
|
|
57d7c1623f | ||
|
|
c86aa9cb3f | ||
|
|
48209d0d22 | ||
|
|
8f6a271cc0 | ||
|
|
a9b610f367 | ||
|
|
1046930f29 | ||
|
|
1b16e5e216 | ||
|
|
e16ba9ac70 | ||
|
|
71ac676b83 | ||
|
|
1b6c0d5d86 | ||
|
|
14db016e98 | ||
|
|
7e2e1626ac | ||
|
|
bce4e7e2bf | ||
|
|
ede327f3d3 | ||
|
|
82718a74dc | ||
|
|
eefd6141a1 | ||
|
|
7894f1871e | ||
|
|
0ef9b5b462 | ||
|
|
9ca75d134e | ||
|
|
b78776e1f7 | ||
|
|
f2f9f8fbab | ||
|
|
5b5acba816 | ||
|
|
9f2729d0ff | ||
|
|
afe98cda9f | ||
|
|
9c4d2e8791 | ||
|
|
cea170359f | ||
|
|
70bb8fbc89 | ||
|
|
82cd0adca6 | ||
|
|
e821f5b2b6 | ||
|
|
4cade467c6 | ||
|
|
b6c9639948 | ||
|
|
ca9319db34 | ||
|
|
beaec9a4c1 | ||
|
|
cbc44e8200 | ||
|
|
017b1a481a | ||
|
|
e15932fe4a | ||
|
|
08c044fe52 | ||
|
|
0e11245cb4 | ||
|
|
cde494d3ef | ||
|
|
9a15decdff | ||
|
|
186b986e02 | ||
|
|
cdbf5653ac | ||
|
|
c403dd7490 | ||
|
|
d15d9fdf2a | ||
|
|
0b618de44c | ||
|
|
875f19f728 | ||
|
|
7bb549732c | ||
|
|
b9baa93ae4 | ||
|
|
315479fcd3 | ||
|
|
1f1334a1fc | ||
|
|
bf0744e03a | ||
|
|
8fb9577660 | ||
|
|
90d58c5c39 | ||
|
|
b6aa79bb38 | ||
|
|
14a0de6b6a | ||
|
|
13e56b7249 | ||
|
|
3753901e38 | ||
|
|
e76075e29f | ||
|
|
284db7f90b | ||
|
|
cabdf4e380 | ||
|
|
9859052c4d | ||
|
|
0feeac9160 | ||
|
|
54b33a0b69 | ||
|
|
e08e7b2c9b | ||
|
|
782e2add88 | ||
|
|
f18a5a6f1b | ||
|
|
6fc971c4cb | ||
|
|
3250c4830d | ||
|
|
9e1a69217d | ||
|
|
46c26a64d8 | ||
|
|
2f12a70647 | ||
|
|
be190d1fa0 | ||
|
|
1e4888209b | ||
|
|
8aa2961c19 | ||
|
|
304cdabc96 | ||
|
|
c60e272eb3 | ||
|
|
c074f55cb2 | ||
|
|
e6af29646e | ||
|
|
b4213328fe | ||
|
|
8a7628c9dc | ||
|
|
d52c146e12 | ||
|
|
1910a4bd4b | ||
|
|
bd0c552f54 | ||
|
|
b29ea98de4 | ||
|
|
dd1db87806 | ||
|
|
6f9e446577 | ||
|
|
664230dca8 | ||
|
|
1a24e7e0aa | ||
|
|
9239815ce6 | ||
|
|
116e19ec06 | ||
|
|
fc0ad622eb | ||
|
|
2c5cdb8780 | ||
|
|
9a309f32fa | ||
|
|
e2e54d342a | ||
|
|
42f7529495 | ||
|
|
f172151252 | ||
|
|
e2ad38d3e0 | ||
|
|
40cc32fc5a | ||
|
|
436c034fdd | ||
|
|
286b1848d9 | ||
|
|
7fffebf6df | ||
|
|
b1764478ec | ||
|
|
6b6a799206 | ||
|
|
0a82ed901e | ||
|
|
d733c9ed14 | ||
|
|
a752ea489c | ||
|
|
876a24586f | ||
|
|
ea2779cf9a | ||
|
|
77aa36163d | ||
|
|
b581d8ecb7 | ||
|
|
83b404d01e | ||
|
|
8deb92c3e5 | ||
|
|
20a6e0170c | ||
|
|
944a78807c | ||
|
|
0b02d294f4 | ||
|
|
a5d86536c3 | ||
|
|
71c08cfe0c | ||
|
|
8ab0d5fc48 | ||
|
|
57f81ee4c8 | ||
|
|
5c28adf266 | ||
|
|
5a57398f81 | ||
|
|
8121a384ef | ||
|
|
8666197e05 | ||
|
|
1ea2b8bbcb | ||
|
|
a71cedd8a9 | ||
|
|
04c5f583f6 | ||
|
|
7716ff4e8c | ||
|
|
6b51a116d1 | ||
|
|
b2f14dc177 | ||
|
|
da1d3b82f9 | ||
|
|
6282d8c828 | ||
|
|
73129b0ce5 | ||
|
|
f71e7a2f28 | ||
|
|
341da327e3 | ||
|
|
3d8adfa7e4 | ||
|
|
279d7769f5 | ||
|
|
b7d3b40353 | ||
|
|
7ecd691ee2 | ||
|
|
f3398c7dec | ||
|
|
90644e662d | ||
|
|
f5c5cb7fb9 | ||
|
|
312e79921a | ||
|
|
b83d346a86 | ||
|
|
3eed67f108 | ||
|
|
15f0bc63b2 | ||
|
|
0a4b0ec929 | ||
|
|
560f6cbf24 | ||
|
|
9165e0238f | ||
|
|
97d6be6809 | ||
|
|
4de14eba0c | ||
|
|
6c64023bf7 | ||
|
|
a923c288e6 | ||
|
|
4c1d8e8e85 | ||
|
|
02f2def88b | ||
|
|
4bcacc5d68 | ||
|
|
913dbe6b1a | ||
|
|
ce8164dd87 | ||
|
|
a5b412f546 | ||
|
|
82bb352624 | ||
|
|
ebbf2659b1 | ||
|
|
d0084becea | ||
|
|
6af2b37ac2 | ||
|
|
814fc6eabd | ||
|
|
50278a679a | ||
|
|
d42e9c75ef | ||
|
|
00b3dced2c | ||
|
|
5c0c00188f | ||
|
|
2ec56626f3 | ||
|
|
e87456b2f8 | ||
|
|
3609b515e5 | ||
|
|
a1609542c3 | ||
|
|
4c4625583a | ||
|
|
7b479316ea | ||
|
|
b021c7690f | ||
|
|
2be060796e | ||
|
|
1b4d55cca4 | ||
|
|
a8cea4119d | ||
|
|
e247aace8d | ||
|
|
41553e9b86 | ||
|
|
e875587260 | ||
|
|
5377483345 | ||
|
|
4112acfb8d | ||
|
|
f3bc02e11c | ||
|
|
8e411a898b | ||
|
|
915edbecc9 | ||
|
|
975a6c34bf | ||
|
|
cdd988b4de | ||
|
|
b58bc97422 | ||
|
|
482688ac3c | ||
|
|
aea31b5e28 | ||
|
|
d7cbc53b4b | ||
|
|
f74c6c2d19 | ||
|
|
3080d2ddc4 | ||
|
|
4ad5881760 | ||
|
|
7e55d1a4fd | ||
|
|
7ef5eed6e2 | ||
|
|
10aa41a7ea | ||
|
|
1f9b362b6f | ||
|
|
4bf9bfb521 | ||
|
|
1d7119114d | ||
|
|
e1b6df6fb1 | ||
|
|
7cf38bb01e | ||
|
|
e34ec22845 | ||
|
|
46506abeb8 | ||
|
|
95654cc4d4 | ||
|
|
47aded820d | ||
|
|
24444ebf08 | ||
|
|
bdc0df8350 | ||
|
|
b2c9a2973c | ||
|
|
da2a347511 | ||
|
|
6fbc3ba060 | ||
|
|
02eff06cd3 | ||
|
|
7586d4b494 | ||
|
|
9059f0fee6 | ||
|
|
c2af9e3d20 | ||
|
|
0b51366526 | ||
|
|
e40260bd9c | ||
|
|
cf2842840d | ||
|
|
17fa8fcb2c | ||
|
|
0d2f9864e2 | ||
|
|
89cbd91204 | ||
|
|
f4d9b57887 | ||
|
|
4b2e4afca5 | ||
|
|
dd1ba30c48 | ||
|
|
3ba4570691 | ||
|
|
848cfabcba | ||
|
|
1bbd10b909 | ||
|
|
a16a4f813d | ||
|
|
91cfa963b2 | ||
|
|
a35557eb62 | ||
|
|
aad4e47b6a | ||
|
|
1b177723ae | ||
|
|
99dba92bd3 | ||
|
|
e13ccff056 | ||
|
|
46528dd29d | ||
|
|
4f611ad810 | ||
|
|
af41985a64 | ||
|
|
d0864e06b5 | ||
|
|
6f0366e146 | ||
|
|
e0cdbcb28c | ||
|
|
f19b99194c | ||
|
|
43a55e2e35 | ||
|
|
b2743825ca | ||
|
|
d4f6cce56e | ||
|
|
6092d206b6 | ||
|
|
c8ad83cc91 | ||
|
|
7d31071ff8 | ||
|
|
c975ef15f1 | ||
|
|
f855011d34 | ||
|
|
fbcf0929d8 | ||
|
|
d89e75cbe8 | ||
|
|
ccaa42ad74 | ||
|
|
56d8dce622 | ||
|
|
c79baf98cf | ||
|
|
d1cab9f68c | ||
|
|
69c5c93353 | ||
|
|
28ebd683e4 | ||
|
|
d752edd625 | ||
|
|
1dab45d493 | ||
|
|
b99982d02b | ||
|
|
fff17ac6c1 | ||
|
|
4086257983 | ||
|
|
bd9e0ac281 | ||
|
|
b075d6db5e | ||
|
|
befd79cf14 | ||
|
|
07f68d2b14 | ||
|
|
d14889bd27 | ||
|
|
91e40c14f9 | ||
|
|
b7b2206262 | ||
|
|
f344d0319c | ||
|
|
0c8a1682b6 | ||
|
|
39866be3f1 | ||
|
|
947e82fa0f | ||
|
|
0335a64a21 | ||
|
|
a9e57e1c34 | ||
|
|
8a8279f97a | ||
|
|
b968889552 | ||
|
|
4068df5e50 | ||
|
|
dc42370322 | ||
|
|
8c24f14ee5 | ||
|
|
494d1743a2 | ||
|
|
4a30d9f6bb | ||
|
|
ed6d25067c | ||
|
|
445ae7e10e | ||
|
|
6f45609161 | ||
|
|
f1230e47f7 | ||
|
|
7e0ef6d43e | ||
|
|
14f9da544a | ||
|
|
5a84036e16 | ||
|
|
4dccf7b7b5 | ||
|
|
66060dbed4 | ||
|
|
cfb824588f | ||
|
|
d2b4316d7a | ||
|
|
3af69b433d | ||
|
|
a6733fa255 | ||
|
|
4277c54009 | ||
|
|
66baa7554a | ||
|
|
ffca4b0543 | ||
|
|
3e3c48314f | ||
|
|
06ff450d31 | ||
|
|
07c57cc640 | ||
|
|
a67f10c99e | ||
|
|
2882bcbf7b | ||
|
|
67cc5b0280 | ||
|
|
b42b178b71 | ||
|
|
7de05cd173 | ||
|
|
3db43743d9 | ||
|
|
14638e4ed8 | ||
|
|
e756b93810 | ||
|
|
358d83dcfc | ||
|
|
331c231a94 | ||
|
|
4403b65bae | ||
|
|
a27d80d765 | ||
|
|
04272fff81 | ||
|
|
e963708c54 | ||
|
|
08c4542847 | ||
|
|
553e9270e5 | ||
|
|
8a7297e131 | ||
|
|
0f260da8e6 | ||
|
|
77560ab3a8 | ||
|
|
e3b2f2d9a8 | ||
|
|
74e01a52b9 | ||
|
|
dc28ba42ef | ||
|
|
406150620a | ||
|
|
43f59a1135 | ||
|
|
5c02eaa66c | ||
|
|
b4eac84097 | ||
|
|
ec3b356f86 | ||
|
|
bf99d5c299 | ||
|
|
a297131440 | ||
|
|
bae2161ee3 | ||
|
|
0fe0de1a7f | ||
|
|
e7845115f6 | ||
|
|
bc11c3fab2 | ||
|
|
1b7546f3f9 | ||
|
|
663be30117 | ||
|
|
cf34713518 | ||
|
|
3f56a8ec53 | ||
|
|
35d105588b | ||
|
|
122d988ed2 | ||
|
|
9fcc5e7a67 | ||
|
|
9a492c3731 | ||
|
|
4f752031f3 | ||
|
|
19be8bb891 | ||
|
|
693e1b08c7 | ||
|
|
9aad380518 | ||
|
|
8c518c8d58 | ||
|
|
9af89a19db | ||
|
|
939b18b86c | ||
|
|
108e775a15 | ||
|
|
653692ade0 | ||
|
|
72c6bfee7e | ||
|
|
ac92939429 | ||
|
|
052957bbd0 | ||
|
|
97e6afe3dc | ||
|
|
1fd028dfb8 | ||
|
|
c73866f47c | ||
|
|
b0e120abee | ||
|
|
b2da38d401 | ||
|
|
cabe2579fa | ||
|
|
18a845ac55 | ||
|
|
a4d14f8259 | ||
|
|
9d084e62f7 | ||
|
|
0393fcd704 | ||
|
|
edb5b2ed5e |
@@ -38,9 +38,16 @@ SEND_REGISTRATION_MAIL=true
|
||||
SEND_ERROR_MESSAGE=true
|
||||
SHOW_INCOMPLETE_TRANSLATIONS=false
|
||||
|
||||
CACHE_PREFIX=firefly
|
||||
|
||||
GOOGLE_MAPS_API_KEY=
|
||||
ANALYTICS_ID=
|
||||
SITE_OWNER=mail@example.com
|
||||
|
||||
PUSHER_KEY=
|
||||
PUSHER_SECRET=
|
||||
PUSHER_APP_ID=
|
||||
|
||||
DEMO_USERNAME=
|
||||
DEMO_PASSWORD=
|
||||
|
||||
|
||||
1
.github/CONTRIBUTING
vendored
Normal file
1
.github/CONTRIBUTING
vendored
Normal file
@@ -0,0 +1 @@
|
||||
If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.github.io/requested-features/) first. Thanks!
|
||||
@@ -2,7 +2,50 @@
|
||||
tools:
|
||||
external_code_coverage: false
|
||||
filter:
|
||||
paths:
|
||||
- app/*
|
||||
- public/js/ff/*
|
||||
excluded_paths:
|
||||
- app/Support/Migration/*
|
||||
- app/database/migrations/*
|
||||
- database/migrations/*
|
||||
- "database/migrations/*"
|
||||
- "bootstrap/*"
|
||||
- "config/*"
|
||||
- "docker/*"
|
||||
- "public/js/lib/*"
|
||||
- "public/lib/adminlte/js/*"
|
||||
- "public/lib/bootstrap/js/*"
|
||||
- "resources/*"
|
||||
- "routes/*"
|
||||
- "storage/*"
|
||||
checks:
|
||||
php:
|
||||
use_self_instead_of_fqcn: true
|
||||
uppercase_constants: true
|
||||
return_doc_comments: true
|
||||
return_doc_comment_if_not_inferrable: true
|
||||
remove_extra_empty_lines: true
|
||||
parameter_doc_comments: true
|
||||
optional_parameters_at_the_end: true
|
||||
no_short_variable_names:
|
||||
minimum: '3'
|
||||
no_short_method_names:
|
||||
minimum: '3'
|
||||
no_long_variable_names:
|
||||
maximum: '20'
|
||||
no_goto: true
|
||||
newline_at_end_of_file: true
|
||||
encourage_single_quotes: true
|
||||
avoid_todo_comments: true
|
||||
avoid_perl_style_comments: true
|
||||
avoid_fixme_comments: true
|
||||
avoid_multiple_statements_on_same_line: true
|
||||
align_assignments: true
|
||||
duplication: false
|
||||
javascript: true
|
||||
|
||||
coding_style:
|
||||
php:
|
||||
spaces:
|
||||
around_operators:
|
||||
concatenation: true
|
||||
other:
|
||||
after_type_cast: false
|
||||
@@ -5,14 +5,13 @@ php:
|
||||
|
||||
install:
|
||||
- phpenv config-rm xdebug.ini
|
||||
- composer selfupdate
|
||||
- rm composer.lock
|
||||
- composer update --no-scripts
|
||||
- php artisan clear-compiled
|
||||
- php artisan optimize
|
||||
- php artisan env
|
||||
- ./test.sh -r
|
||||
- php artisan env
|
||||
- cp .env.testing .env
|
||||
- mv storage/database/databasecopy.sqlite storage/database/database.sqlite
|
||||
|
||||
script:
|
||||
- phpunit
|
||||
87
CHANGELOG.md
87
CHANGELOG.md
@@ -2,7 +2,84 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [4.2.1] - 2015-05-25
|
||||
|
||||
## [4.3.2] - 2017-01-09
|
||||
|
||||
An intermediate release because something in the Twig and Twigbridge libraries is broken and I have to make sure it doesn't affect you guys. But some cool features were on their way so there's that oo.
|
||||
|
||||
### Added
|
||||
- Some code for issue #475, consistent overviews.
|
||||
- Better currency display. Make sure you have locale packages installed.
|
||||
|
||||
### Changed
|
||||
- Uses a new version of Laravel.
|
||||
|
||||
### Fixed
|
||||
- The password reset routine was broken.
|
||||
- Issue #522, thanks to @xpfgsyb
|
||||
- Issue #524, thanks to @worldworm
|
||||
- Issue #526, thanks to @worldworm
|
||||
- Issue #528, thanks to @skibbipl
|
||||
- Various other fixes.
|
||||
|
||||
## [4.3.1] - 2017-01-04
|
||||
### Added
|
||||
- Support for Russian and Polish.
|
||||
- Support for a proper demo website.
|
||||
- Support for custom decimal places in currencies (#506, suggested by @xpfgsyb).
|
||||
- Most amounts are now right-aligned (#511, suggested by @xpfgsyb).
|
||||
- German is now a "complete" language, more than 75% translated!
|
||||
|
||||
### Changed
|
||||
- **[New Github repository!](github.com/firefly-iii/firefly-iii)**
|
||||
- Better category overview.
|
||||
- #502, thanks to @zjean
|
||||
|
||||
### Removed
|
||||
- Removed a lot of administration functions.
|
||||
- Removed ability to activate users.
|
||||
|
||||
### Fixed
|
||||
- #501, thanks to @zjean
|
||||
- #513, thanks to @skibbipl
|
||||
|
||||
### Security
|
||||
- #519, thanks to @xpfgsyb
|
||||
|
||||
## [4.3.0] - 2015-12-26
|
||||
### Added
|
||||
- New method of keeping track of available budget, see issue #489
|
||||
- Support for Spanish
|
||||
- Firefly III now has an extended demo mode. Will expand further in the future.
|
||||
|
||||
|
||||
### Changed
|
||||
- New favicon
|
||||
- Import routine no longer gives transactions a description #483
|
||||
|
||||
|
||||
### Removed
|
||||
- All test data generation code.
|
||||
|
||||
### Fixed
|
||||
- Removed import accounts from search results #478
|
||||
- Redirect after delete will no longer go back to deleted item #477
|
||||
- Cannot math #482
|
||||
- Fixed bug in virtual balance field #479
|
||||
|
||||
## [4.2.2] - 2016-12-18
|
||||
### Added
|
||||
- New budget report (still a bit of a beta)
|
||||
- Can now edit user
|
||||
|
||||
### Changed
|
||||
- New config for specific events. Still need to build Notifications.
|
||||
|
||||
### Fixed
|
||||
- Various bugs
|
||||
- Issue #472 thanks to @zjean
|
||||
|
||||
## [4.2.1] - 2016-12-09
|
||||
### Added
|
||||
- BIC support (see #430)
|
||||
- New category report section and chart (see the general financial report)
|
||||
@@ -245,7 +322,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
### Fixed
|
||||
- Bug in the mass edit routines.
|
||||
- Firefly III over a proxy will now work (see [issue #290](https://github.com/JC5/firefly-iii/issues/290)), thanks @dfiel for reporting.
|
||||
- Firefly III over a proxy will now work (see [issue #290](https://github.com/firefly-iii/firefly-iii/issues/290)), thanks @dfiel for reporting.
|
||||
- Sneaky bug in the import routine, fixed by @Bonno
|
||||
|
||||
## [3.10.1] - 2016-08-25
|
||||
@@ -266,7 +343,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Fixed a bug where a migration would check an empty table name.
|
||||
- Fixed various bugs in the import routine.
|
||||
- Fixed various bugs in the piggy banks pages.
|
||||
- Fixed a bug in the ``firefly:verify`` routine
|
||||
- Fixed a bug in the `firefly:verify` routine
|
||||
|
||||
## [3.10] - 2015-05-25
|
||||
### Added
|
||||
@@ -295,11 +372,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Bulk update problems, #280, thanks @stickgrinder
|
||||
- Fixed various problems with amount reporting of split transactions.
|
||||
|
||||
[3.9.1]
|
||||
## [3.9.1]
|
||||
### Fixed
|
||||
- Fixed a bug where removing money from a piggy bank would not work. See issue #265 and #269
|
||||
|
||||
[3.9.0]
|
||||
## [3.9.0]
|
||||
### Added
|
||||
- @zjean has added code that allows you to force "https://"-URL's.
|
||||
- @tonicospinelli has added Portuguese (Brazil) translations.
|
||||
|
||||
16
README.md
16
README.md
@@ -1,15 +1,17 @@
|
||||
# Firefly III [](https://secure.php.net/downloads.php#v7.0.4) [](https://packagist.org/packages/grumpydictator/firefly-iii) [](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master)
|
||||
# Firefly III: A personal finances manager
|
||||
|
||||
[](https://travis-ci.org/JC5/firefly-iii)
|
||||
[](https://secure.php.net/downloads.php#v7.0.4) [](https://packagist.org/packages/grumpydictator/firefly-iii) [](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/?branch=master) [](https://travis-ci.org/firefly-iii/firefly-iii) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA)
|
||||
|
||||
## A personal finances manager
|
||||
[](https://i.nder.be/h2b37243) [](https://i.nder.be/hv70pbwc)
|
||||
|
||||
[](https://i.nder.be/hhfv03hp) [](https://i.nder.be/hhmwmqw9)
|
||||
|
||||
[](https://i.nder.be/g63q05m0) [](https://i.nder.be/c2g30ngg)
|
||||
[](https://i.nder.be/ccn0u2mp) [](https://i.nder.be/gm8hbh7z)
|
||||
|
||||
"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.
|
||||
|
||||
## Try it out!
|
||||
|
||||
Try out Firefly III on the [demo site](https://firefly-iii.nder.be/).
|
||||
|
||||
## Installation
|
||||
|
||||
To install Firefly III, you'll need a web server (preferrably on Linux) and access to the command line. Then, please read the [installation guide](https://firefly-iii.github.io/installation-guide/).
|
||||
@@ -29,4 +31,6 @@ Firefly works on the principle that if you know where you're money is going, you
|
||||
|
||||
Firefly is pretty awesome. [You can read more about Firefly III, and its features, on the Github Pages](https://firefly-iii.github.io/).
|
||||
|
||||
If you like Firefly and if it helps you save lots of money, why not send me [a dime for every dollar saved](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA) (this is a joke, although the Paypal form works just fine, try it!)
|
||||
|
||||
If you want to contact me, please open an issue or [email me](mailto:thegrumpydictator@gmail.com).
|
||||
|
||||
@@ -50,11 +50,10 @@ class CreateImport extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) // cannot be helped
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// find the file
|
||||
/** @var UserRepositoryInterface $userRepository */
|
||||
$userRepository = app(UserRepositoryInterface::class);
|
||||
$file = $this->argument('file');
|
||||
@@ -67,7 +66,6 @@ class CreateImport extends Command
|
||||
return;
|
||||
}
|
||||
|
||||
// try to parse configuration data:
|
||||
$configurationData = json_decode(file_get_contents($configuration));
|
||||
if (is_null($configurationData)) {
|
||||
$this->error(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd));
|
||||
@@ -82,21 +80,17 @@ class CreateImport extends Command
|
||||
|
||||
/** @var ImportJobRepositoryInterface $jobRepository */
|
||||
$jobRepository = app(ImportJobRepositoryInterface::class, [$user]);
|
||||
|
||||
$job = $jobRepository->create($type);
|
||||
$this->line(sprintf('Created job "%s"...', $job->key));
|
||||
|
||||
// put the file in the proper place:
|
||||
Artisan::call('firefly:encrypt', ['file' => $file, 'key' => $job->key]);
|
||||
$this->line('Stored import data...');
|
||||
|
||||
// store the configuration in the job:
|
||||
$job->configuration = $configurationData;
|
||||
$job->status = 'settings_complete';
|
||||
$job->save();
|
||||
$this->line('Stored configuration...');
|
||||
|
||||
// if user wants to run it, do!
|
||||
if ($this->option('start') === true) {
|
||||
$this->line('The import will start in a moment. This process is not visible...');
|
||||
Log::debug('Go for import!');
|
||||
@@ -109,10 +103,10 @@ class CreateImport extends Command
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five exactly.
|
||||
*/
|
||||
private function validArguments(): bool
|
||||
{
|
||||
// find the file
|
||||
/** @var UserRepositoryInterface $userRepository */
|
||||
$userRepository = app(UserRepositoryInterface::class);
|
||||
$file = $this->argument('file');
|
||||
|
||||
@@ -18,6 +18,7 @@ use FireflyIII\Import\Logging\CommandHandler;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
@@ -69,32 +70,15 @@ class Import extends Command
|
||||
$monolog = Log::getMonolog();
|
||||
$handler = new CommandHandler($this);
|
||||
$monolog->pushHandler($handler);
|
||||
$importProcedure = new ImportProcedure;
|
||||
$result = $importProcedure->runImport($job);
|
||||
|
||||
$result = ImportProcedure::runImport($job);
|
||||
|
||||
|
||||
/**
|
||||
* @var int $index
|
||||
* @var TransactionJournal $journal
|
||||
*/
|
||||
foreach ($result as $index => $journal) {
|
||||
if (!is_null($journal->id)) {
|
||||
$this->line(sprintf('Line #%d has been imported as transaction #%d.', $index, $journal->id));
|
||||
continue;
|
||||
}
|
||||
$this->error(sprintf('Could not store line #%d', $index));
|
||||
}
|
||||
|
||||
// display result to user:
|
||||
$this->presentResults($result);
|
||||
$this->line('The import has completed.');
|
||||
|
||||
// get any errors from the importer:
|
||||
$extendedStatus = $job->extended_status;
|
||||
if (isset($extendedStatus['errors']) && count($extendedStatus['errors']) > 0) {
|
||||
$this->line(sprintf('The following %d error(s) occured during the import:', count($extendedStatus['errors'])));
|
||||
foreach ($extendedStatus['errors'] as $error) {
|
||||
$this->error($error);
|
||||
}
|
||||
}
|
||||
$this->presentErrors($job);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -120,4 +104,36 @@ class Import extends Command
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ImportJob $job
|
||||
*/
|
||||
private function presentErrors(ImportJob $job)
|
||||
{
|
||||
$extendedStatus = $job->extended_status;
|
||||
if (isset($extendedStatus['errors']) && count($extendedStatus['errors']) > 0) {
|
||||
$this->line(sprintf('The following %d error(s) occured during the import:', count($extendedStatus['errors'])));
|
||||
foreach ($extendedStatus['errors'] as $error) {
|
||||
$this->error($error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $result
|
||||
*/
|
||||
private function presentResults(Collection $result)
|
||||
{
|
||||
/**
|
||||
* @var int $index
|
||||
* @var TransactionJournal $journal
|
||||
*/
|
||||
foreach ($result as $index => $journal) {
|
||||
if (!is_null($journal->id)) {
|
||||
$this->line(sprintf('Line #%d has been imported as transaction #%d.', $index, $journal->id));
|
||||
continue;
|
||||
}
|
||||
$this->error(sprintf('Could not store line #%d', $index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* MoveRepository.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Console\Commands;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class MoveRepository
|
||||
*
|
||||
* @package FireflyIII\Console\Commands
|
||||
*/
|
||||
class MoveRepository extends Command
|
||||
{
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Alerts the user that the Github repository will move, if they are interested to know this.';
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly:github-move';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$moveDate = new Carbon('2017-01-01');
|
||||
$final = new Carbon('2017-03-01');
|
||||
$now = new Carbon;
|
||||
|
||||
// display message before 2017-01-01
|
||||
if ($moveDate > $now) {
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
$this->line('');
|
||||
$this->line('The Github repository for Firefly III will MOVE');
|
||||
$this->line('This move will be on January 1st 2017');
|
||||
$this->line('');
|
||||
$this->error('READ THIS WIKI PAGE FOR MORE INFORMATION');
|
||||
$this->line('');
|
||||
$this->info('https://github.com/firefly-iii/help/wiki/New-Github-repository');
|
||||
$this->line('');
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
}
|
||||
|
||||
// display message after 2017-01-01 but before 2017-03-01
|
||||
if ($moveDate <= $now && $now <= $final) {
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
$this->line('');
|
||||
$this->line('The Github repository for Firefly III has MOVED');
|
||||
$this->line('This move was on January 1st 2017!');
|
||||
$this->line('');
|
||||
$this->error('READ THIS WIKI PAGE FOR MORE INFORMATION');
|
||||
$this->line('');
|
||||
$this->info('https://github.com/firefly-iii/help/wiki/New-Github-repository');
|
||||
$this->line('');
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -60,42 +60,26 @@ class ScanAttachments extends Command
|
||||
/** @var Attachment $attachment */
|
||||
foreach ($attachments as $attachment) {
|
||||
$fileName = $attachment->fileName();
|
||||
|
||||
// try to grab file content:
|
||||
try {
|
||||
$content = $disk->get($fileName);
|
||||
} catch (FileNotFoundException $e) {
|
||||
$this->error(sprintf('Could not find data for attachment #%d', $attachment->id));
|
||||
continue;
|
||||
}
|
||||
// try to decrypt content.
|
||||
try {
|
||||
$decrypted = Crypt::decrypt($content);
|
||||
} catch (DecryptException $e) {
|
||||
$this->error(sprintf('Could not decrypt data of attachment #%d', $attachment->id));
|
||||
continue;
|
||||
}
|
||||
|
||||
// make temp file:
|
||||
$tmpfname = tempnam(sys_get_temp_dir(), 'FireflyIII');
|
||||
|
||||
// store content in temp file:
|
||||
file_put_contents($tmpfname, $decrypted);
|
||||
|
||||
// get md5 and mime
|
||||
$md5 = md5_file($tmpfname);
|
||||
$mime = mime_content_type($tmpfname);
|
||||
|
||||
// update attachment:
|
||||
$attachment->md5 = $md5;
|
||||
$attachment->mime = $mime;
|
||||
$attachment->save();
|
||||
|
||||
|
||||
$this->line(sprintf('Fixed attachment #%d', $attachment->id));
|
||||
|
||||
// find file:
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace FireflyIII\Console\Commands;
|
||||
|
||||
|
||||
use DB;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -57,8 +59,29 @@ class UpgradeDatabase extends Command
|
||||
public function handle()
|
||||
{
|
||||
$this->setTransactionIdentifier();
|
||||
$this->migrateRepetitions();
|
||||
}
|
||||
|
||||
|
||||
private function migrateRepetitions()
|
||||
{
|
||||
if (!Schema::hasTable('budget_limits')) {
|
||||
return;
|
||||
}
|
||||
// get all budget limits with end_date NULL
|
||||
$set = BudgetLimit::whereNull('end_date')->get();
|
||||
$this->line(sprintf('Found %d budget limit(s) to update', $set->count()));
|
||||
/** @var BudgetLimit $budgetLimit */
|
||||
foreach ($set as $budgetLimit) {
|
||||
// get limit repetition (should be just one):
|
||||
/** @var LimitRepetition $repetition */
|
||||
$repetition = $budgetLimit->limitrepetitions()->first();
|
||||
if (!is_null($repetition)) {
|
||||
$budgetLimit->end_date = $repetition->enddate;
|
||||
$budgetLimit->save();
|
||||
$this->line(sprintf('Updated budget limit #%d', $budgetLimit->id));
|
||||
$repetition->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,8 +108,18 @@ class UpgradeDatabase extends Command
|
||||
$journalIds = array_unique($result->pluck('id')->toArray());
|
||||
|
||||
foreach ($journalIds as $journalId) {
|
||||
// grab all positive transactiosn from this journal that are not deleted.
|
||||
// for each one, grab the negative opposing one which has 0 as an identifier and give it the same identifier.
|
||||
$this->updateJournal(intval($journalId));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* grab all positive transactiosn from this journal that are not deleted. for each one, grab the negative opposing one
|
||||
* which has 0 as an identifier and give it the same identifier.
|
||||
*
|
||||
* @param int $journalId
|
||||
*/
|
||||
private function updateJournal(int $journalId)
|
||||
{
|
||||
$identifier = 0;
|
||||
$processed = [];
|
||||
$transactions = Transaction::where('transaction_journal_id', $journalId)->where('amount', '>', 0)->get();
|
||||
@@ -107,7 +140,8 @@ class UpgradeDatabase extends Command
|
||||
$this->error(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version')));
|
||||
$this->error('Please run "php artisan migrate" to add this field to the table.');
|
||||
$this->info('Then, run "php artisan firefly:upgrade-database" to try again.');
|
||||
break 2;
|
||||
|
||||
return;
|
||||
}
|
||||
if (!is_null($opposing)) {
|
||||
// give both a new identifier:
|
||||
@@ -123,4 +157,3 @@ class UpgradeDatabase extends Command
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,19 +63,20 @@ class UpgradeFireflyInstructions extends Command
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (is_null($text)) {
|
||||
$this->line(sprintf('Thank you for installing Firefly III, v%s', $version));
|
||||
$this->info('There are no extra upgrade instructions.');
|
||||
$this->line('Firefly III should be ready for use.');
|
||||
} else {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
$this->line('');
|
||||
$this->line(sprintf('Thank you for installing Firefly III, v%s', $version));
|
||||
$this->info(wordwrap($text));
|
||||
$this->line('');
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@ use Crypt;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
@@ -126,16 +124,15 @@ class VerifyDatabase extends Command
|
||||
{
|
||||
$set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id')
|
||||
->leftJoin('users', 'budgets.user_id', '=', 'users.id')
|
||||
->groupBy(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email'])
|
||||
->groupBy(['budgets.id', 'budgets.name', 'budgets.encrypted', 'budgets.user_id', 'users.email'])
|
||||
->whereNull('budget_limits.id')
|
||||
->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email']);
|
||||
->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'budgets.encrypted', 'users.email']);
|
||||
|
||||
/** @var stdClass $entry */
|
||||
/** @var Budget $entry */
|
||||
foreach ($set as $entry) {
|
||||
|
||||
$line = sprintf(
|
||||
'Notice: User #%d (%s) has budget #%d ("%s") which has no budget limits.',
|
||||
$entry->user_id, $entry->email, $entry->id, Crypt::decrypt($entry->name)
|
||||
$entry->user_id, $entry->email, $entry->id, $entry->name
|
||||
);
|
||||
$this->line($line);
|
||||
}
|
||||
@@ -176,10 +173,8 @@ class VerifyDatabase extends Command
|
||||
$configuration = [
|
||||
// a withdrawal can not have revenue account:
|
||||
TransactionType::WITHDRAWAL => [AccountType::REVENUE],
|
||||
|
||||
// deposit cannot have an expense account:
|
||||
TransactionType::DEPOSIT => [AccountType::EXPENSE],
|
||||
|
||||
// transfer cannot have either:
|
||||
TransactionType::TRANSFER => [AccountType::EXPENSE, AccountType::REVENUE],
|
||||
];
|
||||
@@ -192,7 +187,10 @@ class VerifyDatabase extends Command
|
||||
->where('transaction_types.type', $transactionType)
|
||||
->whereIn('account_types.type', $accountTypes)
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->get(['transaction_journals.id', 'transaction_journals.user_id', 'users.email', 'account_types.type as a_type', 'transaction_types.type']);
|
||||
->get(
|
||||
['transaction_journals.id', 'transaction_journals.user_id', 'users.email', 'account_types.type as a_type',
|
||||
'transaction_types.type']
|
||||
);
|
||||
foreach ($set as $entry) {
|
||||
$this->error(
|
||||
sprintf(
|
||||
|
||||
13
app/Console/Kernel.php
Executable file → Normal file
13
app/Console/Kernel.php
Executable file → Normal file
@@ -21,7 +21,6 @@ use FireflyIII\Console\Commands\ScanAttachments;
|
||||
use FireflyIII\Console\Commands\UpgradeDatabase;
|
||||
use FireflyIII\Console\Commands\UpgradeFireflyInstructions;
|
||||
use FireflyIII\Console\Commands\VerifyDatabase;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
/**
|
||||
@@ -64,7 +63,6 @@ class Kernel extends ConsoleKernel
|
||||
EncryptFile::class,
|
||||
ScanAttachments::class,
|
||||
UpgradeDatabase::class,
|
||||
MoveRepository::class,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -76,15 +74,4 @@ class Kernel extends ConsoleKernel
|
||||
{
|
||||
require base_path('routes/console.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ConfirmedUser.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Events;
|
||||
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class ConfirmedUser
|
||||
*
|
||||
* @package FireflyIII\Events
|
||||
*/
|
||||
class ConfirmedUser extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
public $ipAddress;
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* Create a new event instance. This event is triggered when a user confirms their new account.
|
||||
*
|
||||
* @param User $user
|
||||
* @param string $ipAddress
|
||||
*/
|
||||
public function __construct(User $user, string $ipAddress)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->ipAddress = $ipAddress;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ResentConfirmation.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Events;
|
||||
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class ResentConfirmation
|
||||
*
|
||||
* @package FireflyIII\Events
|
||||
*/
|
||||
class ResentConfirmation extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
public $ipAddress;
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* Create a new event instance. This event is triggered when a users wants a new confirmation.
|
||||
*
|
||||
* @param User $user
|
||||
* @param string $ipAddress
|
||||
*/
|
||||
public function __construct(User $user, string $ipAddress)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->ipAddress = $ipAddress;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* StoredBudgetLimit.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Events;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class StoredBudgetLimit
|
||||
*
|
||||
* @package FireflyIII\Events
|
||||
*/
|
||||
class StoredBudgetLimit extends Event
|
||||
{
|
||||
|
||||
use SerializesModels;
|
||||
|
||||
/** @var BudgetLimit */
|
||||
public $budgetLimit;
|
||||
|
||||
/** @var Carbon */
|
||||
public $end; // the only variable we can't get from the budget limit (if necessary).
|
||||
|
||||
/**
|
||||
* BudgetLimitEvents constructor.
|
||||
*
|
||||
* @param BudgetLimit $budgetLimit
|
||||
* @param Carbon $end
|
||||
*/
|
||||
public function __construct(BudgetLimit $budgetLimit, Carbon $end)
|
||||
{
|
||||
//
|
||||
$this->budgetLimit = $budgetLimit;
|
||||
$this->end = $end;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* UpdatedBudgetLimit.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Events;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class UpdatedBudgetLimit
|
||||
*
|
||||
* @package FireflyIII\Events
|
||||
*/
|
||||
class UpdatedBudgetLimit extends Event
|
||||
{
|
||||
|
||||
use SerializesModels;
|
||||
|
||||
/** @var BudgetLimit */
|
||||
public $budgetLimit;
|
||||
|
||||
/** @var Carbon */
|
||||
public $end; // the only variable we can't get from the budget limit (if necessary).
|
||||
|
||||
/**
|
||||
* BudgetLimitEvents constructor.
|
||||
*
|
||||
* @param BudgetLimit $budgetLimit
|
||||
* @param Carbon $end
|
||||
*/
|
||||
public function __construct(BudgetLimit $budgetLimit, Carbon $end)
|
||||
{
|
||||
//
|
||||
$this->budgetLimit = $budgetLimit;
|
||||
$this->end = $end;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
6
app/Exceptions/Handler.php
Executable file → Normal file
6
app/Exceptions/Handler.php
Executable file → Normal file
@@ -21,6 +21,7 @@ use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Illuminate\Session\TokenMismatchException;
|
||||
use Illuminate\Validation\ValidationException as ValException;
|
||||
use Request;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
/**
|
||||
@@ -72,6 +73,7 @@ class Handler extends ExceptionHandler
|
||||
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
|
||||
*
|
||||
* @param Exception $exception
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@@ -98,8 +100,8 @@ class Handler extends ExceptionHandler
|
||||
];
|
||||
|
||||
// create job that will mail.
|
||||
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
|
||||
$job = new MailError($userData, env('SITE_OWNER', ''), $ip, $data);
|
||||
$ipAddress = Request::ip() ?? '0.0.0.0';
|
||||
$job = new MailError($userData, env('SITE_OWNER', ''), $ipAddress, $data);
|
||||
dispatch($job);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ interface CollectorInterface
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
*/
|
||||
public function setEntries(Collection $entries);
|
||||
|
||||
|
||||
@@ -287,7 +287,7 @@ class JournalExportCollector extends BasicCollector implements CollectorInterfac
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
||||
*/
|
||||
private function getWorkSet()
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@ use Crypt;
|
||||
*
|
||||
*
|
||||
* Class Entry
|
||||
* @SuppressWarnings(PHPMD.LongVariable)
|
||||
*
|
||||
* @package FireflyIII\Export\Entry
|
||||
*/
|
||||
@@ -72,33 +73,21 @@ final class Entry
|
||||
public static function fromObject($object): Entry
|
||||
{
|
||||
$entry = new self;
|
||||
|
||||
// journal information:
|
||||
$entry->journal_id = $object->transaction_journal_id;
|
||||
$entry->description = $object->journal_encrypted === 1 ? Crypt::decrypt($object->journal_description) : $object->journal_description;
|
||||
$entry->amount = round($object->amount, 2); // always positive
|
||||
$entry->description = self::decrypt($object->journal_encrypted, $object->journal_description);
|
||||
$entry->amount = $object->amount;
|
||||
$entry->date = $object->date;
|
||||
$entry->transaction_type = $object->transaction_type;
|
||||
$entry->currency_code = $object->transaction_currency_code;
|
||||
|
||||
// source information:
|
||||
$entry->source_account_id = $object->account_id;
|
||||
$entry->source_account_name = $object->account_name_encrypted === 1 ? Crypt::decrypt($object->account_name) : $object->account_name;
|
||||
|
||||
|
||||
// destination information
|
||||
$entry->source_account_name = self::decrypt($object->account_name_encrypted, $object->account_name);
|
||||
$entry->destination_account_id = $object->opposing_account_id;
|
||||
$entry->destination_account_name = $object->opposing_account_encrypted === 1 ? Crypt::decrypt($object->opposing_account_name)
|
||||
: $object->opposing_account_name;
|
||||
|
||||
|
||||
// category and budget
|
||||
$entry->destination_account_name = self::decrypt($object->opposing_account_encrypted, $object->opposing_account_name);
|
||||
$entry->category_id = $object->category_id ?? '';
|
||||
$entry->category_name = $object->category_name ?? '';
|
||||
$entry->budget_id = $object->budget_id ?? '';
|
||||
$entry->budget_name = $object->budget_name ?? '';
|
||||
|
||||
|
||||
// update description when transaction description is different:
|
||||
if (!is_null($object->description) && $object->description != $entry->description) {
|
||||
$entry->description = $entry->description . ' (' . $object->description . ')';
|
||||
@@ -107,4 +96,19 @@ final class Entry
|
||||
return $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $isEncrypted
|
||||
* @param $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function decrypt(int $isEncrypted, $value)
|
||||
{
|
||||
if ($isEncrypted === 1) {
|
||||
return Crypt::decrypt($value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ interface ExporterInterface
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
*/
|
||||
public function setEntries(Collection $entries);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ use ZipArchive;
|
||||
*
|
||||
* @package FireflyIII\Export
|
||||
*/
|
||||
class Processor
|
||||
class Processor implements ProcessorInterface
|
||||
{
|
||||
|
||||
/** @var Collection */
|
||||
@@ -155,7 +155,7 @@ class Processor
|
||||
$zip->close();
|
||||
|
||||
// delete the files:
|
||||
$this->deleteFiles($disk);
|
||||
$this->deleteFiles();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -183,10 +183,11 @@ class Processor
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FilesystemAdapter $disk
|
||||
*
|
||||
*/
|
||||
private function deleteFiles(FilesystemAdapter $disk)
|
||||
private function deleteFiles()
|
||||
{
|
||||
$disk = Storage::disk('export');
|
||||
foreach ($this->getFiles() as $file) {
|
||||
$disk->delete($file);
|
||||
}
|
||||
|
||||
68
app/Export/ProcessorInterface.php
Normal file
68
app/Export/ProcessorInterface.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/**
|
||||
* ProcessorInterface.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Export;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface ProcessorInterface
|
||||
*
|
||||
* @package FireflyIII\Export
|
||||
*/
|
||||
interface ProcessorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Processor constructor.
|
||||
*
|
||||
* @param array $settings
|
||||
*
|
||||
*/
|
||||
public function __construct(array $settings);
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function collectAttachments(): bool;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function collectJournals(): bool;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function collectOldUploads(): bool;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function convertJournals(): bool;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function createZipFile(): bool;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function exportJournals(): bool;
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getFiles(): Collection;
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* AccountChartGeneratorInterface.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Account;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Account;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface AccountChartGeneratorInterface
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Account
|
||||
*/
|
||||
interface AccountChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param array $values
|
||||
* @param array $names
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function pieChart(array $values, array $names): array;
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end): array;
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $accounts, Carbon $start, Carbon $end): array;
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function revenueAccounts(Collection $accounts, Carbon $start, Carbon $end): array;
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param array $labels
|
||||
* @param array $dataSet
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function single(Account $account, array $labels, array $dataSet): array;
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ChartJsAccountChartGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
namespace FireflyIII\Generator\Chart\Account;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Support\ChartColour;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class ChartJsAccountChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Account
|
||||
*/
|
||||
class ChartJsAccountChartGenerator implements AccountChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end): array
|
||||
{
|
||||
$data = [
|
||||
'count' => 1,
|
||||
'labels' => [], 'datasets' => [[
|
||||
'label' => trans('firefly.spent'),
|
||||
'data' => []]]];
|
||||
foreach ($accounts as $account) {
|
||||
if ($account->difference > 0) {
|
||||
$data['labels'][] = $account->name;
|
||||
$data['datasets'][0]['data'][] = $account->difference;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $accounts, Carbon $start, Carbon $end): array
|
||||
{
|
||||
// language:
|
||||
$format = (string)trans('config.month_and_day');
|
||||
$data = ['count' => 0, 'labels' => [], 'datasets' => [],];
|
||||
$current = clone $start;
|
||||
while ($current <= $end) {
|
||||
$data['labels'][] = $current->formatLocalized($format);
|
||||
$current->addDay();
|
||||
}
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$data['datasets'][] = [
|
||||
'label' => $account->name,
|
||||
'fillColor' => 'rgba(220,220,220,0.2)',
|
||||
'strokeColor' => 'rgba(220,220,220,1)',
|
||||
'pointColor' => 'rgba(220,220,220,1)',
|
||||
'pointStrokeColor' => '#fff',
|
||||
'pointHighlightFill' => '#fff',
|
||||
'pointHighlightStroke' => 'rgba(220,220,220,1)',
|
||||
'data' => $account->balances,
|
||||
];
|
||||
}
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $values
|
||||
* @param array $names
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function pieChart(array $values, array $names): array
|
||||
{
|
||||
$data = [
|
||||
'datasets' => [
|
||||
0 => [],
|
||||
],
|
||||
'labels' => [],
|
||||
];
|
||||
$index = 0;
|
||||
foreach ($values as $categoryId => $value) {
|
||||
|
||||
// make larger than 0
|
||||
if (bccomp($value, '0') === -1) {
|
||||
$value = bcmul($value, '-1');
|
||||
}
|
||||
|
||||
$data['datasets'][0]['data'][] = round($value, 2);
|
||||
$data['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index);
|
||||
$data['labels'][] = $names[$categoryId];
|
||||
$index++;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function revenueAccounts(Collection $accounts, Carbon $start, Carbon $end): array
|
||||
{
|
||||
$data = [
|
||||
'count' => 1,
|
||||
'labels' => [], 'datasets' => [[
|
||||
'label' => trans('firefly.earned'),
|
||||
'data' => []]]];
|
||||
foreach ($accounts as $account) {
|
||||
if ($account->difference > 0) {
|
||||
$data['labels'][] = $account->name;
|
||||
$data['datasets'][0]['data'][] = $account->difference;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param array $labels
|
||||
* @param array $dataSet
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function single(Account $account, array $labels, array $dataSet): array
|
||||
{
|
||||
$data = [
|
||||
'count' => 1,
|
||||
'labels' => $labels,
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => $account->name,
|
||||
'data' => $dataSet,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
150
app/Generator/Chart/Basic/ChartJsGenerator.php
Normal file
150
app/Generator/Chart/Basic/ChartJsGenerator.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
/**
|
||||
* ChartJsGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Basic;
|
||||
|
||||
use FireflyIII\Support\ChartColour;
|
||||
|
||||
/**
|
||||
* Class ChartJsGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Basic
|
||||
*/
|
||||
class ChartJsGenerator implements GeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Will generate a Chart JS compatible array from the given input. Expects this format
|
||||
*
|
||||
* Will take labels for all from first set.
|
||||
*
|
||||
* 0: [
|
||||
* 'label' => 'label of set',
|
||||
* 'type' => bar or line, optional
|
||||
* 'yAxisID' => ID of yAxis, optional, will not be included when unused.
|
||||
* 'fill' => if to fill a line? optional, will not be included when unused.
|
||||
* 'entries' =>
|
||||
* [
|
||||
* 'label-of-entry' => 'value'
|
||||
* ]
|
||||
* ]
|
||||
* 1: [
|
||||
* 'label' => 'label of another set',
|
||||
* 'type' => bar or line, optional
|
||||
* 'yAxisID' => ID of yAxis, optional, will not be included when unused.
|
||||
* 'fill' => if to fill a line? optional, will not be included when unused.
|
||||
* 'entries' =>
|
||||
* [
|
||||
* 'label-of-entry' => 'value'
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiSet(array $data): array
|
||||
{
|
||||
reset($data);
|
||||
$first = current($data);
|
||||
$labels = is_array($first['entries']) ? array_keys($first['entries']) : [];
|
||||
|
||||
$chartData = [
|
||||
'count' => count($data),
|
||||
'labels' => $labels, // take ALL labels from the first set.
|
||||
'datasets' => [],
|
||||
];
|
||||
unset($first, $labels);
|
||||
|
||||
foreach ($data as $set) {
|
||||
$currentSet = [
|
||||
'label' => $set['label'],
|
||||
'type' => $set['type'] ?? 'line',
|
||||
'data' => array_values($set['entries']),
|
||||
];
|
||||
if (isset($set['yAxisID'])) {
|
||||
$currentSet['yAxisID'] = $set['yAxisID'];
|
||||
}
|
||||
if (isset($set['fill'])) {
|
||||
$currentSet['fill'] = $set['fill'];
|
||||
}
|
||||
|
||||
$chartData['datasets'][] = $currentSet;
|
||||
}
|
||||
|
||||
return $chartData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expects data as:
|
||||
*
|
||||
* key => value
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function pieChart(array $data): array
|
||||
{
|
||||
$chartData = [
|
||||
'datasets' => [
|
||||
0 => [],
|
||||
],
|
||||
'labels' => [],
|
||||
];
|
||||
$index = 0;
|
||||
foreach ($data as $key => $value) {
|
||||
|
||||
// make larger than 0
|
||||
if (bccomp($value, '0') === -1) {
|
||||
$value = bcmul($value, '-1');
|
||||
}
|
||||
|
||||
$chartData['datasets'][0]['data'][] = $value;
|
||||
$chartData['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index);
|
||||
$chartData['labels'][] = $key;
|
||||
$index++;
|
||||
}
|
||||
|
||||
return $chartData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will generate a (ChartJS) compatible array from the given input. Expects this format:
|
||||
*
|
||||
* 'label-of-entry' => value
|
||||
* 'label-of-entry' => value
|
||||
*
|
||||
* @param string $setLabel
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function singleSet(string $setLabel, array $data): array
|
||||
{
|
||||
$chartData = [
|
||||
'count' => 1,
|
||||
'labels' => array_keys($data), // take ALL labels from the first set.
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => $setLabel,
|
||||
'data' => array_values($data),
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $chartData;
|
||||
}
|
||||
}
|
||||
73
app/Generator/Chart/Basic/GeneratorInterface.php
Normal file
73
app/Generator/Chart/Basic/GeneratorInterface.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/**
|
||||
* GeneratorInterface.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Basic;
|
||||
|
||||
/**
|
||||
* Interface GeneratorInterface
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Basic
|
||||
*/
|
||||
interface GeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Will generate a (ChartJS) compatible array from the given input. Expects this format:
|
||||
*
|
||||
* 0: [
|
||||
* 'label' => 'label of set',
|
||||
* 'entries' =>
|
||||
* [
|
||||
* 'label-of-entry' => 'value'
|
||||
* ]
|
||||
* ]
|
||||
* 1: [
|
||||
* 'label' => 'label of another set',
|
||||
* 'entries' =>
|
||||
* [
|
||||
* 'label-of-entry' => 'value'
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiSet(array $data): array;
|
||||
|
||||
/**
|
||||
* Expects data as:
|
||||
*
|
||||
* key => value
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function pieChart(array $data): array;
|
||||
|
||||
/**
|
||||
* Will generate a (ChartJS) compatible array from the given input. Expects this format:
|
||||
*
|
||||
* 'label-of-entry' => value
|
||||
* 'label-of-entry' => value
|
||||
*
|
||||
* @param string $setLabel
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function singleSet(string $setLabel, array $data): array;
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* BillChartGeneratorInterface.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Bill;
|
||||
|
||||
|
||||
use FireflyIII\Models\Bill;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface BillChartGeneratorInterface
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Bill
|
||||
*/
|
||||
interface BillChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $paid
|
||||
* @param string $unpaid
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(string $paid, string $unpaid): array;
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function single(Bill $bill, Collection $entries): array;
|
||||
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ChartJsBillChartGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Bill;
|
||||
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Support\ChartColour;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class ChartJsBillChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Bill
|
||||
*/
|
||||
class ChartJsBillChartGenerator implements BillChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $paid
|
||||
* @param string $unpaid
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(string $paid, string $unpaid): array
|
||||
{
|
||||
$data = [
|
||||
'datasets' => [
|
||||
[
|
||||
'data' => [round($unpaid, 2), round(bcmul($paid, '-1'), 2)],
|
||||
'backgroundColor' => [ChartColour::getColour(0), ChartColour::getColour(1)],
|
||||
],
|
||||
|
||||
],
|
||||
'labels' => [strval(trans('firefly.unpaid')), strval(trans('firefly.paid'))],
|
||||
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function single(Bill $bill, Collection $entries): array
|
||||
{
|
||||
$format = (string)trans('config.month');
|
||||
$data = ['count' => 3, 'labels' => [], 'datasets' => [],];
|
||||
$minAmount = [];
|
||||
$maxAmount = [];
|
||||
$actualAmount = [];
|
||||
/** @var Transaction $entry */
|
||||
foreach ($entries as $entry) {
|
||||
$data['labels'][] = $entry->date->formatLocalized($format);
|
||||
$minAmount[] = round($bill->amount_min, 2);
|
||||
$maxAmount[] = round($bill->amount_max, 2);
|
||||
// journalAmount has been collected in BillRepository::getJournals
|
||||
$actualAmount[] = bcmul($entry->transaction_amount, '-1');
|
||||
}
|
||||
|
||||
$data['datasets'][] = [
|
||||
'type' => 'bar',
|
||||
'label' => trans('firefly.minAmount'),
|
||||
'data' => $minAmount,
|
||||
];
|
||||
$data['datasets'][] = [
|
||||
'type' => 'line',
|
||||
'label' => trans('firefly.billEntry'),
|
||||
'data' => $actualAmount,
|
||||
];
|
||||
$data['datasets'][] = [
|
||||
'type' => 'bar',
|
||||
'label' => trans('firefly.maxAmount'),
|
||||
'data' => $maxAmount,
|
||||
];
|
||||
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* BudgetChartGeneratorInterface.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Budget;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface BudgetChartGeneratorInterface
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Budget
|
||||
*/
|
||||
interface BudgetChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
* @param string $dateFormat
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function budgetLimit(Collection $entries, string $dateFormat): array;
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $entries): array;
|
||||
|
||||
/**
|
||||
* @param array $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function period(array $entries): array;
|
||||
|
||||
/**
|
||||
* @param array $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function periodNoBudget(array $entries): array;
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ChartJsBudgetChartGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
namespace FireflyIII\Generator\Chart\Budget;
|
||||
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class ChartJsBudgetChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Budget
|
||||
*/
|
||||
class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @param Collection $entries
|
||||
* @param string $dateFormat
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function budgetLimit(Collection $entries, string $dateFormat = 'month_and_day'): array
|
||||
{
|
||||
$format = strval(trans('config.' . $dateFormat));
|
||||
$data = [
|
||||
'labels' => [],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => 'Amount',
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
/** @var array $entry */
|
||||
foreach ($entries as $entry) {
|
||||
$data['labels'][] = $entry[0]->formatLocalized($format);
|
||||
$data['datasets'][0]['data'][] = $entry[1];
|
||||
|
||||
}
|
||||
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $entries): array
|
||||
{
|
||||
$data = [
|
||||
'count' => 0,
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
$left = [];
|
||||
$spent = [];
|
||||
$overspent = [];
|
||||
$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(bcmul($entry[2], '-1'), 2); // spent is coming in negative, must be positive
|
||||
$overspent[] = round(bcmul($entry[3], '-1'), 2); // same
|
||||
}
|
||||
|
||||
$data['datasets'][] = [
|
||||
'label' => trans('firefly.overspent'),
|
||||
'data' => $overspent,
|
||||
];
|
||||
$data['datasets'][] = [
|
||||
'label' => trans('firefly.left'),
|
||||
'data' => $left,
|
||||
];
|
||||
$data['datasets'][] = [
|
||||
'label' => trans('firefly.spent'),
|
||||
'data' => $spent,
|
||||
];
|
||||
|
||||
$data['count'] = 3;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function period(array $entries): array
|
||||
{
|
||||
|
||||
$data = [
|
||||
'labels' => array_keys($entries),
|
||||
'datasets' => [
|
||||
0 => [
|
||||
'label' => trans('firefly.budgeted'),
|
||||
'data' => [],
|
||||
],
|
||||
1 => [
|
||||
'label' => trans('firefly.spent'),
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
'count' => 2,
|
||||
];
|
||||
|
||||
foreach ($entries as $label => $entry) {
|
||||
// data set 0 is budgeted
|
||||
// data set 1 is spent:
|
||||
$data['datasets'][0]['data'][] = $entry['budgeted'];
|
||||
$data['datasets'][1]['data'][] = round(($entry['spent'] * -1), 2);
|
||||
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function periodNoBudget(array $entries): array
|
||||
{
|
||||
$data = [
|
||||
'labels' => array_keys($entries),
|
||||
'datasets' => [
|
||||
0 => [
|
||||
'label' => trans('firefly.spent'),
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
'count' => 1,
|
||||
];
|
||||
|
||||
foreach ($entries as $label => $entry) {
|
||||
// data set 0 is budgeted
|
||||
// data set 1 is spent:
|
||||
$data['datasets'][0]['data'][] = round(($entry['spent'] * -1), 2);
|
||||
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* CategoryChartGeneratorInterface.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
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): array;
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $entries): array;
|
||||
|
||||
/**
|
||||
* @param array $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function mainReportChart(array $entries): array;
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function period(Collection $entries): array;
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function pieChart(array $data): array;
|
||||
|
||||
/**
|
||||
* @param array $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function reportPeriod(array $entries): array;
|
||||
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ChartJsCategoryChartGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
namespace FireflyIII\Generator\Chart\Category;
|
||||
|
||||
use FireflyIII\Support\ChartColour;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
|
||||
/**
|
||||
* Class ChartJsCategoryChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Category
|
||||
*/
|
||||
class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all(Collection $entries): array
|
||||
{
|
||||
$data = [
|
||||
'count' => 2,
|
||||
'labels' => [],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => trans('firefly.spent'),
|
||||
'data' => [],
|
||||
],
|
||||
[
|
||||
'label' => trans('firefly.earned'),
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$data['labels'][] = $entry[1];
|
||||
$spent = $entry[2];
|
||||
$earned = $entry[3];
|
||||
|
||||
$data['datasets'][0]['data'][] = bccomp($spent, '0') === 0 ? null : round(bcmul($spent, '-1'), 4);
|
||||
$data['datasets'][1]['data'][] = bccomp($earned, '0') === 0 ? null : round($earned, 4);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function frontpage(Collection $entries): array
|
||||
{
|
||||
$data = [
|
||||
'count' => 1,
|
||||
'labels' => [],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => trans('firefly.spent'),
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
foreach ($entries as $entry) {
|
||||
if ($entry->spent != 0) {
|
||||
$data['labels'][] = $entry->name;
|
||||
$data['datasets'][0]['data'][] = round(bcmul($entry->spent, '-1'), 2);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function mainReportChart(array $entries): array
|
||||
{
|
||||
|
||||
$data = [
|
||||
'count' => 0,
|
||||
'labels' => array_keys($entries),
|
||||
'datasets' => [],
|
||||
];
|
||||
|
||||
|
||||
foreach ($entries as $row) {
|
||||
foreach ($row['in'] as $categoryId => $amount) {
|
||||
// get in:
|
||||
$data['datasets'][$categoryId . 'in']['data'][] = round($amount, 2);
|
||||
|
||||
// get out:
|
||||
$opposite = $row['out'][$categoryId];
|
||||
$data['datasets'][$categoryId . 'out']['data'][] = round($opposite, 2);
|
||||
|
||||
// set name:
|
||||
$data['datasets'][$categoryId . 'out']['label'] = $row['name'][$categoryId] . ' (' . strtolower(strval(trans('firefly.expenses'))) . ')';
|
||||
$data['datasets'][$categoryId . 'in']['label'] = $row['name'][$categoryId] . ' (' . strtolower(strval(trans('firefly.income'))) . ')';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// remove empty rows:
|
||||
foreach ($data['datasets'] as $key => $content) {
|
||||
if (array_sum($content['data']) === 0.0) {
|
||||
unset($data['datasets'][$key]);
|
||||
}
|
||||
}
|
||||
|
||||
// re-key the datasets array:
|
||||
$data['datasets'] = array_values($data['datasets']);
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function period(Collection $entries): array
|
||||
{
|
||||
return $this->all($entries);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function reportPeriod(array $entries): array
|
||||
{
|
||||
|
||||
$data = [
|
||||
'labels' => array_keys($entries),
|
||||
'datasets' => [
|
||||
0 => [
|
||||
'label' => trans('firefly.earned'),
|
||||
'data' => [],
|
||||
],
|
||||
1 => [
|
||||
'label' => trans('firefly.spent'),
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
'count' => 2,
|
||||
];
|
||||
|
||||
foreach ($entries as $label => $entry) {
|
||||
// data set 0 is budgeted
|
||||
// data set 1 is spent:
|
||||
$data['datasets'][0]['data'][] = round($entry['earned'], 2);
|
||||
$data['datasets'][1]['data'][] = round(bcmul($entry['spent'], '-1'), 2);
|
||||
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function pieChart(array $entries): array
|
||||
{
|
||||
$data = [
|
||||
'datasets' => [
|
||||
0 => [],
|
||||
],
|
||||
'labels' => [],
|
||||
];
|
||||
$index = 0;
|
||||
foreach ($entries as $entry) {
|
||||
|
||||
if (bccomp($entry['amount'], '0') === -1) {
|
||||
$entry['amount'] = bcmul($entry['amount'], '-1');
|
||||
}
|
||||
|
||||
$data['datasets'][0]['data'][] = round($entry['amount'], 2);
|
||||
$data['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index);
|
||||
$data['labels'][] = $entry['name'];
|
||||
$index++;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ChartJsPiggyBankChartGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
namespace FireflyIII\Generator\Chart\PiggyBank;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
|
||||
/**
|
||||
* Class ChartJsPiggyBankChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\PiggyBank
|
||||
*/
|
||||
class ChartJsPiggyBankChartGenerator implements PiggyBankChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $set
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function history(Collection $set): array
|
||||
{
|
||||
|
||||
// language:
|
||||
$format = (string)trans('config.month_and_day');
|
||||
|
||||
$data = [
|
||||
'count' => 1,
|
||||
'labels' => [],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => 'Diff',
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
$sum = '0';
|
||||
foreach ($set as $key => $value) {
|
||||
$date = new Carbon($key);
|
||||
$sum = bcadd($sum, $value);
|
||||
$data['labels'][] = $date->formatLocalized($format);
|
||||
$data['datasets'][0]['data'][] = round($sum, 2);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ChartJsReportChartGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
namespace FireflyIII\Generator\Chart\Report;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class ChartJsReportChartGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Chart\Report
|
||||
*/
|
||||
class ChartJsReportChartGenerator implements ReportChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Same as above but other translations.
|
||||
*
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYearOperations(Collection $entries): array
|
||||
{
|
||||
$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 multiYearSum(string $income, string $expense, int $count): array
|
||||
{
|
||||
$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
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function netWorth(Collection $entries) : array
|
||||
{
|
||||
$format = (string)trans('config.month_and_day');
|
||||
$data = [
|
||||
'count' => 1,
|
||||
'labels' => [],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => trans('firefly.net_worth'),
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
foreach ($entries as $entry) {
|
||||
$data['labels'][] = trim($entry['date']->formatLocalized($format));
|
||||
$data['datasets'][0]['data'][] = round($entry['net-worth'], 2);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function yearOperations(Collection $entries): array
|
||||
{
|
||||
// language:
|
||||
$format = (string)trans('config.month');
|
||||
|
||||
$data = [
|
||||
'count' => 2,
|
||||
'labels' => [],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => trans('firefly.income'),
|
||||
'data' => [],
|
||||
],
|
||||
[
|
||||
'label' => trans('firefly.expenses'),
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$data['labels'][] = $entry[0]->formatLocalized($format);
|
||||
$data['datasets'][0]['data'][] = round($entry[1], 2);
|
||||
$data['datasets'][1]['data'][] = round($entry[2], 2);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $income
|
||||
* @param string $expense
|
||||
* @param int $count
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function yearSum(string $income, string $expense, int $count): array
|
||||
{
|
||||
|
||||
$data = [
|
||||
'count' => 2,
|
||||
'labels' => [trans('firefly.sum_of_year'), trans('firefly.average_of_year')],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => trans('firefly.income'),
|
||||
'data' => [],
|
||||
],
|
||||
[
|
||||
'label' => trans('firefly.expenses'),
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
$data['datasets'][0]['data'][] = round($income, 2);
|
||||
$data['datasets'][1]['data'][] = round($expense, 2);
|
||||
$data['datasets'][0]['data'][] = round(($income / $count), 2);
|
||||
$data['datasets'][1]['data'][] = round(($expense / $count), 2);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ReportChartGeneratorInterface.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
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 multiYearOperations(Collection $entries): array;
|
||||
|
||||
/**
|
||||
* @param string $income
|
||||
* @param string $expense
|
||||
* @param int $count
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYearSum(string $income, string $expense, int $count): array;
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function netWorth(Collection $entries) : array;
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function yearOperations(Collection $entries): array;
|
||||
|
||||
/**
|
||||
* @param string $income
|
||||
* @param string $expense
|
||||
* @param int $count
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function yearSum(string $income, string $expense, int $count): array;
|
||||
|
||||
}
|
||||
@@ -16,7 +16,7 @@ namespace FireflyIII\Generator\Report\Audit;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -25,7 +25,7 @@ use Steam;
|
||||
/**
|
||||
* Class MonthReportGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Report\Standard
|
||||
* @package FireflyIII\Generator\Report\Audit
|
||||
*/
|
||||
class MonthReportGenerator implements ReportGeneratorInterface
|
||||
{
|
||||
@@ -50,32 +50,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
foreach ($this->accounts as $account) {
|
||||
// balance the day before:
|
||||
$id = $account->id;
|
||||
$dayBeforeBalance = Steam::balance($account, $dayBefore);
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($this->start, $this->end);
|
||||
$journals = $collector->getJournals();
|
||||
$journals = $journals->reverse();
|
||||
$startBalance = $dayBeforeBalance;
|
||||
|
||||
|
||||
/** @var Transaction $journal */
|
||||
foreach ($journals as $transaction) {
|
||||
$transaction->before = $startBalance;
|
||||
$transactionAmount = $transaction->transaction_amount;
|
||||
$newBalance = bcadd($startBalance, $transactionAmount);
|
||||
$transaction->after = $newBalance;
|
||||
$startBalance = $newBalance;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reverse set again.
|
||||
*/
|
||||
$auditData[$id]['journals'] = $journals->reverse();
|
||||
$auditData[$id]['exists'] = $journals->count() > 0;
|
||||
$auditData[$id]['end'] = $this->end->formatLocalized(strval(trans('config.month_and_day')));
|
||||
$auditData[$id]['endBalance'] = Steam::balance($account, $this->end);
|
||||
$auditData[$id]['dayBefore'] = $dayBefore->formatLocalized(strval(trans('config.month_and_day')));
|
||||
$auditData[$id]['dayBeforeBalance'] = $dayBeforeBalance;
|
||||
$auditData[$id] = $this->getAuditReport($account, $dayBefore);
|
||||
}
|
||||
|
||||
$defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to'];
|
||||
@@ -110,6 +85,16 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setBudgets(Collection $budgets): ReportGeneratorInterface
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
@@ -143,4 +128,46 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getAuditReport(Account $account, Carbon $date): array
|
||||
{
|
||||
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($this->start, $this->end);
|
||||
$journals = $collector->getJournals();
|
||||
$journals = $journals->reverse();
|
||||
$dayBeforeBalance = Steam::balance($account, $date);
|
||||
$startBalance = $dayBeforeBalance;
|
||||
|
||||
|
||||
/** @var Transaction $journal */
|
||||
foreach ($journals as $transaction) {
|
||||
$transaction->before = $startBalance;
|
||||
$transactionAmount = $transaction->transaction_amount;
|
||||
$newBalance = bcadd($startBalance, $transactionAmount);
|
||||
$transaction->after = $newBalance;
|
||||
$startBalance = $newBalance;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reverse set again.
|
||||
*/
|
||||
$return = [
|
||||
'journals' => $journals->reverse(),
|
||||
'exists' => $journals->count() > 0,
|
||||
'end' => $this->end->formatLocalized(strval(trans('config.month_and_day'))),
|
||||
'endBalance' => Steam::balance($account, $this->end),
|
||||
'dayBefore' => $date->formatLocalized(strval(trans('config.month_and_day'))),
|
||||
'dayBeforeBalance' => $dayBeforeBalance,
|
||||
];
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
248
app/Generator/Report/Budget/MonthReportGenerator.php
Normal file
248
app/Generator/Report/Budget/MonthReportGenerator.php
Normal file
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
/**
|
||||
* MonthReportGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Report\Budget;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
||||
use FireflyIII\Generator\Report\Support;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class MonthReportGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Report\Budget
|
||||
*/
|
||||
class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
||||
{
|
||||
/** @var Collection */
|
||||
private $accounts;
|
||||
/** @var Collection */
|
||||
private $budgets;
|
||||
/** @var Carbon */
|
||||
private $end;
|
||||
/** @var Collection */
|
||||
private $expenses;
|
||||
/** @var Collection */
|
||||
private $income;
|
||||
/** @var Carbon */
|
||||
private $start;
|
||||
|
||||
/**
|
||||
* MonthReportGenerator constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->income = new Collection;
|
||||
$this->expenses = new Collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function generate(): string
|
||||
{
|
||||
$accountIds = join(',', $this->accounts->pluck('id')->toArray());
|
||||
$budgetIds = join(',', $this->budgets->pluck('id')->toArray());
|
||||
$expenses = $this->getExpenses();
|
||||
$accountSummary = $this->summarizeByAccount($expenses);
|
||||
$budgetSummary = $this->summarizeByBudget($expenses);
|
||||
$averageExpenses = $this->getAverages($expenses, SORT_ASC);
|
||||
$topExpenses = $this->getTopExpenses();
|
||||
|
||||
// render!
|
||||
return view('reports.budget.month', compact('accountIds', 'budgetIds', 'accountSummary', 'budgetSummary', 'averageExpenses', 'topExpenses'))
|
||||
->with('start', $this->start)->with('end', $this->end)
|
||||
->with('budgets', $this->budgets)
|
||||
->with('accounts', $this->accounts)
|
||||
->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): ReportGeneratorInterface
|
||||
{
|
||||
$this->accounts = $accounts;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setBudgets(Collection $budgets): ReportGeneratorInterface
|
||||
{
|
||||
$this->budgets = $budgets;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setCategories(Collection $categories): ReportGeneratorInterface
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setEndDate(Carbon $date): ReportGeneratorInterface
|
||||
{
|
||||
$this->end = $date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setStartDate(Carbon $date): ReportGeneratorInterface
|
||||
{
|
||||
$this->start = $date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $collection
|
||||
* @param int $sortFlag
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getAverages(Collection $collection, int $sortFlag): array
|
||||
{
|
||||
$result = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($collection as $transaction) {
|
||||
// opposing name and ID:
|
||||
$opposingId = $transaction->opposing_account_id;
|
||||
|
||||
// is not set?
|
||||
if (!isset($result[$opposingId])) {
|
||||
$name = $transaction->opposing_account_name;
|
||||
$result[$opposingId] = [
|
||||
'name' => $name,
|
||||
'count' => 1,
|
||||
'id' => $opposingId,
|
||||
'average' => $transaction->transaction_amount,
|
||||
'sum' => $transaction->transaction_amount,
|
||||
];
|
||||
continue;
|
||||
}
|
||||
$result[$opposingId]['count']++;
|
||||
$result[$opposingId]['sum'] = bcadd($result[$opposingId]['sum'], $transaction->transaction_amount);
|
||||
$result[$opposingId]['average'] = bcdiv($result[$opposingId]['sum'], strval($result[$opposingId]['count']));
|
||||
}
|
||||
|
||||
// sort result by average:
|
||||
$average = [];
|
||||
foreach ($result as $key => $row) {
|
||||
$average[$key] = floatval($row['average']);
|
||||
}
|
||||
|
||||
array_multisort($average, $sortFlag, $result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
private function getExpenses(): Collection
|
||||
{
|
||||
if ($this->expenses->count() > 0) {
|
||||
Log::debug('Return previous set of expenses.');
|
||||
|
||||
return $this->expenses;
|
||||
}
|
||||
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
|
||||
->setTypes([TransactionType::WITHDRAWAL])
|
||||
->setBudgets($this->budgets)->withOpposingAccount()->disableFilter();
|
||||
|
||||
$accountIds = $this->accounts->pluck('id')->toArray();
|
||||
$transactions = $collector->getJournals();
|
||||
$transactions = self::filterExpenses($transactions, $accountIds);
|
||||
$this->expenses = $transactions;
|
||||
|
||||
return $transactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
private function getTopExpenses(): Collection
|
||||
{
|
||||
$transactions = $this->getExpenses()->sortBy('transaction_amount');
|
||||
|
||||
return $transactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $collection
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function summarizeByAccount(Collection $collection): array
|
||||
{
|
||||
$result = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($collection as $transaction) {
|
||||
$accountId = $transaction->account_id;
|
||||
$result[$accountId] = $result[$accountId] ?? '0';
|
||||
$result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $collection
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function summarizeByBudget(Collection $collection): array
|
||||
{
|
||||
$result = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($collection as $transaction) {
|
||||
$jrnlBudId = intval($transaction->transaction_journal_budget_id);
|
||||
$transBudId = intval($transaction->transaction_budget_id);
|
||||
$budgetId = max($jrnlBudId, $transBudId);
|
||||
$result[$budgetId] = $result[$budgetId] ?? '0';
|
||||
$result[$budgetId] = bcadd($transaction->transaction_amount, $result[$budgetId]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
27
app/Generator/Report/Budget/MultiYearReportGenerator.php
Normal file
27
app/Generator/Report/Budget/MultiYearReportGenerator.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* MultiYearReportGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Report\Budget;
|
||||
|
||||
|
||||
/**
|
||||
* Class MultiYearReportGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Report\Budget
|
||||
*/
|
||||
class MultiYearReportGenerator extends MonthReportGenerator
|
||||
{
|
||||
/**
|
||||
* Doesn't do anything different.
|
||||
*/
|
||||
}
|
||||
28
app/Generator/Report/Budget/YearReportGenerator.php
Normal file
28
app/Generator/Report/Budget/YearReportGenerator.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* YearReportGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Report\Budget;
|
||||
|
||||
|
||||
/**
|
||||
* Class YearReportGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Report\Budget
|
||||
*/
|
||||
class YearReportGenerator extends MonthReportGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* Doesn't do anything different.
|
||||
*/
|
||||
}
|
||||
@@ -15,9 +15,9 @@ namespace FireflyIII\Generator\Report\Category;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Crypt;
|
||||
use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Generator\Report\Support;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -95,6 +95,16 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setBudgets(Collection $budgets): ReportGeneratorInterface
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
@@ -184,7 +194,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
||||
return $this->expenses;
|
||||
}
|
||||
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
|
||||
->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
|
||||
->setCategories($this->categories)->withOpposingAccount()->disableFilter();
|
||||
@@ -206,7 +217,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
||||
return $this->income;
|
||||
}
|
||||
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
|
||||
->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
|
||||
->setCategories($this->categories)->withOpposingAccount();
|
||||
@@ -219,6 +231,7 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
|
||||
* @param array $spent
|
||||
* @param array $earned
|
||||
*
|
||||
|
||||
@@ -36,6 +36,13 @@ interface ReportGeneratorInterface
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): ReportGeneratorInterface;
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setBudgets(Collection $budgets): ReportGeneratorInterface;
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
|
||||
@@ -63,6 +63,16 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setBudgets(Collection $budgets): ReportGeneratorInterface
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
|
||||
@@ -60,6 +60,16 @@ class MultiYearReportGenerator implements ReportGeneratorInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setBudgets(Collection $budgets): ReportGeneratorInterface
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
|
||||
@@ -60,6 +60,16 @@ class YearReportGenerator implements ReportGeneratorInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setBudgets(Collection $budgets): ReportGeneratorInterface
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Report\Category;
|
||||
namespace FireflyIII\Generator\Report;
|
||||
|
||||
use FireflyIII\Models\Transaction;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -34,27 +34,7 @@ class Support
|
||||
*/
|
||||
public static function filterExpenses(Collection $collection, array $accounts): Collection
|
||||
{
|
||||
$result = $collection->filter(
|
||||
function (Transaction $transaction) use ($accounts) {
|
||||
$opposing = $transaction->opposing_account_id;
|
||||
// remove internal transfer
|
||||
if (in_array($opposing, $accounts)) {
|
||||
Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id));
|
||||
|
||||
return null;
|
||||
}
|
||||
// remove positive amount
|
||||
if (bccomp($transaction->transaction_amount, '0') === 1) {
|
||||
Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $transaction;
|
||||
}
|
||||
);
|
||||
|
||||
return $result;
|
||||
return self::filterTransactions($collection, $accounts, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,9 +44,21 @@ class Support
|
||||
* @return Collection
|
||||
*/
|
||||
public static function filterIncome(Collection $collection, array $accounts): Collection
|
||||
{
|
||||
return self::filterTransactions($collection, $accounts, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $collection
|
||||
* @param array $accounts
|
||||
* @param int $modifier
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public static function filterTransactions(Collection $collection, array $accounts, int $modifier): Collection
|
||||
{
|
||||
$result = $collection->filter(
|
||||
function (Transaction $transaction) use ($accounts) {
|
||||
function (Transaction $transaction) use ($accounts, $modifier) {
|
||||
$opposing = $transaction->opposing_account_id;
|
||||
// remove internal transfer
|
||||
if (in_array($opposing, $accounts)) {
|
||||
@@ -75,7 +67,7 @@ class Support
|
||||
return null;
|
||||
}
|
||||
// remove positive amount
|
||||
if (bccomp($transaction->transaction_amount, '0') === -1) {
|
||||
if (bccomp($transaction->transaction_amount, '0') === $modifier) {
|
||||
Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount));
|
||||
|
||||
return null;
|
||||
@@ -1,93 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* BudgetEventHandler.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Events\StoredBudgetLimit;
|
||||
use FireflyIII\Events\UpdatedBudgetLimit;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Handles budget related events.
|
||||
*
|
||||
* Class BudgetEventHandler
|
||||
*
|
||||
* @package FireflyIII\Handlers\Events
|
||||
*/
|
||||
class BudgetEventHandler
|
||||
{
|
||||
/**
|
||||
* This method creates a new budget limit repetition when a new budget limit has been created.
|
||||
*
|
||||
* @param StoredBudgetLimit $budgetLimitEvent
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function storeRepetition(StoredBudgetLimit $budgetLimitEvent):bool
|
||||
{
|
||||
return $this->processRepetitionChange($budgetLimitEvent->budgetLimit, $budgetLimitEvent->end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates, if present the budget limit repetition part of a budget limit.
|
||||
*
|
||||
* @param UpdatedBudgetLimit $budgetLimitEvent
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function updateRepetition(UpdatedBudgetLimit $budgetLimitEvent): bool
|
||||
{
|
||||
return $this->processRepetitionChange($budgetLimitEvent->budgetLimit, $budgetLimitEvent->end);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetLimit $budgetLimit
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function processRepetitionChange(BudgetLimit $budgetLimit, Carbon $date):bool
|
||||
{
|
||||
$set = $budgetLimit->limitrepetitions()
|
||||
->where('startdate', $budgetLimit->startdate->format('Y-m-d 00:00:00'))
|
||||
->where('enddate', $date->format('Y-m-d 00:00:00'))
|
||||
->get();
|
||||
if ($set->count() == 0) {
|
||||
$repetition = new LimitRepetition;
|
||||
$repetition->startdate = $budgetLimit->startdate;
|
||||
$repetition->enddate = $date;
|
||||
$repetition->amount = $budgetLimit->amount;
|
||||
$repetition->budgetLimit()->associate($budgetLimit);
|
||||
|
||||
try {
|
||||
$repetition->save();
|
||||
} catch (QueryException $e) {
|
||||
Log::error('Trying to save new LimitRepetition failed: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if ($set->count() == 1) {
|
||||
$repetition = $set->first();
|
||||
$repetition->amount = $budgetLimit->amount;
|
||||
$repetition->save();
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ namespace FireflyIII\Handlers\Events;
|
||||
use FireflyIII\Events\StoredTransactionJournal;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Models\PiggyBankRepetition;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
@@ -33,79 +34,40 @@ class StoredJournalEventHandler
|
||||
/**
|
||||
* This method connects a new transfer to a piggy bank.
|
||||
*
|
||||
* @param StoredTransactionJournal $storedJournalEvent
|
||||
* @param StoredTransactionJournal $event
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function connectToPiggyBank(StoredTransactionJournal $storedJournalEvent): bool
|
||||
public function connectToPiggyBank(StoredTransactionJournal $event): bool
|
||||
{
|
||||
/** @var TransactionJournal $journal */
|
||||
$journal = $storedJournalEvent->journal;
|
||||
$piggyBankId = $storedJournalEvent->piggyBankId;
|
||||
|
||||
$journal = $event->journal;
|
||||
$piggyBankId = $event->piggyBankId;
|
||||
Log::debug(sprintf('Trying to connect journal %d to piggy bank %d.', $journal->id, $piggyBankId));
|
||||
|
||||
/** @var PiggyBank $piggyBank */
|
||||
/*
|
||||
* Verify existence of piggy bank:
|
||||
*/
|
||||
if (!$this->verifyExistence($event)) {
|
||||
Log::error(sprintf('No such piggy bank or no repetition on %s', $journal->date->format('Y-m-d')));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get relevant data:
|
||||
*/
|
||||
$piggyBank = $journal->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
|
||||
|
||||
if (is_null($piggyBank)) {
|
||||
Log::error('No such piggy bank!');
|
||||
|
||||
return true;
|
||||
}
|
||||
Log::debug(sprintf('Found piggy bank #%d: "%s"', $piggyBank->id, $piggyBank->name));
|
||||
// update piggy bank rep for date of transaction journal.
|
||||
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
|
||||
if (is_null($repetition)) {
|
||||
Log::error(sprintf('No piggy bank repetition on %s!', $journal->date->format('Y-m-d')));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$amount = TransactionJournal::amountPositive($journal);
|
||||
Log::debug(sprintf('Will add/remove %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
|
||||
// if piggy account matches source account, the amount is positive
|
||||
$sources = TransactionJournal::sourceAccountList($journal)->pluck('id')->toArray();
|
||||
if (in_array($piggyBank->account_id, $sources)) {
|
||||
$amount = bcmul($amount, '-1');
|
||||
Log::debug(sprintf('Account #%d is the source, so will remove amount from piggy bank.', $piggyBank->account_id));
|
||||
}
|
||||
|
||||
// if the amount is positive:
|
||||
// make sure it fits in piggy bank:
|
||||
if (bccomp($amount, '0') === 1) {
|
||||
// amount is positive
|
||||
$room = bcsub(strval($piggyBank->targetamount), strval($repetition->currentamount));
|
||||
Log::debug(sprintf('Room in piggy bank for extra money is %f', $room));
|
||||
if (bccomp($room, $amount) === -1) {
|
||||
// $room is smaller than $amount
|
||||
Log::debug(sprintf('There is NO room to add %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
|
||||
Log::debug(sprintf('New amount is %f', $room));
|
||||
$amount = $room;
|
||||
}
|
||||
}
|
||||
|
||||
if (bccomp($amount, '0') === -1) {
|
||||
// amount is negative
|
||||
Log::debug(sprintf('Max amount to remove is %f', $repetition->currentamount));
|
||||
$compare = bcmul($repetition->currentamount, '-1');
|
||||
if (bccomp($compare, $amount) === 1) {
|
||||
// $currentamount is smaller than $amount
|
||||
Log::debug(sprintf('Cannot remove %f from piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
|
||||
Log::debug(sprintf('New amount is %f', $compare));
|
||||
$amount = $compare;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$amount = $this->getExactAmount($journal, $piggyBank, $repetition);
|
||||
$repetition->currentamount = bcadd($repetition->currentamount, $amount);
|
||||
$repetition->save();
|
||||
|
||||
/** @var PiggyBankEvent $storedJournalEvent */
|
||||
$storedJournalEvent = PiggyBankEvent::create(
|
||||
/** @var PiggyBankEvent $event */
|
||||
$event = PiggyBankEvent::create(
|
||||
['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]
|
||||
);
|
||||
Log::debug(sprintf('Created piggy bank event #%d', $storedJournalEvent->id));
|
||||
Log::debug(sprintf('Created piggy bank event #%d', $event->id));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -161,4 +123,81 @@ class StoredJournalEventHandler
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's 6 but I can live with it.
|
||||
* @param TransactionJournal $journal
|
||||
* @param PiggyBank $piggyBank
|
||||
* @param PiggyBankRepetition $repetition
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getExactAmount(TransactionJournal $journal, PiggyBank $piggyBank, PiggyBankRepetition $repetition): string
|
||||
{
|
||||
$amount = TransactionJournal::amountPositive($journal);
|
||||
$sources = TransactionJournal::sourceAccountList($journal)->pluck('id')->toArray();
|
||||
$room = bcsub(strval($piggyBank->targetamount), strval($repetition->currentamount));
|
||||
$compare = bcmul($repetition->currentamount, '-1');
|
||||
|
||||
Log::debug(sprintf('Will add/remove %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
|
||||
|
||||
// if piggy account matches source account, the amount is positive
|
||||
if (in_array($piggyBank->account_id, $sources)) {
|
||||
$amount = bcmul($amount, '-1');
|
||||
Log::debug(sprintf('Account #%d is the source, so will remove amount from piggy bank.', $piggyBank->account_id));
|
||||
}
|
||||
|
||||
|
||||
// if the amount is positive, make sure it fits in piggy bank:
|
||||
if (bccomp($amount, '0') === 1 && bccomp($room, $amount) === -1) {
|
||||
// amount is positive and $room is smaller than $amount
|
||||
Log::debug(sprintf('Room in piggy bank for extra money is %f', $room));
|
||||
Log::debug(sprintf('There is NO room to add %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
|
||||
Log::debug(sprintf('New amount is %f', $room));
|
||||
|
||||
return $room;
|
||||
}
|
||||
|
||||
// amount is negative and $currentamount is smaller than $amount
|
||||
if (bccomp($amount, '0') === -1 && bccomp($compare, $amount) === 1) {
|
||||
Log::debug(sprintf('Max amount to remove is %f', $repetition->currentamount));
|
||||
Log::debug(sprintf('Cannot remove %f from piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
|
||||
Log::debug(sprintf('New amount is %f', $compare));
|
||||
|
||||
return $compare;
|
||||
}
|
||||
|
||||
return $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param StoredTransactionJournal $event
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function verifyExistence(StoredTransactionJournal $event): bool
|
||||
{
|
||||
/** @var TransactionJournal $journal */
|
||||
$journal = $event->journal;
|
||||
$piggyBankId = $event->piggyBankId;
|
||||
|
||||
/** @var PiggyBank $piggyBank */
|
||||
$piggyBank = $journal->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
|
||||
|
||||
if (is_null($piggyBank)) {
|
||||
Log::error('No such piggy bank!');
|
||||
|
||||
return false;
|
||||
}
|
||||
Log::debug(sprintf('Found piggy bank #%d: "%s"', $piggyBank->id, $piggyBank->name));
|
||||
// update piggy bank rep for date of transaction journal.
|
||||
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
|
||||
if (is_null($repetition)) {
|
||||
Log::error(sprintf('No piggy bank repetition on %s!', $journal->date->format('Y-m-d')));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,18 +13,12 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use Exception;
|
||||
use FireflyConfig;
|
||||
use FireflyIII\Events\ConfirmedUser;
|
||||
use FireflyIII\Events\RegisteredUser;
|
||||
use FireflyIII\Events\RequestedNewPassword;
|
||||
use FireflyIII\Events\ResentConfirmation;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Mail\Message;
|
||||
use Log;
|
||||
use Mail;
|
||||
use Preferences;
|
||||
use Session;
|
||||
use Swift_TransportException;
|
||||
|
||||
@@ -35,7 +29,6 @@ use Swift_TransportException;
|
||||
*
|
||||
* The method name reflects what is being done. This is in the present tense.
|
||||
*
|
||||
*
|
||||
* @package FireflyIII\Handlers\Events
|
||||
*/
|
||||
class UserEventHandler
|
||||
@@ -75,33 +68,6 @@ class UserEventHandler
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will send a newly registered user a confirmation message, urging him or her to activate their account.
|
||||
*
|
||||
* @param RegisteredUser $event
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function sendConfirmationMessage(RegisteredUser $event): bool
|
||||
{
|
||||
return $this->sendConfirmation($event->user, $event->ipAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the user has somehow lost his or her confirmation message, this event will send it to the user again.
|
||||
*
|
||||
* At the moment, this method is exactly the same as the ::sendConfirmationMessage method, but that will change.
|
||||
*
|
||||
* @param ResentConfirmation $event
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function sendConfirmationMessageAgain(ResentConfirmation $event): bool
|
||||
{
|
||||
return $this->sendConfirmation($event->user, $event->ipAddress);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RequestedNewPassword $event
|
||||
*
|
||||
@@ -161,76 +127,4 @@ class UserEventHandler
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* When the user is confirmed, this method stores the IP address of the user
|
||||
* as a preference. Since this preference cannot be edited, it is effectively hidden
|
||||
* from the user yet stored conveniently.
|
||||
*
|
||||
* @param ConfirmedUser $event
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function storeConfirmationIpAddress(ConfirmedUser $event): bool
|
||||
{
|
||||
Preferences::setForUser($event->user, 'confirmation_ip_address', $event->ipAddress);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This message stores the users IP address on registration, in much the same
|
||||
* fashion as the previous method.
|
||||
*
|
||||
* @param RegisteredUser $event
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function storeRegistrationIpAddress(RegisteredUser $event): bool
|
||||
{
|
||||
Preferences::setForUser($event->user, 'registration_ip_address', $event->ipAddress);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param string $ipAddress
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function sendConfirmation(User $user, string $ipAddress): bool
|
||||
{
|
||||
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
|
||||
if ($mustConfirmAccount === false) {
|
||||
Preferences::setForUser($user, 'user_confirmed', true);
|
||||
Preferences::setForUser($user, 'user_confirmed_last_mail', 0);
|
||||
Preferences::mark();
|
||||
|
||||
return true;
|
||||
}
|
||||
$email = $user->email;
|
||||
$code = str_random(16);
|
||||
$route = route('do_confirm_account', [$code]);
|
||||
Preferences::setForUser($user, 'user_confirmed', false);
|
||||
Preferences::setForUser($user, 'user_confirmed_last_mail', time());
|
||||
Preferences::setForUser($user, 'user_confirmed_code', $code);
|
||||
try {
|
||||
Mail::send(
|
||||
['emails.confirm-account-html', 'emails.confirm-account-text'], ['route' => $route, 'ip' => $ipAddress],
|
||||
function (Message $message) use ($email) {
|
||||
$message->to($email, $email)->subject('Please confirm your Firefly III account');
|
||||
}
|
||||
);
|
||||
} catch (Swift_TransportException $e) {
|
||||
Log::error($e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ use Crypt;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\MessageBag;
|
||||
use Input;
|
||||
use Storage;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
|
||||
@@ -82,19 +81,18 @@ class AttachmentHelper implements AttachmentHelperInterface
|
||||
|
||||
/**
|
||||
* @param Model $model
|
||||
* @param array|null $files
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveAttachmentsForModel(Model $model): bool
|
||||
public function saveAttachmentsForModel(Model $model, array $files = null): bool
|
||||
{
|
||||
$files = $this->getFiles();
|
||||
|
||||
if (!is_null($files) && !is_array($files)) {
|
||||
$this->processFile($files, $model);
|
||||
}
|
||||
|
||||
if (is_array($files)) {
|
||||
$this->processFiles($files, $model);
|
||||
foreach ($files as $entry) {
|
||||
if (!is_null($entry)) {
|
||||
$this->processFile($entry, $model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -227,37 +225,4 @@ class AttachmentHelper implements AttachmentHelperInterface
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null|UploadedFile
|
||||
*/
|
||||
private function getFiles()
|
||||
{
|
||||
$files = null;
|
||||
if (Input::hasFile('attachments')) {
|
||||
$files = Input::file('attachments');
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $files
|
||||
*
|
||||
* @param Model $model
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function processFiles(array $files, Model $model): bool
|
||||
{
|
||||
foreach ($files as $entry) {
|
||||
if (!is_null($entry)) {
|
||||
$this->processFile($entry, $model);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -46,6 +46,6 @@ interface AttachmentHelperInterface
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveAttachmentsForModel(Model $model): bool;
|
||||
public function saveAttachmentsForModel(Model $model, array $files = null): bool;
|
||||
|
||||
}
|
||||
|
||||
278
app/Helpers/Chart/MetaPieChart.php
Normal file
278
app/Helpers/Chart/MetaPieChart.php
Normal file
@@ -0,0 +1,278 @@
|
||||
<?php
|
||||
/**
|
||||
* MetaPieChart.php
|
||||
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
||||
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Helpers\Chart;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Report\Support;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class MetaPieChart
|
||||
*
|
||||
* @package FireflyIII\Helpers\Chart
|
||||
*/
|
||||
class MetaPieChart implements MetaPieChartInterface
|
||||
{
|
||||
/** @var Collection */
|
||||
protected $accounts;
|
||||
/** @var Collection */
|
||||
protected $budgets;
|
||||
/** @var Collection */
|
||||
protected $categories;
|
||||
/** @var bool */
|
||||
protected $collectOtherObjects = false;
|
||||
/** @var Carbon */
|
||||
protected $end;
|
||||
/** @var array */
|
||||
protected $grouping
|
||||
= [
|
||||
'account' => ['opposing_account_id'],
|
||||
'budget' => ['transaction_journal_budget_id', 'transaction_budget_id'],
|
||||
'category' => ['transaction_journal_category_id', 'transaction_category_id'],
|
||||
];
|
||||
|
||||
/** @var array */
|
||||
protected $repositories
|
||||
= [
|
||||
'account' => AccountRepositoryInterface::class,
|
||||
'budget' => BudgetRepositoryInterface::class,
|
||||
'category' => CategoryRepositoryInterface::class,
|
||||
];
|
||||
|
||||
|
||||
/** @var Carbon */
|
||||
protected $start;
|
||||
/** @var string */
|
||||
protected $total = '0';
|
||||
/** @var User */
|
||||
protected $user;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->accounts = new Collection;
|
||||
$this->budgets = new Collection;
|
||||
$this->categories = new Collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $direction
|
||||
* @param string $group
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function generate(string $direction, string $group): array
|
||||
{
|
||||
$transactions = $this->getTransactions($direction);
|
||||
$grouped = $this->groupByFields($transactions, $this->grouping[$group]);
|
||||
$chartData = $this->organizeByType($group, $grouped);
|
||||
|
||||
// also collect all other transactions
|
||||
if ($this->collectOtherObjects && $direction === 'expense') {
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [$this->user]);
|
||||
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::WITHDRAWAL]);
|
||||
$journals = $collector->getJournals();
|
||||
$sum = strval($journals->sum('transaction_amount'));
|
||||
$sum = bcmul($sum, '-1');
|
||||
$sum = bcsub($sum, $this->total);
|
||||
$chartData[strval(trans('firefly.everything_else'))] = $sum;
|
||||
}
|
||||
|
||||
if ($this->collectOtherObjects && $direction === 'income') {
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::DEPOSIT]);
|
||||
$journals = $collector->getJournals();
|
||||
$sum = strval($journals->sum('transaction_amount'));
|
||||
$sum = bcsub($sum, $this->total);
|
||||
$chartData[strval(trans('firefly.everything_else'))] = $sum;
|
||||
}
|
||||
|
||||
return $chartData;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return MetaPieChartInterface
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): MetaPieChartInterface
|
||||
{
|
||||
$this->accounts = $accounts;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
*
|
||||
* @return MetaPieChartInterface
|
||||
*/
|
||||
public function setBudgets(Collection $budgets): MetaPieChartInterface
|
||||
{
|
||||
$this->budgets = $budgets;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
* @return MetaPieChartInterface
|
||||
*/
|
||||
public function setCategories(Collection $categories): MetaPieChartInterface
|
||||
{
|
||||
$this->categories = $categories;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $collectOtherObjects
|
||||
*
|
||||
* @return MetaPieChartInterface
|
||||
*/
|
||||
public function setCollectOtherObjects(bool $collectOtherObjects): MetaPieChartInterface
|
||||
{
|
||||
$this->collectOtherObjects = $collectOtherObjects;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return MetaPieChartInterface
|
||||
*/
|
||||
public function setEnd(Carbon $end): MetaPieChartInterface
|
||||
{
|
||||
$this->end = $end;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
*
|
||||
* @return MetaPieChartInterface
|
||||
*/
|
||||
public function setStart(Carbon $start): MetaPieChartInterface
|
||||
{
|
||||
$this->start = $start;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*
|
||||
* @return MetaPieChartInterface
|
||||
*/
|
||||
public function setUser(User $user): MetaPieChartInterface
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getTransactions(string $direction)
|
||||
{
|
||||
$types = [TransactionType::DEPOSIT, TransactionType::TRANSFER];
|
||||
$modifier = -1;
|
||||
if ($direction === 'expense') {
|
||||
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
||||
$modifier = 1;
|
||||
}
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAccounts($this->accounts);
|
||||
$collector->setRange($this->start, $this->end);
|
||||
$collector->setTypes($types);
|
||||
$collector->withOpposingAccount();
|
||||
|
||||
if ($direction === 'income') {
|
||||
$collector->disableFilter();
|
||||
}
|
||||
|
||||
if ($this->budgets->count() > 0) {
|
||||
$collector->setBudgets($this->budgets);
|
||||
}
|
||||
if ($this->categories->count() > 0) {
|
||||
$collector->setCategories($this->categories);
|
||||
}
|
||||
|
||||
$accountIds = $this->accounts->pluck('id')->toArray();
|
||||
$transactions = $collector->getJournals();
|
||||
$set = Support::filterTransactions($transactions, $accountIds, $modifier);
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $set
|
||||
* @param array $fields
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function groupByFields(Collection $set, array $fields)
|
||||
{
|
||||
$grouped = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($set as $transaction) {
|
||||
$values = [];
|
||||
foreach ($fields as $field) {
|
||||
$values[] = intval($transaction->$field);
|
||||
}
|
||||
$value = max($values);
|
||||
$grouped[$value] = $grouped[$value] ?? '0';
|
||||
$grouped[$value] = bcadd($transaction->transaction_amount, $grouped[$value]);
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param array $array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function organizeByType(string $type, array $array): array
|
||||
{
|
||||
$chartData = [];
|
||||
$names = [];
|
||||
$repository = app($this->repositories[$type], [$this->user]);
|
||||
foreach ($array as $objectId => $amount) {
|
||||
if (!isset($names[$objectId])) {
|
||||
$object = $repository->find(intval($objectId));
|
||||
$names[$objectId] = $object->name;
|
||||
}
|
||||
if (bccomp($amount, '0') === -1) {
|
||||
$amount = bcmul($amount, '-1');
|
||||
}
|
||||
|
||||
$this->total = bcadd($this->total, $amount);
|
||||
$chartData[$names[$objectId]] = $amount;
|
||||
}
|
||||
|
||||
return $chartData;
|
||||
|
||||
}
|
||||
}
|
||||
82
app/Helpers/Chart/MetaPieChartInterface.php
Normal file
82
app/Helpers/Chart/MetaPieChartInterface.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* MetaPieChartInterface.php
|
||||
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
||||
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Helpers\Chart;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface MetaPieChartInterface
|
||||
*
|
||||
* @package FireflyIII\Helpers\Chart
|
||||
*/
|
||||
interface MetaPieChartInterface
|
||||
{
|
||||
/**
|
||||
* @param string $direction
|
||||
* @param string $group
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function generate(string $direction, string $group): array;
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return MetaPieChartInterface
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): MetaPieChartInterface;
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
*
|
||||
* @return MetaPieChartInterface
|
||||
*/
|
||||
public function setBudgets(Collection $budgets): MetaPieChartInterface;
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
* @return MetaPieChartInterface
|
||||
*/
|
||||
public function setCategories(Collection $categories): MetaPieChartInterface;
|
||||
|
||||
/**
|
||||
* @param bool $collectOtherObjects
|
||||
*
|
||||
* @return MetaPieChartInterface
|
||||
*/
|
||||
public function setCollectOtherObjects(bool $collectOtherObjects): MetaPieChartInterface;
|
||||
|
||||
/**
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return MetaPieChartInterface
|
||||
*/
|
||||
public function setEnd(Carbon $end): MetaPieChartInterface;
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
*
|
||||
* @return MetaPieChartInterface
|
||||
*/
|
||||
public function setStart(Carbon $start): MetaPieChartInterface;
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*
|
||||
* @return MetaPieChartInterface
|
||||
*/
|
||||
public function setUser(User $user): MetaPieChartInterface;
|
||||
|
||||
}
|
||||
@@ -14,6 +14,7 @@ namespace FireflyIII\Helpers\Collection;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Budget as BudgetModel;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@@ -34,12 +35,10 @@ class BalanceLine
|
||||
|
||||
/** @var BudgetModel */
|
||||
protected $budget;
|
||||
/** @var Carbon */
|
||||
protected $endDate;
|
||||
/** @var BudgetLimit */
|
||||
protected $budgetLimit;
|
||||
/** @var int */
|
||||
protected $role = self::ROLE_DEFAULTROLE;
|
||||
/** @var Carbon */
|
||||
protected $startDate;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -90,20 +89,28 @@ class BalanceLine
|
||||
$this->budget = $budget;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BudgetLimit
|
||||
*/
|
||||
public function getBudgetLimit(): BudgetLimit
|
||||
{
|
||||
return $this->budgetLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetLimit $budgetLimit
|
||||
*/
|
||||
public function setBudgetLimit(BudgetLimit $budgetLimit)
|
||||
{
|
||||
$this->budgetLimit = $budgetLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Carbon
|
||||
*/
|
||||
public function getEndDate()
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $endDate
|
||||
*/
|
||||
public function setEndDate($endDate)
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
return $this->budgetLimit->end_date ?? new Carbon;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,18 +134,11 @@ class BalanceLine
|
||||
*/
|
||||
public function getStartDate()
|
||||
{
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $startDate
|
||||
*/
|
||||
public function setStartDate($startDate)
|
||||
{
|
||||
$this->startDate = $startDate;
|
||||
return $this->budgetLimit->start_date ?? new Carbon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(): string
|
||||
@@ -147,13 +147,13 @@ class BalanceLine
|
||||
return $this->getBudget()->name;
|
||||
}
|
||||
if ($this->getRole() == self::ROLE_DEFAULTROLE) {
|
||||
return trans('firefly.no_budget');
|
||||
return strval(trans('firefly.no_budget'));
|
||||
}
|
||||
if ($this->getRole() == self::ROLE_TAGROLE) {
|
||||
return trans('firefly.coveredWithTags');
|
||||
return strval(trans('firefly.coveredWithTags'));
|
||||
}
|
||||
if ($this->getRole() == self::ROLE_DIFFROLE) {
|
||||
return trans('firefly.leftUnbalanced');
|
||||
return strval(trans('firefly.leftUnbalanced'));
|
||||
}
|
||||
|
||||
return '';
|
||||
@@ -169,7 +169,7 @@ class BalanceLine
|
||||
*/
|
||||
public function leftOfRepetition(): string
|
||||
{
|
||||
$start = $this->budget->amount ?? '0';
|
||||
$start = $this->budgetLimit->amount ?? '0';
|
||||
/** @var BalanceEntry $balanceEntry */
|
||||
foreach ($this->getBalanceEntries() as $balanceEntry) {
|
||||
$start = bcadd($balanceEntry->getSpent(), $start);
|
||||
|
||||
@@ -79,6 +79,22 @@ class BillLine
|
||||
$this->bill = $bill;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Carbon
|
||||
*/
|
||||
public function getLastHitDate(): Carbon
|
||||
{
|
||||
return $this->lastHitDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $lastHitDate
|
||||
*/
|
||||
public function setLastHitDate(Carbon $lastHitDate)
|
||||
{
|
||||
$this->lastHitDate = $lastHitDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -151,21 +167,5 @@ class BillLine
|
||||
$this->hit = $hit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $lastHitDate
|
||||
*/
|
||||
public function setLastHitDate(Carbon $lastHitDate)
|
||||
{
|
||||
$this->lastHitDate = $lastHitDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Carbon
|
||||
*/
|
||||
public function getLastHitDate(): Carbon
|
||||
{
|
||||
return $this->lastHitDate;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ class Budget
|
||||
*/
|
||||
public function addBudgeted(string $add): Budget
|
||||
{
|
||||
$add = strval(round($add, 2));
|
||||
$this->budgeted = bcadd($this->budgeted, $add);
|
||||
|
||||
return $this;
|
||||
@@ -73,7 +72,6 @@ class Budget
|
||||
*/
|
||||
public function addLeft(string $add): Budget
|
||||
{
|
||||
$add = strval(round($add, 2));
|
||||
$this->left = bcadd($this->left, $add);
|
||||
|
||||
return $this;
|
||||
@@ -86,7 +84,6 @@ class Budget
|
||||
*/
|
||||
public function addOverspent(string $add): Budget
|
||||
{
|
||||
$add = strval(round($add, 2));
|
||||
$this->overspent = bcadd($this->overspent, $add);
|
||||
|
||||
return $this;
|
||||
@@ -99,7 +96,6 @@ class Budget
|
||||
*/
|
||||
public function addSpent(string $add): Budget
|
||||
{
|
||||
$add = strval(round($add, 2));
|
||||
$this->spent = bcadd($this->spent, $add);
|
||||
|
||||
return $this;
|
||||
@@ -168,7 +164,7 @@ class Budget
|
||||
*/
|
||||
public function setOverspent(string $overspent): Budget
|
||||
{
|
||||
$this->overspent = strval(round($overspent, 2));
|
||||
$this->overspent = $overspent;
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -188,7 +184,7 @@ class Budget
|
||||
*/
|
||||
public function setSpent(string $spent): Budget
|
||||
{
|
||||
$this->spent = strval(round($spent, 2));
|
||||
$this->spent = $spent;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Helpers\Collection;
|
||||
|
||||
use FireflyIII\Models\Budget as BudgetModel;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -26,14 +26,14 @@ class BudgetLine
|
||||
|
||||
/** @var BudgetModel */
|
||||
protected $budget;
|
||||
/** @var BudgetLimit */
|
||||
protected $budgetLimit;
|
||||
/** @var string */
|
||||
protected $budgeted = '0';
|
||||
/** @var string */
|
||||
protected $left = '0';
|
||||
/** @var string */
|
||||
protected $overspent = '0';
|
||||
/** @var LimitRepetition */
|
||||
protected $repetition;
|
||||
/** @var string */
|
||||
protected $spent = '0';
|
||||
|
||||
@@ -57,6 +57,26 @@ class BudgetLine
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BudgetLimit
|
||||
*/
|
||||
public function getBudgetLimit(): BudgetLimit
|
||||
{
|
||||
return $this->budgetLimit ?? new BudgetLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetLimit $budgetLimit
|
||||
*
|
||||
* @return BudgetLimit
|
||||
*/
|
||||
public function setBudgetLimit(BudgetLimit $budgetLimit): BudgetLine
|
||||
{
|
||||
$this->budgetLimit = $budgetLimit;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -117,26 +137,6 @@ class BudgetLine
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LimitRepetition
|
||||
*/
|
||||
public function getRepetition(): LimitRepetition
|
||||
{
|
||||
return $this->repetition ?? new LimitRepetition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LimitRepetition $repetition
|
||||
*
|
||||
* @return BudgetLine
|
||||
*/
|
||||
public function setRepetition(LimitRepetition $repetition): BudgetLine
|
||||
{
|
||||
$this->repetition = $repetition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -55,7 +55,6 @@ class Category
|
||||
*/
|
||||
public function addTotal(string $add)
|
||||
{
|
||||
$add = strval(round($add, 2));
|
||||
$this->total = bcadd($this->total, $add);
|
||||
}
|
||||
|
||||
@@ -79,7 +78,7 @@ class Category
|
||||
*/
|
||||
public function getTotal(): string
|
||||
{
|
||||
return strval(round($this->total, 2));
|
||||
return $this->total;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ class JournalCollector implements JournalCollectorInterface
|
||||
'transaction_types.type as transaction_type_type',
|
||||
'transaction_journals.bill_id',
|
||||
'bills.name as bill_name',
|
||||
'bills.name_encrypted as bill_name_encrypted',
|
||||
'transactions.id as id',
|
||||
'transactions.amount as transaction_amount',
|
||||
'transactions.description as transaction_description',
|
||||
@@ -180,10 +181,12 @@ class JournalCollector implements JournalCollectorInterface
|
||||
$set->each(
|
||||
function (Transaction $transaction) {
|
||||
$transaction->date = new Carbon($transaction->date);
|
||||
$transaction->description = intval($transaction->encrypted) === 1 ? Crypt::decrypt($transaction->description) : $transaction->description;
|
||||
$transaction->bill_name = !is_null($transaction->bill_name) ? Crypt::decrypt($transaction->bill_name) : '';
|
||||
$transaction->description = $transaction->encrypted ? Crypt::decrypt($transaction->description) : $transaction->description;
|
||||
|
||||
if (!is_null($transaction->bill_name)) {
|
||||
$transaction->bill_name = $transaction->bill_name_encrypted ? Crypt::decrypt($transaction->bill_name) : $transaction->bill_name;
|
||||
}
|
||||
|
||||
// optionally decrypted:
|
||||
try {
|
||||
$transaction->opposing_account_name = Crypt::decrypt($transaction->opposing_account_name);
|
||||
} catch (DecryptException $e) {
|
||||
@@ -560,7 +563,7 @@ class JournalCollector implements JournalCollectorInterface
|
||||
return $set;
|
||||
}
|
||||
if ($this->joinedOpposing === false) {
|
||||
Log::error('Cannot filter internal transfers because no opposing information is present.');
|
||||
Log::info('Cannot filter internal transfers because no opposing information is present.');
|
||||
|
||||
return $set;
|
||||
}
|
||||
@@ -646,9 +649,17 @@ class JournalCollector implements JournalCollectorInterface
|
||||
// join some extra tables:
|
||||
$this->joinedBudget = true;
|
||||
$this->query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
|
||||
$this->query->leftJoin('budgets as transaction_journal_budgets', 'transaction_journal_budgets.id', '=', 'budget_transaction_journal.budget_id');
|
||||
$this->query->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id');
|
||||
$this->query->leftJoin('budgets as transaction_budgets', 'transaction_budgets.id', '=', 'budget_transaction.budget_id');
|
||||
|
||||
$this->fields[] = 'budget_transaction_journal.budget_id as transaction_journal_budget_id';
|
||||
$this->fields[] = 'transaction_journal_budgets.encrypted as transaction_journal_budget_encrypted';
|
||||
$this->fields[] = 'transaction_journal_budgets.name as transaction_journal_budget_name';
|
||||
|
||||
$this->fields[] = 'budget_transaction.budget_id as transaction_budget_id';
|
||||
$this->fields[] = 'transaction_budgets.encrypted as transaction_budget_encrypted';
|
||||
$this->fields[] = 'transaction_budgets.name as transaction_budget_name';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,12 +672,26 @@ class JournalCollector implements JournalCollectorInterface
|
||||
// join some extra tables:
|
||||
$this->joinedCategory = true;
|
||||
$this->query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
|
||||
$this->query->leftJoin(
|
||||
'categories as transaction_journal_categories', 'transaction_journal_categories.id', '=', 'category_transaction_journal.category_id'
|
||||
);
|
||||
|
||||
$this->query->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id');
|
||||
$this->query->leftJoin('categories as transaction_categories', 'transaction_categories.id', '=', 'category_transaction.category_id');
|
||||
|
||||
$this->fields[] = 'category_transaction_journal.category_id as transaction_journal_category_id';
|
||||
$this->fields[] = 'transaction_journal_categories.encrypted as transaction_journal_category_encrypted';
|
||||
$this->fields[] = 'transaction_journal_categories.name as transaction_journal_category_name';
|
||||
|
||||
$this->fields[] = 'category_transaction.category_id as transaction_category_id';
|
||||
$this->fields[] = 'transaction_categories.encrypted as transaction_category_encrypted';
|
||||
$this->fields[] = 'transaction_categories.name as transaction_category_name';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function joinOpposingTables()
|
||||
{
|
||||
if (!$this->joinedOpposing) {
|
||||
@@ -709,7 +734,7 @@ class JournalCollector implements JournalCollectorInterface
|
||||
*/
|
||||
private function startQuery(): EloquentBuilder
|
||||
{
|
||||
|
||||
/** @var EloquentBuilder $query */
|
||||
$query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id')
|
||||
->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id')
|
||||
|
||||
@@ -74,12 +74,8 @@ class Help implements HelpInterface
|
||||
$converter = new CommonMarkConverter();
|
||||
$content = $converter->convertToHtml($content);
|
||||
}
|
||||
if (strlen($content) === 0) {
|
||||
Log::warning('Raw content length is zero.');
|
||||
}
|
||||
|
||||
return $content;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,17 +19,18 @@ use FireflyIII\Helpers\Collection\Balance;
|
||||
use FireflyIII\Helpers\Collection\BalanceEntry;
|
||||
use FireflyIII\Helpers\Collection\BalanceHeader;
|
||||
use FireflyIII\Helpers\Collection\BalanceLine;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class BalanceReportHelper
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) // I can't really help it.
|
||||
* @package FireflyIII\Helpers\Report
|
||||
*/
|
||||
class BalanceReportHelper implements BalanceReportHelperInterface
|
||||
@@ -59,19 +60,21 @@ class BalanceReportHelper implements BalanceReportHelperInterface
|
||||
*/
|
||||
public function getBalanceReport(Collection $accounts, Carbon $start, Carbon $end): Balance
|
||||
{
|
||||
Log::debug('Start of balance report');
|
||||
$balance = new Balance;
|
||||
$header = new BalanceHeader;
|
||||
$limitRepetitions = $this->budgetRepository->getAllBudgetLimitRepetitions($start, $end);
|
||||
$budgetLimits = $this->budgetRepository->getAllBudgetLimits($start, $end);
|
||||
foreach ($accounts as $account) {
|
||||
Log::debug(sprintf('Add account %s to headers.', $account->name));
|
||||
$header->addAccount($account);
|
||||
}
|
||||
|
||||
/** @var LimitRepetition $repetition */
|
||||
foreach ($limitRepetitions as $repetition) {
|
||||
$budget = $this->budgetRepository->find($repetition->budget_id);
|
||||
$line = $this->createBalanceLine($budget, $repetition, $accounts);
|
||||
/** @var BudgetLimit $budgetLimit */
|
||||
foreach ($budgetLimits as $budgetLimit) {
|
||||
$line = $this->createBalanceLine($budgetLimit, $accounts);
|
||||
$balance->addBalanceLine($line);
|
||||
}
|
||||
Log::debug('Create rest of the things.');
|
||||
$noBudgetLine = $this->createNoBudgetLine($accounts, $start, $end);
|
||||
$coveredByTagLine = $this->createTagsBalanceLine($accounts, $start, $end);
|
||||
$leftUnbalancedLine = $this->createLeftUnbalancedLine($noBudgetLine, $coveredByTagLine);
|
||||
@@ -81,9 +84,12 @@ class BalanceReportHelper implements BalanceReportHelperInterface
|
||||
$balance->addBalanceLine($leftUnbalancedLine);
|
||||
$balance->setBalanceHeader($header);
|
||||
|
||||
Log::debug('Clear unused budgets.');
|
||||
// remove budgets without expenses from balance lines:
|
||||
$balance = $this->removeUnusedBudgets($balance);
|
||||
|
||||
Log::debug('Return report.');
|
||||
|
||||
return $balance;
|
||||
}
|
||||
|
||||
@@ -137,27 +143,22 @@ class BalanceReportHelper implements BalanceReportHelperInterface
|
||||
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
* @param LimitRepetition $repetition
|
||||
* @param BudgetLimit $budgetLimit
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return BalanceLine
|
||||
*/
|
||||
private function createBalanceLine(Budget $budget, LimitRepetition $repetition, Collection $accounts): BalanceLine
|
||||
private function createBalanceLine(BudgetLimit $budgetLimit, Collection $accounts): BalanceLine
|
||||
{
|
||||
$line = new BalanceLine;
|
||||
$budget->amount = $repetition->amount;
|
||||
$line->setBudget($budget);
|
||||
$line->setStartDate($repetition->startdate);
|
||||
$line->setEndDate($repetition->enddate);
|
||||
$line->setBudget($budgetLimit->budget);
|
||||
$line->setBudgetLimit($budgetLimit);
|
||||
|
||||
// loop accounts:
|
||||
foreach ($accounts as $account) {
|
||||
$balanceEntry = new BalanceEntry;
|
||||
$balanceEntry->setAccount($account);
|
||||
$spent = $this->budgetRepository->spentInPeriod(
|
||||
new Collection([$budget]), new Collection([$account]), $repetition->startdate, $repetition->enddate
|
||||
);
|
||||
$spent = $this->budgetRepository->spentInPeriod(new Collection([$budgetLimit->budget]), new Collection([$account]), $budgetLimit->start_date, $budgetLimit->end_date);
|
||||
$balanceEntry->setSpent($spent);
|
||||
$line->addBalanceEntry($balanceEntry);
|
||||
}
|
||||
@@ -212,7 +213,7 @@ class BalanceReportHelper implements BalanceReportHelperInterface
|
||||
$empty = new BalanceLine;
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$spent = $this->budgetRepository->spentInPeriodWithoutBudget(new Collection([$account]), $start, $end);
|
||||
$spent = $this->budgetRepository->spentInPeriodWoBudget(new Collection([$account]), $start, $end);
|
||||
// budget
|
||||
$budgetEntry = new BalanceEntry;
|
||||
$budgetEntry->setAccount($account);
|
||||
|
||||
@@ -18,7 +18,7 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
|
||||
use FireflyIII\Helpers\Collection\BudgetLine;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
@@ -43,6 +43,7 @@ class BudgetReportHelper implements BudgetReportHelperInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly 5.
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
@@ -56,13 +57,9 @@ class BudgetReportHelper implements BudgetReportHelperInterface
|
||||
|
||||
/** @var Budget $budget */
|
||||
foreach ($set as $budget) {
|
||||
$repetitions = $budget->limitrepetitions()->before($end)->after($start)->get();
|
||||
|
||||
// no repetition(s) for this budget:
|
||||
if ($repetitions->count() == 0) {
|
||||
// spent for budget in time range:
|
||||
$spent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);
|
||||
|
||||
$budgetLimits = $this->repository->getBudgetLimits($budget, $start, $end);
|
||||
if ($budgetLimits->count() == 0) { // no budget limit(s) for this budget
|
||||
$spent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);// spent for budget in time range
|
||||
if ($spent > 0) {
|
||||
$budgetLine = new BudgetLine;
|
||||
$budgetLine->setBudget($budget)->setOverspent($spent);
|
||||
@@ -70,26 +67,20 @@ class BudgetReportHelper implements BudgetReportHelperInterface
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// one or more repetitions for budget:
|
||||
/** @var LimitRepetition $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$data = $this->calculateExpenses($budget, $repetition, $accounts);
|
||||
|
||||
/** @var BudgetLimit $budgetLimit */
|
||||
foreach ($budgetLimits as $budgetLimit) { // one or more repetitions for budget
|
||||
$data = $this->calculateExpenses($budget, $budgetLimit, $accounts);
|
||||
$budgetLine = new BudgetLine;
|
||||
$budgetLine->setBudget($budget)->setRepetition($repetition)
|
||||
$budgetLine->setBudget($budget)->setBudgetLimit($budgetLimit)
|
||||
->setLeft($data['left'])->setSpent($data['expenses'])->setOverspent($data['overspent'])
|
||||
->setBudgeted(strval($repetition->amount));
|
||||
->setBudgeted(strval($budgetLimit->amount));
|
||||
|
||||
$object->addBudgeted(strval($repetition->amount))->addSpent($data['spent'])
|
||||
$object->addBudgeted(strval($budgetLimit->amount))->addSpent($data['spent'])
|
||||
->addLeft($data['left'])->addOverspent($data['overspent'])->addBudgetLine($budgetLine);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// stuff outside of budgets:
|
||||
|
||||
$noBudget = $this->repository->spentInPeriodWithoutBudget($accounts, $start, $end);
|
||||
$noBudget = $this->repository->spentInPeriodWoBudget($accounts, $start, $end); // stuff outside of budgets
|
||||
$budgetLine = new BudgetLine;
|
||||
$budgetLine->setOverspent($noBudget)->setSpent($noBudget);
|
||||
$object->addOverspent($noBudget)->addBudgetLine($budgetLine);
|
||||
@@ -129,18 +120,18 @@ class BudgetReportHelper implements BudgetReportHelperInterface
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
* @param LimitRepetition $repetition
|
||||
* @param BudgetLimit $budgetLimit
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function calculateExpenses(Budget $budget, LimitRepetition $repetition, Collection $accounts): array
|
||||
private function calculateExpenses(Budget $budget, BudgetLimit $budgetLimit, Collection $accounts): array
|
||||
{
|
||||
$array = [];
|
||||
$expenses = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $repetition->startdate, $repetition->enddate);
|
||||
$array['left'] = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? bcadd($repetition->amount, $expenses) : '0';
|
||||
$array['spent'] = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? $expenses : '0';
|
||||
$array['overspent'] = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? '0' : bcadd($expenses, $repetition->amount);
|
||||
$expenses = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $budgetLimit->start_date, $budgetLimit->end_date);
|
||||
$array['left'] = bccomp(bcadd($budgetLimit->amount, $expenses), '0') === 1 ? bcadd($budgetLimit->amount, $expenses) : '0';
|
||||
$array['spent'] = bccomp(bcadd($budgetLimit->amount, $expenses), '0') === 1 ? $expenses : '0';
|
||||
$array['overspent'] = bccomp(bcadd($budgetLimit->amount, $expenses), '0') === 1 ? '0' : bcadd($expenses, $budgetLimit->amount);
|
||||
$array['expenses'] = $expenses;
|
||||
|
||||
return $array;
|
||||
|
||||
@@ -53,6 +53,8 @@ class ReportHelper implements ReportHelperInterface
|
||||
* This method generates a full report for the given period on all
|
||||
* the users bills and their payments.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly 5.
|
||||
*
|
||||
* Excludes bills which have not had a payment on the mentioned accounts.
|
||||
*
|
||||
* @param Carbon $start
|
||||
@@ -80,8 +82,6 @@ class ReportHelper implements ReportHelperInterface
|
||||
$billLine->setMin(strval($bill->amount_min));
|
||||
$billLine->setMax(strval($bill->amount_max));
|
||||
$billLine->setHit(false);
|
||||
// is hit in period?
|
||||
|
||||
$entry = $journals->filter(
|
||||
function (Transaction $transaction) use ($bill) {
|
||||
return $transaction->bill_id === $bill->id;
|
||||
@@ -94,44 +94,15 @@ class ReportHelper implements ReportHelperInterface
|
||||
$billLine->setLastHitDate($first->date);
|
||||
$billLine->setHit(true);
|
||||
}
|
||||
|
||||
// bill is active, or bill is hit:
|
||||
if ($billLine->isActive() || $billLine->isHit()) {
|
||||
$collection->addBill($billLine);
|
||||
}
|
||||
}
|
||||
|
||||
// do some extra filtering.
|
||||
$collection->filterBills();
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return CategoryCollection
|
||||
*/
|
||||
public function getCategoryReport(Collection $accounts, Carbon $start, Carbon $end): CategoryCollection
|
||||
{
|
||||
$object = new CategoryCollection;
|
||||
/** @var CategoryRepositoryInterface $repository */
|
||||
$repository = app(CategoryRepositoryInterface::class);
|
||||
$categories = $repository->getCategories();
|
||||
|
||||
/** @var Category $category */
|
||||
foreach ($categories as $category) {
|
||||
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end);
|
||||
// CategoryCollection expects the amount in $spent:
|
||||
$category->spent = $spent;
|
||||
$object->addCategory($category);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
|
||||
@@ -42,15 +42,6 @@ interface ReportHelperInterface
|
||||
*/
|
||||
public function getBillReport(Carbon $start, Carbon $end, Collection $accounts): BillCollection;
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return CategoryCollection
|
||||
*/
|
||||
public function getCategoryReport(Collection $accounts, Carbon $start, Carbon $end): CategoryCollection;
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
|
||||
@@ -17,7 +17,6 @@ use Amount;
|
||||
use Carbon\Carbon;
|
||||
use ExpandedForm;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Requests\AccountFormRequest;
|
||||
use FireflyIII\Models\Account;
|
||||
@@ -28,8 +27,8 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
use Log;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
@@ -66,7 +65,7 @@ class AccountController extends Controller
|
||||
/**
|
||||
* @param string $what
|
||||
*
|
||||
* @return View
|
||||
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory|View
|
||||
*/
|
||||
public function create(string $what = 'asset')
|
||||
{
|
||||
@@ -76,12 +75,9 @@ class AccountController extends Controller
|
||||
$defaultCurrency = Amount::getDefaultCurrency();
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
||||
$subTitle = trans('firefly.make_new_' . $what . '_account');
|
||||
Session::flash(
|
||||
'preFilled',
|
||||
[
|
||||
'currency_id' => $defaultCurrency->id,
|
||||
]
|
||||
);
|
||||
|
||||
// pre fill some data
|
||||
Session::flash('preFilled', ['currency_id' => $defaultCurrency->id,]);
|
||||
|
||||
// put previous url in session if not redirect from store (not "create another").
|
||||
if (session('accounts.create.fromStore') !== true) {
|
||||
@@ -117,24 +113,32 @@ class AccountController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param ARI $repository
|
||||
* @param Account $account
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function destroy(ARI $repository, Account $account)
|
||||
public function destroy(Request $request, ARI $repository, Account $account)
|
||||
{
|
||||
$type = $account->accountType->type;
|
||||
$typeName = config('firefly.shortNamesByFullName.' . $type);
|
||||
$name = $account->name;
|
||||
$moveTo = $repository->find(intval(Input::get('move_account_before_delete')));
|
||||
$accountId = $account->id;
|
||||
$moveTo = $repository->find(intval($request->get('move_account_before_delete')));
|
||||
|
||||
$repository->destroy($account, $moveTo);
|
||||
|
||||
Session::flash('success', strval(trans('firefly.' . $typeName . '_deleted', ['name' => $name])));
|
||||
Preferences::mark();
|
||||
|
||||
return redirect(session('accounts.delete.url'));
|
||||
$uri = session('accounts.delete.url');
|
||||
if (!(strpos($uri, sprintf('accounts/show/%s', $accountId)) === false)) {
|
||||
// uri would point back to account
|
||||
$uri = route('accounts.index', [$typeName]);
|
||||
}
|
||||
|
||||
return redirect($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +197,6 @@ class AccountController extends Controller
|
||||
public function index(ARI $repository, string $what)
|
||||
{
|
||||
$what = $what ?? 'asset';
|
||||
|
||||
$subTitle = trans('firefly.' . $what . '_accounts');
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
||||
$types = config('firefly.accountTypesByIdentifier.' . $what);
|
||||
@@ -214,6 +217,7 @@ class AccountController extends Controller
|
||||
$account->lastActivityDate = $this->isInArray($activities, $account->id);
|
||||
$account->startBalance = $this->isInArray($startBalances, $account->id);
|
||||
$account->endBalance = $this->isInArray($endBalances, $account->id);
|
||||
$account->difference = bcsub($account->endBalance, $account->startBalance);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -221,12 +225,13 @@ class AccountController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param JournalCollectorInterface $collector
|
||||
* @param Account $account
|
||||
*
|
||||
* @return View
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||
*/
|
||||
public function show(JournalCollectorInterface $collector, Account $account)
|
||||
public function show(Request $request, JournalCollectorInterface $collector, Account $account)
|
||||
{
|
||||
if ($account->accountType->type === AccountType::INITIAL_BALANCE) {
|
||||
return $this->redirectToOriginalAccount($account);
|
||||
@@ -237,8 +242,10 @@ class AccountController extends Controller
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
|
||||
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$chartUri = route('chart.account.single', [$account->id]);
|
||||
$accountType = $account->accountType->type;
|
||||
|
||||
// grab those journals:
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
|
||||
@@ -248,23 +255,26 @@ class AccountController extends Controller
|
||||
// generate entries for each period (and cache those)
|
||||
$entries = $this->periodEntries($account);
|
||||
|
||||
return view('accounts.show', compact('account', 'what', 'entries', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end'));
|
||||
return view('accounts.show', compact('account', 'accountType', 'entries', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param ARI $repository
|
||||
* @param Account $account
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function showAll(AccountRepositoryInterface $repository, Account $account)
|
||||
public function showAll(Request $request, AccountRepositoryInterface $repository, Account $account)
|
||||
{
|
||||
$subTitle = sprintf('%s (%s)', $account->name, strtolower(trans('firefly.everything')));
|
||||
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
|
||||
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$chartUri = route('chart.account.all', [$account->id]);
|
||||
|
||||
// replace with journal collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('accounts/show/' . $account->id . '/all');
|
||||
@@ -273,32 +283,41 @@ class AccountController extends Controller
|
||||
$start = $repository->oldestJournalDate($account);
|
||||
$end = $repository->newestJournalDate($account);
|
||||
|
||||
return view('accounts.show_with_date', compact('account', 'journals', 'subTitle', 'start', 'end'));
|
||||
// same call, except "entries".
|
||||
return view('accounts.show', compact('account', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Account $account
|
||||
* @param string $date
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function showByDate(Account $account, string $date)
|
||||
public function showByDate(Request $request, Account $account, string $date)
|
||||
{
|
||||
$carbon = new Carbon($date);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($carbon, $range);
|
||||
$end = Navigation::endOfPeriod($carbon, $range);
|
||||
$subTitle = $account->name . ' (' . Navigation::periodShow($start, $range) . ')';
|
||||
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
|
||||
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$chartUri = route('chart.account.period', [$account->id, $carbon->format('Y-m-d')]);
|
||||
$accountType = $account->accountType->type;
|
||||
|
||||
// replace with journal collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('accounts/show/' . $account->id . '/' . $date);
|
||||
|
||||
return view('accounts.show-by-date', compact('category', 'date', 'account', 'journals', 'subTitle', 'carbon', 'start', 'end'));
|
||||
// generate entries for each period (and cache those)
|
||||
$entries = $this->periodEntries($account);
|
||||
|
||||
// same call, except "entries".
|
||||
return view('accounts.show', compact('account', 'accountType', 'entries', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,7 +342,7 @@ class AccountController extends Controller
|
||||
Preferences::set('frontPageAccounts', $frontPage);
|
||||
}
|
||||
|
||||
if (intval(Input::get('create_another')) === 1) {
|
||||
if (intval($request->get('create_another')) === 1) {
|
||||
// set value so create routine will not overwrite URL:
|
||||
Session::put('accounts.create.fromStore', true);
|
||||
|
||||
@@ -349,7 +368,7 @@ class AccountController extends Controller
|
||||
Session::flash('success', strval(trans('firefly.updated_account', ['name' => $account->name])));
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('return_to_edit')) === 1) {
|
||||
if (intval($request->get('return_to_edit')) === 1) {
|
||||
// set value so edit routine will not overwrite URL:
|
||||
Session::put('accounts.edit.fromUpdate', true);
|
||||
|
||||
@@ -374,7 +393,7 @@ class AccountController extends Controller
|
||||
return $array[$entryId];
|
||||
}
|
||||
|
||||
return '';
|
||||
return '0';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -425,7 +444,7 @@ class AccountController extends Controller
|
||||
$earned = $tasker->amountInInPeriod(new Collection([$account]), $assets, $end, $currentEnd);
|
||||
$dateStr = $end->format('Y-m-d');
|
||||
$dateName = Navigation::periodShow($end, $range);
|
||||
$entries->push([$dateStr, $dateName, $spent, $earned]);
|
||||
$entries->push([$dateStr, $dateName, $spent, $earned, clone $end]);
|
||||
$end = Navigation::subtractPeriod($end, $range, 1);
|
||||
|
||||
}
|
||||
|
||||
@@ -59,10 +59,13 @@ class ConfigurationController extends Controller
|
||||
// all available configuration and their default value in case
|
||||
// they don't exist yet.
|
||||
$singleUserMode = FireflyConfig::get('single_user_mode', config('firefly.configuration.single_user_mode'))->data;
|
||||
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
|
||||
$isDemoSite = FireflyConfig::get('is_demo_site', config('firefly.configuration.is_demo_site'))->data;
|
||||
$siteOwner = env('SITE_OWNER');
|
||||
|
||||
return view('admin.configuration.index', compact('subTitle', 'subTitleIcon', 'singleUserMode', 'mustConfirmAccount', 'isDemoSite'));
|
||||
return view(
|
||||
'admin.configuration.index',
|
||||
compact('subTitle', 'subTitleIcon', 'singleUserMode', 'isDemoSite', 'siteOwner')
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@@ -78,7 +81,6 @@ class ConfigurationController extends Controller
|
||||
|
||||
// store config values
|
||||
FireflyConfig::set('single_user_mode', $data['single_user_mode']);
|
||||
FireflyConfig::set('must_confirm_account', $data['must_confirm_account']);
|
||||
FireflyConfig::set('is_demo_site', $data['is_demo_site']);
|
||||
|
||||
// flash message
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* DomainController.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Admin;
|
||||
|
||||
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Session;
|
||||
|
||||
/**
|
||||
* Class DomainController
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers\Admin
|
||||
*/
|
||||
class DomainController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function domains()
|
||||
{
|
||||
|
||||
$title = strval(trans('firefly.administration'));
|
||||
$mainTitleIcon = 'fa-hand-spock-o';
|
||||
$subTitle = strval(trans('firefly.blocked_domains'));
|
||||
$subTitleIcon = 'fa-exclamation-circle';
|
||||
$domains = FireflyConfig::get('blocked-domains', [])->data;
|
||||
|
||||
// known domains
|
||||
$knownDomains = $this->getKnownDomains();
|
||||
|
||||
return view('admin.domains.index', compact('title', 'mainTitleIcon', 'knownDomains', 'subTitle', 'subTitleIcon', 'domains'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function manual(Request $request)
|
||||
{
|
||||
if (strlen($request->get('domain')) === 0) {
|
||||
Session::flash('error', trans('firefly.no_domain_filled_in'));
|
||||
|
||||
return redirect(route('admin.users.domains'));
|
||||
}
|
||||
|
||||
$domain = strtolower($request->get('domain'));
|
||||
$blocked = FireflyConfig::get('blocked-domains', [])->data;
|
||||
|
||||
if (in_array($domain, $blocked)) {
|
||||
Session::flash('error', trans('firefly.domain_already_blocked', ['domain' => $domain]));
|
||||
|
||||
return redirect(route('admin.users.domains'));
|
||||
}
|
||||
$blocked[] = $domain;
|
||||
FireflyConfig::set('blocked-domains', $blocked);
|
||||
|
||||
Session::flash('success', trans('firefly.domain_is_now_blocked', ['domain' => $domain]));
|
||||
|
||||
return redirect(route('admin.users.domains'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $domain
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function toggleDomain(string $domain)
|
||||
{
|
||||
$domain = strtolower($domain);
|
||||
$blocked = FireflyConfig::get('blocked-domains', [])->data;
|
||||
|
||||
if (in_array($domain, $blocked)) {
|
||||
$key = array_search($domain, $blocked);
|
||||
unset($blocked[$key]);
|
||||
sort($blocked);
|
||||
|
||||
FireflyConfig::set('blocked-domains', $blocked);
|
||||
Session::flash('message', trans('firefly.domain_now_unblocked', ['domain' => $domain]));
|
||||
|
||||
|
||||
return redirect(route('admin.users.domains'));
|
||||
|
||||
}
|
||||
|
||||
$blocked[] = $domain;
|
||||
|
||||
FireflyConfig::set('blocked-domains', $blocked);
|
||||
Session::flash('message', trans('firefly.domain_now_blocked', ['domain' => $domain]));
|
||||
|
||||
return redirect(route('admin.users.domains'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getKnownDomains(): array
|
||||
{
|
||||
/** @var UserRepositoryInterface $repository */
|
||||
$repository = app(UserRepositoryInterface::class);
|
||||
$users = $repository->all();
|
||||
$set = [];
|
||||
$filtered = [];
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$email = $user->email;
|
||||
$parts = explode('@', $email);
|
||||
$set[] = strtolower($parts[1]);
|
||||
}
|
||||
$set = array_unique($set);
|
||||
// filter for already banned domains:
|
||||
$blocked = FireflyConfig::get('blocked-domains', [])->data;
|
||||
|
||||
foreach ($set as $domain) {
|
||||
// in the block array? ignore it.
|
||||
if (!in_array($domain, $blocked)) {
|
||||
$filtered[] = $domain;
|
||||
}
|
||||
}
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
}
|
||||
@@ -14,11 +14,14 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Controllers\Admin;
|
||||
|
||||
|
||||
use FireflyConfig;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Requests\UserFormRequest;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Preferences;
|
||||
use Session;
|
||||
use URL;
|
||||
use View;
|
||||
|
||||
/**
|
||||
* Class UserController
|
||||
@@ -27,57 +30,75 @@ use Preferences;
|
||||
*/
|
||||
class UserController extends Controller
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('title', strval(trans('firefly.administration')));
|
||||
View::share('mainTitleIcon', 'fa-hand-spock-o');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*
|
||||
* @return int
|
||||
* @return View
|
||||
*/
|
||||
public function edit(User $user)
|
||||
{
|
||||
return $user->id;
|
||||
// put previous url in session if not redirect from store (not "return_to_edit").
|
||||
if (session('users.edit.fromUpdate') !== true) {
|
||||
Session::put('users.edit.url', URL::previous());
|
||||
}
|
||||
Session::forget('users.edit.fromUpdate');
|
||||
|
||||
$subTitle = strval(trans('firefly.edit_user', ['email' => $user->email]));
|
||||
$subTitleIcon = 'fa-user-o';
|
||||
$codes = [
|
||||
'' => strval(trans('firefly.no_block_code')),
|
||||
'bounced' => strval(trans('firefly.block_code_bounced')),
|
||||
'expired' => strval(trans('firefly.block_code_expired')),
|
||||
];
|
||||
|
||||
return view('admin.users.edit', compact('user', 'subTitle', 'subTitleIcon', 'codes'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UserRepositoryInterface $repository
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
* @return View
|
||||
*/
|
||||
public function index(UserRepositoryInterface $repository)
|
||||
{
|
||||
$title = strval(trans('firefly.administration'));
|
||||
$mainTitleIcon = 'fa-hand-spock-o';
|
||||
$subTitle = strval(trans('firefly.user_administration'));
|
||||
$subTitleIcon = 'fa-users';
|
||||
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
|
||||
$users = $repository->all();
|
||||
|
||||
// add meta stuff.
|
||||
$users->each(
|
||||
function (User $user) use ($mustConfirmAccount) {
|
||||
$list = ['user_confirmed', 'twoFactorAuthEnabled', 'twoFactorAuthSecret', 'registration_ip_address', 'confirmation_ip_address'];
|
||||
function (User $user) {
|
||||
$list = ['twoFactorAuthEnabled', 'twoFactorAuthSecret'];
|
||||
$preferences = Preferences::getArrayForUser($user, $list);
|
||||
|
||||
$user->activated = true;
|
||||
if (!($preferences['user_confirmed'] === true) && $mustConfirmAccount === true) {
|
||||
$user->activated = false;
|
||||
}
|
||||
|
||||
$user->isAdmin = $user->hasRole('owner');
|
||||
$is2faEnabled = $preferences['twoFactorAuthEnabled'] === true;
|
||||
$has2faSecret = !is_null($preferences['twoFactorAuthSecret']);
|
||||
$user->has2FA = false;
|
||||
if ($is2faEnabled && $has2faSecret) {
|
||||
$user->has2FA = true;
|
||||
}
|
||||
$user->has2FA = ($is2faEnabled && $has2faSecret) ? true : false;
|
||||
$user->prefs = $preferences;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
return view('admin.users.index', compact('title', 'mainTitleIcon', 'subTitle', 'subTitleIcon', 'users'));
|
||||
return view('admin.users.index', compact('subTitle', 'subTitleIcon', 'users'));
|
||||
|
||||
}
|
||||
|
||||
@@ -93,40 +114,51 @@ class UserController extends Controller
|
||||
$mainTitleIcon = 'fa-hand-spock-o';
|
||||
$subTitle = strval(trans('firefly.single_user_administration', ['email' => $user->email]));
|
||||
$subTitleIcon = 'fa-user';
|
||||
|
||||
// get IP info:
|
||||
$defaultIp = '0.0.0.0';
|
||||
$regPref = Preferences::getForUser($user, 'registration_ip_address');
|
||||
$registration = $defaultIp;
|
||||
$conPref = Preferences::getForUser($user, 'confirmation_ip_address');
|
||||
$confirmation = $defaultIp;
|
||||
if (!is_null($regPref)) {
|
||||
$registration = $regPref->data;
|
||||
}
|
||||
if (!is_null($conPref)) {
|
||||
$confirmation = $conPref->data;
|
||||
}
|
||||
|
||||
$registrationHost = '';
|
||||
$confirmationHost = '';
|
||||
|
||||
if ($registration != $defaultIp) {
|
||||
$registrationHost = gethostbyaddr($registration);
|
||||
}
|
||||
if ($confirmation != $defaultIp) {
|
||||
$confirmationHost = gethostbyaddr($confirmation);
|
||||
}
|
||||
|
||||
$information = $repository->getUserData($user);
|
||||
|
||||
return view(
|
||||
'admin.users.show',
|
||||
compact(
|
||||
'title', 'mainTitleIcon', 'subTitle', 'subTitleIcon', 'information',
|
||||
'user', 'registration', 'confirmation', 'registrationHost', 'confirmationHost'
|
||||
'title', 'mainTitleIcon', 'subTitle', 'subTitleIcon', 'information', 'user'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UserFormRequest $request
|
||||
* @param User $user
|
||||
*
|
||||
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function update(UserFormRequest $request, User $user)
|
||||
{
|
||||
$data = $request->getUserData();
|
||||
|
||||
// update password
|
||||
if (strlen($data['password']) > 0) {
|
||||
$user->password = bcrypt($data['password']);
|
||||
$user->save();
|
||||
}
|
||||
|
||||
// change blocked status and code:
|
||||
$user->blocked = $data['blocked'];
|
||||
$user->blocked_code = $data['blocked_code'];
|
||||
$user->save();
|
||||
|
||||
Session::flash('success', strval(trans('firefly.updated_user', ['email' => $user->email])));
|
||||
Preferences::mark();
|
||||
|
||||
if (intval($request->get('return_to_edit')) === 1) {
|
||||
// set value so edit routine will not overwrite URL:
|
||||
Session::put('users.edit.fromUpdate', true);
|
||||
|
||||
return redirect(route('admin.users.edit', [$user->id]))->withInput(['return_to_edit' => 1]);
|
||||
}
|
||||
|
||||
// redirect to previous URL.
|
||||
return redirect(session('users.edit.url'));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -13,24 +13,22 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Crypt;
|
||||
use File;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Requests\AttachmentFormRequest;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
|
||||
use Input;
|
||||
use Log;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use Session;
|
||||
use Storage;
|
||||
use URL;
|
||||
use View;
|
||||
|
||||
/**
|
||||
* Class AttachmentController
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) // it's 13.
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers
|
||||
*/
|
||||
class AttachmentController extends Controller
|
||||
@@ -57,7 +55,7 @@ class AttachmentController extends Controller
|
||||
/**
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
|
||||
* @return View
|
||||
*/
|
||||
public function delete(Attachment $attachment)
|
||||
{
|
||||
@@ -90,23 +88,17 @@ class AttachmentController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AttachmentRepositoryInterface $repository
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @return mixed
|
||||
* @throws FireflyException
|
||||
*
|
||||
*/
|
||||
public function download(Attachment $attachment)
|
||||
public function download(AttachmentRepositoryInterface $repository, Attachment $attachment)
|
||||
{
|
||||
// create a disk.
|
||||
$disk = Storage::disk('upload');
|
||||
$file = $attachment->fileName();
|
||||
|
||||
if ($disk->exists($file)) {
|
||||
|
||||
if ($repository->exists($attachment)) {
|
||||
$content = $repository->getContent($attachment);
|
||||
$quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\'));
|
||||
$content = Crypt::decrypt($disk->get($file));
|
||||
|
||||
Log::debug('Send file to user', ['file' => $quoted, 'size' => strlen($content)]);
|
||||
|
||||
return response($content, 200)
|
||||
->header('Content-Description', 'File Transfer')
|
||||
@@ -118,7 +110,6 @@ class AttachmentController extends Controller
|
||||
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
|
||||
->header('Pragma', 'public')
|
||||
->header('Content-Length', strlen($content));
|
||||
|
||||
}
|
||||
throw new FireflyException('Could not find the indicated attachment. The file is no longer there.');
|
||||
}
|
||||
@@ -151,7 +142,6 @@ class AttachmentController extends Controller
|
||||
{
|
||||
$image = 'images/page_green.png';
|
||||
|
||||
|
||||
if ($attachment->mime == 'application/pdf') {
|
||||
$image = 'images/page_white_acrobat.png';
|
||||
}
|
||||
@@ -178,7 +168,7 @@ class AttachmentController extends Controller
|
||||
Session::flash('success', strval(trans('firefly.attachment_updated', ['name' => $attachment->filename])));
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('return_to_edit')) === 1) {
|
||||
if (intval($request->get('return_to_edit')) === 1) {
|
||||
// set value so edit routine will not overwrite URL:
|
||||
Session::put('attachments.edit.fromUpdate', true);
|
||||
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ConfirmationController.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Auth;
|
||||
|
||||
use FireflyIII\Events\ConfirmedUser;
|
||||
use FireflyIII\Events\ResentConfirmation;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Preferences;
|
||||
use Session;
|
||||
|
||||
/**
|
||||
* Class ConfirmationController
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers\Auth
|
||||
*/
|
||||
class ConfirmationController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function confirmationError()
|
||||
{
|
||||
return view('auth.confirmation.error');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param string $code
|
||||
*
|
||||
* @return mixed
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function doConfirmation(Request $request, string $code)
|
||||
{
|
||||
// check user_confirmed_last_mail
|
||||
|
||||
$database = Preferences::get('user_confirmed_code')->data;
|
||||
$time = Preferences::get('user_confirmed_last_mail', 0)->data;
|
||||
$now = time();
|
||||
$maxDiff = config('firefly.confirmation_age');
|
||||
|
||||
if ($database === $code && ($now - $time <= $maxDiff)) {
|
||||
|
||||
// trigger user registration event:
|
||||
event(new ConfirmedUser(auth()->user(), $request->ip()));
|
||||
|
||||
Preferences::setForUser(auth()->user(), 'user_confirmed', true);
|
||||
Preferences::setForUser(auth()->user(), 'user_confirmed_confirmed', time());
|
||||
Session::flash('success', strval(trans('firefly.account_is_confirmed')));
|
||||
|
||||
return redirect(route('home'));
|
||||
}
|
||||
throw new FireflyException(trans('firefly.invalid_activation_code'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function resendConfirmation(Request $request)
|
||||
{
|
||||
$time = Preferences::get('user_confirmed_last_mail', 0)->data;
|
||||
$now = time();
|
||||
$maxDiff = config('firefly.resend_confirmation');
|
||||
$owner = env('SITE_OWNER', 'mail@example.com');
|
||||
$view = 'auth.confirmation.no-resent';
|
||||
if ($now - $time > $maxDiff) {
|
||||
event(new ResentConfirmation(auth()->user(), $request->ip()));
|
||||
$view = 'auth.confirmation.resent';
|
||||
}
|
||||
|
||||
return view($view, ['owner' => $owner]);
|
||||
}
|
||||
|
||||
}
|
||||
38
app/Http/Controllers/Auth/ForgotPasswordController.php
Executable file → Normal file
38
app/Http/Controllers/Auth/ForgotPasswordController.php
Executable file → Normal file
@@ -13,7 +13,10 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Controllers\Auth;
|
||||
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||
use Illuminate\Http\Request;
|
||||
use Password;
|
||||
|
||||
/**
|
||||
* Class ForgotPasswordController
|
||||
@@ -33,4 +36,39 @@ class ForgotPasswordController extends Controller
|
||||
parent::__construct();
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a reset link to the given user.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function sendResetLinkEmail(Request $request)
|
||||
{
|
||||
$this->validate($request, ['email' => 'required|email']);
|
||||
|
||||
// verify if the user is not a demo user. If so, we give him back an error.
|
||||
$user = User::where('email', $request->get('email'))->first();
|
||||
if (!is_null($user) && $user->hasRole('demo')) {
|
||||
return back()->withErrors(
|
||||
['email' => trans('firefly.cannot_reset_demo_user')]
|
||||
);
|
||||
}
|
||||
|
||||
$response = $this->broker()->sendResetLink(
|
||||
$request->only('email')
|
||||
);
|
||||
|
||||
if ($response === Password::RESET_LINK_SENT) {
|
||||
return back()->with('status', trans($response));
|
||||
}
|
||||
|
||||
// If an error was returned by the password broker, we will get this message
|
||||
// translated so we can notify a user of the problem. We'll redirect back
|
||||
// to where the users came from so they can attempt this process again.
|
||||
return back()->withErrors(
|
||||
['email' => trans($response)]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
76
app/Http/Controllers/Auth/LoginController.php
Executable file → Normal file
76
app/Http/Controllers/Auth/LoginController.php
Executable file → Normal file
@@ -18,11 +18,7 @@ use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Mail\Message;
|
||||
use Lang;
|
||||
use Log;
|
||||
use Mail;
|
||||
use Swift_TransportException;
|
||||
|
||||
/**
|
||||
* Class LoginController
|
||||
@@ -31,16 +27,6 @@ use Swift_TransportException;
|
||||
*/
|
||||
class LoginController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Login Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller handles authenticating users for the application and
|
||||
| redirecting them to your home screen. The controller uses a trait
|
||||
| to conveniently provide its functionality to your applications.
|
||||
|
|
||||
*/
|
||||
|
||||
use AuthenticatesUsers;
|
||||
|
||||
@@ -49,7 +35,7 @@ class LoginController extends Controller
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/home';
|
||||
protected $redirectTo = '/';
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
@@ -64,17 +50,13 @@ class LoginController extends Controller
|
||||
/**
|
||||
* Handle a login request to the application.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response
|
||||
*/
|
||||
public function login(Request $request)
|
||||
{
|
||||
$this->validateLogin($request);
|
||||
|
||||
// If the class is using the ThrottlesLogins trait, we can automatically throttle
|
||||
// the login attempts for this application. We'll key this by the username and
|
||||
// the IP address of the client making these requests into this application.
|
||||
$lockedOut = $this->hasTooManyLoginAttempts($request);
|
||||
if ($lockedOut) {
|
||||
$this->fireLockoutEvent($request);
|
||||
@@ -83,26 +65,14 @@ class LoginController extends Controller
|
||||
}
|
||||
|
||||
$credentials = $this->credentials($request);
|
||||
$credentials['blocked'] = 0; // most not be blocked.
|
||||
$credentials['blocked'] = 0; // must not be blocked.
|
||||
|
||||
if ($this->guard()->attempt($credentials, $request->has('remember'))) {
|
||||
return $this->sendLoginResponse($request);
|
||||
}
|
||||
|
||||
// check if user is blocked:
|
||||
$errorMessage = '';
|
||||
/** @var User $foundUser */
|
||||
$foundUser = User::where('email', $credentials['email'])->where('blocked', 1)->first();
|
||||
if (!is_null($foundUser)) {
|
||||
// if it exists, show message:
|
||||
$code = strlen(strval($foundUser->blocked_code)) > 0 ? $foundUser->blocked_code : 'general_blocked';
|
||||
$errorMessage = strval(trans('firefly.' . $code . '_error', ['email' => $credentials['email']]));
|
||||
$this->reportBlockedUserLoginAttempt($foundUser, $code, $request->ip());
|
||||
}
|
||||
$errorMessage = $this->getBlockedError($credentials['email']);
|
||||
|
||||
// If the login attempt was unsuccessful we will increment the number of attempts
|
||||
// to login and redirect the user back to the login form. Of course, when this
|
||||
// user surpasses their maximum number of attempts they will get locked out.
|
||||
if (!$lockedOut) {
|
||||
$this->incrementLoginAttempts($request);
|
||||
}
|
||||
@@ -169,32 +139,22 @@ class LoginController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message home about the blocked attempt to login.
|
||||
* Perhaps in a later stage, simply log these messages.
|
||||
* @param string $email
|
||||
*
|
||||
* @param User $user
|
||||
* @param string $code
|
||||
* @param string $ipAddress
|
||||
* @return string
|
||||
*/
|
||||
private function reportBlockedUserLoginAttempt(User $user, string $code, string $ipAddress)
|
||||
private function getBlockedError(string $email): string
|
||||
{
|
||||
// check if user is blocked:
|
||||
$errorMessage = '';
|
||||
/** @var User $foundUser */
|
||||
$foundUser = User::where('email', $email)->where('blocked', 1)->first();
|
||||
if (!is_null($foundUser)) {
|
||||
// user exists, but is blocked:
|
||||
$code = strlen(strval($foundUser->blocked_code)) > 0 ? $foundUser->blocked_code : 'general_blocked';
|
||||
$errorMessage = strval(trans('firefly.' . $code . '_error', ['email' => $email]));
|
||||
}
|
||||
|
||||
try {
|
||||
$email = env('SITE_OWNER', false);
|
||||
$fields = [
|
||||
'user_id' => $user->id,
|
||||
'user_address' => $user->email,
|
||||
'code' => $code,
|
||||
'ip' => $ipAddress,
|
||||
];
|
||||
|
||||
Mail::send(
|
||||
['emails.blocked-login-html', 'emails.blocked-login-text'], $fields, function (Message $message) use ($email, $user) {
|
||||
$message->to($email, $email)->subject('Blocked a login attempt from ' . trim($user->email) . '.');
|
||||
}
|
||||
);
|
||||
} catch (Swift_TransportException $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
return $errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,21 +26,11 @@ use Illuminate\Support\Facades\Password;
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers\Auth
|
||||
* @method getEmailSubject()
|
||||
* @method getSendResetLinkEmailSuccessResponse()
|
||||
* @method getSendResetLinkEmailFailureResponse()
|
||||
* @method getSendResetLinkEmailSuccessResponse(string $response)
|
||||
* @method getSendResetLinkEmailFailureResponse(string $response)
|
||||
*/
|
||||
class PasswordController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Reset Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller is responsible for handling password reset requests
|
||||
| and uses a simple trait to include this behavior. You're free to
|
||||
| explore this trait and override any methods you wish to tweak.
|
||||
|
|
||||
*/
|
||||
|
||||
use ResetsPasswords;
|
||||
|
||||
@@ -57,6 +47,7 @@ class PasswordController extends Controller
|
||||
|
||||
/**
|
||||
* Send a reset link to the given user.
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's 7 but ok
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
*
|
||||
|
||||
87
app/Http/Controllers/Auth/RegisterController.php
Executable file → Normal file
87
app/Http/Controllers/Auth/RegisterController.php
Executable file → Normal file
@@ -14,17 +14,13 @@ namespace FireflyIII\Http\Controllers\Auth;
|
||||
|
||||
use Auth;
|
||||
use Config;
|
||||
use FireflyConfig;
|
||||
use FireflyIII\Events\RegisteredUser;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Mail\Message;
|
||||
use Log;
|
||||
use Mail;
|
||||
use Session;
|
||||
use Swift_TransportException;
|
||||
use Validator;
|
||||
|
||||
/**
|
||||
@@ -34,16 +30,6 @@ use Validator;
|
||||
*/
|
||||
class RegisterController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller handles the registration of new users as well as their
|
||||
| validation and creation. By default this controller uses a trait to
|
||||
| provide this functionality without requiring any additional code.
|
||||
|
|
||||
*/
|
||||
|
||||
use RegistersUsers;
|
||||
|
||||
@@ -86,19 +72,6 @@ class RegisterController extends Controller
|
||||
$this->throwValidationException($request, $validator);
|
||||
}
|
||||
|
||||
$data = $request->all();
|
||||
$data['password'] = bcrypt($data['password']);
|
||||
|
||||
// is user email domain blocked?
|
||||
if ($this->isBlockedDomain($data['email'])) {
|
||||
$validator->getMessageBag()->add('email', (string)trans('validation.invalid_domain'));
|
||||
|
||||
$this->reportBlockedDomainRegistrationAttempt($data['email'], $request->ip());
|
||||
|
||||
$this->throwValidationException($request, $validator);
|
||||
}
|
||||
|
||||
|
||||
$user = $this->create($request->all());
|
||||
|
||||
// trigger user registration event:
|
||||
@@ -126,9 +99,6 @@ class RegisterController extends Controller
|
||||
// is demo site?
|
||||
$isDemoSite = FireflyConfig::get('is_demo_site', Config::get('firefly.configuration.is_demo_site'))->data;
|
||||
|
||||
// activate account?
|
||||
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', Config::get('firefly.configuration.must_confirm_account'))->data;
|
||||
|
||||
// is allowed to?
|
||||
$singleUserMode = FireflyConfig::get('single_user_mode', Config::get('firefly.configuration.single_user_mode'))->data;
|
||||
$userCount = User::count();
|
||||
@@ -140,7 +110,7 @@ class RegisterController extends Controller
|
||||
|
||||
$email = $request->old('email');
|
||||
|
||||
return view('auth.register', compact('isDemoSite', 'email', 'mustConfirmAccount'));
|
||||
return view('auth.register', compact('isDemoSite', 'email'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,57 +146,4 @@ class RegisterController extends Controller
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getBlockedDomains()
|
||||
{
|
||||
return FireflyConfig::get('blocked-domains', [])->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isBlockedDomain(string $email)
|
||||
{
|
||||
$parts = explode('@', $email);
|
||||
$blocked = $this->getBlockedDomains();
|
||||
|
||||
if (isset($parts[1]) && in_array($parts[1], $blocked)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message home about a blocked domain and the address attempted to register.
|
||||
*
|
||||
* @param string $registrationMail
|
||||
* @param string $ipAddress
|
||||
*/
|
||||
private function reportBlockedDomainRegistrationAttempt(string $registrationMail, string $ipAddress)
|
||||
{
|
||||
try {
|
||||
$email = env('SITE_OWNER', false);
|
||||
$parts = explode('@', $registrationMail);
|
||||
$domain = $parts[1];
|
||||
$fields = [
|
||||
'email_address' => $registrationMail,
|
||||
'blocked_domain' => $domain,
|
||||
'ip' => $ipAddress,
|
||||
];
|
||||
|
||||
Mail::send(
|
||||
['emails.blocked-registration-html', 'emails.blocked-registration-text'], $fields, function (Message $message) use ($email, $domain) {
|
||||
$message->to($email, $email)->subject('Blocked a registration attempt with domain ' . $domain . '.');
|
||||
}
|
||||
);
|
||||
} catch (Swift_TransportException $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
app/Http/Controllers/Auth/ResetPasswordController.php
Executable file → Normal file
10
app/Http/Controllers/Auth/ResetPasswordController.php
Executable file → Normal file
@@ -22,16 +22,6 @@ use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
*/
|
||||
class ResetPasswordController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Reset Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller is responsible for handling password reset requests
|
||||
| and uses a simple trait to include this behavior. You're free to
|
||||
| explore this trait and override any methods you wish to tweak.
|
||||
|
|
||||
*/
|
||||
|
||||
use ResetsPasswords;
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ class TwoFactorController extends Controller
|
||||
|
||||
/**
|
||||
* @param TokenFormRequest $request
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter) // it's unused but the class does some validation.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
|
||||
@@ -14,13 +14,13 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Requests\BillFormRequest;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
use Preferences;
|
||||
use Session;
|
||||
use URL;
|
||||
@@ -100,12 +100,19 @@ class BillController extends Controller
|
||||
public function destroy(BillRepositoryInterface $repository, Bill $bill)
|
||||
{
|
||||
$name = $bill->name;
|
||||
$billId = $bill->id;
|
||||
$repository->destroy($bill);
|
||||
|
||||
Session::flash('success', strval(trans('firefly.deleted_bill', ['name' => $name])));
|
||||
Preferences::mark();
|
||||
|
||||
return redirect(session('bills.delete.url'));
|
||||
$uri = session('bills.delete.url');
|
||||
if (!(strpos($uri, sprintf('bills/show/%s', $billId)) === false)) {
|
||||
// uri would point back to bill
|
||||
$uri = route('bills.index');
|
||||
}
|
||||
|
||||
return redirect($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,24 +197,27 @@ class BillController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param BillRepositoryInterface $repository
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function show(BillRepositoryInterface $repository, Bill $bill)
|
||||
public function show(Request $request, BillRepositoryInterface $repository, Bill $bill)
|
||||
{
|
||||
/** @var Carbon $date */
|
||||
$date = session('start');
|
||||
$year = $date->year;
|
||||
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
|
||||
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$yearAverage = $repository->getYearAverage($bill, $date);
|
||||
$overallAverage = $repository->getOverallAverage($bill);
|
||||
|
||||
// use collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->setPage($page)->setLimit($pageSize);
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->setLimit($pageSize)->setPage($page)->withBudgetInformation()
|
||||
->withCategoryInformation();
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/bills/show/' . $bill->id);
|
||||
|
||||
@@ -231,7 +241,7 @@ class BillController extends Controller
|
||||
Session::flash('success', strval(trans('firefly.stored_new_bill', ['name' => e($bill->name)])));
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('create_another')) === 1) {
|
||||
if (intval($request->get('create_another')) === 1) {
|
||||
// set value so create routine will not overwrite URL:
|
||||
Session::put('bills.create.fromStore', true);
|
||||
|
||||
@@ -258,7 +268,7 @@ class BillController extends Controller
|
||||
Session::flash('success', strval(trans('firefly.updated_bill', ['name' => e($bill->name)])));
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('return_to_edit')) === 1) {
|
||||
if (intval($request->get('return_to_edit')) === 1) {
|
||||
// set value so edit routine will not overwrite URL:
|
||||
Session::put('bills.edit.fromUpdate', true);
|
||||
|
||||
|
||||
@@ -15,19 +15,18 @@ namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Amount;
|
||||
use Carbon\Carbon;
|
||||
use Config;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Requests\BudgetFormRequest;
|
||||
use FireflyIII\Http\Requests\BudgetIncomeRequest;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
use Log;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use Session;
|
||||
@@ -42,6 +41,9 @@ use View;
|
||||
class BudgetController extends Controller
|
||||
{
|
||||
|
||||
/** @var BudgetRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -55,6 +57,7 @@ class BudgetController extends Controller
|
||||
function ($request, $next) {
|
||||
View::share('title', trans('firefly.budgets'));
|
||||
View::share('mainTitleIcon', 'fa-tasks');
|
||||
$this->repository = app(BudgetRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@@ -62,32 +65,25 @@ class BudgetController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param Request $request
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function amount(BudgetRepositoryInterface $repository, Budget $budget)
|
||||
public function amount(Request $request, Budget $budget)
|
||||
{
|
||||
$amount = intval(Input::get('amount'));
|
||||
$amount = intval($request->get('amount'));
|
||||
/** @var Carbon $start */
|
||||
$start = session('start', Carbon::now()->startOfMonth());
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', Carbon::now()->endOfMonth());
|
||||
$viewRange = Preferences::get('viewRange', '1M')->data;
|
||||
|
||||
// is custom view range?
|
||||
if (session('is_custom_range') === true) {
|
||||
$viewRange = 'custom';
|
||||
}
|
||||
|
||||
$limitRepetition = $repository->updateLimitAmount($budget, $start, $end, $viewRange, $amount);
|
||||
$budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount);
|
||||
if ($amount == 0) {
|
||||
$limitRepetition = null;
|
||||
$budgetLimit = null;
|
||||
}
|
||||
Preferences::mark();
|
||||
|
||||
return Response::json(['name' => $budget->name, 'repetition' => $limitRepetition ? $limitRepetition->id : 0]);
|
||||
return Response::json(['name' => $budget->name, 'limit' => $budgetLimit ? $budgetLimit->id : 0, 'amount' => $amount]);
|
||||
|
||||
}
|
||||
|
||||
@@ -127,22 +123,27 @@ class BudgetController extends Controller
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy(Budget $budget, BudgetRepositoryInterface $repository)
|
||||
public function destroy(Budget $budget)
|
||||
{
|
||||
|
||||
$name = $budget->name;
|
||||
$repository->destroy($budget);
|
||||
$budgetId = $budget->id;
|
||||
$this->repository->destroy($budget);
|
||||
|
||||
|
||||
Session::flash('success', strval(trans('firefly.deleted_budget', ['name' => e($name)])));
|
||||
Preferences::mark();
|
||||
|
||||
$uri = session('budgets.delete.url');
|
||||
if (!(strpos($uri, sprintf('budgets/show/%s', $budgetId)) === false)) {
|
||||
// uri would point back to budget
|
||||
$uri = route('budgets.index');
|
||||
}
|
||||
|
||||
return redirect(session('budgets.delete.url'));
|
||||
return redirect($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,95 +168,42 @@ class BudgetController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param AccountRepositoryInterface $accountRepository
|
||||
*
|
||||
* @return View
|
||||
*
|
||||
*/
|
||||
public function index(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository)
|
||||
public function index()
|
||||
{
|
||||
$repository->cleanupBudgets();
|
||||
$this->repository->cleanupBudgets();
|
||||
|
||||
$budgets = $repository->getActiveBudgets();
|
||||
$inactive = $repository->getInactiveBudgets();
|
||||
$spent = '0';
|
||||
$budgeted = '0';
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$repeatFreq = Config::get('firefly.range_to_repeat_freq.' . $range);
|
||||
|
||||
if (session('is_custom_range') === true) {
|
||||
$repeatFreq = 'custom';
|
||||
}
|
||||
|
||||
/** @var Carbon $start */
|
||||
$budgets = $this->repository->getActiveBudgets();
|
||||
$inactive = $this->repository->getInactiveBudgets();
|
||||
$start = session('start', new Carbon);
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', new Carbon);
|
||||
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
|
||||
$budgetIncomeTotal = Preferences::get($key, 1000)->data;
|
||||
$period = Navigation::periodShow($start, $range);
|
||||
$periodStart = $start->formatLocalized($this->monthAndDayFormat);
|
||||
$periodEnd = $end->formatLocalized($this->monthAndDayFormat);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
||||
$startAsString = $start->format('Y-m-d');
|
||||
$endAsString = $end->format('Y-m-d');
|
||||
Log::debug('Now at /budgets');
|
||||
|
||||
// loop the budgets:
|
||||
/** @var Budget $budget */
|
||||
foreach ($budgets as $budget) {
|
||||
Log::debug(sprintf('Now at budget #%d ("%s")', $budget->id, $budget->name));
|
||||
$budget->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);
|
||||
$allRepetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
|
||||
$otherRepetitions = new Collection;
|
||||
|
||||
/** @var LimitRepetition $repetition */
|
||||
foreach ($allRepetitions as $repetition) {
|
||||
if ($repetition->budget_id == $budget->id) {
|
||||
if ($repetition->budgetLimit->repeat_freq == $repeatFreq
|
||||
&& $repetition->startdate->format('Y-m-d') == $startAsString
|
||||
&& $repetition->enddate->format('Y-m-d') == $endAsString
|
||||
) {
|
||||
// do something
|
||||
$budget->currentRep = $repetition;
|
||||
continue;
|
||||
}
|
||||
$otherRepetitions->push($repetition);
|
||||
}
|
||||
}
|
||||
$budget->otherRepetitions = $otherRepetitions;
|
||||
|
||||
if (!is_null($budget->currentRep) && !is_null($budget->currentRep->id)) {
|
||||
$budgeted = bcadd($budgeted, $budget->currentRep->amount);
|
||||
}
|
||||
$spent = bcadd($spent, $budget->spent);
|
||||
|
||||
}
|
||||
|
||||
|
||||
$budgetInformation = $this->collectBudgetInformation($budgets, $start, $end);
|
||||
$defaultCurrency = Amount::getDefaultCurrency();
|
||||
$available = $this->repository->getAvailableBudget($defaultCurrency, $start, $end);
|
||||
$spent = array_sum(array_column($budgetInformation, 'spent'));
|
||||
$budgeted = array_sum(array_column($budgetInformation, 'budgeted'));
|
||||
|
||||
return view(
|
||||
'budgets.index', compact(
|
||||
'periodStart', 'periodEnd',
|
||||
'period', 'range', 'budgetIncomeTotal',
|
||||
'defaultCurrency', 'inactive', 'budgets',
|
||||
'spent', 'budgeted'
|
||||
)
|
||||
'budgets.index',
|
||||
compact('available', 'periodStart', 'periodEnd', 'budgetInformation', 'inactive', 'budgets', 'spent', 'budgeted')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function noBudget()
|
||||
public function noBudget(Request $request)
|
||||
{
|
||||
/** @var Carbon $start */
|
||||
$start = session('start', Carbon::now()->startOfMonth());
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', Carbon::now()->endOfMonth());
|
||||
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
|
||||
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$subTitle = trans(
|
||||
'firefly.without_budget_between',
|
||||
@@ -263,7 +211,8 @@ class BudgetController extends Controller
|
||||
);
|
||||
|
||||
// collector
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutBudget();
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/budgets/list/noBudget');
|
||||
@@ -274,113 +223,105 @@ class BudgetController extends Controller
|
||||
/**
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postUpdateIncome()
|
||||
public function postUpdateIncome(BudgetIncomeRequest $request)
|
||||
{
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
/** @var Carbon $date */
|
||||
$date = session('start', new Carbon);
|
||||
$start = Navigation::startOfPeriod($date, $range);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
|
||||
$start = session('start', new Carbon);
|
||||
$end = session('end', new Carbon);
|
||||
$defaultCurrency = Amount::getDefaultCurrency();
|
||||
$amount = $request->get('amount');
|
||||
|
||||
Preferences::set($key, intval(Input::get('amount')));
|
||||
$this->repository->setAvailableBudget($defaultCurrency, $start, $end, $amount);
|
||||
Preferences::mark();
|
||||
|
||||
return redirect(route('budgets.index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param AccountRepositoryInterface $accountRepository
|
||||
* @param Request $request
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function show(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget)
|
||||
public function show(Request $request, Budget $budget)
|
||||
{
|
||||
/** @var Carbon $start */
|
||||
$start = session('first', Carbon::create()->startOfYear());
|
||||
$end = new Carbon;
|
||||
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
|
||||
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
||||
|
||||
$limits = $this->getLimits($budget, $start, $end);
|
||||
$repetition = null;
|
||||
// collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page);
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page)->withCategoryInformation();
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/budgets/show/' . $budget->id);
|
||||
|
||||
|
||||
$set = $budget->limitrepetitions()->orderBy('startdate', 'DESC')->get();
|
||||
$subTitle = e($budget->name);
|
||||
$limits = new Collection();
|
||||
|
||||
/** @var LimitRepetition $entry */
|
||||
foreach ($set as $entry) {
|
||||
$entry->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $entry->startdate, $entry->enddate);
|
||||
$limits->push($entry);
|
||||
}
|
||||
|
||||
return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Budget $budget
|
||||
* @param LimitRepetition $repetition
|
||||
* @param BudgetLimit $budgetLimit
|
||||
*
|
||||
* @return View
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function showByRepetition(Budget $budget, LimitRepetition $repetition)
|
||||
public function showByBudgetLimit(Request $request, Budget $budget, BudgetLimit $budgetLimit)
|
||||
{
|
||||
if ($repetition->budgetLimit->budget->id != $budget->id) {
|
||||
if ($budgetLimit->budget->id != $budget->id) {
|
||||
throw new FireflyException('This budget limit is not part of this budget.');
|
||||
}
|
||||
|
||||
/** @var BudgetRepositoryInterface $repository */
|
||||
$repository = app(BudgetRepositoryInterface::class);
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$start = $repetition->startdate;
|
||||
$end = $repetition->enddate;
|
||||
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
|
||||
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$subTitle = trans(
|
||||
'firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]
|
||||
'firefly.budget_in_period', [
|
||||
'name' => $budget->name,
|
||||
'start' => $budgetLimit->start_date->formatLocalized($this->monthAndDayFormat),
|
||||
'end' => $budgetLimit->end_date->formatLocalized($this->monthAndDayFormat),
|
||||
]
|
||||
);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
||||
|
||||
|
||||
// collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page);
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAllAssetAccounts()->setRange($budgetLimit->start_date, $budgetLimit->end_date)
|
||||
->setBudget($budget)->setLimit($pageSize)->setPage($page)->withCategoryInformation();
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/budgets/show/' . $budget->id . '/' . $repetition->id);
|
||||
$journals->setPath('/budgets/show/' . $budget->id . '/' . $budgetLimit->id);
|
||||
|
||||
|
||||
$repetition->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $repetition->startdate, $repetition->enddate);
|
||||
$limits = new Collection([$repetition]);
|
||||
$start = session('first', Carbon::create()->startOfYear());
|
||||
$end = new Carbon;
|
||||
$limits = $this->getLimits($budget, $start, $end);
|
||||
|
||||
return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle'));
|
||||
return view('budgets.show', compact('limits', 'budget', 'budgetLimit', 'journals', 'subTitle'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetFormRequest $request
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(BudgetFormRequest $request, BudgetRepositoryInterface $repository)
|
||||
public function store(BudgetFormRequest $request)
|
||||
{
|
||||
$data = $request->getBudgetData();
|
||||
$budget = $repository->store($data);
|
||||
$budget = $this->repository->store($data);
|
||||
|
||||
Session::flash('success', strval(trans('firefly.stored_new_budget', ['name' => e($budget->name)])));
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('create_another')) === 1) {
|
||||
if (intval($request->get('create_another')) === 1) {
|
||||
// set value so create routine will not overwrite URL:
|
||||
Session::put('budgets.create.fromStore', true);
|
||||
|
||||
@@ -394,20 +335,19 @@ class BudgetController extends Controller
|
||||
|
||||
/**
|
||||
* @param BudgetFormRequest $request
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(BudgetFormRequest $request, BudgetRepositoryInterface $repository, Budget $budget)
|
||||
public function update(BudgetFormRequest $request, Budget $budget)
|
||||
{
|
||||
$data = $request->getBudgetData();
|
||||
$repository->update($budget, $data);
|
||||
$this->repository->update($budget, $data);
|
||||
|
||||
Session::flash('success', strval(trans('firefly.updated_budget', ['name' => e($budget->name)])));
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('return_to_edit')) === 1) {
|
||||
if (intval($request->get('return_to_edit')) === 1) {
|
||||
// set value so edit routine will not overwrite URL:
|
||||
Session::put('budgets.edit.fromUpdate', true);
|
||||
|
||||
@@ -424,19 +364,93 @@ class BudgetController extends Controller
|
||||
*/
|
||||
public function updateIncome()
|
||||
{
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$format = strval(trans('config.month_and_day'));
|
||||
$start = session('start', new Carbon);
|
||||
$end = session('end', new Carbon);
|
||||
$defaultCurrency = Amount::getDefaultCurrency();
|
||||
$available = $this->repository->getAvailableBudget($defaultCurrency, $start, $end);
|
||||
|
||||
/** @var Carbon $date */
|
||||
$date = session('start', new Carbon);
|
||||
$start = Navigation::startOfPeriod($date, $range);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
|
||||
$amount = Preferences::get($key, 1000);
|
||||
$displayStart = $start->formatLocalized($format);
|
||||
$displayEnd = $end->formatLocalized($format);
|
||||
|
||||
return view('budgets.income', compact('amount', 'displayStart', 'displayEnd'));
|
||||
return view('budgets.income', compact('available', 'start', 'end'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function collectBudgetInformation(Collection $budgets, Carbon $start, Carbon $end): array
|
||||
{
|
||||
// get account information
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
||||
$return = [];
|
||||
/** @var Budget $budget */
|
||||
foreach ($budgets as $budget) {
|
||||
$budgetId = $budget->id;
|
||||
$return[$budgetId] = [
|
||||
'spent' => $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end),
|
||||
'budgeted' => '0',
|
||||
'currentRep' => false,
|
||||
];
|
||||
$budgetLimits = $this->repository->getBudgetLimits($budget, $start, $end);
|
||||
$otherLimits = new Collection;
|
||||
|
||||
// get all the budget limits relevant between start and end and examine them:
|
||||
/** @var BudgetLimit $limit */
|
||||
foreach ($budgetLimits as $limit) {
|
||||
if ($limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end)
|
||||
) {
|
||||
$return[$budgetId]['currentLimit'] = $limit;
|
||||
$return[$budgetId]['budgeted'] = $limit->amount;
|
||||
continue;
|
||||
}
|
||||
// otherwise it's just one of the many relevant repetitions:
|
||||
$otherLimits->push($limit);
|
||||
}
|
||||
$return[$budgetId]['otherLimits'] = $otherLimits;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function getLimits(Budget $budget, Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
// properties for cache
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($budget->id);
|
||||
$cache->addProperty('get-limits');
|
||||
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
||||
$set = $this->repository->getBudgetLimits($budget, $start, $end);
|
||||
$limits = new Collection();
|
||||
|
||||
/** @var BudgetLimit $entry */
|
||||
foreach ($set as $entry) {
|
||||
$entry->spent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $entry->start_date, $entry->end_date);
|
||||
$limits->push($entry);
|
||||
}
|
||||
$cache->store($limits);
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,15 +14,15 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Requests\CategoryFormRequest;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
use Session;
|
||||
@@ -60,7 +60,6 @@ class CategoryController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
// put previous url in session if not redirect from store (not "create another").
|
||||
if (session('categories.create.fromStore') !== true) {
|
||||
Session::put('categories.create.url', URL::previous());
|
||||
}
|
||||
@@ -89,22 +88,30 @@ class CategoryController extends Controller
|
||||
return view('categories.delete', compact('category', 'subTitle'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param CRI $repository
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param Category $category
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function destroy(CRI $repository, Category $category)
|
||||
public function destroy(CategoryRepositoryInterface $repository, Category $category)
|
||||
{
|
||||
|
||||
$name = $category->name;
|
||||
$categoryId = $category->id;
|
||||
$repository->destroy($category);
|
||||
|
||||
Session::flash('success', strval(trans('firefly.deleted_category', ['name' => e($name)])));
|
||||
Preferences::mark();
|
||||
|
||||
return redirect(session('categories.delete.url'));
|
||||
$uri = session('categories.delete.url');
|
||||
if (!(strpos($uri, sprintf('categories/show/%s', $categoryId)) === false)) {
|
||||
// uri would point back to category
|
||||
$uri = route('categories.index');
|
||||
}
|
||||
|
||||
return redirect($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,11 +136,11 @@ class CategoryController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CRI $repository
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function index(CRI $repository)
|
||||
public function index(CategoryRepositoryInterface $repository)
|
||||
{
|
||||
$categories = $repository->getCategories();
|
||||
|
||||
@@ -157,7 +164,8 @@ class CategoryController extends Controller
|
||||
$end = session('end', Carbon::now()->startOfMonth());
|
||||
|
||||
// new collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->withoutCategory();//->groupJournals();
|
||||
$journals = $collector->getJournals();
|
||||
$subTitle = trans(
|
||||
@@ -169,110 +177,102 @@ class CategoryController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CRI $repository
|
||||
* @param AccountRepositoryInterface $accountRepository
|
||||
* @param Request $request
|
||||
* @param JournalCollectorInterface $collector
|
||||
* @param Category $category
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function show(CRI $repository, AccountRepositoryInterface $accountRepository, Category $category)
|
||||
public function show(Request $request, JournalCollectorInterface $collector, Category $category)
|
||||
{
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
/** @var Carbon $start */
|
||||
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
$hideCategory = true; // used in list.
|
||||
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
|
||||
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$subTitle = $category->name;
|
||||
$subTitleIcon = 'fa-bar-chart';
|
||||
$entries = $this->getGroupedEntries($category);
|
||||
$method = 'default';
|
||||
|
||||
// use journal collector
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setPage($page)->setLimit($pageSize)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category);
|
||||
// get journals
|
||||
$collector->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category)->withBudgetInformation();
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('categories/show/' . $category->id);
|
||||
|
||||
// oldest transaction in category:
|
||||
|
||||
return view('categories.show', compact('category', 'method', 'journals', 'entries', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param Category $category
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function showAll(Request $request, CategoryRepositoryInterface $repository, Category $category)
|
||||
{
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = $repository->firstUseDate($category);
|
||||
if ($start->year == 1900) {
|
||||
$start = new Carbon;
|
||||
}
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = Navigation::endOfX(new Carbon, $range);
|
||||
$entries = new Collection;
|
||||
$end = Navigation::endOfPeriod(new Carbon, $range);
|
||||
$subTitle = $category->name;
|
||||
$subTitleIcon = 'fa-bar-chart';
|
||||
$hideCategory = true; // used in list.
|
||||
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$method = 'all';
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('category-show');
|
||||
$cache->addProperty($category->id);
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setCategory($category)->withBudgetInformation();
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('categories/show/' . $category->id . '/all');
|
||||
|
||||
|
||||
if ($cache->has()) {
|
||||
$entries = $cache->get();
|
||||
|
||||
return view('categories.show', compact('category', 'journals', 'entries', 'subTitleIcon', 'hideCategory', 'subTitle'));
|
||||
}
|
||||
|
||||
|
||||
$categoryCollection = new Collection([$category]);
|
||||
|
||||
while ($end >= $start) {
|
||||
$end = Navigation::startOfPeriod($end, $range);
|
||||
$currentEnd = Navigation::endOfPeriod($end, $range);
|
||||
$spent = $repository->spentInPeriod($categoryCollection, $accounts, $end, $currentEnd);
|
||||
$earned = $repository->earnedInPeriod($categoryCollection, $accounts, $end, $currentEnd);
|
||||
$dateStr = $end->format('Y-m-d');
|
||||
$dateName = Navigation::periodShow($end, $range);
|
||||
$entries->push([$dateStr, $dateName, $spent, $earned]);
|
||||
|
||||
$end = Navigation::subtractPeriod($end, $range, 1);
|
||||
|
||||
}
|
||||
$cache->store($entries);
|
||||
|
||||
return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle', 'subTitleIcon'));
|
||||
return view('categories.show', compact('category', 'method', 'journals', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Category $category
|
||||
* @param $date
|
||||
* @param string $date
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function showByDate(Category $category, string $date)
|
||||
public function showByDate(Request $request, Category $category, string $date)
|
||||
{
|
||||
$carbon = new Carbon($date);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($carbon, $range);
|
||||
$end = Navigation::endOfPeriod($carbon, $range);
|
||||
$subTitle = $category->name;
|
||||
$subTitleIcon = 'fa-bar-chart';
|
||||
$hideCategory = true; // used in list.
|
||||
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
|
||||
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$entries = $this->getGroupedEntries($category);
|
||||
$method = 'date';
|
||||
|
||||
// new collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setPage($page)->setLimit($pageSize)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category);
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category)->withBudgetInformation();
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('categories/show/' . $category->id . '/' . $date);
|
||||
|
||||
|
||||
return view('categories.show-by-date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon'));
|
||||
return view('categories.show', compact('category', 'method', 'entries', 'journals', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CategoryFormRequest $request
|
||||
* @param CRI $repository
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function store(CategoryFormRequest $request, CRI $repository)
|
||||
public function store(CategoryFormRequest $request, CategoryRepositoryInterface $repository)
|
||||
{
|
||||
$data = $request->getCategoryData();
|
||||
$category = $repository->store($data);
|
||||
@@ -280,7 +280,7 @@ class CategoryController extends Controller
|
||||
Session::flash('success', strval(trans('firefly.stored_category', ['name' => e($category->name)])));
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('create_another')) === 1) {
|
||||
if (intval($request->get('create_another')) === 1) {
|
||||
Session::put('categories.create.fromStore', true);
|
||||
|
||||
return redirect(route('categories.create'))->withInput();
|
||||
@@ -292,12 +292,12 @@ class CategoryController extends Controller
|
||||
|
||||
/**
|
||||
* @param CategoryFormRequest $request
|
||||
* @param CRI $repository
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param Category $category
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function update(CategoryFormRequest $request, CRI $repository, Category $category)
|
||||
public function update(CategoryFormRequest $request, CategoryRepositoryInterface $repository, Category $category)
|
||||
{
|
||||
$data = $request->getCategoryData();
|
||||
$repository->update($category, $data);
|
||||
@@ -305,7 +305,7 @@ class CategoryController extends Controller
|
||||
Session::flash('success', strval(trans('firefly.updated_category', ['name' => e($category->name)])));
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('return_to_edit')) === 1) {
|
||||
if (intval($request->get('return_to_edit')) === 1) {
|
||||
Session::put('categories.edit.fromUpdate', true);
|
||||
|
||||
return redirect(route('categories.edit', [$category->id]));
|
||||
@@ -316,4 +316,50 @@ class CategoryController extends Controller
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function getGroupedEntries(Category $category): Collection
|
||||
{
|
||||
/** @var CategoryRepositoryInterface $repository */
|
||||
$repository = app(CategoryRepositoryInterface::class);
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
$first = $repository->firstUseDate($category);
|
||||
if ($first->year == 1900) {
|
||||
$first = new Carbon;
|
||||
}
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$first = Navigation::startOfPeriod($first, $range);
|
||||
$end = Navigation::endOfX(new Carbon, $range);
|
||||
$entries = new Collection;
|
||||
|
||||
// properties for entries with their amounts.
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($first);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('categories.entries');
|
||||
$cache->addProperty($category->id);
|
||||
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
while ($end >= $first) {
|
||||
$end = Navigation::startOfPeriod($end, $range);
|
||||
$currentEnd = Navigation::endOfPeriod($end, $range);
|
||||
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $end, $currentEnd);
|
||||
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $end, $currentEnd);
|
||||
$dateStr = $end->format('Y-m-d');
|
||||
$dateName = Navigation::periodShow($end, $range);
|
||||
$entries->push([$dateStr, $dateName, $spent, $earned, clone $end]);
|
||||
$end = Navigation::subtractPeriod($end, $range, 1);
|
||||
}
|
||||
$cache->store($entries);
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace FireflyIII\Http\Controllers\Chart;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Generator\Chart\Account\AccountChartGeneratorInterface;
|
||||
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Account;
|
||||
@@ -42,7 +42,7 @@ use Steam;
|
||||
class AccountController extends Controller
|
||||
{
|
||||
|
||||
/** @var \FireflyIII\Generator\Chart\Account\AccountChartGeneratorInterface */
|
||||
/** @var GeneratorInterface */
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
@@ -51,8 +51,49 @@ class AccountController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
// create chart generator:
|
||||
$this->generator = app(AccountChartGeneratorInterface::class);
|
||||
$this->generator = app(GeneratorInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function all(Account $account)
|
||||
{
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty('chart.account.all');
|
||||
$cache->addProperty($account->id);
|
||||
if ($cache->has()) {
|
||||
Log::debug('Return chart.account.all from cache.');
|
||||
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
Log::debug('Regenerate chart.account.all from scratch.');
|
||||
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$start = $repository->oldestJournalDate($account);
|
||||
$end = new Carbon;
|
||||
$format = (string)trans('config.month_and_day');
|
||||
$range = Steam::balanceInRange($account, $start, $end);
|
||||
$current = clone $start;
|
||||
$previous = array_values($range)[0];
|
||||
$chartData = [];
|
||||
|
||||
while ($end >= $current) {
|
||||
$theDate = $current->format('Y-m-d');
|
||||
$balance = $range[$theDate] ?? $previous;
|
||||
$label = $current->formatLocalized($format);
|
||||
$chartData[$label] = $balance;
|
||||
$previous = $balance;
|
||||
$current->addDay();
|
||||
}
|
||||
|
||||
$data = $this->generator->singleSet($account->name, $chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,36 +110,29 @@ class AccountController extends Controller
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('expenseAccounts');
|
||||
$cache->addProperty('accounts');
|
||||
$cache->addProperty('chart.account.expense-accounts');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
$accounts = $repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY]);
|
||||
|
||||
$start->subDay();
|
||||
|
||||
$accounts = $repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY]);
|
||||
$ids = $accounts->pluck('id')->toArray();
|
||||
$startBalances = Steam::balancesById($ids, $start);
|
||||
$endBalances = Steam::balancesById($ids, $end);
|
||||
$chartData = [];
|
||||
|
||||
$accounts->each(
|
||||
function (Account $account) use ($startBalances, $endBalances) {
|
||||
foreach ($accounts as $account) {
|
||||
$id = $account->id;
|
||||
$startBalance = $startBalances[$id] ?? '0';
|
||||
$endBalance = $endBalances[$id] ?? '0';
|
||||
$diff = bcsub($endBalance, $startBalance);
|
||||
$account->difference = round($diff, 2);
|
||||
if (bccomp($diff, '0') !== 0) {
|
||||
$chartData[$account->name] = $diff;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
$accounts = $accounts->sortByDesc(
|
||||
function (Account $account) {
|
||||
return $account->difference;
|
||||
}
|
||||
);
|
||||
|
||||
$data = $this->generator->expenseAccounts($accounts, $start, $end);
|
||||
arsort($chartData);
|
||||
$data = $this->generator->singleSet(strval(trans('firefly.spent')), $chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
@@ -118,28 +152,33 @@ class AccountController extends Controller
|
||||
$cache->addProperty($account->id);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('expenseByBudget');
|
||||
$cache->addProperty('chart.account.expense-budget');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
|
||||
// grab all journals:
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withBudgetInformation()->setTypes([TransactionType::WITHDRAWAL]);
|
||||
$collector->setAccounts(new Collection([$account]))
|
||||
->setRange($start, $end)
|
||||
->withBudgetInformation()
|
||||
->setTypes([TransactionType::WITHDRAWAL]);
|
||||
$transactions = $collector->getJournals();
|
||||
|
||||
$chartData = [];
|
||||
$result = [];
|
||||
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$jrnlBudgetId = intval($transaction->transaction_journal_budget_id);
|
||||
$transBudgetId = intval($transaction->transaction_budget_id);
|
||||
$budgetId = max($jrnlBudgetId, $transBudgetId);
|
||||
|
||||
$result[$budgetId] = $result[$budgetId] ?? '0';
|
||||
$result[$budgetId] = bcadd($transaction->transaction_amount, $result[$budgetId]);
|
||||
}
|
||||
|
||||
$names = $this->getBudgetNames(array_keys($result));
|
||||
$data = $this->generator->pieChart($result, $names);
|
||||
foreach ($result as $budgetId => $amount) {
|
||||
$chartData[$names[$budgetId]] = $amount;
|
||||
}
|
||||
|
||||
$data = $this->generator->pieChart($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
@@ -159,26 +198,30 @@ class AccountController extends Controller
|
||||
$cache->addProperty($account->id);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('expenseByCategory');
|
||||
$cache->addProperty('chart.account.expense-category');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
// grab all journals:
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::WITHDRAWAL]);
|
||||
$transactions = $collector->getJournals();
|
||||
$result = [];
|
||||
$chartData = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$jrnlCatId = intval($transaction->transaction_journal_category_id);
|
||||
$transCatId = intval($transaction->transaction_category_id);
|
||||
$categoryId = max($jrnlCatId, $transCatId);
|
||||
|
||||
$result[$categoryId] = $result[$categoryId] ?? '0';
|
||||
$result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]);
|
||||
}
|
||||
|
||||
$names = $this->getCategoryNames(array_keys($result));
|
||||
$data = $this->generator->pieChart($result, $names);
|
||||
foreach ($result as $categoryId => $amount) {
|
||||
$chartData[$names[$categoryId]] = $amount;
|
||||
}
|
||||
|
||||
$data = $this->generator->pieChart($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
@@ -196,7 +239,15 @@ class AccountController extends Controller
|
||||
{
|
||||
$start = clone session('start', Carbon::now()->startOfMonth());
|
||||
$end = clone session('end', Carbon::now()->endOfMonth());
|
||||
$frontPage = Preferences::get('frontPageAccounts', $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray());
|
||||
$defaultSet = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray();
|
||||
Log::debug('Default set is ', $defaultSet);
|
||||
$frontPage = Preferences::get('frontPageAccounts', $defaultSet);
|
||||
Log::debug('Frontpage preference set is ', $frontPage->data);
|
||||
if (count($frontPage->data) === 0) {
|
||||
$frontPage->data = $defaultSet;
|
||||
Log::debug('frontpage set is empty!');
|
||||
$frontPage->save();
|
||||
}
|
||||
$accounts = $repository->getAccountsById($frontPage->data);
|
||||
|
||||
return Response::json($this->accountBalanceChart($accounts, $start, $end));
|
||||
@@ -216,7 +267,7 @@ class AccountController extends Controller
|
||||
$cache->addProperty($account->id);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('incomeByCategory');
|
||||
$cache->addProperty('chart.account.income-category');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
@@ -225,23 +276,74 @@ class AccountController extends Controller
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::DEPOSIT]);
|
||||
$transactions = $collector->getJournals();
|
||||
$result = [];
|
||||
$chartData = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$jrnlCatId = intval($transaction->transaction_journal_category_id);
|
||||
$transCatId = intval($transaction->transaction_category_id);
|
||||
$categoryId = max($jrnlCatId, $transCatId);
|
||||
|
||||
$result[$categoryId] = $result[$categoryId] ?? '0';
|
||||
$result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]);
|
||||
}
|
||||
|
||||
$names = $this->getCategoryNames(array_keys($result));
|
||||
$data = $this->generator->pieChart($result, $names);
|
||||
foreach ($result as $categoryId => $amount) {
|
||||
$chartData[$names[$categoryId]] = $amount;
|
||||
}
|
||||
$data = $this->generator->pieChart($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param string $date
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function period(Account $account, string $date)
|
||||
{
|
||||
try {
|
||||
$start = new Carbon($date);
|
||||
} catch (Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
throw new FireflyException('"' . e($date) . '" does not seem to be a valid date. Should be in the format YYYY-MM-DD');
|
||||
}
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('chart.account.period');
|
||||
$cache->addProperty($account->id);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
$format = (string)trans('config.month_and_day');
|
||||
$range = Steam::balanceInRange($account, $start, $end);
|
||||
$current = clone $start;
|
||||
$previous = array_values($range)[0];
|
||||
$chartData = [];
|
||||
|
||||
while ($end >= $current) {
|
||||
$theDate = $current->format('Y-m-d');
|
||||
$balance = $range[$theDate] ?? $previous;
|
||||
$label = $current->formatLocalized($format);
|
||||
$chartData[$label] = $balance;
|
||||
$previous = $balance;
|
||||
$current->addDay();
|
||||
}
|
||||
|
||||
$data = $this->generator->singleSet($account->name, $chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the balances for a given set of dates and accounts.
|
||||
*
|
||||
@@ -267,11 +369,11 @@ class AccountController extends Controller
|
||||
{
|
||||
$start = clone session('start', Carbon::now()->startOfMonth());
|
||||
$end = clone session('end', Carbon::now()->endOfMonth());
|
||||
$chartData = [];
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('revenueAccounts');
|
||||
$cache->addProperty('accounts');
|
||||
$cache->addProperty('chart.account.revenue-accounts');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
@@ -282,25 +384,19 @@ class AccountController extends Controller
|
||||
$startBalances = Steam::balancesById($ids, $start);
|
||||
$endBalances = Steam::balancesById($ids, $end);
|
||||
|
||||
$accounts->each(
|
||||
function (Account $account) use ($startBalances, $endBalances) {
|
||||
foreach ($accounts as $account) {
|
||||
$id = $account->id;
|
||||
$startBalance = $startBalances[$id] ?? '0';
|
||||
$endBalance = $endBalances[$id] ?? '0';
|
||||
$diff = bcsub($endBalance, $startBalance);
|
||||
$diff = bcmul($diff, '-1');
|
||||
$account->difference = round($diff, 2);
|
||||
if (bccomp($diff, '0') !== 0) {
|
||||
$chartData[$account->name] = $diff;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
$accounts = $accounts->sortByDesc(
|
||||
function (Account $account) {
|
||||
return $account->difference;
|
||||
}
|
||||
);
|
||||
|
||||
$data = $this->generator->revenueAccounts($accounts, $start, $end);
|
||||
arsort($chartData);
|
||||
$data = $this->generator->singleSet(strval(trans('firefly.spent')), $chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
@@ -322,8 +418,7 @@ class AccountController extends Controller
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('frontpage');
|
||||
$cache->addProperty('single');
|
||||
$cache->addProperty('chart.account.single');
|
||||
$cache->addProperty($account->id);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
@@ -333,73 +428,18 @@ class AccountController extends Controller
|
||||
$range = Steam::balanceInRange($account, $start, $end);
|
||||
$current = clone $start;
|
||||
$previous = array_values($range)[0];
|
||||
$labels = [];
|
||||
$chartData = [];
|
||||
|
||||
while ($end >= $current) {
|
||||
$theDate = $current->format('Y-m-d');
|
||||
$balance = $range[$theDate] ?? $previous;
|
||||
|
||||
$labels[] = $current->formatLocalized($format);
|
||||
$chartData[] = $balance;
|
||||
$label = $current->formatLocalized($format);
|
||||
$chartData[$label] = $balance;
|
||||
$previous = $balance;
|
||||
$current->addDay();
|
||||
}
|
||||
|
||||
|
||||
$data = $this->generator->single($account, $labels, $chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param string $date
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function specificPeriod(Account $account, string $date)
|
||||
{
|
||||
try {
|
||||
$start = new Carbon($date);
|
||||
} catch (Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
throw new FireflyException('"' . e($date) . '" does not seem to be a valid date. Should be in the format YYYY-MM-DD');
|
||||
}
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('frontpage');
|
||||
$cache->addProperty('specificPeriod');
|
||||
$cache->addProperty($account->id);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
$format = (string)trans('config.month_and_day');
|
||||
$range = Steam::balanceInRange($account, $start, $end);
|
||||
$current = clone $start;
|
||||
$previous = array_values($range)[0];
|
||||
$labels = [];
|
||||
$chartData = [];
|
||||
|
||||
while ($end >= $current) {
|
||||
$theDate = $current->format('Y-m-d');
|
||||
$balance = $range[$theDate] ?? $previous;
|
||||
|
||||
$labels[] = $current->formatLocalized($format);
|
||||
$chartData[] = $balance;
|
||||
$previous = $balance;
|
||||
$current->addDay();
|
||||
}
|
||||
|
||||
|
||||
$data = $this->generator->single($account, $labels, $chartData);
|
||||
$data = $this->generator->singleSet($account->name, $chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
@@ -418,27 +458,35 @@ class AccountController extends Controller
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('account-balance-chart');
|
||||
$cache->addProperty('chart.account.account-balance-chart');
|
||||
$cache->addProperty($accounts);
|
||||
if ($cache->has()) {
|
||||
Log::debug('Return chart.account.account-balance-chart from cache.');
|
||||
|
||||
return $cache->get();
|
||||
}
|
||||
Log::debug('Regenerate chart.account.account-balance-chart from scratch.');
|
||||
|
||||
$chartData = [];
|
||||
foreach ($accounts as $account) {
|
||||
$balances = [];
|
||||
$current = clone $start;
|
||||
$currentSet = [
|
||||
'label' => $account->name,
|
||||
'entries' => [],
|
||||
];
|
||||
$currentStart = clone $start;
|
||||
$range = Steam::balanceInRange($account, $start, clone $end);
|
||||
$previous = round(array_values($range)[0], 2);
|
||||
while ($current <= $end) {
|
||||
$format = $current->format('Y-m-d');
|
||||
$balance = isset($range[$format]) ? round($range[$format], 2) : $previous;
|
||||
$previous = array_values($range)[0];
|
||||
while ($currentStart <= $end) {
|
||||
$format = $currentStart->format('Y-m-d');
|
||||
$label = $currentStart->formatLocalized(strval(trans('config.month_and_day')));
|
||||
$balance = isset($range[$format]) ? round($range[$format], 12) : $previous;
|
||||
$previous = $balance;
|
||||
$balances[] = $balance;
|
||||
$current->addDay();
|
||||
$currentStart->addDay();
|
||||
$currentSet['entries'][$label] = $balance;
|
||||
}
|
||||
$account->balances = $balances;
|
||||
$chartData[] = $currentSet;
|
||||
}
|
||||
$data = $this->generator->frontpage($accounts, $start, $end);
|
||||
$data = $this->generator->multiSet($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return $data;
|
||||
|
||||
@@ -14,8 +14,8 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Controllers\Chart;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Chart\Bill\BillChartGeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Transaction;
|
||||
@@ -32,7 +32,7 @@ use Response;
|
||||
class BillController extends Controller
|
||||
{
|
||||
|
||||
/** @var \FireflyIII\Generator\Chart\Bill\BillChartGeneratorInterface */
|
||||
/** @var GeneratorInterface */
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
@@ -41,8 +41,7 @@ class BillController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
// create chart generator:
|
||||
$this->generator = app(BillChartGeneratorInterface::class);
|
||||
$this->generator = app(GeneratorInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,43 +55,63 @@ class BillController extends Controller
|
||||
{
|
||||
$start = session('start', Carbon::now()->startOfMonth());
|
||||
$end = session('end', Carbon::now()->endOfMonth());
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('chart.bill.frontpage');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
$paid = $repository->getBillsPaidInRange($start, $end); // will be a negative amount.
|
||||
$unpaid = $repository->getBillsUnpaidInRange($start, $end); // will be a positive amount.
|
||||
$data = $this->generator->frontpage($paid, $unpaid);
|
||||
$chartData = [
|
||||
strval(trans('firefly.unpaid')) => $unpaid,
|
||||
strval(trans('firefly.paid')) => $paid,
|
||||
];
|
||||
|
||||
$data = $this->generator->pieChart($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the overview for a bill. The min/max amount and matched journals.
|
||||
*
|
||||
* @param JournalCollectorInterface $collector
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function single(Bill $bill)
|
||||
public function single(JournalCollectorInterface $collector, Bill $bill)
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('single');
|
||||
$cache->addProperty('bill');
|
||||
$cache->addProperty('chart.bill.single');
|
||||
$cache->addProperty($bill->id);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
// get first transaction or today for start:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setBills(new Collection([$bill]));
|
||||
$results = $collector->getJournals();
|
||||
|
||||
// resort:
|
||||
$results = $collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->getJournals();
|
||||
$results = $results->sortBy(
|
||||
function (Transaction $transaction) {
|
||||
return $transaction->date->format('U');
|
||||
}
|
||||
);
|
||||
$chartData = [
|
||||
['type' => 'bar', 'label' => trans('firefly.min-amount'), 'entries' => [],],
|
||||
['type' => 'bar', 'label' => trans('firefly.max-amount'), 'entries' => [],],
|
||||
['type' => 'line', 'label' => trans('firefly.journal-amount'), 'entries' => [],],
|
||||
];
|
||||
|
||||
$data = $this->generator->single($bill, $results);
|
||||
/** @var Transaction $entry */
|
||||
foreach ($results as $entry) {
|
||||
$date = $entry->date->formatLocalized(strval(trans('config.month_and_day')));
|
||||
$chartData[0]['entries'][$date] = $bill->amount_min; // minimum amount of bill
|
||||
$chartData[1]['entries'][$date] = $bill->amount_max; // maximum amount of bill
|
||||
$chartData[2]['entries'][$date] = bcmul($entry->transaction_amount, '-1'); // amount of journal
|
||||
}
|
||||
|
||||
$data = $this->generator->multiSet($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
@@ -14,11 +14,12 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Controllers\Chart;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Chart\Budget\BudgetChartGeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
@@ -31,42 +32,52 @@ use Response;
|
||||
/**
|
||||
* Class BudgetController
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) // can't realy be helped.
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers\Chart
|
||||
*/
|
||||
class BudgetController extends Controller
|
||||
{
|
||||
|
||||
/** @var BudgetChartGeneratorInterface */
|
||||
/** @var GeneratorInterface */
|
||||
protected $generator;
|
||||
|
||||
/** @var BudgetRepositoryInterface */
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* BudgetController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
// create chart generator:
|
||||
$this->generator = app(BudgetChartGeneratorInterface::class);
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->generator = app(GeneratorInterface::class);
|
||||
$this->repository = app(BudgetRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* checked
|
||||
*
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function budget(BudgetRepositoryInterface $repository, Budget $budget)
|
||||
public function budget(Budget $budget)
|
||||
{
|
||||
$first = $repository->firstUseDate($budget);
|
||||
$first = $this->repository->firstUseDate($budget);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$last = session('end', new Carbon);
|
||||
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($first);
|
||||
$cache->addProperty($last);
|
||||
$cache->addProperty('budget');
|
||||
$cache->addProperty('chart.budget.budget');
|
||||
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
@@ -74,10 +85,9 @@ class BudgetController extends Controller
|
||||
|
||||
$final = clone $last;
|
||||
$final->addYears(2);
|
||||
|
||||
$budgetCollection = new Collection([$budget]);
|
||||
$last = Navigation::endOfX($last, $range, $final); // not to overshoot.
|
||||
$entries = new Collection;
|
||||
$entries = [];
|
||||
while ($first < $last) {
|
||||
|
||||
// periodspecific dates:
|
||||
@@ -85,13 +95,14 @@ class BudgetController extends Controller
|
||||
$currentEnd = Navigation::endOfPeriod($first, $range);
|
||||
// sub another day because reasons.
|
||||
$currentEnd->subDay();
|
||||
$spent = $repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd);
|
||||
$entry = [$first, ($spent * -1)];
|
||||
$entries->push($entry);
|
||||
$spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd);
|
||||
$format = Navigation::periodShow($first, $range);
|
||||
$entries[$format] = bcmul($spent, '-1');
|
||||
$first = Navigation::addPeriod($first, $range, 0);
|
||||
}
|
||||
|
||||
$data = $this->generator->budgetLimit($entries, 'month');
|
||||
$data = $this->generator->singleSet(strval(trans('firefly.spent')), $entries);
|
||||
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
@@ -100,38 +111,43 @@ class BudgetController extends Controller
|
||||
/**
|
||||
* Shows the amount left in a specific budget limit.
|
||||
*
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
|
||||
* @param Budget $budget
|
||||
* @param LimitRepetition $repetition
|
||||
* @param BudgetLimit $budgetLimit
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function budgetLimit(BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition)
|
||||
public function budgetLimit(Budget $budget, BudgetLimit $budgetLimit)
|
||||
{
|
||||
$start = clone $repetition->startdate;
|
||||
$end = $repetition->enddate;
|
||||
if ($budgetLimit->budget->id != $budget->id) {
|
||||
throw new FireflyException('This budget limit is not part of this budget.');
|
||||
}
|
||||
|
||||
$start = clone $budgetLimit->start_date;
|
||||
$end = clone $budgetLimit->end_date;
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('budget-limit');
|
||||
$cache->addProperty($budget->id);
|
||||
$cache->addProperty($repetition->id);
|
||||
$cache->addProperty('chart.budget.budget.limit');
|
||||
$cache->addProperty($budgetLimit->id);
|
||||
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
$entries = new Collection;
|
||||
$amount = $repetition->amount;
|
||||
$entries = [];
|
||||
$amount = $budgetLimit->amount;
|
||||
$budgetCollection = new Collection([$budget]);
|
||||
while ($start <= $end) {
|
||||
$spent = $repository->spentInPeriod($budgetCollection, new Collection, $start, $start);
|
||||
$spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $start, $start);
|
||||
$amount = bcadd($amount, $spent);
|
||||
$entries->push([clone $start, round($amount, 2)]);
|
||||
$format = $start->formatLocalized(strval(trans('config.month_and_day')));
|
||||
$entries[$format] = $amount;
|
||||
|
||||
$start->addDay();
|
||||
}
|
||||
$data = $this->generator->budgetLimit($entries, 'month_and_day');
|
||||
$data = $this->generator->singleSet(strval(trans('firefly.left')), $entries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
@@ -139,12 +155,12 @@ class BudgetController extends Controller
|
||||
|
||||
/**
|
||||
* Shows a budget list with spent/left/overspent.
|
||||
*
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
|
||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) // 46 lines, I'm fine with this.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function frontpage(BudgetRepositoryInterface $repository)
|
||||
public function frontpage()
|
||||
{
|
||||
$start = session('start', Carbon::now()->startOfMonth());
|
||||
$end = session('end', Carbon::now()->endOfMonth());
|
||||
@@ -152,32 +168,39 @@ class BudgetController extends Controller
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('budget');
|
||||
$cache->addProperty('all');
|
||||
$cache->addProperty('chart.budget.frontpage');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
$budgets = $repository->getActiveBudgets();
|
||||
$repetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
|
||||
$allEntries = new Collection;
|
||||
$budgets = $this->repository->getActiveBudgets();
|
||||
$chartData = [
|
||||
['label' => strval(trans('firefly.spent_in_budget')), 'entries' => [], 'type' => 'bar',],
|
||||
['label' => strval(trans('firefly.left_to_spend')), 'entries' => [], 'type' => 'bar',],
|
||||
['label' => strval(trans('firefly.overspent')), 'entries' => [], 'type' => 'bar',],
|
||||
];
|
||||
|
||||
|
||||
/** @var Budget $budget */
|
||||
foreach ($budgets as $budget) {
|
||||
// get relevant repetitions:
|
||||
$reps = $this->filterRepetitions($repetitions, $budget, $start, $end);
|
||||
|
||||
if ($reps->count() === 0) {
|
||||
$collection = $this->spentInPeriodSingle($repository, $budget, $start, $end);
|
||||
$allEntries = $allEntries->merge($collection);
|
||||
continue;
|
||||
$limits = $this->repository->getBudgetLimits($budget, $start, $end);
|
||||
$expenses = $this->getExpensesForBudget($limits, $budget, $start, $end);
|
||||
foreach ($expenses as $name => $row) {
|
||||
$chartData[0]['entries'][$name] = $row['spent'];
|
||||
$chartData[1]['entries'][$name] = $row['left'];
|
||||
$chartData[2]['entries'][$name] = $row['overspent'];
|
||||
}
|
||||
$collection = $this->spentInPeriodMulti($repository, $budget, $reps);
|
||||
$allEntries = $allEntries->merge($collection);
|
||||
|
||||
}
|
||||
$entry = $this->spentInPeriodWithout($start, $end);
|
||||
$allEntries->push($entry);
|
||||
$data = $this->generator->frontpage($allEntries);
|
||||
// for no budget:
|
||||
$spent = $this->spentInPeriodWithout($start, $end);
|
||||
$name = strval(trans('firefly.no_budget'));
|
||||
if (bccomp($spent, '0') !== 0) {
|
||||
$chartData[0]['entries'][$name] = bcmul($spent, '-1');
|
||||
$chartData[1]['entries'][$name] = '0';
|
||||
$chartData[2]['entries'][$name] = '0';
|
||||
}
|
||||
|
||||
$data = $this->generator->multiSet($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
@@ -185,7 +208,8 @@ class BudgetController extends Controller
|
||||
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
|
||||
*
|
||||
* @param Budget $budget
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
@@ -193,7 +217,7 @@ class BudgetController extends Controller
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function period(BudgetRepositoryInterface $repository, Budget $budget, Collection $accounts, Carbon $start, Carbon $end)
|
||||
public function period(Budget $budget, Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
@@ -201,184 +225,198 @@ class BudgetController extends Controller
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($budget->id);
|
||||
$cache->addProperty('budget');
|
||||
$cache->addProperty('period');
|
||||
$cache->addProperty('chart.budget.period');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
// the expenses:
|
||||
$periods = Navigation::listOfPeriods($start, $end);
|
||||
$entries = $repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end, false);
|
||||
$budgeted = [];
|
||||
$key = Navigation::preferredCarbonFormat($start, $end);
|
||||
$range = Navigation::preferredRangeFormat($start, $end);
|
||||
$entries = $this->repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end); // get the expenses
|
||||
$budgeted = $this->getBudgetedInPeriod($budget, $start, $end);
|
||||
|
||||
// get budgeted:
|
||||
$repetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
|
||||
$current = clone $start;
|
||||
while ($current < $end) {
|
||||
$currentStart = Navigation::startOfPeriod($current, $range);
|
||||
$currentEnd = Navigation::endOfPeriod($current, $range);
|
||||
$reps = $repetitions->filter(
|
||||
function (LimitRepetition $repetition) use ($budget, $currentStart, $currentEnd) {
|
||||
if ($repetition->budget_id === $budget->id && $repetition->startdate >= $currentStart && $repetition->enddate <= $currentEnd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
$index = $currentStart->format($key);
|
||||
$budgeted[$index] = $reps->sum('amount');
|
||||
$currentEnd->addDay();
|
||||
$current = clone $currentEnd;
|
||||
}
|
||||
|
||||
// join them:
|
||||
$result = [];
|
||||
foreach (array_keys($periods) as $period) {
|
||||
$nice = $periods[$period];
|
||||
$result[$nice] = [
|
||||
'spent' => isset($entries[$budget->id]['entries'][$period]) ? $entries[$budget->id]['entries'][$period] : '0',
|
||||
'budgeted' => isset($entries[$period]) ? $budgeted[$period] : 0,
|
||||
// join them into one set of data:
|
||||
$chartData = [
|
||||
['label' => strval(trans('firefly.spent')), 'type' => 'bar', 'entries' => [],],
|
||||
['label' => strval(trans('firefly.budgeted')), 'type' => 'bar', 'entries' => [],],
|
||||
];
|
||||
|
||||
foreach (array_keys($periods) as $period) {
|
||||
$label = $periods[$period];
|
||||
$spent = isset($entries[$budget->id]['entries'][$period]) ? $entries[$budget->id]['entries'][$period] : '0';
|
||||
$limit = isset($budgeted[$period]) ? $budgeted[$period] : 0;
|
||||
$chartData[0]['entries'][$label] = round(bcmul($spent, '-1'), 12);
|
||||
$chartData[1]['entries'][$label] = $limit;
|
||||
}
|
||||
|
||||
$data = $this->generator->period($result);
|
||||
|
||||
$data = $this->generator->multiSet($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function periodNoBudget(BudgetRepositoryInterface $repository, Collection $accounts, Carbon $start, Carbon $end)
|
||||
public function periodNoBudget(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty('no-budget');
|
||||
$cache->addProperty('period');
|
||||
$cache->addProperty('chart.budget.no-budget');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
// the expenses:
|
||||
$periods = Navigation::listOfPeriods($start, $end);
|
||||
$entries = $repository->getNoBudgetPeriodReport($accounts, $start, $end);
|
||||
$entries = $this->repository->getNoBudgetPeriodReport($accounts, $start, $end);
|
||||
$chartData = [];
|
||||
|
||||
// join them:
|
||||
$result = [];
|
||||
foreach (array_keys($periods) as $period) {
|
||||
$nice = $periods[$period];
|
||||
$result[$nice] = [
|
||||
'spent' => isset($entries['entries'][$period]) ? $entries['entries'][$period] : '0',
|
||||
];
|
||||
$label = $periods[$period];
|
||||
$spent = isset($entries['entries'][$period]) ? $entries['entries'][$period] : '0';
|
||||
$chartData[$label] = bcmul($spent, '-1');
|
||||
}
|
||||
|
||||
$data = $this->generator->periodNoBudget($result);
|
||||
|
||||
$data = $this->generator->singleSet(strval(trans('firefly.spent')), $chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $repetitions
|
||||
* @param Budget $budget
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function filterRepetitions(Collection $repetitions, Budget $budget, Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
|
||||
return $repetitions->filter(
|
||||
function (LimitRepetition $repetition) use ($budget, $start, $end) {
|
||||
if ($repetition->startdate < $end && $repetition->enddate > $start && $repetition->budget_id === $budget->id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param Budget $budget
|
||||
* @param Collection $repetitions
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function spentInPeriodMulti(BudgetRepositoryInterface $repository, Budget $budget, Collection $repetitions): Collection
|
||||
{
|
||||
$format = strval(trans('config.month_and_day'));
|
||||
$collection = new Collection;
|
||||
$name = $budget->name;
|
||||
/** @var LimitRepetition $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$expenses = $repository->spentInPeriod(new Collection([$budget]), new Collection, $repetition->startdate, $repetition->enddate);
|
||||
|
||||
if ($repetitions->count() > 1) {
|
||||
$name = $budget->name . ' ' . trans(
|
||||
'firefly.between_dates',
|
||||
['start' => $repetition->startdate->formatLocalized($format), 'end' => $repetition->enddate->formatLocalized($format)]
|
||||
);
|
||||
}
|
||||
$amount = $repetition->amount;
|
||||
$left = bccomp(bcadd($amount, $expenses), '0') < 1 ? '0' : bcadd($amount, $expenses);
|
||||
$spent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcmul($amount, '-1') : $expenses;
|
||||
$overspent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcadd($amount, $expenses) : '0';
|
||||
$array = [$name, $left, $spent, $overspent, $amount, $spent];
|
||||
$collection->push($array);
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param Budget $budget
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function spentInPeriodSingle(BudgetRepositoryInterface $repository, Budget $budget, Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
$collection = new Collection;
|
||||
$amount = '0';
|
||||
$left = '0';
|
||||
$spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end);
|
||||
$overspent = '0';
|
||||
$array = [$budget->name, $left, $spent, $overspent, $amount, $spent];
|
||||
$collection->push($array);
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function spentInPeriodWithout(Carbon $start, Carbon $end): array
|
||||
private function getBudgetedInPeriod(Budget $budget, Carbon $start, Carbon $end): array
|
||||
{
|
||||
$key = Navigation::preferredCarbonFormat($start, $end);
|
||||
$range = Navigation::preferredRangeFormat($start, $end);
|
||||
$current = clone $start;
|
||||
$budgeted = [];
|
||||
while ($current < $end) {
|
||||
$currentStart = Navigation::startOfPeriod($current, $range);
|
||||
$currentEnd = Navigation::endOfPeriod($current, $range);
|
||||
$budgetLimits = $this->repository->getBudgetLimits($budget, $currentStart, $currentEnd);
|
||||
$index = $currentStart->format($key);
|
||||
$budgeted[$index] = $budgetLimits->sum('amount');
|
||||
$currentEnd->addDay();
|
||||
$current = clone $currentEnd;
|
||||
}
|
||||
|
||||
return $budgeted;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's 6 but ok.
|
||||
*
|
||||
* @param Collection $limits
|
||||
* @param Budget $budget
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getExpensesForBudget(Collection $limits, Budget $budget, Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
if ($limits->count() === 0) {
|
||||
$spent = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end);
|
||||
if (bccomp($spent, '0') !== 0) {
|
||||
$return[$budget->name]['spent'] = bcmul($spent, '-1');
|
||||
$return[$budget->name]['left'] = 0;
|
||||
$return[$budget->name]['overspent'] = 0;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
$rows = $this->spentInPeriodMulti($budget, $limits);
|
||||
foreach ($rows as $name => $row) {
|
||||
if (bccomp($row['spent'], '0') !== 0 || bccomp($row['left'], '0') !== 0) {
|
||||
$return[$name]['spent'] = bcmul($row['spent'], '-1');
|
||||
$return[$name]['left'] = $row['left'];
|
||||
$return[$name]['overspent'] = bcmul($row['overspent'], '-1');
|
||||
}
|
||||
}
|
||||
unset($rows, $row);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
|
||||
*
|
||||
* Returns an array with the following values:
|
||||
* 0 =>
|
||||
* 'name' => name of budget + repetition
|
||||
* 'left' => left in budget repetition (always zero)
|
||||
* 'overspent' => spent more than budget repetition? (always zero)
|
||||
* 'spent' => actually spent in period for budget
|
||||
* 1 => (etc)
|
||||
*
|
||||
* @param Budget $budget
|
||||
* @param Collection $limits
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function spentInPeriodMulti(Budget $budget, Collection $limits): array
|
||||
{
|
||||
$return = [];
|
||||
$format = strval(trans('config.month_and_day'));
|
||||
$name = $budget->name;
|
||||
/** @var BudgetLimit $budgetLimit */
|
||||
foreach ($limits as $budgetLimit) {
|
||||
$expenses = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $budgetLimit->start_date, $budgetLimit->end_date);
|
||||
|
||||
if ($limits->count() > 1) {
|
||||
$name = $budget->name . ' ' . trans(
|
||||
'firefly.between_dates',
|
||||
[
|
||||
'start' => $budgetLimit->start_date->formatLocalized($format),
|
||||
'end' => $budgetLimit->end_date->formatLocalized($format),
|
||||
]
|
||||
);
|
||||
}
|
||||
$amount = $budgetLimit->amount;
|
||||
$left = bccomp(bcadd($amount, $expenses), '0') < 1 ? '0' : bcadd($amount, $expenses);
|
||||
$spent = $expenses;
|
||||
$overspent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcadd($amount, $expenses) : '0';
|
||||
$return[$name] = [
|
||||
'left' => $left,
|
||||
'overspent' => $overspent,
|
||||
'spent' => $spent,
|
||||
];
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with the following values:
|
||||
* 'name' => "no budget" in local language
|
||||
* 'repetition_left' => left in budget repetition (always zero)
|
||||
* 'repetition_overspent' => spent more than budget repetition? (always zero)
|
||||
* 'spent' => actually spent in period for budget
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function spentInPeriodWithout(Carbon $start, Carbon $end): string
|
||||
{
|
||||
// collector
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$types = [TransactionType::WITHDRAWAL];
|
||||
$collector->setAllAssetAccounts()->setTypes($types)->setRange($start, $end)->withoutBudget();
|
||||
$journals = $collector->getJournals();
|
||||
@@ -388,6 +426,6 @@ class BudgetController extends Controller
|
||||
$sum = bcadd($entry->transaction_amount, $sum);
|
||||
}
|
||||
|
||||
return [trans('firefly.no_budget'), '0', '0', $sum, '0', '0'];
|
||||
return $sum;
|
||||
}
|
||||
}
|
||||
|
||||
290
app/Http/Controllers/Chart/BudgetReportController.php
Normal file
290
app/Http/Controllers/Chart/BudgetReportController.php
Normal file
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
/**
|
||||
* BudgetReportController.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Chart;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||
use FireflyIII\Generator\Report\Category\MonthReportGenerator;
|
||||
use FireflyIII\Helpers\Chart\MetaPieChartInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
use Navigation;
|
||||
use Response;
|
||||
|
||||
/**
|
||||
* Separate controller because many helper functions are shared.
|
||||
*
|
||||
* Class BudgetReportController
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers\Chart
|
||||
*/
|
||||
class BudgetReportController extends Controller
|
||||
{
|
||||
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $accountRepository;
|
||||
/** @var BudgetRepositoryInterface */
|
||||
private $budgetRepository;
|
||||
/** @var GeneratorInterface */
|
||||
private $generator;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->generator = app(GeneratorInterface::class);
|
||||
$this->budgetRepository = app(BudgetRepositoryInterface::class);
|
||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Collection $budgets
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param string $others
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function accountExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end, string $others)
|
||||
{
|
||||
/** @var MetaPieChartInterface $helper */
|
||||
$helper = app(MetaPieChartInterface::class);
|
||||
$helper->setAccounts($accounts);
|
||||
$helper->setBudgets($budgets);
|
||||
$helper->setUser(auth()->user());
|
||||
$helper->setStart($start);
|
||||
$helper->setEnd($end);
|
||||
$helper->setCollectOtherObjects(intval($others) === 1);
|
||||
$chartData = $helper->generate('expense', 'account');
|
||||
$data = $this->generator->pieChart($chartData);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Collection $budgets
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param string $others
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function budgetExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end, string $others)
|
||||
{
|
||||
/** @var MetaPieChartInterface $helper */
|
||||
$helper = app(MetaPieChartInterface::class);
|
||||
$helper->setAccounts($accounts);
|
||||
$helper->setBudgets($budgets);
|
||||
$helper->setUser(auth()->user());
|
||||
$helper->setStart($start);
|
||||
$helper->setEnd($end);
|
||||
$helper->setCollectOtherObjects(intval($others) === 1);
|
||||
$chartData = $helper->generate('expense', 'budget');
|
||||
$data = $this->generator->pieChart($chartData);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Collection $budgets
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function mainChart(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end)
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('chart.budget.report.main');
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($budgets);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
/** @var BudgetRepositoryInterface $repository */
|
||||
$repository = app(BudgetRepositoryInterface::class);
|
||||
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
||||
$function = Navigation::preferredEndOfPeriod($start, $end);
|
||||
$chartData = [];
|
||||
$currentStart = clone $start;
|
||||
|
||||
// prep chart data:
|
||||
foreach ($budgets as $budget) {
|
||||
$chartData[$budget->id] = [
|
||||
'label' => strval(trans('firefly.spent_in_specific_budget', ['budget' => $budget->name])),
|
||||
'type' => 'bar',
|
||||
'yAxisID' => 'y-axis-0',
|
||||
'entries' => [],
|
||||
];
|
||||
$chartData[$budget->id . '-sum'] = [
|
||||
'label' => strval(trans('firefly.sum_of_expenses_in_budget', ['budget' => $budget->name])),
|
||||
'type' => 'line',
|
||||
'fill' => false,
|
||||
'yAxisID' => 'y-axis-1',
|
||||
'entries' => [],
|
||||
];
|
||||
$chartData[$budget->id . '-left'] = [
|
||||
'label' => strval(trans('firefly.left_in_budget_limit', ['budget' => $budget->name])),
|
||||
'type' => 'bar',
|
||||
'fill' => false,
|
||||
'yAxisID' => 'y-axis-0',
|
||||
'entries' => [],
|
||||
];
|
||||
}
|
||||
$allBudgetLimits = $repository->getAllBudgetLimits($start, $end);
|
||||
$sumOfExpenses = [];
|
||||
$leftOfLimits = [];
|
||||
while ($currentStart < $end) {
|
||||
$currentEnd = clone $currentStart;
|
||||
$currentEnd = $currentEnd->$function();
|
||||
$expenses = $this->groupByBudget($this->getExpenses($accounts, $budgets, $currentStart, $currentEnd));
|
||||
$label = $currentStart->formatLocalized($format);
|
||||
|
||||
/** @var Budget $budget */
|
||||
foreach ($budgets as $budget) {
|
||||
// get budget limit(s) for this period):
|
||||
$budgetLimits = $this->filterBudgetLimits($allBudgetLimits, $budget, $currentStart, $currentEnd);
|
||||
$currentExpenses = $expenses[$budget->id] ?? '0';
|
||||
$sumOfExpenses[$budget->id] = $sumOfExpenses[$budget->id] ?? '0';
|
||||
$sumOfExpenses[$budget->id] = bcadd($currentExpenses, $sumOfExpenses[$budget->id]);
|
||||
$chartData[$budget->id]['entries'][$label] = bcmul($currentExpenses, '-1');
|
||||
$chartData[$budget->id . '-sum']['entries'][$label] = bcmul($sumOfExpenses[$budget->id], '-1');
|
||||
|
||||
if (count($budgetLimits) > 0) {
|
||||
$budgetLimitId = $budgetLimits->first()->id;
|
||||
$leftOfLimits[$budgetLimitId] = $leftOfLimits[$budgetLimitId] ?? strval($budgetLimits->sum('amount'));
|
||||
$leftOfLimits[$budgetLimitId] = bcadd($leftOfLimits[$budgetLimitId], $currentExpenses);
|
||||
$chartData[$budget->id . '-left']['entries'][$label] = $leftOfLimits[$budgetLimitId];
|
||||
}
|
||||
}
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay();
|
||||
}
|
||||
|
||||
$data = $this->generator->multiSet($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the budget limits belonging to the given budget and valid on the given day.
|
||||
*
|
||||
* @param Collection $budgetLimits
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function filterBudgetLimits(Collection $budgetLimits, Budget $budget, Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
$set = $budgetLimits->filter(
|
||||
function (BudgetLimit $budgetLimit) use ($budget, $start, $end) {
|
||||
if ($budgetLimit->budget_id === $budget->id
|
||||
&& $budgetLimit->start_date->lte($start) // start of budget limit is on or before start
|
||||
&& $budgetLimit->end_date->gte($end) // end of budget limit is on or after end
|
||||
) {
|
||||
return $budgetLimit;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Collection $budgets
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function getExpenses(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
|
||||
->setBudgets($budgets)->withOpposingAccount()->disableFilter();
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
$transactions = $collector->getJournals();
|
||||
$set = MonthReportGenerator::filterExpenses($transactions, $accountIds);
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $set
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function groupByBudget(Collection $set): array
|
||||
{
|
||||
// group by category ID:
|
||||
$grouped = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($set as $transaction) {
|
||||
$jrnlBudId = intval($transaction->transaction_journal_budget_id);
|
||||
$transBudId = intval($transaction->transaction_budget_id);
|
||||
$budgetId = max($jrnlBudId, $transBudId);
|
||||
$grouped[$budgetId] = $grouped[$budgetId] ?? '0';
|
||||
$grouped[$budgetId] = bcadd($transaction->transaction_amount, $grouped[$budgetId]);
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $set
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function groupByOpposingAccount(Collection $set): array
|
||||
{
|
||||
$grouped = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($set as $transaction) {
|
||||
$accountId = $transaction->opposing_account_id;
|
||||
$grouped[$accountId] = $grouped[$accountId] ?? '0';
|
||||
$grouped[$accountId] = bcadd($transaction->transaction_amount, $grouped[$accountId]);
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace FireflyIII\Http\Controllers\Chart;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface;
|
||||
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Category;
|
||||
@@ -26,7 +26,6 @@ use Illuminate\Support\Collection;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class CategoryController
|
||||
@@ -35,7 +34,7 @@ use stdClass;
|
||||
*/
|
||||
class CategoryController extends Controller
|
||||
{
|
||||
/** @var CategoryChartGeneratorInterface */
|
||||
/** @var GeneratorInterface */
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
@@ -45,7 +44,7 @@ class CategoryController extends Controller
|
||||
{
|
||||
parent::__construct();
|
||||
// create chart generator:
|
||||
$this->generator = app(CategoryChartGeneratorInterface::class);
|
||||
$this->generator = app(GeneratorInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,34 +58,47 @@ class CategoryController extends Controller
|
||||
*/
|
||||
public function all(CRI $repository, AccountRepositoryInterface $accountRepository, Category $category)
|
||||
{
|
||||
$start = $repository->firstUseDate($category);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$categoryCollection = new Collection([$category]);
|
||||
$end = new Carbon;
|
||||
$entries = new Collection;
|
||||
$cache = new CacheProperties;
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('all');
|
||||
$cache->addProperty('categories');
|
||||
$cache->addProperty('chart.category.all');
|
||||
$cache->addProperty($category->id);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
$start = $repository->firstUseDate($category);
|
||||
|
||||
if ($start->year == 1900) {
|
||||
$start = new Carbon;
|
||||
}
|
||||
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = new Carbon;
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
$chartData = [
|
||||
[
|
||||
'label' => strval(trans('firefly.spent')),
|
||||
'entries' => [],
|
||||
'type' => 'bar',
|
||||
],
|
||||
[
|
||||
'label' => strval(trans('firefly.earned')),
|
||||
'entries' => [],
|
||||
'type' => 'bar',
|
||||
],
|
||||
];
|
||||
|
||||
while ($start <= $end) {
|
||||
$currentEnd = Navigation::endOfPeriod($start, $range);
|
||||
$spent = $repository->spentInPeriod($categoryCollection, $accounts, $start, $currentEnd);
|
||||
$earned = $repository->earnedInPeriod($categoryCollection, $accounts, $start, $currentEnd);
|
||||
$date = Navigation::periodShow($start, $range);
|
||||
$entries->push([clone $start, $date, $spent, $earned]);
|
||||
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $currentEnd);
|
||||
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $currentEnd);
|
||||
$label = Navigation::periodShow($start, $range);
|
||||
$chartData[0]['entries'][$label] = bcmul($spent, '-1');
|
||||
$chartData[1]['entries'][$label] = $earned;
|
||||
$start = Navigation::addPeriod($start, $range, 0);
|
||||
}
|
||||
$entries = $entries->reverse();
|
||||
$entries = $entries->slice(0, 48);
|
||||
$entries = $entries->reverse();
|
||||
$data = $this->generator->all($entries);
|
||||
|
||||
$data = $this->generator->multiSet($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
@@ -122,34 +134,30 @@ class CategoryController extends Controller
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('category');
|
||||
$cache->addProperty('frontpage');
|
||||
$cache->addProperty('chart.category.frontpage');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
$chartData = [];
|
||||
$categories = $repository->getCategories();
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]);
|
||||
$set = new Collection;
|
||||
/** @var Category $category */
|
||||
foreach ($categories as $category) {
|
||||
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end);
|
||||
if (bccomp($spent, '0') === -1) {
|
||||
$category->spent = $spent;
|
||||
$set->push($category);
|
||||
$chartData[$category->name] = bcmul($spent, '-1');
|
||||
}
|
||||
}
|
||||
// this is a "fake" entry for the "no category" entry.
|
||||
$entry = new stdClass;
|
||||
$entry->name = trans('firefly.no_category');
|
||||
$entry->spent = $repository->spentInPeriodWithoutCategory(new Collection, $start, $end);
|
||||
$set->push($entry);
|
||||
|
||||
$set = $set->sortBy('spent');
|
||||
$data = $this->generator->frontpage($set);
|
||||
$chartData[strval(trans('firefly.no_category'))] = bcmul($repository->spentInPeriodWithoutCategory(new Collection, $start, $end), '-1');
|
||||
|
||||
// sort
|
||||
arsort($chartData);
|
||||
|
||||
$data = $this->generator->singleSet(strval(trans('firefly.spent')), $chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,35 +174,43 @@ class CategoryController extends Controller
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('category-period-chart');
|
||||
$cache->addProperty('chart.category.period');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
$cache->addProperty($category);
|
||||
if ($cache->has()) {
|
||||
|
||||
return $cache->get();
|
||||
}
|
||||
$expenses = $repository->periodExpenses(new Collection([$category]), $accounts, $start, $end);
|
||||
$income = $repository->periodIncome(new Collection([$category]), $accounts, $start, $end);
|
||||
$periods = Navigation::listOfPeriods($start, $end);
|
||||
|
||||
|
||||
// join them:
|
||||
$result = [];
|
||||
foreach (array_keys($periods) as $period) {
|
||||
$nice = $periods[$period];
|
||||
$result[$nice] = [
|
||||
'earned' => $income[$category->id]['entries'][$period] ?? '0',
|
||||
'spent' => $expenses[$category->id]['entries'][$period] ?? '0',
|
||||
$chartData = [
|
||||
[
|
||||
'label' => strval(trans('firefly.spent')),
|
||||
'entries' => [],
|
||||
'type' => 'bar',
|
||||
],
|
||||
[
|
||||
'label' => strval(trans('firefly.earned')),
|
||||
'entries' => [],
|
||||
'type' => 'bar',
|
||||
],
|
||||
];
|
||||
|
||||
foreach (array_keys($periods) as $period) {
|
||||
$label = $periods[$period];
|
||||
$spent = $expenses[$category->id]['entries'][$period] ?? '0';
|
||||
$chartData[0]['entries'][$label] = bcmul($spent, '-1');
|
||||
$chartData[1]['entries'][$label] = $income[$category->id]['entries'][$period] ?? '0';
|
||||
}
|
||||
$data = $this->generator->reportPeriod($result);
|
||||
|
||||
$data = $this->generator->multiSet($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CRI $repository
|
||||
* @param Category $category
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
@@ -206,26 +222,36 @@ class CategoryController extends Controller
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('no-category-period-chart');
|
||||
$cache->addProperty('chart.category.period.no-cat');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
|
||||
return $cache->get();
|
||||
}
|
||||
$expenses = $repository->periodExpensesNoCategory($accounts, $start, $end);
|
||||
$income = $repository->periodIncomeNoCategory($accounts, $start, $end);
|
||||
$periods = Navigation::listOfPeriods($start, $end);
|
||||
|
||||
// join them:
|
||||
$result = [];
|
||||
foreach (array_keys($periods) as $period) {
|
||||
$nice = $periods[$period];
|
||||
$result[$nice] = [
|
||||
'earned' => $income['entries'][$period] ?? '0',
|
||||
'spent' => $expenses['entries'][$period] ?? '0',
|
||||
$chartData = [
|
||||
[
|
||||
'label' => strval(trans('firefly.spent')),
|
||||
'entries' => [],
|
||||
'type' => 'bar',
|
||||
],
|
||||
[
|
||||
'label' => strval(trans('firefly.earned')),
|
||||
'entries' => [],
|
||||
'type' => 'bar',
|
||||
],
|
||||
];
|
||||
|
||||
foreach (array_keys($periods) as $period) {
|
||||
$label = $periods[$period];
|
||||
$spent = $expenses['entries'][$period] ?? '0';
|
||||
$chartData[0]['entries'][$label] = bcmul($spent, '-1');
|
||||
$chartData[1]['entries'][$label] = $income['entries'][$period] ?? '0';
|
||||
|
||||
}
|
||||
$data = $this->generator->reportPeriod($result);
|
||||
$data = $this->generator->multiSet($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
@@ -260,33 +286,47 @@ class CategoryController extends Controller
|
||||
*/
|
||||
private function makePeriodChart(CRI $repository, Category $category, Carbon $start, Carbon $end)
|
||||
{
|
||||
$categoryCollection = new Collection([$category]);
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($category->id);
|
||||
$cache->addProperty('chart.category.period-chart');
|
||||
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($category->id);
|
||||
$cache->addProperty('specific-period');
|
||||
|
||||
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
$entries = new Collection;
|
||||
|
||||
// chart data
|
||||
$chartData = [
|
||||
[
|
||||
'label' => strval(trans('firefly.spent')),
|
||||
'entries' => [],
|
||||
'type' => 'bar',
|
||||
],
|
||||
[
|
||||
'label' => strval(trans('firefly.earned')),
|
||||
'entries' => [],
|
||||
'type' => 'bar',
|
||||
],
|
||||
];
|
||||
|
||||
while ($start <= $end) {
|
||||
$spent = $repository->spentInPeriod($categoryCollection, $accounts, $start, $start);
|
||||
$earned = $repository->earnedInPeriod($categoryCollection, $accounts, $start, $start);
|
||||
$date = Navigation::periodShow($start, '1D');
|
||||
$entries->push([clone $start, $date, $spent, $earned]);
|
||||
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $start);
|
||||
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $start);
|
||||
$label = Navigation::periodShow($start, '1D');
|
||||
|
||||
$chartData[0]['entries'][$label] = bcmul($spent, '-1');
|
||||
$chartData[1]['entries'][$label] = $earned;
|
||||
|
||||
|
||||
$start->addDay();
|
||||
}
|
||||
|
||||
$data = $this->generator->period($entries);
|
||||
$data = $this->generator->multiSet($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return $data;
|
||||
|
||||
@@ -15,17 +15,19 @@ namespace FireflyIII\Http\Controllers\Chart;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface;
|
||||
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||
use FireflyIII\Generator\Report\Category\MonthReportGenerator;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Helpers\Chart\MetaPieChartInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Navigation;
|
||||
use Response;
|
||||
|
||||
|
||||
@@ -43,7 +45,7 @@ class CategoryReportController extends Controller
|
||||
private $accountRepository;
|
||||
/** @var CategoryRepositoryInterface */
|
||||
private $categoryRepository;
|
||||
/** @var CategoryChartGeneratorInterface */
|
||||
/** @var GeneratorInterface */
|
||||
private $generator;
|
||||
|
||||
/**
|
||||
@@ -54,7 +56,7 @@ class CategoryReportController extends Controller
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->generator = app(CategoryChartGeneratorInterface::class);
|
||||
$this->generator = app(GeneratorInterface::class);
|
||||
$this->categoryRepository = app(CategoryRepositoryInterface::class);
|
||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||
|
||||
@@ -74,40 +76,16 @@ class CategoryReportController extends Controller
|
||||
*/
|
||||
public function accountExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
|
||||
{
|
||||
/** @var bool $others */
|
||||
$others = intval($others) === 1;
|
||||
$names = [];
|
||||
|
||||
// collect journals (just like the category report does):
|
||||
$set = $this->getExpenses($accounts, $categories, $start, $end);
|
||||
$grouped = $this->groupByOpposingAccount($set);
|
||||
|
||||
// show the grouped results:
|
||||
$result = [];
|
||||
$total = '0';
|
||||
foreach ($grouped as $accountId => $amount) {
|
||||
if (!isset($names[$accountId])) {
|
||||
$account = $this->accountRepository->find(intval($accountId));
|
||||
$names[$accountId] = $account->name;
|
||||
}
|
||||
$amount = bcmul($amount, '-1');
|
||||
$total = bcadd($total, $amount);
|
||||
$result[] = ['name' => $names[$accountId], 'id' => $accountId, 'amount' => $amount];
|
||||
}
|
||||
|
||||
// also collect all transactions NOT in these categories.
|
||||
if ($others) {
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]);
|
||||
$journals = $collector->getJournals();
|
||||
$sum = strval($journals->sum('transaction_amount'));
|
||||
$sum = bcmul($sum, '-1');
|
||||
Log::debug(sprintf('Sum of others in accountExpense is %f', $sum));
|
||||
$sum = bcsub($sum, $total);
|
||||
$result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum];
|
||||
}
|
||||
|
||||
$data = $this->generator->pieChart($result);
|
||||
/** @var MetaPieChartInterface $helper */
|
||||
$helper = app(MetaPieChartInterface::class);
|
||||
$helper->setAccounts($accounts);
|
||||
$helper->setCategories($categories);
|
||||
$helper->setUser(auth()->user());
|
||||
$helper->setStart($start);
|
||||
$helper->setEnd($end);
|
||||
$helper->setCollectOtherObjects(intval($others) === 1);
|
||||
$chartData = $helper->generate('expense', 'account');
|
||||
$data = $this->generator->pieChart($chartData);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
@@ -123,38 +101,16 @@ class CategoryReportController extends Controller
|
||||
*/
|
||||
public function accountIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
|
||||
{
|
||||
/** @var bool $others */
|
||||
$others = intval($others) === 1;
|
||||
$names = [];
|
||||
|
||||
// collect journals (just like the category report does):
|
||||
$set = $this->getIncome($accounts, $categories, $start, $end);
|
||||
$grouped = $this->groupByOpposingAccount($set);
|
||||
|
||||
// loop and show the grouped results:
|
||||
$result = [];
|
||||
$total = '0';
|
||||
foreach ($grouped as $accountId => $amount) {
|
||||
if (!isset($names[$accountId])) {
|
||||
$account = $this->accountRepository->find(intval($accountId));
|
||||
$names[$accountId] = $account->name;
|
||||
}
|
||||
$total = bcadd($total, $amount);
|
||||
$result[] = ['name' => $names[$accountId], 'id' => $accountId, 'amount' => $amount];
|
||||
}
|
||||
|
||||
// also collect others?
|
||||
if ($others) {
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]);
|
||||
$journals = $collector->getJournals();
|
||||
$sum = strval($journals->sum('transaction_amount'));
|
||||
Log::debug(sprintf('Sum of others in accountIncome is %f', $sum));
|
||||
$sum = bcsub($sum, $total);
|
||||
$result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum];
|
||||
}
|
||||
|
||||
$data = $this->generator->pieChart($result);
|
||||
/** @var MetaPieChartInterface $helper */
|
||||
$helper = app(MetaPieChartInterface::class);
|
||||
$helper->setAccounts($accounts);
|
||||
$helper->setCategories($categories);
|
||||
$helper->setUser(auth()->user());
|
||||
$helper->setStart($start);
|
||||
$helper->setEnd($end);
|
||||
$helper->setCollectOtherObjects(intval($others) === 1);
|
||||
$chartData = $helper->generate('income', 'account');
|
||||
$data = $this->generator->pieChart($chartData);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
@@ -170,40 +126,16 @@ class CategoryReportController extends Controller
|
||||
*/
|
||||
public function categoryExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
|
||||
{
|
||||
/** @var bool $others */
|
||||
$others = intval($others) === 1;
|
||||
$names = [];
|
||||
|
||||
// collect journals (just like the category report does):
|
||||
$set = $this->getExpenses($accounts, $categories, $start, $end);
|
||||
$grouped = $this->groupByCategory($set);
|
||||
|
||||
// show the grouped results:
|
||||
$result = [];
|
||||
$total = '0';
|
||||
foreach ($grouped as $categoryId => $amount) {
|
||||
if (!isset($names[$categoryId])) {
|
||||
$category = $this->categoryRepository->find(intval($categoryId));
|
||||
$names[$categoryId] = $category->name;
|
||||
}
|
||||
$amount = bcmul($amount, '-1');
|
||||
$total = bcadd($total, $amount);
|
||||
$result[] = ['name' => $names[$categoryId], 'id' => $categoryId, 'amount' => $amount];
|
||||
}
|
||||
|
||||
// also collect all transactions NOT in these categories.
|
||||
if ($others) {
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]);
|
||||
$journals = $collector->getJournals();
|
||||
$sum = strval($journals->sum('transaction_amount'));
|
||||
$sum = bcmul($sum, '-1');
|
||||
Log::debug(sprintf('Sum of others in categoryExpense is %f', $sum));
|
||||
$sum = bcsub($sum, $total);
|
||||
$result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum];
|
||||
}
|
||||
|
||||
$data = $this->generator->pieChart($result);
|
||||
/** @var MetaPieChartInterface $helper */
|
||||
$helper = app(MetaPieChartInterface::class);
|
||||
$helper->setAccounts($accounts);
|
||||
$helper->setCategories($categories);
|
||||
$helper->setUser(auth()->user());
|
||||
$helper->setStart($start);
|
||||
$helper->setEnd($end);
|
||||
$helper->setCollectOtherObjects(intval($others) === 1);
|
||||
$chartData = $helper->generate('expense', 'category');
|
||||
$data = $this->generator->pieChart($chartData);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
@@ -219,38 +151,17 @@ class CategoryReportController extends Controller
|
||||
*/
|
||||
public function categoryIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
|
||||
{
|
||||
/** @var bool $others */
|
||||
$others = intval($others) === 1;
|
||||
$names = [];
|
||||
|
||||
// collect journals (just like the category report does):
|
||||
$set = $this->getIncome($accounts, $categories, $start, $end);
|
||||
$grouped = $this->groupByCategory($set);
|
||||
|
||||
// loop and show the grouped results:
|
||||
$result = [];
|
||||
$total = '0';
|
||||
foreach ($grouped as $categoryId => $amount) {
|
||||
if (!isset($names[$categoryId])) {
|
||||
$category = $this->categoryRepository->find(intval($categoryId));
|
||||
$names[$categoryId] = $category->name;
|
||||
}
|
||||
$total = bcadd($total, $amount);
|
||||
$result[] = ['name' => $names[$categoryId], 'id' => $categoryId, 'amount' => $amount];
|
||||
}
|
||||
|
||||
// also collect others?
|
||||
if ($others) {
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]);
|
||||
$journals = $collector->getJournals();
|
||||
$sum = strval($journals->sum('transaction_amount'));
|
||||
Log::debug(sprintf('Sum of others in categoryIncome is %f', $sum));
|
||||
$sum = bcsub($sum, $total);
|
||||
$result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum];
|
||||
}
|
||||
|
||||
$data = $this->generator->pieChart($result);
|
||||
/** @var MetaPieChartInterface $helper */
|
||||
$helper = app(MetaPieChartInterface::class);
|
||||
$helper->setAccounts($accounts);
|
||||
$helper->setCategories($categories);
|
||||
$helper->setUser(auth()->user());
|
||||
$helper->setStart($start);
|
||||
$helper->setEnd($end);
|
||||
$helper->setCollectOtherObjects(intval($others) === 1);
|
||||
$chartData = $helper->generate('income', 'category');
|
||||
$data = $this->generator->pieChart($chartData);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
@@ -265,52 +176,98 @@ class CategoryReportController extends Controller
|
||||
*/
|
||||
public function mainChart(Collection $accounts, Collection $categories, Carbon $start, Carbon $end)
|
||||
{
|
||||
// determin optimal period:
|
||||
$period = '1D';
|
||||
$format = 'month_and_day';
|
||||
$function = 'endOfDay';
|
||||
if ($start->diffInMonths($end) > 1) {
|
||||
$period = '1M';
|
||||
$format = 'month';
|
||||
$function = 'endOfMonth';
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('chart.category.report.main');
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($categories);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
if ($start->diffInMonths($end) > 13) {
|
||||
$period = '1Y';
|
||||
$format = 'year';
|
||||
$function = 'endOfYear';
|
||||
}
|
||||
Log::debug(sprintf('Period is %s', $period));
|
||||
$data = [];
|
||||
|
||||
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
||||
$function = Navigation::preferredEndOfPeriod($start, $end);
|
||||
$chartData = [];
|
||||
$currentStart = clone $start;
|
||||
|
||||
// prep chart data:
|
||||
foreach ($categories as $category) {
|
||||
$chartData[$category->id . '-in'] = [
|
||||
'label' => $category->name . ' (' . strtolower(strval(trans('firefly.income'))) . ')',
|
||||
'type' => 'bar',
|
||||
'yAxisID' => 'y-axis-0',
|
||||
'entries' => [],
|
||||
];
|
||||
$chartData[$category->id . '-out'] = [
|
||||
'label' => $category->name . ' (' . strtolower(strval(trans('firefly.expenses'))) . ')',
|
||||
'type' => 'bar',
|
||||
'yAxisID' => 'y-axis-0',
|
||||
'entries' => [],
|
||||
];
|
||||
// total in, total out:
|
||||
$chartData[$category->id . '-total-in'] = [
|
||||
'label' => $category->name . ' (' . strtolower(strval(trans('firefly.sum_of_income'))) . ')',
|
||||
'type' => 'line',
|
||||
'fill' => false,
|
||||
'yAxisID' => 'y-axis-1',
|
||||
'entries' => [],
|
||||
];
|
||||
$chartData[$category->id . '-total-out'] = [
|
||||
'label' => $category->name . ' (' . strtolower(strval(trans('firefly.sum_of_expenses'))) . ')',
|
||||
'type' => 'line',
|
||||
'fill' => false,
|
||||
'yAxisID' => 'y-axis-1',
|
||||
'entries' => [],
|
||||
];
|
||||
}
|
||||
$sumOfIncome = [];
|
||||
$sumOfExpense = [];
|
||||
|
||||
while ($currentStart < $end) {
|
||||
$currentEnd = clone $currentStart;
|
||||
Log::debug(sprintf('Function is %s', $function));
|
||||
$currentEnd = $currentEnd->$function();
|
||||
$expenses = $this->groupByCategory($this->getExpenses($accounts, $categories, $currentStart, $currentEnd));
|
||||
$income = $this->groupByCategory($this->getIncome($accounts, $categories, $currentStart, $currentEnd));
|
||||
$label = $currentStart->formatLocalized(strval(trans('config.' . $format)));
|
||||
|
||||
Log::debug(sprintf('Now grabbing CMC expenses between %s and %s', $currentStart->format('Y-m-d'), $currentEnd->format('Y-m-d')));
|
||||
|
||||
$data[$label] = [
|
||||
'in' => [],
|
||||
'out' => [],
|
||||
];
|
||||
$label = $currentStart->formatLocalized($format);
|
||||
|
||||
/** @var Category $category */
|
||||
foreach ($categories as $category) {
|
||||
// get sum, and get label:
|
||||
$categoryId = $category->id;
|
||||
$data[$label]['name'][$categoryId] = $category->name;
|
||||
$data[$label]['in'][$categoryId] = $income[$categoryId] ?? '0';
|
||||
$data[$label]['out'][$categoryId] = $expenses[$categoryId] ?? '0';
|
||||
}
|
||||
$labelIn = $category->id . '-in';
|
||||
$labelOut = $category->id . '-out';
|
||||
$labelSumIn = $category->id . '-total-in';
|
||||
$labelSumOut = $category->id . '-total-out';
|
||||
$currentIncome = $income[$category->id] ?? '0';
|
||||
$currentExpense = $expenses[$category->id] ?? '0';
|
||||
|
||||
|
||||
// add to sum:
|
||||
$sumOfIncome[$category->id] = $sumOfIncome[$category->id] ?? '0';
|
||||
$sumOfExpense[$category->id] = $sumOfExpense[$category->id] ?? '0';
|
||||
$sumOfIncome[$category->id] = bcadd($sumOfIncome[$category->id], $currentIncome);
|
||||
$sumOfExpense[$category->id] = bcadd($sumOfExpense[$category->id], $currentExpense);
|
||||
|
||||
// add to chart:
|
||||
$chartData[$labelIn]['entries'][$label] = $currentIncome;
|
||||
$chartData[$labelOut]['entries'][$label] = $currentExpense;
|
||||
$chartData[$labelSumIn]['entries'][$label] = $sumOfIncome[$category->id];
|
||||
$chartData[$labelSumOut]['entries'][$label] = $sumOfExpense[$category->id];
|
||||
}
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay();
|
||||
}
|
||||
|
||||
$data = $this->generator->mainReportChart($data);
|
||||
// remove all empty entries to prevent cluttering:
|
||||
$newSet = [];
|
||||
foreach ($chartData as $key => $entry) {
|
||||
if (!array_sum($entry['entries']) == 0) {
|
||||
$newSet[$key] = $chartData[$key];
|
||||
}
|
||||
}
|
||||
if (count($newSet) === 0) {
|
||||
$newSet = $chartData;
|
||||
}
|
||||
$data = $this->generator->multiSet($newSet);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
@@ -326,7 +283,8 @@ class CategoryReportController extends Controller
|
||||
*/
|
||||
private function getExpenses(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
|
||||
->setCategories($categories)->withOpposingAccount()->disableFilter();
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
@@ -346,7 +304,8 @@ class CategoryReportController extends Controller
|
||||
*/
|
||||
private function getIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
|
||||
->setCategories($categories)->withOpposingAccount();
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
|
||||
@@ -13,13 +13,12 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Chart;
|
||||
|
||||
use FireflyIII\Generator\Chart\PiggyBank\PiggyBankChartGeneratorInterface;
|
||||
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
use Response;
|
||||
|
||||
|
||||
@@ -31,7 +30,7 @@ use Response;
|
||||
class PiggyBankController extends Controller
|
||||
{
|
||||
|
||||
/** @var PiggyBankChartGeneratorInterface */
|
||||
/** @var GeneratorInterface */
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
@@ -41,7 +40,7 @@ class PiggyBankController extends Controller
|
||||
{
|
||||
parent::__construct();
|
||||
// create chart generator:
|
||||
$this->generator = app(PiggyBankChartGeneratorInterface::class);
|
||||
$this->generator = app(GeneratorInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,7 +55,7 @@ class PiggyBankController extends Controller
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('piggy-history');
|
||||
$cache->addProperty('chart.piggy-bank.history');
|
||||
$cache->addProperty($piggyBank->id);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
@@ -64,18 +63,16 @@ class PiggyBankController extends Controller
|
||||
|
||||
$set = $repository->getEvents($piggyBank);
|
||||
$set = $set->reverse();
|
||||
$collection = [];
|
||||
$chartData = [];
|
||||
$sum = '0';
|
||||
/** @var PiggyBankEvent $entry */
|
||||
foreach ($set as $entry) {
|
||||
$date = $entry->date->format('Y-m-d');
|
||||
$amount = $entry->amount;
|
||||
if (isset($collection[$date])) {
|
||||
$amount = bcadd($amount, $collection[$date]);
|
||||
}
|
||||
$collection[$date] = $amount;
|
||||
$label = $entry->date->formatLocalized(strval(trans('config.month_and_day')));
|
||||
$sum = bcadd($sum, $entry->amount);
|
||||
$chartData[$label] = $sum;
|
||||
}
|
||||
|
||||
$data = $this->generator->history(new Collection($collection));
|
||||
$data = $this->generator->singleSet($piggyBank->name, $chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace FireflyIII\Http\Controllers\Chart;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Chart\Report\ReportChartGeneratorInterface;
|
||||
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
@@ -32,7 +32,7 @@ use Steam;
|
||||
class ReportController extends Controller
|
||||
{
|
||||
|
||||
/** @var ReportChartGeneratorInterface */
|
||||
/** @var GeneratorInterface */
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
@@ -42,7 +42,7 @@ class ReportController extends Controller
|
||||
{
|
||||
parent::__construct();
|
||||
// create chart generator:
|
||||
$this->generator = app(ReportChartGeneratorInterface::class);
|
||||
$this->generator = app(GeneratorInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,13 +52,14 @@ class ReportController extends Controller
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function netWorth(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('netWorth');
|
||||
$cache->addProperty('chart.report.net-worth');
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($end);
|
||||
@@ -67,21 +68,16 @@ class ReportController extends Controller
|
||||
}
|
||||
$ids = $accounts->pluck('id')->toArray();
|
||||
$current = clone $start;
|
||||
$entries = new Collection;
|
||||
$chartData = [];
|
||||
while ($current < $end) {
|
||||
$balances = Steam::balancesById($ids, $current);
|
||||
$sum = $this->arraySum($balances);
|
||||
$entries->push(
|
||||
[
|
||||
'date' => clone $current,
|
||||
'net-worth' => $sum,
|
||||
]
|
||||
);
|
||||
|
||||
$label = $current->formatLocalized(strval(trans('config.month_and_day')));
|
||||
$chartData[$label] = $sum;
|
||||
$current->addDays(7);
|
||||
}
|
||||
$data = $this->generator->netWorth($entries);
|
||||
|
||||
$data = $this->generator->singleSet(strval(trans('firefly.net_worth')), $chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
@@ -102,35 +98,52 @@ class ReportController extends Controller
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('yearInOut');
|
||||
$cache->addProperty('chart.report.operations');
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($end);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
||||
$source = $this->getChartData($accounts, $start, $end);
|
||||
$chartData = [
|
||||
[
|
||||
'label' => trans('firefly.income'),
|
||||
'type' => 'bar',
|
||||
'entries' => [],
|
||||
],
|
||||
[
|
||||
'label' => trans('firefly.expenses'),
|
||||
'type' => 'bar',
|
||||
'entries' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$chartSource = $this->getYearData($accounts, $start, $end);
|
||||
|
||||
if ($start->diffInMonths($end) > 12) {
|
||||
// data = method X
|
||||
$data = $this->multiYearOperations($chartSource['earned'], $chartSource['spent'], $start, $end);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
foreach ($source['earned'] as $date => $amount) {
|
||||
$carbon = new Carbon($date);
|
||||
$label = $carbon->formatLocalized($format);
|
||||
$earned = $chartData[0]['entries'][$label] ?? '0';
|
||||
$chartData[0]['entries'][$label] = bcadd($earned, $amount);
|
||||
}
|
||||
foreach ($source['spent'] as $date => $amount) {
|
||||
$carbon = new Carbon($date);
|
||||
$label = $carbon->formatLocalized($format);
|
||||
$spent = $chartData[1]['entries'][$label] ?? '0';
|
||||
$chartData[1]['entries'][$label] = bcadd($spent, $amount);
|
||||
}
|
||||
|
||||
// data = method Y
|
||||
$data = $this->singleYearOperations($chartSource['earned'], $chartSource['spent'], $start, $end);
|
||||
|
||||
$data = $this->generator->multiSet($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows sum income and expense, debet/credit: operations
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
@@ -140,162 +153,67 @@ class ReportController extends Controller
|
||||
public function sum(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('yearInOutSummarized');
|
||||
$cache->addProperty('chart.report.sum');
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($accounts);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
$chartSource = $this->getYearData($accounts, $start, $end);
|
||||
|
||||
if ($start->diffInMonths($end) > 12) {
|
||||
// per year
|
||||
$data = $this->multiYearSum($chartSource['earned'], $chartSource['spent'], $start, $end);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
$source = $this->getChartData($accounts, $start, $end);
|
||||
$numbers = [
|
||||
'sum_earned' => '0',
|
||||
'avg_earned' => '0',
|
||||
'count_earned' => 0,
|
||||
'sum_spent' => '0',
|
||||
'avg_spent' => '0',
|
||||
'count_spent' => 0,
|
||||
];
|
||||
foreach ($source['earned'] as $amount) {
|
||||
$numbers['sum_earned'] = bcadd($amount, $numbers['sum_earned']);
|
||||
$numbers['count_earned']++;
|
||||
}
|
||||
// per month!
|
||||
$data = $this->singleYearSum($chartSource['earned'], $chartSource['spent'], $start, $end);
|
||||
if ($numbers['count_earned'] > 0) {
|
||||
$numbers['avg_earned'] = $numbers['sum_earned'] / $numbers['count_earned'];
|
||||
}
|
||||
foreach ($source['spent'] as $amount) {
|
||||
$numbers['sum_spent'] = bcadd($amount, $numbers['sum_spent']);
|
||||
$numbers['count_spent']++;
|
||||
}
|
||||
if ($numbers['count_spent'] > 0) {
|
||||
$numbers['avg_spent'] = $numbers['sum_spent'] / $numbers['count_spent'];
|
||||
}
|
||||
|
||||
$chartData = [
|
||||
[
|
||||
'label' => strval(trans('firefly.income')),
|
||||
'type' => 'bar',
|
||||
'entries' => [
|
||||
strval(trans('firefly.sum_of_period')) => $numbers['sum_earned'],
|
||||
strval(trans('firefly.average_in_period')) => $numbers['avg_earned'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => trans('firefly.expenses'),
|
||||
'type' => 'bar',
|
||||
'entries' => [
|
||||
strval(trans('firefly.sum_of_period')) => $numbers['sum_spent'],
|
||||
strval(trans('firefly.average_in_period')) => $numbers['avg_spent'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
$data = $this->generator->multiSet($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $earned
|
||||
* @param array $spent
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function multiYearOperations(array $earned, array $spent, Carbon $start, Carbon $end)
|
||||
{
|
||||
$entries = new Collection;
|
||||
while ($start < $end) {
|
||||
|
||||
$incomeSum = $this->pluckFromArray($start->year, $earned);
|
||||
$expenseSum = $this->pluckFromArray($start->year, $spent);
|
||||
|
||||
$entries->push([clone $start, $incomeSum, $expenseSum]);
|
||||
$start->addYear();
|
||||
}
|
||||
|
||||
$data = $this->generator->multiYearOperations($entries);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $earned
|
||||
* @param array $spent
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function multiYearSum(array $earned, array $spent, Carbon $start, Carbon $end)
|
||||
{
|
||||
$income = '0';
|
||||
$expense = '0';
|
||||
$count = 0;
|
||||
while ($start < $end) {
|
||||
|
||||
$currentIncome = $this->pluckFromArray($start->year, $earned);
|
||||
$currentExpense = $this->pluckFromArray($start->year, $spent);
|
||||
$income = bcadd($income, $currentIncome);
|
||||
$expense = bcadd($expense, $currentExpense);
|
||||
|
||||
$count++;
|
||||
$start->addYear();
|
||||
}
|
||||
|
||||
$data = $this->generator->multiYearSum($income, $expense, $count);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $year
|
||||
* @param array $set
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function pluckFromArray($year, array $set)
|
||||
{
|
||||
$sum = '0';
|
||||
foreach ($set as $date => $amount) {
|
||||
if (substr($date, 0, 4) == $year) {
|
||||
$sum = bcadd($sum, $amount);
|
||||
}
|
||||
}
|
||||
|
||||
return $sum;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $earned
|
||||
* @param array $spent
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function singleYearOperations(array $earned, array $spent, Carbon $start, Carbon $end)
|
||||
{
|
||||
// per month? simply use each month.
|
||||
|
||||
$entries = new Collection;
|
||||
while ($start < $end) {
|
||||
// total income and total expenses:
|
||||
$date = $start->format('Y-m');
|
||||
$incomeSum = isset($earned[$date]) ? $earned[$date] : 0;
|
||||
$expenseSum = isset($spent[$date]) ? $spent[$date] : 0;
|
||||
|
||||
$entries->push([clone $start, $incomeSum, $expenseSum]);
|
||||
$start->addMonth();
|
||||
}
|
||||
|
||||
$data = $this->generator->yearOperations($entries);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $earned
|
||||
* @param array $spent
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function singleYearSum(array $earned, array $spent, Carbon $start, Carbon $end)
|
||||
{
|
||||
$income = '0';
|
||||
$expense = '0';
|
||||
$count = 0;
|
||||
while ($start < $end) {
|
||||
$date = $start->format('Y-m');
|
||||
$currentIncome = isset($earned[$date]) ? $earned[$date] : 0;
|
||||
$currentExpense = isset($spent[$date]) ? $spent[$date] : 0;
|
||||
$income = bcadd($income, $currentIncome);
|
||||
$expense = bcadd($expense, $currentExpense);
|
||||
|
||||
$count++;
|
||||
$start->addMonth();
|
||||
}
|
||||
|
||||
$data = $this->generator->yearSum($income, $expense, $count);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $array
|
||||
*
|
||||
@@ -312,31 +230,45 @@ class ReportController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the incomes and expenses for the given periods, grouped per month. Will cache its results
|
||||
*
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getYearData(Collection $accounts, Carbon $start, Carbon $end): array
|
||||
private function getChartData(Collection $accounts, Carbon $start, Carbon $end): array
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('chart.report.get-chart-data');
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($end);
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
|
||||
$tasker = app(AccountTaskerInterface::class);
|
||||
$currentStart = clone $start;
|
||||
$spentArray = [];
|
||||
$earnedArray = [];
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = Navigation::endOfPeriod($currentStart, '1M');
|
||||
$date = $currentStart->format('Y-m');
|
||||
$label = $currentStart->format('Y-m') . '-01';
|
||||
$spent = $tasker->amountOutInPeriod($accounts, $accounts, $currentStart, $currentEnd);
|
||||
$earned = $tasker->amountInInPeriod($accounts, $accounts, $currentStart, $currentEnd);
|
||||
$spentArray[$date] = bcmul($spent, '-1');
|
||||
$earnedArray[$date] = $earned;
|
||||
$spentArray[$label] = bcmul($spent, '-1');
|
||||
$earnedArray[$label] = $earned;
|
||||
$currentStart = Navigation::addPeriod($currentStart, '1M', 0);
|
||||
}
|
||||
|
||||
return [
|
||||
$result = [
|
||||
'spent' => $spentArray,
|
||||
'earned' => $earnedArray,
|
||||
];
|
||||
$cache->store($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
6
app/Http/Controllers/Controller.php
Executable file → Normal file
6
app/Http/Controllers/Controller.php
Executable file → Normal file
@@ -13,6 +13,7 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use FireflyConfig;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
@@ -49,7 +50,10 @@ class Controller extends BaseController
|
||||
View::share('hideCategories', false);
|
||||
View::share('hideBills', false);
|
||||
View::share('hideTags', false);
|
||||
|
||||
$isDemoSite = FireflyConfig::get('is_demo_site', config('firefly.configuration.is_demo_site'))->data;
|
||||
View::share('IS_DEMO_SITE', $isDemoSite);
|
||||
View::share('DEMO_USERNAME', env('DEMO_USERNAME', ''));
|
||||
View::share('DEMO_PASSWORD', env('DEMO_PASSWORD', ''));
|
||||
|
||||
// translations:
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ use Cache;
|
||||
use FireflyIII\Http\Requests\CurrencyFormRequest;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use Input;
|
||||
use Log;
|
||||
use Preferences;
|
||||
use Session;
|
||||
@@ -89,14 +88,16 @@ class CurrencyController extends Controller
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param CurrencyRepositoryInterface $repository
|
||||
* @param TransactionCurrency $currency
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|View
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||
*/
|
||||
public function delete(TransactionCurrency $currency)
|
||||
public function delete(CurrencyRepositoryInterface $repository, TransactionCurrency $currency)
|
||||
{
|
||||
if (!$this->canDeleteCurrency($currency)) {
|
||||
if (!$repository->canDeleteCurrency($currency)) {
|
||||
Session::flash('error', trans('firefly.cannot_delete_currency', ['name' => $currency->name]));
|
||||
|
||||
return redirect(route('currencies.index'));
|
||||
@@ -114,23 +115,21 @@ class CurrencyController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CurrencyRepositoryInterface $repository
|
||||
* @param TransactionCurrency $currency
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Exception
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function destroy(TransactionCurrency $currency)
|
||||
public function destroy(CurrencyRepositoryInterface $repository, TransactionCurrency $currency)
|
||||
{
|
||||
if (!$this->canDeleteCurrency($currency)) {
|
||||
if (!$repository->canDeleteCurrency($currency)) {
|
||||
Session::flash('error', trans('firefly.cannot_delete_currency', ['name' => $currency->name]));
|
||||
|
||||
return redirect(route('currencies.index'));
|
||||
}
|
||||
|
||||
$repository->destroy($currency);
|
||||
Session::flash('success', trans('firefly.deleted_currency', ['name' => $currency->name]));
|
||||
if (auth()->user()->hasRole('owner')) {
|
||||
$currency->forceDelete();
|
||||
}
|
||||
|
||||
return redirect(session('currencies.delete.url'));
|
||||
}
|
||||
@@ -170,7 +169,7 @@ class CurrencyController extends Controller
|
||||
|
||||
|
||||
if (!auth()->user()->hasRole('owner')) {
|
||||
Session::flash('warning', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
|
||||
Session::flash('info', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
|
||||
}
|
||||
|
||||
|
||||
@@ -196,7 +195,7 @@ class CurrencyController extends Controller
|
||||
$currency = $repository->store($data);
|
||||
Session::flash('success', trans('firefly.created_currency', ['name' => $currency->name]));
|
||||
|
||||
if (intval(Input::get('create_another')) === 1) {
|
||||
if (intval($request->get('create_another')) === 1) {
|
||||
Session::put('currencies.create.fromStore', true);
|
||||
|
||||
return redirect(route('currencies.create'))->withInput();
|
||||
@@ -225,7 +224,7 @@ class CurrencyController extends Controller
|
||||
Preferences::mark();
|
||||
|
||||
|
||||
if (intval(Input::get('return_to_edit')) === 1) {
|
||||
if (intval($request->get('return_to_edit')) === 1) {
|
||||
Session::put('currencies.edit.fromUpdate', true);
|
||||
|
||||
return redirect(route('currencies.edit', [$currency->id]));
|
||||
@@ -235,40 +234,4 @@ class CurrencyController extends Controller
|
||||
return redirect(session('currencies.edit.url'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionCurrency $currency
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function canDeleteCurrency(TransactionCurrency $currency): bool
|
||||
{
|
||||
$repository = app(CurrencyRepositoryInterface::class);
|
||||
|
||||
// has transactions still
|
||||
if ($repository->countJournals($currency) > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// is the only currency left
|
||||
if ($repository->get()->count() === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// is the default currency for the user or the system
|
||||
$defaultCode = Preferences::get('currencyPreference', config('firefly.default_currency', 'EUR'))->data;
|
||||
if ($currency->code === $defaultCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// is the default currency for the system
|
||||
$defaultSystemCode = config('firefly.default_currency', 'EUR');
|
||||
if ($currency->code === $defaultSystemCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// can be deleted
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,15 +17,15 @@ namespace FireflyIII\Http\Controllers;
|
||||
use Carbon\Carbon;
|
||||
use ExpandedForm;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Export\Processor;
|
||||
use FireflyIII\Export\ProcessorInterface;
|
||||
use FireflyIII\Http\Requests\ExportFormRequest;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\ExportJob;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface;
|
||||
use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface as EJRI;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use Storage;
|
||||
use View;
|
||||
|
||||
/**
|
||||
@@ -59,22 +59,22 @@ class ExportController extends Controller
|
||||
* @return \Symfony\Component\HttpFoundation\Response|\Illuminate\Contracts\Routing\ResponseFactory
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function download(ExportJob $job)
|
||||
public function download(ExportJobRepositoryInterface $repository, ExportJob $job)
|
||||
{
|
||||
$disk = Storage::disk('export');
|
||||
$file = $job->key . '.zip';
|
||||
$date = date('Y-m-d \a\t H-i-s');
|
||||
$name = 'Export job on ' . $date . '.zip';
|
||||
$quoted = sprintf('"%s"', addcslashes($name, '"\\'));
|
||||
|
||||
if (!$disk->exists($file)) {
|
||||
if (!$repository->exists($job)) {
|
||||
throw new FireflyException('Against all expectations, zip file "' . $file . '" does not exist.');
|
||||
}
|
||||
$content = $repository->getContent($job);
|
||||
|
||||
|
||||
$job->change('export_downloaded');
|
||||
|
||||
return response($disk->get($file), 200)
|
||||
return response($content, 200)
|
||||
->header('Content-Description', 'File Transfer')
|
||||
->header('Content-Type', 'application/octet-stream')
|
||||
->header('Content-Disposition', 'attachment; filename=' . $quoted)
|
||||
@@ -83,7 +83,7 @@ class ExportController extends Controller
|
||||
->header('Expires', '0')
|
||||
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
|
||||
->header('Pragma', 'public')
|
||||
->header('Content-Length', $disk->size($file));
|
||||
->header('Content-Length', strlen($content));
|
||||
|
||||
}
|
||||
|
||||
@@ -133,7 +133,6 @@ class ExportController extends Controller
|
||||
*/
|
||||
public function postIndex(ExportFormRequest $request, AccountRepositoryInterface $repository, EJRI $jobs)
|
||||
{
|
||||
set_time_limit(0);
|
||||
$job = $jobs->findByKey($request->get('job'));
|
||||
$settings = [
|
||||
'accounts' => $repository->getAccountsById($request->get('accounts')),
|
||||
@@ -145,53 +144,54 @@ class ExportController extends Controller
|
||||
'job' => $job,
|
||||
];
|
||||
|
||||
$job->change('export_status_make_exporter');
|
||||
$processor = new Processor($settings);
|
||||
$jobs->changeStatus($job, 'export_status_make_exporter');
|
||||
|
||||
/** @var ProcessorInterface $processor */
|
||||
$processor = app(ProcessorInterface::class, [$settings]);
|
||||
|
||||
/*
|
||||
* Collect journals:
|
||||
*/
|
||||
$job->change('export_status_collecting_journals');
|
||||
$jobs->changeStatus($job, 'export_status_collecting_journals');
|
||||
$processor->collectJournals();
|
||||
$job->change('export_status_collected_journals');
|
||||
$jobs->changeStatus($job, 'export_status_collected_journals');
|
||||
/*
|
||||
* Transform to exportable entries:
|
||||
*/
|
||||
$job->change('export_status_converting_to_export_format');
|
||||
$jobs->changeStatus($job, 'export_status_converting_to_export_format');
|
||||
$processor->convertJournals();
|
||||
$job->change('export_status_converted_to_export_format');
|
||||
$jobs->changeStatus($job, 'export_status_converted_to_export_format');
|
||||
/*
|
||||
* Transform to (temporary) file:
|
||||
*/
|
||||
$job->change('export_status_creating_journal_file');
|
||||
$jobs->changeStatus($job, 'export_status_creating_journal_file');
|
||||
$processor->exportJournals();
|
||||
$job->change('export_status_created_journal_file');
|
||||
$jobs->changeStatus($job, 'export_status_created_journal_file');
|
||||
/*
|
||||
* Collect attachments, if applicable.
|
||||
*/
|
||||
if ($settings['includeAttachments']) {
|
||||
$job->change('export_status_collecting_attachments');
|
||||
$jobs->changeStatus($job, 'export_status_collecting_attachments');
|
||||
$processor->collectAttachments();
|
||||
$job->change('export_status_collected_attachments');
|
||||
$jobs->changeStatus($job, 'export_status_collected_attachments');
|
||||
}
|
||||
|
||||
/*
|
||||
* Collect old uploads
|
||||
*/
|
||||
if ($settings['includeOldUploads']) {
|
||||
$job->change('export_status_collecting_old_uploads');
|
||||
$jobs->changeStatus($job, 'export_status_collecting_old_uploads');
|
||||
$processor->collectOldUploads();
|
||||
$job->change('export_status_collected_old_uploads');
|
||||
$jobs->changeStatus($job, 'export_status_collected_old_uploads');
|
||||
}
|
||||
|
||||
/*
|
||||
* Create ZIP file:
|
||||
*/
|
||||
$job->change('export_status_creating_zip_file');
|
||||
$jobs->changeStatus($job, 'export_status_creating_zip_file');
|
||||
$processor->createZipFile();
|
||||
$job->change('export_status_created_zip_file');
|
||||
|
||||
$job->change('export_status_finished');
|
||||
$jobs->changeStatus($job, 'export_status_created_zip_file');
|
||||
$jobs->changeStatus($job, 'export_status_finished');
|
||||
|
||||
return Response::json('ok');
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ class HomeController extends Controller
|
||||
$journal->save();
|
||||
}
|
||||
}
|
||||
|
||||
Session::forget(['start', 'end', 'viewRange', 'range', 'is_custom_range']);
|
||||
|
||||
Session::clear();
|
||||
Artisan::call('cache:clear');
|
||||
@@ -166,9 +166,48 @@ class HomeController extends Controller
|
||||
public function routes()
|
||||
{
|
||||
// these routes are not relevant for the help pages:
|
||||
$ignore = ['login', 'registe', 'logout', 'two-fac', 'lost-two', 'confirm', 'resend', 'do_confirm', 'testFla', 'json.', 'piggy-banks.add',
|
||||
'piggy-banks.remove', 'preferences.', 'rules.rule.up', 'rules.rule.down', 'rules.rule-group.up', 'rules.rule-group.down', 'popup.report',
|
||||
'admin.users.domains.block-', 'import.json', 'help.',
|
||||
$ignore = [
|
||||
// login and two-factor routes:
|
||||
'login',
|
||||
'registe',
|
||||
'password.rese',
|
||||
'logout',
|
||||
'two-fac',
|
||||
'lost-two',
|
||||
// test troutes
|
||||
'test-flash',
|
||||
'all-routes',
|
||||
// json routes
|
||||
'json.',
|
||||
// routes that point to modals or that redirect immediately.
|
||||
'piggy-banks.add',
|
||||
'piggy-banks.remove',
|
||||
'rules.rule.up',
|
||||
'attachments.download',
|
||||
'bills.rescan',
|
||||
'rules.rule.down',
|
||||
'rules.rule-group.up',
|
||||
'rules.rule-group.down',
|
||||
'popup.',
|
||||
'error',
|
||||
'flush',
|
||||
//'preferences.',
|
||||
'admin.users.domains.block-',
|
||||
'help.',
|
||||
// ajax routes:
|
||||
'import.json',
|
||||
// charts:
|
||||
'chart.',
|
||||
// report data:
|
||||
'report-data.',
|
||||
|
||||
// others:
|
||||
'debugbar',
|
||||
'attachments.preview',
|
||||
'budgets.income',
|
||||
'currencies.default',
|
||||
|
||||
|
||||
];
|
||||
$routes = Route::getRoutes();
|
||||
$return = '<pre>';
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace FireflyIII\Http\Controllers;
|
||||
use Crypt;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Requests\ImportUploadRequest;
|
||||
use FireflyIII\Import\ImportProcedure;
|
||||
use FireflyIII\Import\ImportProcedureInterface;
|
||||
use FireflyIII\Import\Setup\SetupInterface;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
|
||||
@@ -23,6 +23,7 @@ use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Log;
|
||||
use Response;
|
||||
use Session;
|
||||
use SplFileObject;
|
||||
use Storage;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
@@ -145,10 +146,13 @@ class ImportController extends Controller
|
||||
return $this->redirectToCorrectStep($job);
|
||||
}
|
||||
|
||||
// if there is a tag (there might not be), we can link to it:
|
||||
$tagId = $job->extended_status['importTag'] ?? 0;
|
||||
|
||||
$subTitle = trans('firefly.import_finished');
|
||||
$subTitleIcon = 'fa-star';
|
||||
|
||||
return view('import.finished', compact('job', 'subTitle', 'subTitleIcon'));
|
||||
return view('import.finished', compact('job', 'subTitle', 'subTitleIcon', 'tagId'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -312,13 +316,14 @@ class ImportController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ImportProcedureInterface $importProcedure
|
||||
* @param ImportJob $job
|
||||
*/
|
||||
public function start(ImportJob $job)
|
||||
public function start(ImportProcedureInterface $importProcedure, ImportJob $job)
|
||||
{
|
||||
set_time_limit(0);
|
||||
if ($job->status == 'settings_complete') {
|
||||
ImportProcedure::runImport($job);
|
||||
$importProcedure->runImport($job);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,7 +335,7 @@ class ImportController extends Controller
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||
*/
|
||||
public function status(ImportJob $job)
|
||||
{
|
||||
{ //
|
||||
Log::debug('Now in status()', ['job' => $job->key]);
|
||||
if (!$this->jobInCorrectStep($job, 'status')) {
|
||||
return $this->redirectToCorrectStep($job);
|
||||
@@ -364,14 +369,33 @@ class ImportController extends Controller
|
||||
$content = $uploaded->fread($uploaded->getSize());
|
||||
$contentEncrypted = Crypt::encrypt($content);
|
||||
$disk = Storage::disk('upload');
|
||||
|
||||
// user is demo user, replace upload with prepared file.
|
||||
if (auth()->user()->hasRole('demo')) {
|
||||
$stubsDisk = Storage::disk('stubs');
|
||||
$content = $stubsDisk->get('demo-import.csv');
|
||||
$contentEncrypted = Crypt::encrypt($content);
|
||||
$disk->put($newName, $contentEncrypted);
|
||||
Log::debug('Replaced upload with demo file.');
|
||||
|
||||
// also set up prepared configuration.
|
||||
$configuration = json_decode($stubsDisk->get('demo-configuration.json'), true);
|
||||
$job->configuration = $configuration;
|
||||
$job->save();
|
||||
Log::debug('Set configuration for demo user', $configuration);
|
||||
|
||||
// also flash info
|
||||
Session::flash('info', trans('demo.import-configure-security'));
|
||||
}
|
||||
if (!auth()->user()->hasRole('demo')) {
|
||||
// user is not demo, process original upload:
|
||||
$disk->put($newName, $contentEncrypted);
|
||||
Log::debug('Uploaded file', ['name' => $upload->getClientOriginalName(), 'size' => $upload->getSize(), 'mime' => $upload->getClientMimeType()]);
|
||||
}
|
||||
|
||||
// store configuration file's content into the job's configuration
|
||||
// thing.
|
||||
// otherwise, leave it empty.
|
||||
if ($request->files->has('configuration_file')) {
|
||||
// store configuration file's content into the job's configuration thing. Otherwise, leave it empty.
|
||||
// demo user's configuration upload is ignored completely.
|
||||
if ($request->files->has('configuration_file') && !auth()->user()->hasRole('demo')) {
|
||||
/** @var UploadedFile $configFile */
|
||||
$configFile = $request->files->get('configuration_file');
|
||||
Log::debug(
|
||||
@@ -390,6 +414,9 @@ class ImportController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
// if user is demo user, replace config with prepared config:
|
||||
|
||||
|
||||
return redirect(route('import.configure', [$job->key]));
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace FireflyIII\Http\Controllers;
|
||||
use Amount;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
@@ -23,7 +23,7 @@ use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Input;
|
||||
use Illuminate\Http\Request;
|
||||
use Preferences;
|
||||
use Response;
|
||||
|
||||
@@ -43,11 +43,13 @@ class JsonController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function action()
|
||||
public function action(Request $request)
|
||||
{
|
||||
$count = intval(Input::get('count')) > 0 ? intval(Input::get('count')) : 1;
|
||||
$count = intval($request->get('count')) > 0 ? intval($request->get('count')) : 1;
|
||||
$keys = array_keys(config('firefly.rule-actions'));
|
||||
$actions = [];
|
||||
foreach ($keys as $key) {
|
||||
@@ -269,18 +271,18 @@ class JsonController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $what
|
||||
* @param JournalCollectorInterface $collector
|
||||
* @param string $what
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function transactionJournals($what)
|
||||
public function transactionJournals(JournalCollectorInterface $collector, string $what)
|
||||
{
|
||||
$descriptions = [];
|
||||
$type = config('firefly.transactionTypesByWhat.' . $what);
|
||||
$types = [$type];
|
||||
|
||||
// use journal collector instead:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setTypes($types)->setLimit(100)->setPage(1);
|
||||
$journals = $collector->getJournals();
|
||||
foreach ($journals as $j) {
|
||||
@@ -296,11 +298,13 @@ class JsonController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function trigger()
|
||||
public function trigger(Request $request)
|
||||
{
|
||||
$count = intval(Input::get('count')) > 0 ? intval(Input::get('count')) : 1;
|
||||
$count = intval($request->get('count')) > 0 ? intval($request->get('count')) : 1;
|
||||
$keys = array_keys(config('firefly.rule-triggers'));
|
||||
$triggers = [];
|
||||
foreach ($keys as $key) {
|
||||
|
||||
@@ -117,7 +117,7 @@ class NewUserController extends Controller
|
||||
'virtualBalance' => 0,
|
||||
'active' => true,
|
||||
'accountRole' => 'defaultAsset',
|
||||
'openingBalance' => round($request->input('bank_balance'), 2),
|
||||
'openingBalance' => round($request->input('bank_balance'), 12),
|
||||
'openingBalanceDate' => new Carbon,
|
||||
'openingBalanceCurrency' => intval($request->input('amount_currency_id_bank_balance')),
|
||||
];
|
||||
@@ -142,7 +142,7 @@ class NewUserController extends Controller
|
||||
'virtualBalance' => 0,
|
||||
'active' => true,
|
||||
'accountRole' => 'savingAsset',
|
||||
'openingBalance' => round($request->input('savings_balance'), 2),
|
||||
'openingBalance' => round($request->input('savings_balance'), 12),
|
||||
'openingBalanceDate' => new Carbon,
|
||||
'openingBalanceCurrency' => intval($request->input('amount_currency_id_savings_balance')),
|
||||
];
|
||||
@@ -163,7 +163,7 @@ class NewUserController extends Controller
|
||||
'name' => 'Credit card',
|
||||
'iban' => null,
|
||||
'accountType' => 'asset',
|
||||
'virtualBalance' => round($request->get('credit_card_limit'), 2),
|
||||
'virtualBalance' => round($request->get('credit_card_limit'), 12),
|
||||
'active' => true,
|
||||
'accountRole' => 'ccAsset',
|
||||
'openingBalance' => null,
|
||||
|
||||
@@ -20,10 +20,11 @@ use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
use Log;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use Session;
|
||||
use Steam;
|
||||
use URL;
|
||||
@@ -149,13 +150,18 @@ class PiggyBankController extends Controller
|
||||
*/
|
||||
public function destroy(PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
|
||||
{
|
||||
|
||||
|
||||
Session::flash('success', strval(trans('firefly.deleted_piggy_bank', ['name' => e($piggyBank->name)])));
|
||||
Preferences::mark();
|
||||
$piggyBankId = $piggyBank->id;
|
||||
$repository->destroy($piggyBank);
|
||||
|
||||
return redirect(session('piggy-banks.delete.url'));
|
||||
$uri = session('piggy-banks.delete.url');
|
||||
if (!(strpos($uri, sprintf('piggy-banks/show/%s', $piggyBankId)) === false)) {
|
||||
// uri would point back to piggy bank
|
||||
$uri = route('piggy-banks.index');
|
||||
}
|
||||
|
||||
return redirect($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,7 +219,7 @@ class PiggyBankController extends Controller
|
||||
$accounts = [];
|
||||
/** @var PiggyBank $piggyBank */
|
||||
foreach ($piggyBanks as $piggyBank) {
|
||||
$piggyBank->savedSoFar = round($piggyBank->currentRelevantRep()->currentamount, 2);
|
||||
$piggyBank->savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
|
||||
$piggyBank->percentage = $piggyBank->savedSoFar != 0 ? intval($piggyBank->savedSoFar / $piggyBank->targetamount * 100) : 0;
|
||||
$piggyBank->leftToSave = bcsub($piggyBank->targetamount, strval($piggyBank->savedSoFar));
|
||||
$piggyBank->percentage = $piggyBank->percentage > 100 ? 100 : $piggyBank->percentage;
|
||||
@@ -228,7 +234,7 @@ class PiggyBankController extends Controller
|
||||
'balance' => Steam::balanceIgnoreVirtual($account, $end),
|
||||
'leftForPiggyBanks' => $piggyBank->leftOnAccount($end),
|
||||
'sumOfSaved' => strval($piggyBank->savedSoFar),
|
||||
'sumOfTargets' => strval(round($piggyBank->targetamount, 2)),
|
||||
'sumOfTargets' => $piggyBank->targetamount,
|
||||
'leftToSave' => $piggyBank->leftToSave,
|
||||
];
|
||||
} else {
|
||||
@@ -242,11 +248,14 @@ class PiggyBankController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param PiggyBankRepositoryInterface $repository
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function order(PiggyBankRepositoryInterface $repository)
|
||||
public function order(Request $request, PiggyBankRepositoryInterface $repository)
|
||||
{
|
||||
$data = Input::get('order');
|
||||
$data = $request->get('order');
|
||||
|
||||
// set all users piggy banks to zero:
|
||||
$repository->reset();
|
||||
@@ -257,25 +266,29 @@ class PiggyBankController extends Controller
|
||||
$repository->setOrder(intval($id), ($order + 1));
|
||||
}
|
||||
}
|
||||
|
||||
return Response::json(['result' => 'ok']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param PiggyBankRepositoryInterface $repository
|
||||
* @param PiggyBank $piggyBank
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postAdd(PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
|
||||
public function postAdd(Request $request, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
|
||||
{
|
||||
$amount = strval(round(Input::get('amount'), 2));
|
||||
$amount = $request->get('amount');
|
||||
Log::debug(sprintf('Found amount is %s', $amount));
|
||||
/** @var Carbon $date */
|
||||
$date = session('end', Carbon::now()->endOfMonth());
|
||||
$leftOnAccount = $piggyBank->leftOnAccount($date);
|
||||
$savedSoFar = strval($piggyBank->currentRelevantRep()->currentamount);
|
||||
$leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);
|
||||
$maxAmount = round(min($leftOnAccount, $leftToSave), 2);
|
||||
$maxAmount = strval(min(round($leftOnAccount, 12), round($leftToSave, 12)));
|
||||
|
||||
if ($amount <= $maxAmount) {
|
||||
if (bccomp($amount, $maxAmount) <= 0) {
|
||||
$repetition = $piggyBank->currentRelevantRep();
|
||||
$currentAmount = $repetition->currentamount ?? '0';
|
||||
$repetition->currentamount = bcadd($currentAmount, $amount);
|
||||
@@ -299,18 +312,19 @@ class PiggyBankController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param PiggyBankRepositoryInterface $repository
|
||||
* @param PiggyBank $piggyBank
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function postRemove(PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
|
||||
public function postRemove(Request $request, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
|
||||
{
|
||||
$amount = strval(round(Input::get('amount'), 2));
|
||||
$amount = strval(round($request->get('amount'), 12));
|
||||
|
||||
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
|
||||
|
||||
if ($amount <= $savedSoFar) {
|
||||
if (bccomp($amount, $savedSoFar) <= 0) {
|
||||
$repetition = $piggyBank->currentRelevantRep();
|
||||
$repetition->currentamount = bcsub($repetition->currentamount, $amount);
|
||||
$repetition->save();
|
||||
@@ -384,7 +398,7 @@ class PiggyBankController extends Controller
|
||||
Session::flash('success', strval(trans('firefly.stored_piggy_bank', ['name' => e($piggyBank->name)])));
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('create_another')) === 1) {
|
||||
if (intval($request->get('create_another')) === 1) {
|
||||
Session::put('piggy-banks.create.fromStore', true);
|
||||
|
||||
return redirect(route('piggy-banks.create'))->withInput();
|
||||
@@ -400,7 +414,7 @@ class PiggyBankController extends Controller
|
||||
* @param PiggyBankFormRequest $request
|
||||
* @param PiggyBank $piggyBank
|
||||
*
|
||||
* @return $this
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function update(PiggyBankRepositoryInterface $repository, PiggyBankFormRequest $request, PiggyBank $piggyBank)
|
||||
{
|
||||
@@ -410,7 +424,7 @@ class PiggyBankController extends Controller
|
||||
Session::flash('success', strval(trans('firefly.updated_piggy_bank', ['name' => e($piggyBank->name)])));
|
||||
Preferences::mark();
|
||||
|
||||
if (intval(Input::get('return_to_edit')) === 1) {
|
||||
if (intval($request->get('return_to_edit')) === 1) {
|
||||
Session::put('piggy-banks.edit.fromUpdate', true);
|
||||
|
||||
return redirect(route('piggy-banks.edit', [$piggyBank->id]));
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace FireflyIII\Http\Controllers\Popup;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collection\BalanceLine;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
@@ -102,7 +102,8 @@ class ReportController extends Controller
|
||||
|
||||
switch (true) {
|
||||
case ($role === BalanceLine::ROLE_DEFAULTROLE && !is_null($budget->id)):
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector
|
||||
->setAccounts(new Collection([$account]))
|
||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||
@@ -112,8 +113,8 @@ class ReportController extends Controller
|
||||
break;
|
||||
case ($role === BalanceLine::ROLE_DEFAULTROLE && is_null($budget->id)):
|
||||
$budget->name = strval(trans('firefly.no_budget'));
|
||||
// collector
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector
|
||||
->setAccounts(new Collection([$account]))
|
||||
->setTypes($types)
|
||||
@@ -122,8 +123,8 @@ class ReportController extends Controller
|
||||
$journals = $collector->getJournals();
|
||||
break;
|
||||
case ($role === BalanceLine::ROLE_DIFFROLE):
|
||||
// journals no budget, not corrected by a tag.
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector
|
||||
->setAccounts(new Collection([$account]))
|
||||
->setTypes($types)
|
||||
@@ -133,8 +134,8 @@ class ReportController extends Controller
|
||||
|
||||
$budget->name = strval(trans('firefly.leftUnbalanced'));
|
||||
$journals = $journals->filter(
|
||||
function (TransactionJournal $journal) {
|
||||
$tags = $journal->tags()->where('tagMode', 'balancingAct')->count();
|
||||
function (Transaction $transaction) {
|
||||
$tags = $transaction->transactionJournal->tags()->where('tagMode', 'balancingAct')->count();
|
||||
if ($tags === 0) {
|
||||
return true;
|
||||
}
|
||||
@@ -167,7 +168,8 @@ class ReportController extends Controller
|
||||
/** @var BudgetRepositoryInterface $repository */
|
||||
$repository = app(BudgetRepositoryInterface::class);
|
||||
$budget = $repository->find(intval($attributes['budgetId']));
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
|
||||
$collector
|
||||
->setAccounts($attributes['accounts'])
|
||||
@@ -200,8 +202,8 @@ class ReportController extends Controller
|
||||
$repository = app(CategoryRepositoryInterface::class);
|
||||
$category = $repository->find(intval($attributes['categoryId']));
|
||||
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
||||
// get journal collector instead:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAccounts($attributes['accounts'])->setTypes($types)
|
||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||
->setCategory($category);
|
||||
@@ -227,7 +229,8 @@ class ReportController extends Controller
|
||||
|
||||
$account = $repository->find(intval($attributes['accountId']));
|
||||
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types);
|
||||
$journals = $collector->getJournals();
|
||||
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
|
||||
@@ -262,7 +265,8 @@ class ReportController extends Controller
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$account = $repository->find(intval($attributes['accountId']));
|
||||
$types = [TransactionType::DEPOSIT, TransactionType::TRANSFER];
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types);
|
||||
$journals = $collector->getJournals();
|
||||
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
|
||||
|
||||
@@ -113,6 +113,7 @@ class PreferencesController extends Controller
|
||||
* @param TokenFormRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter) // it's unused but the class does some validation.
|
||||
*/
|
||||
public function postCode(TokenFormRequest $request)
|
||||
{
|
||||
@@ -150,7 +151,7 @@ class PreferencesController extends Controller
|
||||
|
||||
// custom fiscal year
|
||||
$customFiscalYear = intval($request->get('customFiscalYear')) === 1;
|
||||
$fiscalYearStart = date('m-d', strtotime($request->get('fiscalYearStart')));
|
||||
$fiscalYearStart = date('m-d', strtotime(strval($request->get('fiscalYearStart'))));
|
||||
Preferences::set('customFiscalYear', $customFiscalYear);
|
||||
Preferences::set('fiscalYearStart', $fiscalYearStart);
|
||||
|
||||
@@ -166,6 +167,9 @@ class PreferencesController extends Controller
|
||||
Preferences::set('transactionPageSize', 50);
|
||||
}
|
||||
|
||||
$twoFactorAuthEnabled = false;
|
||||
$hasTwoFactorAuthSecret = false;
|
||||
if (!auth()->user()->hasRole('demo')) {
|
||||
// two factor auth
|
||||
$twoFactorAuthEnabled = intval($request->get('twoFactorAuthEnabled'));
|
||||
$hasTwoFactorAuthSecret = !is_null(Preferences::get('twoFactorAuthSecret'));
|
||||
@@ -174,6 +178,7 @@ class PreferencesController extends Controller
|
||||
if ($hasTwoFactorAuthSecret) {
|
||||
Preferences::set('twoFactorAuthEnabled', $twoFactorAuthEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
// language:
|
||||
$lang = $request->get('language');
|
||||
|
||||
@@ -13,11 +13,12 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use FireflyIII\Exceptions\ValidationException;
|
||||
use FireflyIII\Http\Requests\DeleteAccountFormRequest;
|
||||
use FireflyIII\Http\Requests\ProfileFormRequest;
|
||||
use FireflyIII\User;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use Hash;
|
||||
use Preferences;
|
||||
use Log;
|
||||
use Session;
|
||||
use View;
|
||||
|
||||
@@ -51,6 +52,12 @@ class ProfileController extends Controller
|
||||
*/
|
||||
public function changePassword()
|
||||
{
|
||||
if (auth()->user()->hasRole('demo')) {
|
||||
Session::flash('info', strval(trans('firefly.cannot_change_demo')));
|
||||
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
|
||||
$title = auth()->user()->email;
|
||||
$subTitle = strval(trans('firefly.change_your_password'));
|
||||
$subTitleIcon = 'fa-key';
|
||||
@@ -63,6 +70,12 @@ class ProfileController extends Controller
|
||||
*/
|
||||
public function deleteAccount()
|
||||
{
|
||||
if (auth()->user()->hasRole('demo')) {
|
||||
Session::flash('info', strval(trans('firefly.cannot_delete_demo')));
|
||||
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
|
||||
$title = auth()->user()->email;
|
||||
$subTitle = strval(trans('firefly.delete_account'));
|
||||
$subTitleIcon = 'fa-trash';
|
||||
@@ -84,90 +97,85 @@ class ProfileController extends Controller
|
||||
|
||||
/**
|
||||
* @param ProfileFormRequest $request
|
||||
* @param UserRepositoryInterface $repository
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function postChangePassword(ProfileFormRequest $request)
|
||||
public function postChangePassword(ProfileFormRequest $request, UserRepositoryInterface $repository)
|
||||
{
|
||||
if (auth()->user()->hasRole('demo')) {
|
||||
Session::flash('info', strval(trans('firefly.cannot_change_demo')));
|
||||
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
|
||||
// old, new1, new2
|
||||
if (!Hash::check($request->get('current_password'), auth()->user()->password)) {
|
||||
Session::flash('error', strval(trans('firefly.invalid_current_password')));
|
||||
|
||||
return redirect(route('profile.change-password'));
|
||||
}
|
||||
$result = $this->validatePassword($request->get('current_password'), $request->get('new_password'));
|
||||
if (!($result === true)) {
|
||||
Session::flash('error', $result);
|
||||
|
||||
try {
|
||||
$this->validatePassword($request->get('current_password'), $request->get('new_password'));
|
||||
} catch (ValidationException $e) {
|
||||
Session::flash('error', $e->getMessage());
|
||||
|
||||
return redirect(route('profile.change-password'));
|
||||
}
|
||||
|
||||
// update the user with the new password.
|
||||
auth()->user()->password = bcrypt($request->get('new_password'));
|
||||
auth()->user()->save();
|
||||
|
||||
$repository->changePassword(auth()->user(), $request->get('new_password'));
|
||||
Session::flash('success', strval(trans('firefly.password_changed')));
|
||||
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UserRepositoryInterface $repository
|
||||
* @param DeleteAccountFormRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \Exception
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function postDeleteAccount(DeleteAccountFormRequest $request)
|
||||
public function postDeleteAccount(UserRepositoryInterface $repository, DeleteAccountFormRequest $request)
|
||||
{
|
||||
if (auth()->user()->hasRole('demo')) {
|
||||
Session::flash('info', strval(trans('firefly.cannot_delete_demo')));
|
||||
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
|
||||
// old, new1, new2
|
||||
if (!Hash::check($request->get('password'), auth()->user()->password)) {
|
||||
Session::flash('error', strval(trans('firefly.invalid_password')));
|
||||
|
||||
return redirect(route('profile.delete-account'));
|
||||
}
|
||||
|
||||
// store some stuff for the future:
|
||||
$registration = Preferences::get('registration_ip_address')->data;
|
||||
$confirmation = Preferences::get('confirmation_ip_address')->data;
|
||||
|
||||
// DELETE!
|
||||
$email = auth()->user()->email;
|
||||
auth()->user()->delete();
|
||||
$user = auth()->user();
|
||||
Log::info(sprintf('User #%d has opted to delete their account', auth()->user()->id));
|
||||
// make repository delete user:
|
||||
auth()->logout();
|
||||
Session::flush();
|
||||
$repository->destroy($user);
|
||||
|
||||
Session::flash('gaEventCategory', 'user');
|
||||
Session::flash('gaEventAction', 'delete-account');
|
||||
|
||||
// create a new user with the same email address so re-registration is blocked.
|
||||
$newUser = User::create(
|
||||
[
|
||||
'email' => $email,
|
||||
'password' => 'deleted',
|
||||
'blocked' => 1,
|
||||
'blocked_code' => 'deleted',
|
||||
]
|
||||
);
|
||||
if (strlen($registration) > 0) {
|
||||
Preferences::setForUser($newUser, 'registration_ip_address', $registration);
|
||||
|
||||
}
|
||||
if (strlen($confirmation) > 0) {
|
||||
Preferences::setForUser($newUser, 'confirmation_ip_address', $confirmation);
|
||||
}
|
||||
|
||||
return redirect(route('index'));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $old
|
||||
* @param string $new1
|
||||
* @param string $new
|
||||
*
|
||||
* @return string|bool
|
||||
* @return bool
|
||||
* @throws ValidationException
|
||||
*/
|
||||
protected function validatePassword(string $old, string $new1)
|
||||
protected function validatePassword(string $old, string $new): bool
|
||||
{
|
||||
if ($new1 == $old) {
|
||||
return trans('firefly.should_change');
|
||||
if ($new === $old) {
|
||||
throw new ValidationException(strval(trans('firefly.should_change')));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user