mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-19 10:53:31 +00:00
Compare commits
752 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
094f6a7476 | ||
|
c3b4849fa0 | ||
|
dfd6c5379c | ||
|
9aa53c11e0 | ||
|
b0d93621a8 | ||
|
a337d9a599 | ||
|
7ffd77dc76 | ||
|
96fdf4fd93 | ||
|
27b8ce0f7f | ||
|
2f943c91d2 | ||
|
91c96311de | ||
|
0f929faa16 | ||
|
7a40c34cf0 | ||
|
462d987de9 | ||
|
f64e8d8973 | ||
|
21222eb697 | ||
|
e47d6fb3ac | ||
|
c7fc10ac89 | ||
|
e8b528f520 | ||
|
b22de7bf70 | ||
|
ec119c8f6e | ||
|
a20b38598e | ||
|
aa0eb47205 | ||
|
723db9d71e | ||
|
1d8dc3d65d | ||
|
fe2716876a | ||
|
fac0e97e5d | ||
|
449d009c28 | ||
|
55b2e6fe25 | ||
|
9989b3b9da | ||
|
7ab1cbfc1f | ||
|
62d19c3902 | ||
|
19700e7ee3 | ||
|
de3e8edd6d | ||
|
deda48af4a | ||
|
7688d7c619 | ||
|
76328b5c45 | ||
|
1de17bf06f | ||
|
6724daf995 | ||
|
7caca053a1 | ||
|
ae1bf8c017 | ||
|
d20b0da438 | ||
|
a0218d7df1 | ||
|
5ae5d67b91 | ||
|
8493ed7603 | ||
|
804b681d40 | ||
|
e8303bd059 | ||
|
ac6c5d4e32 | ||
|
90b0d0d52c | ||
|
4093bdbd3e | ||
|
a2097cf981 | ||
|
6a33d0c9dc | ||
|
525d5fb427 | ||
|
e4946b8cd5 | ||
|
76b32df622 | ||
|
bc1079364d | ||
|
8602febe9d | ||
|
d55dfe27dc | ||
|
90c16e2a07 | ||
|
149c1cd9b1 | ||
|
20f1a43369 | ||
|
e8fb8f993d | ||
|
f0c782dc01 | ||
|
50c13e6d20 | ||
|
69bedd035f | ||
|
85337f0a31 | ||
|
f8a7e2f98e | ||
|
ec90a49d43 | ||
|
5812b150c6 | ||
|
c7ebc7273f | ||
|
5226c87304 | ||
|
25dd1c5d35 | ||
|
c5a9e5e56d | ||
|
47ed70d671 | ||
|
cb5526f469 | ||
|
a4f128077f | ||
|
7140ba76d5 | ||
|
872e8f2de6 | ||
|
6c14e9d083 | ||
|
e4b1812b46 | ||
|
1c2c6bb1d0 | ||
|
baefd4f93b | ||
|
4270fe07ab | ||
|
e4ae925d2b | ||
|
dc599361a4 | ||
|
738a311f49 | ||
|
71f6ba3418 | ||
|
d1d573c408 | ||
|
50e39a4a75 | ||
|
8635fe7ebb | ||
|
6b57d4397a | ||
|
8f2b898b2b | ||
|
0d1d360d18 | ||
|
def3b3a155 | ||
|
d344512743 | ||
|
19eef71133 | ||
|
61d58a354e | ||
|
be868d37f2 | ||
|
20bb151cf3 | ||
|
77f889aba6 | ||
|
1e69a54972 | ||
|
6b7a47ca28 | ||
|
c3fdd3b5f7 | ||
|
e9f2121667 | ||
|
161e9e1e11 | ||
|
e336a45f79 | ||
|
9c09f93908 | ||
|
582398e7f6 | ||
|
b118635abd | ||
|
ac0d4a75b5 | ||
|
c212d5c5ea | ||
|
08ac27cccf | ||
|
0b5cab99cf | ||
|
cc0057cc56 | ||
|
1ce49b814b | ||
|
5bbaaece38 | ||
|
30bc4ccfa7 | ||
|
4f64f1d754 | ||
|
c0e578dd47 | ||
|
2b82fca2cf | ||
|
f0028b33e9 | ||
|
7ddea23375 | ||
|
83edccacc6 | ||
|
75e95d6452 | ||
|
423bb4bbcd | ||
|
43585c563c | ||
|
2564a41d05 | ||
|
a0bb1e3625 | ||
|
9b4fd57f51 | ||
|
e67709e339 | ||
|
0c4e913f30 | ||
|
c6de0e51c7 | ||
|
69e85adadf | ||
|
b34068207f | ||
|
68b7b4b3a4 | ||
|
5e3ee30e66 | ||
|
aaf7d12b46 | ||
|
729a348657 | ||
|
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 |
@@ -38,9 +38,17 @@ SEND_REGISTRATION_MAIL=true
|
|||||||
SEND_ERROR_MESSAGE=true
|
SEND_ERROR_MESSAGE=true
|
||||||
SHOW_INCOMPLETE_TRANSLATIONS=false
|
SHOW_INCOMPLETE_TRANSLATIONS=false
|
||||||
|
|
||||||
|
CACHE_PREFIX=firefly
|
||||||
|
|
||||||
|
GOOGLE_MAPS_API_KEY=
|
||||||
ANALYTICS_ID=
|
ANALYTICS_ID=
|
||||||
SITE_OWNER=mail@example.com
|
SITE_OWNER=mail@example.com
|
||||||
|
USE_ENCRYPTION=true
|
||||||
|
|
||||||
PUSHER_KEY=
|
PUSHER_KEY=
|
||||||
PUSHER_SECRET=
|
PUSHER_SECRET=
|
||||||
PUSHER_APP_ID=
|
PUSHER_APP_ID=
|
||||||
|
|
||||||
|
DEMO_USERNAME=
|
||||||
|
DEMO_PASSWORD=
|
||||||
|
|
||||||
|
19
.github/CONTRIBUTING.md
vendored
Normal file
19
.github/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Welcome to Firefly III on Github!
|
||||||
|
|
||||||
|
:+1::tada: Thank you for taking the time to contribute something to Firefly III!
|
||||||
|
|
||||||
|
## Feature requests
|
||||||
|
|
||||||
|
If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.github.io/requested-features/).
|
||||||
|
|
||||||
|
## Bugs
|
||||||
|
|
||||||
|
If you find a bug, include as many log files and details as you think are necessary.
|
||||||
|
|
||||||
|
## Installation problems
|
||||||
|
|
||||||
|
Take the time to read the [installation guide FAQ](https://firefly-iii.github.io/installation-guide-faq/) and make sure you search through closed issues for the problems other people have had. Your problem may be among them!
|
||||||
|
|
||||||
|
## Pull requests
|
||||||
|
|
||||||
|
I can only accept pull requests against the `develop` branch, never the `master` branch
|
@@ -2,7 +2,50 @@
|
|||||||
tools:
|
tools:
|
||||||
external_code_coverage: false
|
external_code_coverage: false
|
||||||
filter:
|
filter:
|
||||||
excluded_paths:
|
paths:
|
||||||
- app/Support/Migration/*
|
- app/*
|
||||||
- app/database/migrations/*
|
- public/js/ff/*
|
||||||
- database/migrations/*
|
excluded_paths:
|
||||||
|
- "database/migrations/*"
|
||||||
|
- "bootstrap/*"
|
||||||
|
- "config/*"
|
||||||
|
- "docker/*"
|
||||||
|
- "public/js/lib/*"
|
||||||
|
- "public/lib/adminlte/js/*"
|
||||||
|
- "public/lib/bootstrap/js/*"
|
||||||
|
- "resources/*"
|
||||||
|
- "routes/*"
|
||||||
|
- "storage/*"
|
||||||
|
checks:
|
||||||
|
php:
|
||||||
|
use_self_instead_of_fqcn: true
|
||||||
|
uppercase_constants: true
|
||||||
|
return_doc_comments: true
|
||||||
|
return_doc_comment_if_not_inferrable: true
|
||||||
|
remove_extra_empty_lines: true
|
||||||
|
parameter_doc_comments: true
|
||||||
|
optional_parameters_at_the_end: true
|
||||||
|
no_short_variable_names:
|
||||||
|
minimum: '3'
|
||||||
|
no_short_method_names:
|
||||||
|
minimum: '3'
|
||||||
|
no_long_variable_names:
|
||||||
|
maximum: '20'
|
||||||
|
no_goto: true
|
||||||
|
newline_at_end_of_file: true
|
||||||
|
encourage_single_quotes: true
|
||||||
|
avoid_todo_comments: true
|
||||||
|
avoid_perl_style_comments: true
|
||||||
|
avoid_fixme_comments: true
|
||||||
|
avoid_multiple_statements_on_same_line: true
|
||||||
|
align_assignments: true
|
||||||
|
duplication: false
|
||||||
|
javascript: true
|
||||||
|
|
||||||
|
coding_style:
|
||||||
|
php:
|
||||||
|
spaces:
|
||||||
|
around_operators:
|
||||||
|
concatenation: true
|
||||||
|
other:
|
||||||
|
after_type_cast: false
|
17
.travis.yml
17
.travis.yml
@@ -1,18 +1,23 @@
|
|||||||
language: php
|
language: php
|
||||||
sudo: false
|
|
||||||
php:
|
php:
|
||||||
- '7.0'
|
- 7.0
|
||||||
|
- 7.1
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- vendor
|
||||||
|
- $HOME/.composer/cache
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- phpenv config-rm xdebug.ini
|
- if [[ "$(php -v | grep 'PHP 7')" ]]; then phpenv config-rm xdebug.ini; fi
|
||||||
- composer selfupdate
|
|
||||||
- rm composer.lock
|
- rm composer.lock
|
||||||
- composer update --no-scripts
|
- composer update --no-scripts
|
||||||
|
- cp .env.testing .env
|
||||||
- php artisan clear-compiled
|
- php artisan clear-compiled
|
||||||
- php artisan optimize
|
- php artisan optimize
|
||||||
- php artisan env
|
- php artisan env
|
||||||
- ./test.sh -r
|
- cp .env.testing .env
|
||||||
- php artisan env
|
- mv storage/database/databasecopy.sqlite storage/database/database.sqlite
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- phpunit
|
- phpunit
|
101
CHANGELOG.md
101
CHANGELOG.md
@@ -2,6 +2,99 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
|
||||||
|
## [4.3.3] - 2017-02-02
|
||||||
|
### Fixed
|
||||||
|
- Fixed bug #550, reported by @worldworm!
|
||||||
|
- Fixed bug #551, reported by @t-me!
|
||||||
|
|
||||||
|
## [4.3.3] - 2017-01-30
|
||||||
|
|
||||||
|
_The 100th release of Firefly!_
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Add locales to Docker (#534) by @elohmeier.
|
||||||
|
- Optional database encryption. On by default.
|
||||||
|
- Datepicker for Firefox and other browsers.
|
||||||
|
- New instruction block for updating and installing.
|
||||||
|
- Ability to clone transactions.
|
||||||
|
- Use multi-select Bootstrap thing instead of massive lists of checkboxes.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Lots of old Javascript
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Missing sort broke various charts
|
||||||
|
- Bug in reports that made amounts behave weird
|
||||||
|
- Various bug fixes
|
||||||
|
|
||||||
|
### Security
|
||||||
|
- Tested FF against the naughty string list.
|
||||||
|
|
||||||
|
## [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
|
## [4.2.2] - 2016-12-18
|
||||||
### Added
|
### Added
|
||||||
- New budget report (still a bit of a beta)
|
- New budget report (still a bit of a beta)
|
||||||
@@ -257,7 +350,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Bug in the mass edit routines.
|
- 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
|
- Sneaky bug in the import routine, fixed by @Bonno
|
||||||
|
|
||||||
## [3.10.1] - 2016-08-25
|
## [3.10.1] - 2016-08-25
|
||||||
@@ -278,7 +371,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Fixed a bug where a migration would check an empty table name.
|
- Fixed a bug where a migration would check an empty table name.
|
||||||
- Fixed various bugs in the import routine.
|
- Fixed various bugs in the import routine.
|
||||||
- Fixed various bugs in the piggy banks pages.
|
- 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
|
## [3.10] - 2015-05-25
|
||||||
### Added
|
### Added
|
||||||
@@ -307,11 +400,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Bulk update problems, #280, thanks @stickgrinder
|
- Bulk update problems, #280, thanks @stickgrinder
|
||||||
- Fixed various problems with amount reporting of split transactions.
|
- Fixed various problems with amount reporting of split transactions.
|
||||||
|
|
||||||
[3.9.1]
|
## [3.9.1]
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fixed a bug where removing money from a piggy bank would not work. See issue #265 and #269
|
- 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
|
### Added
|
||||||
- @zjean has added code that allows you to force "https://"-URL's.
|
- @zjean has added code that allows you to force "https://"-URL's.
|
||||||
- @tonicospinelli has added Portuguese (Brazil) translations.
|
- @tonicospinelli has added Portuguese (Brazil) translations.
|
||||||
|
@@ -11,12 +11,16 @@ RUN apt-get update -y && \
|
|||||||
libtidy-dev \
|
libtidy-dev \
|
||||||
libxml2-dev \
|
libxml2-dev \
|
||||||
libsqlite3-dev \
|
libsqlite3-dev \
|
||||||
libbz2-dev && \
|
libbz2-dev \
|
||||||
|
locales && \
|
||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN docker-php-ext-install -j$(nproc) curl gd intl json mcrypt readline tidy zip bcmath xml mbstring pdo_sqlite pdo_mysql bz2
|
RUN docker-php-ext-install -j$(nproc) curl gd intl json mcrypt readline tidy zip bcmath xml mbstring pdo_sqlite pdo_mysql bz2
|
||||||
|
|
||||||
|
# Generate locales supported by firefly
|
||||||
|
RUN echo "en_US.UTF-8 UTF-8\nde_DE.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8" > /etc/locale.gen && locale-gen
|
||||||
|
|
||||||
# Enable apache mod rewrite..
|
# Enable apache mod rewrite..
|
||||||
RUN a2enmod rewrite
|
RUN a2enmod rewrite
|
||||||
|
|
||||||
|
14
README.md
14
README.md
@@ -1,17 +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/h2b37243) [](https://i.nder.be/hv70pbwc)
|
||||||
|
|
||||||
[](https://i.nder.be/ccn0u2mp) [](https://i.nder.be/gm8hbh7z)
|
[](https://i.nder.be/ccn0u2mp) [](https://i.nder.be/gm8hbh7z)
|
||||||
|
|
||||||
_(You can click on the images for a better view)_
|
|
||||||
|
|
||||||
"Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared household accounts and savings accounts! It's pretty fancy. You should use it to save and organise money.
|
"Firefly 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
|
## 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/).
|
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/).
|
||||||
@@ -31,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/).
|
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).
|
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()
|
public function handle()
|
||||||
{
|
{
|
||||||
// find the file
|
|
||||||
/** @var UserRepositoryInterface $userRepository */
|
/** @var UserRepositoryInterface $userRepository */
|
||||||
$userRepository = app(UserRepositoryInterface::class);
|
$userRepository = app(UserRepositoryInterface::class);
|
||||||
$file = $this->argument('file');
|
$file = $this->argument('file');
|
||||||
@@ -67,7 +66,6 @@ class CreateImport extends Command
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to parse configuration data:
|
|
||||||
$configurationData = json_decode(file_get_contents($configuration));
|
$configurationData = json_decode(file_get_contents($configuration));
|
||||||
if (is_null($configurationData)) {
|
if (is_null($configurationData)) {
|
||||||
$this->error(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd));
|
$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 */
|
/** @var ImportJobRepositoryInterface $jobRepository */
|
||||||
$jobRepository = app(ImportJobRepositoryInterface::class, [$user]);
|
$jobRepository = app(ImportJobRepositoryInterface::class, [$user]);
|
||||||
|
$job = $jobRepository->create($type);
|
||||||
$job = $jobRepository->create($type);
|
|
||||||
$this->line(sprintf('Created job "%s"...', $job->key));
|
$this->line(sprintf('Created job "%s"...', $job->key));
|
||||||
|
|
||||||
// put the file in the proper place:
|
|
||||||
Artisan::call('firefly:encrypt', ['file' => $file, 'key' => $job->key]);
|
Artisan::call('firefly:encrypt', ['file' => $file, 'key' => $job->key]);
|
||||||
$this->line('Stored import data...');
|
$this->line('Stored import data...');
|
||||||
|
|
||||||
// store the configuration in the job:
|
|
||||||
$job->configuration = $configurationData;
|
$job->configuration = $configurationData;
|
||||||
$job->status = 'settings_complete';
|
$job->status = 'settings_complete';
|
||||||
$job->save();
|
$job->save();
|
||||||
$this->line('Stored configuration...');
|
$this->line('Stored configuration...');
|
||||||
|
|
||||||
// if user wants to run it, do!
|
|
||||||
if ($this->option('start') === true) {
|
if ($this->option('start') === true) {
|
||||||
$this->line('The import will start in a moment. This process is not visible...');
|
$this->line('The import will start in a moment. This process is not visible...');
|
||||||
Log::debug('Go for import!');
|
Log::debug('Go for import!');
|
||||||
@@ -109,10 +103,10 @@ class CreateImport extends Command
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five exactly.
|
||||||
*/
|
*/
|
||||||
private function validArguments(): bool
|
private function validArguments(): bool
|
||||||
{
|
{
|
||||||
// find the file
|
|
||||||
/** @var UserRepositoryInterface $userRepository */
|
/** @var UserRepositoryInterface $userRepository */
|
||||||
$userRepository = app(UserRepositoryInterface::class);
|
$userRepository = app(UserRepositoryInterface::class);
|
||||||
$file = $this->argument('file');
|
$file = $this->argument('file');
|
||||||
|
@@ -35,7 +35,7 @@ class EncryptFile extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'firefly:encrypt {file} {key}';
|
protected $signature = 'firefly:encrypt-file {file} {key}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new command instance.
|
* Create a new command instance.
|
||||||
|
@@ -18,6 +18,7 @@ use FireflyIII\Import\Logging\CommandHandler;
|
|||||||
use FireflyIII\Models\ImportJob;
|
use FireflyIII\Models\ImportJob;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Log;
|
use Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,32 +70,15 @@ class Import extends Command
|
|||||||
$monolog = Log::getMonolog();
|
$monolog = Log::getMonolog();
|
||||||
$handler = new CommandHandler($this);
|
$handler = new CommandHandler($this);
|
||||||
$monolog->pushHandler($handler);
|
$monolog->pushHandler($handler);
|
||||||
|
$importProcedure = new ImportProcedure;
|
||||||
|
$result = $importProcedure->runImport($job);
|
||||||
|
|
||||||
$result = ImportProcedure::runImport($job);
|
// display result to user:
|
||||||
|
$this->presentResults($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));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->line('The import has completed.');
|
$this->line('The import has completed.');
|
||||||
|
|
||||||
// get any errors from the importer:
|
// get any errors from the importer:
|
||||||
$extendedStatus = $job->extended_status;
|
$this->presentErrors($job);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -120,4 +104,36 @@ class Import extends Command
|
|||||||
|
|
||||||
return true;
|
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 */
|
/** @var Attachment $attachment */
|
||||||
foreach ($attachments as $attachment) {
|
foreach ($attachments as $attachment) {
|
||||||
$fileName = $attachment->fileName();
|
$fileName = $attachment->fileName();
|
||||||
|
|
||||||
// try to grab file content:
|
|
||||||
try {
|
try {
|
||||||
$content = $disk->get($fileName);
|
$content = $disk->get($fileName);
|
||||||
} catch (FileNotFoundException $e) {
|
} catch (FileNotFoundException $e) {
|
||||||
$this->error(sprintf('Could not find data for attachment #%d', $attachment->id));
|
$this->error(sprintf('Could not find data for attachment #%d', $attachment->id));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// try to decrypt content.
|
|
||||||
try {
|
try {
|
||||||
$decrypted = Crypt::decrypt($content);
|
$decrypted = Crypt::decrypt($content);
|
||||||
} catch (DecryptException $e) {
|
} catch (DecryptException $e) {
|
||||||
$this->error(sprintf('Could not decrypt data of attachment #%d', $attachment->id));
|
$this->error(sprintf('Could not decrypt data of attachment #%d', $attachment->id));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// make temp file:
|
|
||||||
$tmpfname = tempnam(sys_get_temp_dir(), 'FireflyIII');
|
$tmpfname = tempnam(sys_get_temp_dir(), 'FireflyIII');
|
||||||
|
|
||||||
// store content in temp file:
|
|
||||||
file_put_contents($tmpfname, $decrypted);
|
file_put_contents($tmpfname, $decrypted);
|
||||||
|
$md5 = md5_file($tmpfname);
|
||||||
// get md5 and mime
|
$mime = mime_content_type($tmpfname);
|
||||||
$md5 = md5_file($tmpfname);
|
|
||||||
$mime = mime_content_type($tmpfname);
|
|
||||||
|
|
||||||
// update attachment:
|
|
||||||
$attachment->md5 = $md5;
|
$attachment->md5 = $md5;
|
||||||
$attachment->mime = $mime;
|
$attachment->mime = $mime;
|
||||||
$attachment->save();
|
$attachment->save();
|
||||||
|
|
||||||
|
|
||||||
$this->line(sprintf('Fixed attachment #%d', $attachment->id));
|
$this->line(sprintf('Fixed attachment #%d', $attachment->id));
|
||||||
|
|
||||||
// find file:
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,8 @@ namespace FireflyIII\Console\Commands;
|
|||||||
|
|
||||||
|
|
||||||
use DB;
|
use DB;
|
||||||
|
use FireflyIII\Models\BudgetLimit;
|
||||||
|
use FireflyIII\Models\LimitRepetition;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
@@ -57,8 +59,29 @@ class UpgradeDatabase extends Command
|
|||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->setTransactionIdentifier();
|
$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,42 +108,52 @@ class UpgradeDatabase extends Command
|
|||||||
$journalIds = array_unique($result->pluck('id')->toArray());
|
$journalIds = array_unique($result->pluck('id')->toArray());
|
||||||
|
|
||||||
foreach ($journalIds as $journalId) {
|
foreach ($journalIds as $journalId) {
|
||||||
// grab all positive transactiosn from this journal that are not deleted.
|
$this->updateJournal(intval($journalId));
|
||||||
// for each one, grab the negative opposing one which has 0 as an identifier and give it the same identifier.
|
}
|
||||||
$identifier = 0;
|
}
|
||||||
$processed = [];
|
|
||||||
$transactions = Transaction::where('transaction_journal_id', $journalId)->where('amount', '>', 0)->get();
|
|
||||||
/** @var Transaction $transaction */
|
|
||||||
foreach ($transactions as $transaction) {
|
|
||||||
// find opposing:
|
|
||||||
$amount = bcmul(strval($transaction->amount), '-1');
|
|
||||||
|
|
||||||
try {
|
/**
|
||||||
/** @var Transaction $opposing */
|
* grab all positive transactiosn from this journal that are not deleted. for each one, grab the negative opposing one
|
||||||
$opposing = Transaction::where('transaction_journal_id', $journalId)
|
* which has 0 as an identifier and give it the same identifier.
|
||||||
->where('amount', $amount)->where('identifier', '=', 0)
|
*
|
||||||
->whereNotIn('id', $processed)
|
* @param int $journalId
|
||||||
->first();
|
*/
|
||||||
} catch (QueryException $e) {
|
private function updateJournal(int $journalId)
|
||||||
Log::error($e->getMessage());
|
{
|
||||||
$this->error('Firefly III could not find the "identifier" field in the "transactions" table.');
|
$identifier = 0;
|
||||||
$this->error(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version')));
|
$processed = [];
|
||||||
$this->error('Please run "php artisan migrate" to add this field to the table.');
|
$transactions = Transaction::where('transaction_journal_id', $journalId)->where('amount', '>', 0)->get();
|
||||||
$this->info('Then, run "php artisan firefly:upgrade-database" to try again.');
|
/** @var Transaction $transaction */
|
||||||
break 2;
|
foreach ($transactions as $transaction) {
|
||||||
}
|
// find opposing:
|
||||||
if (!is_null($opposing)) {
|
$amount = bcmul(strval($transaction->amount), '-1');
|
||||||
// give both a new identifier:
|
|
||||||
$transaction->identifier = $identifier;
|
try {
|
||||||
$transaction->save();
|
/** @var Transaction $opposing */
|
||||||
$opposing->identifier = $identifier;
|
$opposing = Transaction::where('transaction_journal_id', $journalId)
|
||||||
$opposing->save();
|
->where('amount', $amount)->where('identifier', '=', 0)
|
||||||
$processed[] = $transaction->id;
|
->whereNotIn('id', $processed)
|
||||||
$processed[] = $opposing->id;
|
->first();
|
||||||
$this->line(sprintf('Database upgrade for journal #%d, transactions #%d and #%d', $journalId, $transaction->id, $opposing->id));
|
} catch (QueryException $e) {
|
||||||
}
|
Log::error($e->getMessage());
|
||||||
$identifier++;
|
$this->error('Firefly III could not find the "identifier" field in the "transactions" table.');
|
||||||
|
$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.');
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
if (!is_null($opposing)) {
|
||||||
|
// give both a new identifier:
|
||||||
|
$transaction->identifier = $identifier;
|
||||||
|
$transaction->save();
|
||||||
|
$opposing->identifier = $identifier;
|
||||||
|
$opposing->save();
|
||||||
|
$processed[] = $transaction->id;
|
||||||
|
$processed[] = $opposing->id;
|
||||||
|
$this->line(sprintf('Database upgrade for journal #%d, transactions #%d and #%d', $journalId, $transaction->id, $opposing->id));
|
||||||
|
}
|
||||||
|
$identifier++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -33,7 +33,7 @@ class UpgradeFireflyInstructions extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'firefly:upgrade-instructions';
|
protected $signature = 'firefly:instructions {task}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new command instance.
|
* Create a new command instance.
|
||||||
@@ -49,11 +49,60 @@ class UpgradeFireflyInstructions extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
//
|
|
||||||
|
if ($this->argument('task') == 'update') {
|
||||||
|
$this->updateInstructions();
|
||||||
|
}
|
||||||
|
if ($this->argument('task') == 'install') {
|
||||||
|
$this->installInstructions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a nice box
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
*/
|
||||||
|
private function boxed(string $text)
|
||||||
|
{
|
||||||
|
$parts = explode("\n", wordwrap($text));
|
||||||
|
foreach ($parts as $string) {
|
||||||
|
$this->line('| ' . sprintf('%-77s', $string) . '|');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a nice info box
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
*/
|
||||||
|
private function boxedInfo(string $text)
|
||||||
|
{
|
||||||
|
$parts = explode("\n", wordwrap($text));
|
||||||
|
foreach ($parts as $string) {
|
||||||
|
$this->info('| ' . sprintf('%-77s', $string) . '|');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a line
|
||||||
|
*/
|
||||||
|
private function showLine()
|
||||||
|
{
|
||||||
|
$line = '+';
|
||||||
|
for ($i = 0; $i < 78; $i++) {
|
||||||
|
$line .= '-';
|
||||||
|
}
|
||||||
|
$line .= '+';
|
||||||
|
$this->line($line);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function installInstructions() {
|
||||||
/** @var string $version */
|
/** @var string $version */
|
||||||
$version = config('firefly.version');
|
$version = config('firefly.version');
|
||||||
$config = config('upgrade.text');
|
$config = config('upgrade.text.install');
|
||||||
$text = null;
|
$text = '';
|
||||||
foreach (array_keys($config) as $compare) {
|
foreach (array_keys($config) as $compare) {
|
||||||
// if string starts with:
|
// if string starts with:
|
||||||
$len = strlen($compare);
|
$len = strlen($compare);
|
||||||
@@ -62,21 +111,53 @@ class UpgradeFireflyInstructions extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
$this->showLine();
|
||||||
|
$this->boxed('');
|
||||||
if (is_null($text)) {
|
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->boxed(sprintf('Thank you for installin Firefly III, v%s!', $version));
|
||||||
$this->line('Firefly III should be ready for use.');
|
$this->boxedInfo('There are no extra installation instructions.');
|
||||||
} else {
|
$this->boxed('Firefly III should be ready for use.');
|
||||||
$this->line('+------------------------------------------------------------------------------+');
|
$this->boxed('');
|
||||||
$this->line('');
|
$this->showLine();
|
||||||
$this->line(sprintf('Thank you for installing Firefly III, v%s', $version));
|
return;
|
||||||
$this->info(wordwrap($text));
|
|
||||||
$this->line('');
|
|
||||||
$this->line('+------------------------------------------------------------------------------+');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->boxed(sprintf('Thank you for installing Firefly III, v%s!', $version));
|
||||||
|
$this->boxedInfo($text);
|
||||||
|
$this->boxed('');
|
||||||
|
$this->showLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function updateInstructions()
|
||||||
|
{
|
||||||
|
/** @var string $version */
|
||||||
|
$version = config('firefly.version');
|
||||||
|
$config = config('upgrade.text.upgrade');
|
||||||
|
$text = '';
|
||||||
|
foreach (array_keys($config) as $compare) {
|
||||||
|
// if string starts with:
|
||||||
|
$len = strlen($compare);
|
||||||
|
if (substr($version, 0, $len) === $compare) {
|
||||||
|
$text = $config[$compare];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
$this->showLine();
|
||||||
|
$this->boxed('');
|
||||||
|
if (is_null($text)) {
|
||||||
|
|
||||||
|
$this->boxed(sprintf('Thank you for updating to Firefly III, v%s', $version));
|
||||||
|
$this->boxedInfo('There are no extra upgrade instructions.');
|
||||||
|
$this->boxed('Firefly III should be ready for use.');
|
||||||
|
$this->boxed('');
|
||||||
|
$this->showLine();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->boxed(sprintf('Thank you for updating to Firefly III, v%s!', $version));
|
||||||
|
$this->boxedInfo($text);
|
||||||
|
$this->boxed('');
|
||||||
|
$this->showLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
66
app/Console/Commands/UseEncryption.php
Normal file
66
app/Console/Commands/UseEncryption.php
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace FireflyIII\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class UseEncryption extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'This command will make sure that entries in the database will be encrypted (or not) according to the settings in .env';
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'firefly:use-encryption';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new command instance.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
$this->handleObjects('Account', 'name', 'encrypted');
|
||||||
|
$this->handleObjects('Bill', 'name', 'name_encrypted');
|
||||||
|
$this->handleObjects('Bill', 'match', 'match_encrypted');
|
||||||
|
$this->handleObjects('Budget', 'name', 'encrypted');
|
||||||
|
$this->handleObjects('Category', 'name', 'encrypted');
|
||||||
|
$this->handleObjects('PiggyBank', 'name', 'encrypted');
|
||||||
|
$this->handleObjects('TransactionJournal', 'description', 'encrypted');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $class
|
||||||
|
* @param string $field
|
||||||
|
* @param string $indicator
|
||||||
|
*/
|
||||||
|
public function handleObjects(string $class, string $field, string $indicator)
|
||||||
|
{
|
||||||
|
$fqn = sprintf('FireflyIII\Models\%s', $class);
|
||||||
|
$encrypt = config('firefly.encryption') ? 0 : 1;
|
||||||
|
$set = $fqn::where($indicator, $encrypt)->get();
|
||||||
|
|
||||||
|
foreach ($set as $entry) {
|
||||||
|
$newName = $entry->$field;
|
||||||
|
$entry->$field = $newName;
|
||||||
|
$entry->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->line(sprintf('Updated %d %s.', $set->count(), strtolower(Str::plural($class))));
|
||||||
|
}
|
||||||
|
}
|
@@ -124,16 +124,15 @@ class VerifyDatabase extends Command
|
|||||||
{
|
{
|
||||||
$set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id')
|
$set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id')
|
||||||
->leftJoin('users', 'budgets.user_id', '=', 'users.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')
|
->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) {
|
foreach ($set as $entry) {
|
||||||
|
|
||||||
$line = sprintf(
|
$line = sprintf(
|
||||||
'Notice: User #%d (%s) has budget #%d ("%s") which has no budget limits.',
|
'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);
|
$this->line($line);
|
||||||
}
|
}
|
||||||
@@ -174,10 +173,8 @@ class VerifyDatabase extends Command
|
|||||||
$configuration = [
|
$configuration = [
|
||||||
// a withdrawal can not have revenue account:
|
// a withdrawal can not have revenue account:
|
||||||
TransactionType::WITHDRAWAL => [AccountType::REVENUE],
|
TransactionType::WITHDRAWAL => [AccountType::REVENUE],
|
||||||
|
|
||||||
// deposit cannot have an expense account:
|
// deposit cannot have an expense account:
|
||||||
TransactionType::DEPOSIT => [AccountType::EXPENSE],
|
TransactionType::DEPOSIT => [AccountType::EXPENSE],
|
||||||
|
|
||||||
// transfer cannot have either:
|
// transfer cannot have either:
|
||||||
TransactionType::TRANSFER => [AccountType::EXPENSE, AccountType::REVENUE],
|
TransactionType::TRANSFER => [AccountType::EXPENSE, AccountType::REVENUE],
|
||||||
];
|
];
|
||||||
|
16
app/Console/Kernel.php
Executable file → Normal file
16
app/Console/Kernel.php
Executable file → Normal file
@@ -16,12 +16,11 @@ namespace FireflyIII\Console;
|
|||||||
use FireflyIII\Console\Commands\CreateImport;
|
use FireflyIII\Console\Commands\CreateImport;
|
||||||
use FireflyIII\Console\Commands\EncryptFile;
|
use FireflyIII\Console\Commands\EncryptFile;
|
||||||
use FireflyIII\Console\Commands\Import;
|
use FireflyIII\Console\Commands\Import;
|
||||||
use FireflyIII\Console\Commands\MoveRepository;
|
|
||||||
use FireflyIII\Console\Commands\ScanAttachments;
|
use FireflyIII\Console\Commands\ScanAttachments;
|
||||||
use FireflyIII\Console\Commands\UpgradeDatabase;
|
use FireflyIII\Console\Commands\UpgradeDatabase;
|
||||||
use FireflyIII\Console\Commands\UpgradeFireflyInstructions;
|
use FireflyIII\Console\Commands\UpgradeFireflyInstructions;
|
||||||
|
use FireflyIII\Console\Commands\UseEncryption;
|
||||||
use FireflyIII\Console\Commands\VerifyDatabase;
|
use FireflyIII\Console\Commands\VerifyDatabase;
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
|
||||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,7 +63,7 @@ class Kernel extends ConsoleKernel
|
|||||||
EncryptFile::class,
|
EncryptFile::class,
|
||||||
ScanAttachments::class,
|
ScanAttachments::class,
|
||||||
UpgradeDatabase::class,
|
UpgradeDatabase::class,
|
||||||
MoveRepository::class,
|
UseEncryption::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,15 +75,4 @@ class Kernel extends ConsoleKernel
|
|||||||
{
|
{
|
||||||
require base_path('routes/console.php');
|
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,41 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* BlockedBadLogin.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 Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class LockedOutUser
|
|
||||||
*
|
|
||||||
* @package FireflyIII\Events
|
|
||||||
*/
|
|
||||||
class BlockedBadLogin extends Event
|
|
||||||
{
|
|
||||||
use SerializesModels;
|
|
||||||
|
|
||||||
public $email;
|
|
||||||
public $ipAddress;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new event instance. This event is triggered when a user gets themselves locked out.
|
|
||||||
*
|
|
||||||
* @param string $email
|
|
||||||
* @param string $ipAddress
|
|
||||||
*/
|
|
||||||
public function __construct(string $email, string $ipAddress)
|
|
||||||
{
|
|
||||||
$this->email = $email;
|
|
||||||
$this->ipAddress = $ipAddress;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,42 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* BlockedUseOfDomain.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 Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class BlockedUseOfDomain
|
|
||||||
*
|
|
||||||
* @package FireflyIII\Events
|
|
||||||
*/
|
|
||||||
class BlockedUseOfDomain extends Event
|
|
||||||
{
|
|
||||||
use SerializesModels;
|
|
||||||
|
|
||||||
public $email;
|
|
||||||
public $ipAddress;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new event instance. This event is triggered when a user tries to register with a banned domain (on blocked domain list).
|
|
||||||
*
|
|
||||||
* @param string $email
|
|
||||||
* @param string $ipAddress
|
|
||||||
*/
|
|
||||||
public function __construct(string $email, string $ipAddress)
|
|
||||||
{
|
|
||||||
$this->email = $email;
|
|
||||||
$this->ipAddress = $ipAddress;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,42 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* BlockedUseOfEmail.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 Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class BlockedUseOfEmail
|
|
||||||
*
|
|
||||||
* @package FireflyIII\Events
|
|
||||||
*/
|
|
||||||
class BlockedUseOfEmail extends Event
|
|
||||||
{
|
|
||||||
use SerializesModels;
|
|
||||||
|
|
||||||
public $email;
|
|
||||||
public $ipAddress;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new event instance. This event is triggered when a user tries to register with a banned email address (already used before).
|
|
||||||
*
|
|
||||||
* @param string $email
|
|
||||||
* @param string $ipAddress
|
|
||||||
*/
|
|
||||||
public function __construct(string $email, string $ipAddress)
|
|
||||||
{
|
|
||||||
$this->email = $email;
|
|
||||||
$this->ipAddress = $ipAddress;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,42 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* BlockedUserLogin.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 BlockedUserLogin
|
|
||||||
*
|
|
||||||
* @package FireflyIII\Events
|
|
||||||
*/
|
|
||||||
class BlockedUserLogin extends Event
|
|
||||||
{
|
|
||||||
use SerializesModels;
|
|
||||||
|
|
||||||
public $ipAddress;
|
|
||||||
public $user;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new event instance. This event is triggered when a blocked user logs in.
|
|
||||||
*
|
|
||||||
* @param User $user
|
|
||||||
* @param string $ipAddress
|
|
||||||
*/
|
|
||||||
public function __construct(User $user, string $ipAddress)
|
|
||||||
{
|
|
||||||
$this->user = $user;
|
|
||||||
$this->ipAddress = $ipAddress;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -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,38 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* DeletedUser.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 Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class DeletedUser
|
|
||||||
*
|
|
||||||
* @package FireflyIII\Events
|
|
||||||
*/
|
|
||||||
class DeletedUser extends Event
|
|
||||||
{
|
|
||||||
use SerializesModels;
|
|
||||||
|
|
||||||
public $email;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new event instance. This event is triggered when a user deletes themselves.
|
|
||||||
*
|
|
||||||
* @param string $email
|
|
||||||
*/
|
|
||||||
public function __construct(string $email)
|
|
||||||
{
|
|
||||||
$this->email = $email;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,41 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* LockedOutUser.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 Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class LockedOutUser
|
|
||||||
*
|
|
||||||
* @package FireflyIII\Events
|
|
||||||
*/
|
|
||||||
class LockedOutUser extends Event
|
|
||||||
{
|
|
||||||
use SerializesModels;
|
|
||||||
|
|
||||||
public $email;
|
|
||||||
public $ipAddress;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new event instance. This event is triggered when a user gets themselves locked out.
|
|
||||||
*
|
|
||||||
* @param string $email
|
|
||||||
* @param string $ipAddress
|
|
||||||
*/
|
|
||||||
public function __construct(string $email, string $ipAddress)
|
|
||||||
{
|
|
||||||
$this->email = $email;
|
|
||||||
$this->ipAddress = $ipAddress;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -43,4 +43,4 @@ class RequestedNewPassword extends Event
|
|||||||
$this->ipAddress = $ipAddress;
|
$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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
10
app/Exceptions/Handler.php
Executable file → Normal file
10
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\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||||
use Illuminate\Session\TokenMismatchException;
|
use Illuminate\Session\TokenMismatchException;
|
||||||
use Illuminate\Validation\ValidationException as ValException;
|
use Illuminate\Validation\ValidationException as ValException;
|
||||||
|
use Request;
|
||||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
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.
|
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
|
||||||
*
|
*
|
||||||
* @param Exception $exception
|
* @param Exception $exception
|
||||||
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
@@ -98,8 +100,8 @@ class Handler extends ExceptionHandler
|
|||||||
];
|
];
|
||||||
|
|
||||||
// create job that will mail.
|
// create job that will mail.
|
||||||
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
|
$ipAddress = Request::ip() ?? '0.0.0.0';
|
||||||
$job = new MailError($userData, env('SITE_OWNER', ''), $ip, $data);
|
$job = new MailError($userData, env('SITE_OWNER', ''), $ipAddress, $data);
|
||||||
dispatch($job);
|
dispatch($job);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,9 +111,9 @@ class Handler extends ExceptionHandler
|
|||||||
/**
|
/**
|
||||||
* Convert an authentication exception into an unauthenticated response.
|
* Convert an authentication exception into an unauthenticated response.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param $request
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
|
||||||
*/
|
*/
|
||||||
protected function unauthenticated($request)
|
protected function unauthenticated($request)
|
||||||
{
|
{
|
||||||
|
@@ -35,6 +35,8 @@ interface CollectorInterface
|
|||||||
/**
|
/**
|
||||||
* @param Collection $entries
|
* @param Collection $entries
|
||||||
*
|
*
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public function setEntries(Collection $entries);
|
public function setEntries(Collection $entries);
|
||||||
|
|
||||||
|
@@ -287,7 +287,7 @@ class JournalExportCollector extends BasicCollector implements CollectorInterfac
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
||||||
*/
|
*/
|
||||||
private function getWorkSet()
|
private function getWorkSet()
|
||||||
{
|
{
|
||||||
|
@@ -29,6 +29,7 @@ use Crypt;
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Class Entry
|
* Class Entry
|
||||||
|
* @SuppressWarnings(PHPMD.LongVariable)
|
||||||
*
|
*
|
||||||
* @package FireflyIII\Export\Entry
|
* @package FireflyIII\Export\Entry
|
||||||
*/
|
*/
|
||||||
@@ -50,7 +51,6 @@ final class Entry
|
|||||||
public $destination_account_id;
|
public $destination_account_id;
|
||||||
public $destination_account_name;
|
public $destination_account_name;
|
||||||
|
|
||||||
|
|
||||||
public $budget_id;
|
public $budget_id;
|
||||||
public $budget_name;
|
public $budget_name;
|
||||||
public $category_id;
|
public $category_id;
|
||||||
@@ -71,33 +71,21 @@ final class Entry
|
|||||||
*/
|
*/
|
||||||
public static function fromObject($object): Entry
|
public static function fromObject($object): Entry
|
||||||
{
|
{
|
||||||
$entry = new self;
|
$entry = new self;
|
||||||
|
$entry->journal_id = $object->transaction_journal_id;
|
||||||
// journal information:
|
$entry->description = self::decrypt(intval($object->journal_encrypted), $object->journal_description);
|
||||||
$entry->journal_id = $object->transaction_journal_id;
|
$entry->amount = $object->amount;
|
||||||
$entry->description = $object->journal_encrypted === 1 ? Crypt::decrypt($object->journal_description) : $object->journal_description;
|
$entry->date = $object->date;
|
||||||
$entry->amount = round($object->amount, 2); // always positive
|
$entry->transaction_type = $object->transaction_type;
|
||||||
$entry->date = $object->date;
|
$entry->currency_code = $object->transaction_currency_code;
|
||||||
$entry->transaction_type = $object->transaction_type;
|
$entry->source_account_id = $object->account_id;
|
||||||
$entry->currency_code = $object->transaction_currency_code;
|
$entry->source_account_name = self::decrypt(intval($object->account_name_encrypted), $object->account_name);
|
||||||
|
|
||||||
// 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->destination_account_id = $object->opposing_account_id;
|
$entry->destination_account_id = $object->opposing_account_id;
|
||||||
$entry->destination_account_name = $object->opposing_account_encrypted === 1 ? Crypt::decrypt($object->opposing_account_name)
|
$entry->destination_account_name = self::decrypt(intval($object->opposing_account_encrypted), $object->opposing_account_name);
|
||||||
: $object->opposing_account_name;
|
$entry->category_id = $object->category_id ?? '';
|
||||||
|
$entry->category_name = $object->category_name ?? '';
|
||||||
|
$entry->budget_id = $object->budget_id ?? '';
|
||||||
// category and budget
|
$entry->budget_name = $object->budget_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:
|
// update description when transaction description is different:
|
||||||
if (!is_null($object->description) && $object->description != $entry->description) {
|
if (!is_null($object->description) && $object->description != $entry->description) {
|
||||||
@@ -107,4 +95,19 @@ final class Entry
|
|||||||
return $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
|
* @param Collection $entries
|
||||||
*
|
*
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public function setEntries(Collection $entries);
|
public function setEntries(Collection $entries);
|
||||||
|
|
||||||
|
@@ -155,7 +155,7 @@ class Processor implements ProcessorInterface
|
|||||||
$zip->close();
|
$zip->close();
|
||||||
|
|
||||||
// delete the files:
|
// delete the files:
|
||||||
$this->deleteFiles($disk);
|
$this->deleteFiles();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -183,10 +183,11 @@ class Processor implements ProcessorInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param FilesystemAdapter $disk
|
*
|
||||||
*/
|
*/
|
||||||
private function deleteFiles(FilesystemAdapter $disk)
|
private function deleteFiles()
|
||||||
{
|
{
|
||||||
|
$disk = Storage::disk('export');
|
||||||
foreach ($this->getFiles() as $file) {
|
foreach ($this->getFiles() as $file) {
|
||||||
$disk->delete($file);
|
$disk->delete($file);
|
||||||
}
|
}
|
||||||
|
@@ -27,6 +27,7 @@ interface ProcessorInterface
|
|||||||
* Processor constructor.
|
* Processor constructor.
|
||||||
*
|
*
|
||||||
* @param array $settings
|
* @param array $settings
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public function __construct(array $settings);
|
public function __construct(array $settings);
|
||||||
|
|
||||||
@@ -64,4 +65,4 @@ interface ProcessorInterface
|
|||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public function getFiles(): Collection;
|
public function getFiles(): Collection;
|
||||||
}
|
}
|
||||||
|
@@ -31,6 +31,8 @@ class ChartJsGenerator implements GeneratorInterface
|
|||||||
* 0: [
|
* 0: [
|
||||||
* 'label' => 'label of set',
|
* 'label' => 'label of set',
|
||||||
* 'type' => bar or line, optional
|
* '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' =>
|
* 'entries' =>
|
||||||
* [
|
* [
|
||||||
* 'label-of-entry' => 'value'
|
* 'label-of-entry' => 'value'
|
||||||
@@ -39,12 +41,15 @@ class ChartJsGenerator implements GeneratorInterface
|
|||||||
* 1: [
|
* 1: [
|
||||||
* 'label' => 'label of another set',
|
* 'label' => 'label of another set',
|
||||||
* 'type' => bar or line, optional
|
* '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' =>
|
* 'entries' =>
|
||||||
* [
|
* [
|
||||||
* 'label-of-entry' => 'value'
|
* 'label-of-entry' => 'value'
|
||||||
* ]
|
* ]
|
||||||
* ]
|
* ]
|
||||||
*
|
*
|
||||||
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five.
|
||||||
*
|
*
|
||||||
* @param array $data
|
* @param array $data
|
||||||
*
|
*
|
||||||
@@ -54,7 +59,7 @@ class ChartJsGenerator implements GeneratorInterface
|
|||||||
{
|
{
|
||||||
reset($data);
|
reset($data);
|
||||||
$first = current($data);
|
$first = current($data);
|
||||||
$labels = array_keys($first['entries']);
|
$labels = is_array($first['entries']) ? array_keys($first['entries']) : [];
|
||||||
|
|
||||||
$chartData = [
|
$chartData = [
|
||||||
'count' => count($data),
|
'count' => count($data),
|
||||||
@@ -64,11 +69,19 @@ class ChartJsGenerator implements GeneratorInterface
|
|||||||
unset($first, $labels);
|
unset($first, $labels);
|
||||||
|
|
||||||
foreach ($data as $set) {
|
foreach ($data as $set) {
|
||||||
$chartData['datasets'][] = [
|
$currentSet = [
|
||||||
'label' => $set['label'],
|
'label' => $set['label'],
|
||||||
'type' => $set['type'] ?? 'line',
|
'type' => $set['type'] ?? 'line',
|
||||||
'data' => array_values($set['entries']),
|
'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;
|
return $chartData;
|
||||||
@@ -99,7 +112,7 @@ class ChartJsGenerator implements GeneratorInterface
|
|||||||
$value = bcmul($value, '-1');
|
$value = bcmul($value, '-1');
|
||||||
}
|
}
|
||||||
|
|
||||||
$chartData['datasets'][0]['data'][] = round($value, 2);
|
$chartData['datasets'][0]['data'][] = $value;
|
||||||
$chartData['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index);
|
$chartData['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index);
|
||||||
$chartData['labels'][] = $key;
|
$chartData['labels'][] = $key;
|
||||||
$index++;
|
$index++;
|
||||||
@@ -134,4 +147,4 @@ class ChartJsGenerator implements GeneratorInterface
|
|||||||
|
|
||||||
return $chartData;
|
return $chartData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -70,4 +70,4 @@ interface GeneratorInterface
|
|||||||
*/
|
*/
|
||||||
public function singleSet(string $setLabel, array $data): array;
|
public function singleSet(string $setLabel, array $data): array;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,7 @@ namespace FireflyIII\Generator\Report\Audit;
|
|||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
use FireflyIII\Models\Account;
|
use FireflyIII\Models\Account;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -49,33 +49,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
|||||||
/** @var Account $account */
|
/** @var Account $account */
|
||||||
foreach ($this->accounts as $account) {
|
foreach ($this->accounts as $account) {
|
||||||
// balance the day before:
|
// balance the day before:
|
||||||
$id = $account->id;
|
$id = $account->id;
|
||||||
$dayBeforeBalance = Steam::balance($account, $dayBefore);
|
$auditData[$id] = $this->getAuditReport($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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to'];
|
$defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to'];
|
||||||
@@ -153,4 +128,46 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
|||||||
|
|
||||||
return $this;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -24,4 +24,4 @@ class MultiYearReportGenerator extends MonthReportGenerator
|
|||||||
/**
|
/**
|
||||||
* Doesn't do anything different.
|
* Doesn't do anything different.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
@@ -25,4 +25,4 @@ class YearReportGenerator extends MonthReportGenerator
|
|||||||
/**
|
/**
|
||||||
* Doesn't do anything different.
|
* Doesn't do anything different.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,7 @@ namespace FireflyIII\Generator\Report\Budget;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
||||||
use FireflyIII\Generator\Report\Support;
|
use FireflyIII\Generator\Report\Support;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -184,7 +184,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
|||||||
return $this->expenses;
|
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)
|
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
|
||||||
->setTypes([TransactionType::WITHDRAWAL])
|
->setTypes([TransactionType::WITHDRAWAL])
|
||||||
->setBudgets($this->budgets)->withOpposingAccount()->disableFilter();
|
->setBudgets($this->budgets)->withOpposingAccount()->disableFilter();
|
||||||
@@ -244,4 +245,4 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
|||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,4 +24,4 @@ class MultiYearReportGenerator extends MonthReportGenerator
|
|||||||
/**
|
/**
|
||||||
* Doesn't do anything different.
|
* Doesn't do anything different.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
@@ -25,4 +25,4 @@ class YearReportGenerator extends MonthReportGenerator
|
|||||||
/**
|
/**
|
||||||
* Doesn't do anything different.
|
* Doesn't do anything different.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,7 @@ namespace FireflyIII\Generator\Report\Category;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
||||||
use FireflyIII\Generator\Report\Support;
|
use FireflyIII\Generator\Report\Support;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -194,7 +194,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
|||||||
return $this->expenses;
|
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)
|
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
|
||||||
->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
|
->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
|
||||||
->setCategories($this->categories)->withOpposingAccount()->disableFilter();
|
->setCategories($this->categories)->withOpposingAccount()->disableFilter();
|
||||||
@@ -216,7 +217,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
|||||||
return $this->income;
|
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)
|
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
|
||||||
->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
|
->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
|
||||||
->setCategories($this->categories)->withOpposingAccount();
|
->setCategories($this->categories)->withOpposingAccount();
|
||||||
@@ -229,6 +231,7 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
|
||||||
* @param array $spent
|
* @param array $spent
|
||||||
* @param array $earned
|
* @param array $earned
|
||||||
*
|
*
|
||||||
@@ -325,4 +328,4 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
|||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,4 +24,4 @@ class MultiYearReportGenerator extends MonthReportGenerator
|
|||||||
/**
|
/**
|
||||||
* Doesn't do anything different.
|
* Doesn't do anything different.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
@@ -25,4 +25,4 @@ class YearReportGenerator extends MonthReportGenerator
|
|||||||
/**
|
/**
|
||||||
* Doesn't do anything different.
|
* Doesn't do anything different.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
@@ -57,4 +57,4 @@ class ReportGeneratorFactory
|
|||||||
}
|
}
|
||||||
throw new FireflyException(sprintf('Cannot generate report. There is no "%s"-report for period "%s".', $type, $period));
|
throw new FireflyException(sprintf('Cannot generate report. There is no "%s"-report for period "%s".', $type, $period));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -64,4 +64,4 @@ interface ReportGeneratorInterface
|
|||||||
*/
|
*/
|
||||||
public function setStartDate(Carbon $date): ReportGeneratorInterface;
|
public function setStartDate(Carbon $date): ReportGeneratorInterface;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -106,4 +106,4 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -103,4 +103,4 @@ class MultiYearReportGenerator implements ReportGeneratorInterface
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -103,4 +103,4 @@ class YearReportGenerator implements ReportGeneratorInterface
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -34,27 +34,7 @@ class Support
|
|||||||
*/
|
*/
|
||||||
public static function filterExpenses(Collection $collection, array $accounts): Collection
|
public static function filterExpenses(Collection $collection, array $accounts): Collection
|
||||||
{
|
{
|
||||||
$result = $collection->filter(
|
return self::filterTransactions($collection, $accounts, 1);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,9 +44,21 @@ class Support
|
|||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public static function filterIncome(Collection $collection, array $accounts): 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(
|
$result = $collection->filter(
|
||||||
function (Transaction $transaction) use ($accounts) {
|
function (Transaction $transaction) use ($accounts, $modifier) {
|
||||||
$opposing = $transaction->opposing_account_id;
|
$opposing = $transaction->opposing_account_id;
|
||||||
// remove internal transfer
|
// remove internal transfer
|
||||||
if (in_array($opposing, $accounts)) {
|
if (in_array($opposing, $accounts)) {
|
||||||
@@ -75,7 +67,7 @@ class Support
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// remove positive amount
|
// 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));
|
Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount));
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -88,4 +80,4 @@ class Support
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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\Events\StoredTransactionJournal;
|
||||||
use FireflyIII\Models\PiggyBank;
|
use FireflyIII\Models\PiggyBank;
|
||||||
use FireflyIII\Models\PiggyBankEvent;
|
use FireflyIII\Models\PiggyBankEvent;
|
||||||
|
use FireflyIII\Models\PiggyBankRepetition;
|
||||||
use FireflyIII\Models\Rule;
|
use FireflyIII\Models\Rule;
|
||||||
use FireflyIII\Models\RuleGroup;
|
use FireflyIII\Models\RuleGroup;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
@@ -33,79 +34,40 @@ class StoredJournalEventHandler
|
|||||||
/**
|
/**
|
||||||
* This method connects a new transfer to a piggy bank.
|
* This method connects a new transfer to a piggy bank.
|
||||||
*
|
*
|
||||||
* @param StoredTransactionJournal $storedJournalEvent
|
* @param StoredTransactionJournal $event
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function connectToPiggyBank(StoredTransactionJournal $storedJournalEvent): bool
|
public function connectToPiggyBank(StoredTransactionJournal $event): bool
|
||||||
{
|
{
|
||||||
/** @var TransactionJournal $journal */
|
/** @var TransactionJournal $journal */
|
||||||
$journal = $storedJournalEvent->journal;
|
$journal = $event->journal;
|
||||||
$piggyBankId = $storedJournalEvent->piggyBankId;
|
$piggyBankId = $event->piggyBankId;
|
||||||
|
|
||||||
Log::debug(sprintf('Trying to connect journal %d to piggy bank %d.', $journal->id, $piggyBankId));
|
Log::debug(sprintf('Trying to connect journal %d to piggy bank %d.', $journal->id, $piggyBankId));
|
||||||
|
|
||||||
/** @var PiggyBank $piggyBank */
|
/*
|
||||||
$piggyBank = $journal->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
|
* Verify existence of piggy bank:
|
||||||
|
*/
|
||||||
if (is_null($piggyBank)) {
|
if (!$this->verifyExistence($event)) {
|
||||||
Log::error('No such piggy bank!');
|
Log::error(sprintf('No such piggy bank or no repetition on %s', $journal->date->format('Y-m-d')));
|
||||||
|
|
||||||
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$amount = TransactionJournal::amountPositive($journal);
|
/*
|
||||||
Log::debug(sprintf('Will add/remove %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
|
* Get relevant data:
|
||||||
// if piggy account matches source account, the amount is positive
|
*/
|
||||||
$sources = TransactionJournal::sourceAccountList($journal)->pluck('id')->toArray();
|
$piggyBank = $journal->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
|
||||||
if (in_array($piggyBank->account_id, $sources)) {
|
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
|
||||||
$amount = bcmul($amount, '-1');
|
$amount = $this->getExactAmount($journal, $piggyBank, $repetition);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$repetition->currentamount = bcadd($repetition->currentamount, $amount);
|
$repetition->currentamount = bcadd($repetition->currentamount, $amount);
|
||||||
$repetition->save();
|
$repetition->save();
|
||||||
|
|
||||||
/** @var PiggyBankEvent $storedJournalEvent */
|
/** @var PiggyBankEvent $event */
|
||||||
$storedJournalEvent = PiggyBankEvent::create(
|
$event = PiggyBankEvent::create(
|
||||||
['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]
|
['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;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -161,4 +123,81 @@ class StoredJournalEventHandler
|
|||||||
|
|
||||||
return true;
|
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,25 +13,12 @@ declare(strict_types = 1);
|
|||||||
|
|
||||||
namespace FireflyIII\Handlers\Events;
|
namespace FireflyIII\Handlers\Events;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use FireflyConfig;
|
|
||||||
use FireflyIII\Events\BlockedBadLogin;
|
|
||||||
use FireflyIII\Events\BlockedUseOfDomain;
|
|
||||||
use FireflyIII\Events\BlockedUseOfEmail;
|
|
||||||
use FireflyIII\Events\BlockedUserLogin;
|
|
||||||
use FireflyIII\Events\ConfirmedUser;
|
|
||||||
use FireflyIII\Events\DeletedUser;
|
|
||||||
use FireflyIII\Events\LockedOutUser;
|
|
||||||
use FireflyIII\Events\RegisteredUser;
|
use FireflyIII\Events\RegisteredUser;
|
||||||
use FireflyIII\Events\RequestedNewPassword;
|
use FireflyIII\Events\RequestedNewPassword;
|
||||||
use FireflyIII\Events\ResentConfirmation;
|
|
||||||
use FireflyIII\Models\Configuration;
|
|
||||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||||
use FireflyIII\User;
|
|
||||||
use Illuminate\Mail\Message;
|
use Illuminate\Mail\Message;
|
||||||
use Log;
|
use Log;
|
||||||
use Mail;
|
use Mail;
|
||||||
use Preferences;
|
|
||||||
use Session;
|
use Session;
|
||||||
use Swift_TransportException;
|
use Swift_TransportException;
|
||||||
|
|
||||||
@@ -42,7 +29,6 @@ use Swift_TransportException;
|
|||||||
*
|
*
|
||||||
* The method name reflects what is being done. This is in the present tense.
|
* The method name reflects what is being done. This is in the present tense.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* @package FireflyIII\Handlers\Events
|
* @package FireflyIII\Handlers\Events
|
||||||
*/
|
*/
|
||||||
class UserEventHandler
|
class UserEventHandler
|
||||||
@@ -82,237 +68,6 @@ class UserEventHandler
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param BlockedBadLogin $event
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function reportBadLogin(BlockedBadLogin $event)
|
|
||||||
{
|
|
||||||
$email = $event->email;
|
|
||||||
$owner = env('SITE_OWNER');
|
|
||||||
$ipAddress = $event->ipAddress;
|
|
||||||
/** @var Configuration $sendmail */
|
|
||||||
$sendmail = FireflyConfig::get('mail_for_bad_login', config('firefly.configuration.mail_for_bad_login'));
|
|
||||||
Log::debug(sprintf('Now in reportBadLogin for email address %s', $email));
|
|
||||||
Log::error(sprintf('User %s tried to login with bad credentials.', $email));
|
|
||||||
if (is_null($sendmail) || (!is_null($sendmail) && $sendmail->data === false)) {
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Mail::send(
|
|
||||||
['emails.blocked-bad-creds-html', 'emails.blocked-bad-creds-text'], ['email' => $email, 'ip' => $ipAddress],
|
|
||||||
function (Message $message) use ($owner) {
|
|
||||||
$message->to($owner, $owner)->subject('Blocked login attempt with bad credentials');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (Swift_TransportException $e) {
|
|
||||||
Log::error($e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param BlockedUserLogin $event
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function reportBlockedUser(BlockedUserLogin $event): bool
|
|
||||||
{
|
|
||||||
$user = $event->user;
|
|
||||||
$owner = env('SITE_OWNER');
|
|
||||||
$email = $user->email;
|
|
||||||
$ipAddress = $event->ipAddress;
|
|
||||||
/** @var Configuration $sendmail */
|
|
||||||
$sendmail = FireflyConfig::get('mail_for_blocked_login', config('firefly.configuration.mail_for_blocked_login'));
|
|
||||||
Log::debug(sprintf('Now in reportBlockedUser for email address %s', $email));
|
|
||||||
Log::error(sprintf('User #%d (%s) has their accout blocked (blocked_code is "%s") but tried to login.', $user->id, $email, $user->blocked_code));
|
|
||||||
if (is_null($sendmail) || (!is_null($sendmail) && $sendmail->data === false)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// send email message:
|
|
||||||
try {
|
|
||||||
Mail::send(
|
|
||||||
['emails.blocked-login-html', 'emails.blocked-login-text'],
|
|
||||||
[
|
|
||||||
'user_id' => $user->id,
|
|
||||||
'user_address' => $email,
|
|
||||||
'ip' => $ipAddress,
|
|
||||||
'code' => $user->blocked_code,
|
|
||||||
], function (Message $message) use ($owner, $user) {
|
|
||||||
$message->to($owner, $owner)->subject('Blocked login attempt of blocked user');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (Swift_TransportException $e) {
|
|
||||||
Log::error($e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param LockedOutUser $event
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function reportLockout(LockedOutUser $event): bool
|
|
||||||
{
|
|
||||||
$email = $event->email;
|
|
||||||
$owner = env('SITE_OWNER');
|
|
||||||
$ipAddress = $event->ipAddress;
|
|
||||||
/** @var Configuration $sendmail */
|
|
||||||
$sendmail = FireflyConfig::get('mail_for_lockout', config('firefly.configuration.mail_for_lockout'));
|
|
||||||
Log::debug(sprintf('Now in respondToLockout for email address %s', $email));
|
|
||||||
Log::error(sprintf('User %s was locked out after too many invalid login attempts.', $email));
|
|
||||||
if (is_null($sendmail) || (!is_null($sendmail) && $sendmail->data === false)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// send email message:
|
|
||||||
try {
|
|
||||||
Mail::send(
|
|
||||||
['emails.locked-out-html', 'emails.locked-out-text'], ['email' => $email, 'ip' => $ipAddress], function (Message $message) use ($owner) {
|
|
||||||
$message->to($owner, $owner)->subject('User was locked out');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (Swift_TransportException $e) {
|
|
||||||
Log::error($e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param BlockedUseOfDomain $event
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function reportUseBlockedDomain(BlockedUseOfDomain $event): bool
|
|
||||||
{
|
|
||||||
$email = $event->email;
|
|
||||||
$owner = env('SITE_OWNER');
|
|
||||||
$ipAddress = $event->ipAddress;
|
|
||||||
$parts = explode('@', $email);
|
|
||||||
/** @var Configuration $sendmail */
|
|
||||||
$sendmail = FireflyConfig::get('mail_for_blocked_domain', config('firefly.configuration.mail_for_blocked_domain'));
|
|
||||||
Log::debug(sprintf('Now in reportUseBlockedDomain for email address %s', $email));
|
|
||||||
Log::error(sprintf('Somebody tried to register using an email address (%s) connected to a banned domain (%s).', $email, $parts[1]));
|
|
||||||
if (is_null($sendmail) || (!is_null($sendmail) && $sendmail->data === false)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// send email message:
|
|
||||||
try {
|
|
||||||
Mail::send(
|
|
||||||
['emails.blocked-registration-html', 'emails.blocked-registration-text'],
|
|
||||||
[
|
|
||||||
'email_address' => $email,
|
|
||||||
'blocked_domain' => $parts[1],
|
|
||||||
'ip' => $ipAddress,
|
|
||||||
], function (Message $message) use ($owner) {
|
|
||||||
$message->to($owner, $owner)->subject('Blocked registration attempt with blocked domain');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (Swift_TransportException $e) {
|
|
||||||
Log::error($e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param BlockedUseOfEmail $event
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function reportUseOfBlockedEmail(BlockedUseOfEmail $event): bool
|
|
||||||
{
|
|
||||||
$email = $event->email;
|
|
||||||
$owner = env('SITE_OWNER');
|
|
||||||
$ipAddress = $event->ipAddress;
|
|
||||||
/** @var Configuration $sendmail */
|
|
||||||
$sendmail = FireflyConfig::get('mail_for_blocked_email', config('firefly.configuration.mail_for_blocked_email'));
|
|
||||||
Log::debug(sprintf('Now in reportUseOfBlockedEmail for email address %s', $email));
|
|
||||||
Log::error(sprintf('Somebody tried to register using email address %s which is blocked (SHA2 hash).', $email));
|
|
||||||
if (is_null($sendmail) || (!is_null($sendmail) && $sendmail->data === false)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// send email message:
|
|
||||||
try {
|
|
||||||
Mail::send(
|
|
||||||
['emails.blocked-email-html', 'emails.blocked-email-text'],
|
|
||||||
[
|
|
||||||
'user_address' => $email,
|
|
||||||
'ip' => $ipAddress,
|
|
||||||
], function (Message $message) use ($owner) {
|
|
||||||
$message->to($owner, $owner)->subject('Blocked registration attempt with blocked email address');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (Swift_TransportException $e) {
|
|
||||||
Log::error($e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param DeletedUser $event
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function saveEmailAddress(DeletedUser $event): bool
|
|
||||||
{
|
|
||||||
Preferences::mark();
|
|
||||||
$email = hash('sha256', $event->email);
|
|
||||||
Log::debug(sprintf('Hash of email is %s', $email));
|
|
||||||
/** @var Configuration $configuration */
|
|
||||||
$configuration = FireflyConfig::get('deleted_users', []);
|
|
||||||
$content = $configuration->data;
|
|
||||||
if (!is_array($content)) {
|
|
||||||
$content = [];
|
|
||||||
}
|
|
||||||
$content[] = $email;
|
|
||||||
$configuration->data = $content;
|
|
||||||
Log::debug('New content of deleted_users is ', $content);
|
|
||||||
FireflyConfig::set('deleted_users', $content);
|
|
||||||
|
|
||||||
Preferences::mark();
|
|
||||||
|
|
||||||
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
|
* @param RequestedNewPassword $event
|
||||||
*
|
*
|
||||||
@@ -372,75 +127,4 @@ class UserEventHandler
|
|||||||
|
|
||||||
return true;
|
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 FireflyIII\Models\Attachment;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\MessageBag;
|
use Illuminate\Support\MessageBag;
|
||||||
use Input;
|
|
||||||
use Storage;
|
use Storage;
|
||||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||||
|
|
||||||
@@ -33,9 +32,9 @@ class AttachmentHelper implements AttachmentHelperInterface
|
|||||||
/** @var MessageBag */
|
/** @var MessageBag */
|
||||||
public $messages;
|
public $messages;
|
||||||
/** @var array */
|
/** @var array */
|
||||||
protected $allowedMimes;
|
protected $allowedMimes = [];
|
||||||
/** @var int */
|
/** @var int */
|
||||||
protected $maxUploadSize;
|
protected $maxUploadSize = 0;
|
||||||
|
|
||||||
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
|
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
|
||||||
protected $uploadDisk;
|
protected $uploadDisk;
|
||||||
@@ -45,8 +44,8 @@ class AttachmentHelper implements AttachmentHelperInterface
|
|||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->maxUploadSize = config('firefly.maxUploadSize');
|
$this->maxUploadSize = intval(config('firefly.maxUploadSize'));
|
||||||
$this->allowedMimes = config('firefly.allowedMimes');
|
$this->allowedMimes = (array) config('firefly.allowedMimes');
|
||||||
$this->errors = new MessageBag;
|
$this->errors = new MessageBag;
|
||||||
$this->messages = new MessageBag;
|
$this->messages = new MessageBag;
|
||||||
$this->uploadDisk = Storage::disk('upload');
|
$this->uploadDisk = Storage::disk('upload');
|
||||||
@@ -81,20 +80,19 @@ class AttachmentHelper implements AttachmentHelperInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Model $model
|
* @param Model $model
|
||||||
|
* @param array|null $files
|
||||||
*
|
*
|
||||||
* @return bool
|
* @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)) {
|
if (is_array($files)) {
|
||||||
$this->processFiles($files, $model);
|
foreach ($files as $entry) {
|
||||||
|
if (!is_null($entry)) {
|
||||||
|
$this->processFile($entry, $model);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -227,37 +225,4 @@ class AttachmentHelper implements AttachmentHelperInterface
|
|||||||
|
|
||||||
return true;
|
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
|
* @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;
|
||||||
|
|
||||||
|
}
|
@@ -1,107 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Account.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\Helpers\Collection;
|
|
||||||
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class Account
|
|
||||||
*
|
|
||||||
* @package FireflyIII\Helpers\Collection
|
|
||||||
*/
|
|
||||||
class Account
|
|
||||||
{
|
|
||||||
|
|
||||||
/** @var Collection */
|
|
||||||
protected $accounts;
|
|
||||||
/** @var string */
|
|
||||||
protected $difference = '';
|
|
||||||
/** @var string */
|
|
||||||
protected $end = '';
|
|
||||||
/** @var string */
|
|
||||||
protected $start = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Account constructor.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->accounts = new Collection;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Collection
|
|
||||||
*/
|
|
||||||
public function getAccounts(): Collection
|
|
||||||
{
|
|
||||||
return $this->accounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Collection $accounts
|
|
||||||
*/
|
|
||||||
public function setAccounts(Collection $accounts)
|
|
||||||
{
|
|
||||||
$this->accounts = $accounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getDifference(): string
|
|
||||||
{
|
|
||||||
return $this->difference;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $difference
|
|
||||||
*/
|
|
||||||
public function setDifference(string $difference)
|
|
||||||
{
|
|
||||||
$this->difference = $difference;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getEnd(): string
|
|
||||||
{
|
|
||||||
return $this->end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $end
|
|
||||||
*/
|
|
||||||
public function setEnd(string $end)
|
|
||||||
{
|
|
||||||
$this->end = $end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getStart(): string
|
|
||||||
{
|
|
||||||
return $this->start;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $start
|
|
||||||
*/
|
|
||||||
public function setStart(string $start)
|
|
||||||
{
|
|
||||||
$this->start = $start;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@@ -14,6 +14,7 @@ namespace FireflyIII\Helpers\Collection;
|
|||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Models\Budget as BudgetModel;
|
use FireflyIII\Models\Budget as BudgetModel;
|
||||||
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,12 +35,10 @@ class BalanceLine
|
|||||||
|
|
||||||
/** @var BudgetModel */
|
/** @var BudgetModel */
|
||||||
protected $budget;
|
protected $budget;
|
||||||
/** @var Carbon */
|
/** @var BudgetLimit */
|
||||||
protected $endDate;
|
protected $budgetLimit;
|
||||||
/** @var int */
|
/** @var int */
|
||||||
protected $role = self::ROLE_DEFAULTROLE;
|
protected $role = self::ROLE_DEFAULTROLE;
|
||||||
/** @var Carbon */
|
|
||||||
protected $startDate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -90,20 +89,28 @@ class BalanceLine
|
|||||||
$this->budget = $budget;
|
$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
|
* @return Carbon
|
||||||
*/
|
*/
|
||||||
public function getEndDate()
|
public function getEndDate()
|
||||||
{
|
{
|
||||||
return $this->endDate;
|
return $this->budgetLimit->end_date ?? new Carbon;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Carbon $endDate
|
|
||||||
*/
|
|
||||||
public function setEndDate($endDate)
|
|
||||||
{
|
|
||||||
$this->endDate = $endDate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,18 +134,11 @@ class BalanceLine
|
|||||||
*/
|
*/
|
||||||
public function getStartDate()
|
public function getStartDate()
|
||||||
{
|
{
|
||||||
return $this->startDate;
|
return $this->budgetLimit->start_date ?? new Carbon;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Carbon $startDate
|
|
||||||
*/
|
|
||||||
public function setStartDate($startDate)
|
|
||||||
{
|
|
||||||
$this->startDate = $startDate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getTitle(): string
|
public function getTitle(): string
|
||||||
@@ -147,13 +147,13 @@ class BalanceLine
|
|||||||
return $this->getBudget()->name;
|
return $this->getBudget()->name;
|
||||||
}
|
}
|
||||||
if ($this->getRole() == self::ROLE_DEFAULTROLE) {
|
if ($this->getRole() == self::ROLE_DEFAULTROLE) {
|
||||||
return trans('firefly.no_budget');
|
return strval(trans('firefly.no_budget'));
|
||||||
}
|
}
|
||||||
if ($this->getRole() == self::ROLE_TAGROLE) {
|
if ($this->getRole() == self::ROLE_TAGROLE) {
|
||||||
return trans('firefly.coveredWithTags');
|
return strval(trans('firefly.coveredWithTags'));
|
||||||
}
|
}
|
||||||
if ($this->getRole() == self::ROLE_DIFFROLE) {
|
if ($this->getRole() == self::ROLE_DIFFROLE) {
|
||||||
return trans('firefly.leftUnbalanced');
|
return strval(trans('firefly.leftUnbalanced'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
@@ -169,7 +169,7 @@ class BalanceLine
|
|||||||
*/
|
*/
|
||||||
public function leftOfRepetition(): string
|
public function leftOfRepetition(): string
|
||||||
{
|
{
|
||||||
$start = $this->budget->amount ?? '0';
|
$start = $this->budgetLimit->amount ?? '0';
|
||||||
/** @var BalanceEntry $balanceEntry */
|
/** @var BalanceEntry $balanceEntry */
|
||||||
foreach ($this->getBalanceEntries() as $balanceEntry) {
|
foreach ($this->getBalanceEntries() as $balanceEntry) {
|
||||||
$start = bcadd($balanceEntry->getSpent(), $start);
|
$start = bcadd($balanceEntry->getSpent(), $start);
|
||||||
|
@@ -60,7 +60,6 @@ class Budget
|
|||||||
*/
|
*/
|
||||||
public function addBudgeted(string $add): Budget
|
public function addBudgeted(string $add): Budget
|
||||||
{
|
{
|
||||||
$add = strval(round($add, 2));
|
|
||||||
$this->budgeted = bcadd($this->budgeted, $add);
|
$this->budgeted = bcadd($this->budgeted, $add);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@@ -73,7 +72,6 @@ class Budget
|
|||||||
*/
|
*/
|
||||||
public function addLeft(string $add): Budget
|
public function addLeft(string $add): Budget
|
||||||
{
|
{
|
||||||
$add = strval(round($add, 2));
|
|
||||||
$this->left = bcadd($this->left, $add);
|
$this->left = bcadd($this->left, $add);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@@ -86,7 +84,6 @@ class Budget
|
|||||||
*/
|
*/
|
||||||
public function addOverspent(string $add): Budget
|
public function addOverspent(string $add): Budget
|
||||||
{
|
{
|
||||||
$add = strval(round($add, 2));
|
|
||||||
$this->overspent = bcadd($this->overspent, $add);
|
$this->overspent = bcadd($this->overspent, $add);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@@ -99,7 +96,6 @@ class Budget
|
|||||||
*/
|
*/
|
||||||
public function addSpent(string $add): Budget
|
public function addSpent(string $add): Budget
|
||||||
{
|
{
|
||||||
$add = strval(round($add, 2));
|
|
||||||
$this->spent = bcadd($this->spent, $add);
|
$this->spent = bcadd($this->spent, $add);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@@ -168,7 +164,7 @@ class Budget
|
|||||||
*/
|
*/
|
||||||
public function setOverspent(string $overspent): Budget
|
public function setOverspent(string $overspent): Budget
|
||||||
{
|
{
|
||||||
$this->overspent = strval(round($overspent, 2));
|
$this->overspent = $overspent;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -188,7 +184,7 @@ class Budget
|
|||||||
*/
|
*/
|
||||||
public function setSpent(string $spent): Budget
|
public function setSpent(string $spent): Budget
|
||||||
{
|
{
|
||||||
$this->spent = strval(round($spent, 2));
|
$this->spent = $spent;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,7 @@ declare(strict_types = 1);
|
|||||||
namespace FireflyIII\Helpers\Collection;
|
namespace FireflyIII\Helpers\Collection;
|
||||||
|
|
||||||
use FireflyIII\Models\Budget as BudgetModel;
|
use FireflyIII\Models\Budget as BudgetModel;
|
||||||
use FireflyIII\Models\LimitRepetition;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -26,14 +26,14 @@ class BudgetLine
|
|||||||
|
|
||||||
/** @var BudgetModel */
|
/** @var BudgetModel */
|
||||||
protected $budget;
|
protected $budget;
|
||||||
|
/** @var BudgetLimit */
|
||||||
|
protected $budgetLimit;
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $budgeted = '0';
|
protected $budgeted = '0';
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $left = '0';
|
protected $left = '0';
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $overspent = '0';
|
protected $overspent = '0';
|
||||||
/** @var LimitRepetition */
|
|
||||||
protected $repetition;
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $spent = '0';
|
protected $spent = '0';
|
||||||
|
|
||||||
@@ -57,6 +57,26 @@ class BudgetLine
|
|||||||
return $this;
|
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
|
* @return string
|
||||||
*/
|
*/
|
||||||
@@ -117,26 +137,6 @@ class BudgetLine
|
|||||||
return $this;
|
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
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
@@ -55,7 +55,6 @@ class Category
|
|||||||
*/
|
*/
|
||||||
public function addTotal(string $add)
|
public function addTotal(string $add)
|
||||||
{
|
{
|
||||||
$add = strval(round($add, 2));
|
|
||||||
$this->total = bcadd($this->total, $add);
|
$this->total = bcadd($this->total, $add);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +78,7 @@ class Category
|
|||||||
*/
|
*/
|
||||||
public function getTotal(): string
|
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_types.type as transaction_type_type',
|
||||||
'transaction_journals.bill_id',
|
'transaction_journals.bill_id',
|
||||||
'bills.name as bill_name',
|
'bills.name as bill_name',
|
||||||
|
'bills.name_encrypted as bill_name_encrypted',
|
||||||
'transactions.id as id',
|
'transactions.id as id',
|
||||||
'transactions.amount as transaction_amount',
|
'transactions.amount as transaction_amount',
|
||||||
'transactions.description as transaction_description',
|
'transactions.description as transaction_description',
|
||||||
@@ -166,6 +167,7 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
public function getJournals(): Collection
|
public function getJournals(): Collection
|
||||||
{
|
{
|
||||||
$this->run = true;
|
$this->run = true;
|
||||||
|
/** @var Collection $set */
|
||||||
$set = $this->query->get(array_values($this->fields));
|
$set = $this->query->get(array_values($this->fields));
|
||||||
Log::debug(sprintf('Count of set is %d', $set->count()));
|
Log::debug(sprintf('Count of set is %d', $set->count()));
|
||||||
$set = $this->filterTransfers($set);
|
$set = $this->filterTransfers($set);
|
||||||
@@ -180,10 +182,12 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
$set->each(
|
$set->each(
|
||||||
function (Transaction $transaction) {
|
function (Transaction $transaction) {
|
||||||
$transaction->date = new Carbon($transaction->date);
|
$transaction->date = new Carbon($transaction->date);
|
||||||
$transaction->description = intval($transaction->encrypted) === 1 ? Crypt::decrypt($transaction->description) : $transaction->description;
|
$transaction->description = $transaction->encrypted ? Crypt::decrypt($transaction->description) : $transaction->description;
|
||||||
$transaction->bill_name = !is_null($transaction->bill_name) ? Crypt::decrypt($transaction->bill_name) : '';
|
|
||||||
|
if (!is_null($transaction->bill_name)) {
|
||||||
|
$transaction->bill_name = $transaction->bill_name_encrypted ? Crypt::decrypt($transaction->bill_name) : $transaction->bill_name;
|
||||||
|
}
|
||||||
|
|
||||||
// optionally decrypted:
|
|
||||||
try {
|
try {
|
||||||
$transaction->opposing_account_name = Crypt::decrypt($transaction->opposing_account_name);
|
$transaction->opposing_account_name = Crypt::decrypt($transaction->opposing_account_name);
|
||||||
} catch (DecryptException $e) {
|
} catch (DecryptException $e) {
|
||||||
@@ -560,7 +564,7 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
return $set;
|
return $set;
|
||||||
}
|
}
|
||||||
if ($this->joinedOpposing === false) {
|
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;
|
return $set;
|
||||||
}
|
}
|
||||||
@@ -646,9 +650,17 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
// join some extra tables:
|
// join some extra tables:
|
||||||
$this->joinedBudget = true;
|
$this->joinedBudget = true;
|
||||||
$this->query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
|
$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('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[] = '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[] = '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 +673,26 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
// join some extra tables:
|
// join some extra tables:
|
||||||
$this->joinedCategory = true;
|
$this->joinedCategory = true;
|
||||||
$this->query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
|
$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('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[] = '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[] = '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()
|
private function joinOpposingTables()
|
||||||
{
|
{
|
||||||
if (!$this->joinedOpposing) {
|
if (!$this->joinedOpposing) {
|
||||||
@@ -726,4 +752,4 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
return $query;
|
return $query;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -173,4 +173,4 @@ interface JournalCollectorInterface
|
|||||||
* @return JournalCollectorInterface
|
* @return JournalCollectorInterface
|
||||||
*/
|
*/
|
||||||
public function withoutCategory(): JournalCollectorInterface;
|
public function withoutCategory(): JournalCollectorInterface;
|
||||||
}
|
}
|
||||||
|
@@ -74,12 +74,8 @@ class Help implements HelpInterface
|
|||||||
$converter = new CommonMarkConverter();
|
$converter = new CommonMarkConverter();
|
||||||
$content = $converter->convertToHtml($content);
|
$content = $converter->convertToHtml($content);
|
||||||
}
|
}
|
||||||
if (strlen($content) === 0) {
|
|
||||||
Log::warning('Raw content length is zero.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $content;
|
return $content;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -19,17 +19,18 @@ use FireflyIII\Helpers\Collection\Balance;
|
|||||||
use FireflyIII\Helpers\Collection\BalanceEntry;
|
use FireflyIII\Helpers\Collection\BalanceEntry;
|
||||||
use FireflyIII\Helpers\Collection\BalanceHeader;
|
use FireflyIII\Helpers\Collection\BalanceHeader;
|
||||||
use FireflyIII\Helpers\Collection\BalanceLine;
|
use FireflyIII\Helpers\Collection\BalanceLine;
|
||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Models\LimitRepetition;
|
|
||||||
use FireflyIII\Models\Tag;
|
use FireflyIII\Models\Tag;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
use Illuminate\Database\Query\JoinClause;
|
use Illuminate\Database\Query\JoinClause;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class BalanceReportHelper
|
* Class BalanceReportHelper
|
||||||
*
|
*
|
||||||
|
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) // I can't really help it.
|
||||||
* @package FireflyIII\Helpers\Report
|
* @package FireflyIII\Helpers\Report
|
||||||
*/
|
*/
|
||||||
class BalanceReportHelper implements BalanceReportHelperInterface
|
class BalanceReportHelper implements BalanceReportHelperInterface
|
||||||
@@ -59,19 +60,21 @@ class BalanceReportHelper implements BalanceReportHelperInterface
|
|||||||
*/
|
*/
|
||||||
public function getBalanceReport(Collection $accounts, Carbon $start, Carbon $end): Balance
|
public function getBalanceReport(Collection $accounts, Carbon $start, Carbon $end): Balance
|
||||||
{
|
{
|
||||||
$balance = new Balance;
|
Log::debug('Start of balance report');
|
||||||
$header = new BalanceHeader;
|
$balance = new Balance;
|
||||||
$limitRepetitions = $this->budgetRepository->getAllBudgetLimitRepetitions($start, $end);
|
$header = new BalanceHeader;
|
||||||
|
$budgetLimits = $this->budgetRepository->getAllBudgetLimits($start, $end);
|
||||||
foreach ($accounts as $account) {
|
foreach ($accounts as $account) {
|
||||||
|
Log::debug(sprintf('Add account %s to headers.', $account->name));
|
||||||
$header->addAccount($account);
|
$header->addAccount($account);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var LimitRepetition $repetition */
|
/** @var BudgetLimit $budgetLimit */
|
||||||
foreach ($limitRepetitions as $repetition) {
|
foreach ($budgetLimits as $budgetLimit) {
|
||||||
$budget = $this->budgetRepository->find($repetition->budget_id);
|
$line = $this->createBalanceLine($budgetLimit, $accounts);
|
||||||
$line = $this->createBalanceLine($budget, $repetition, $accounts);
|
|
||||||
$balance->addBalanceLine($line);
|
$balance->addBalanceLine($line);
|
||||||
}
|
}
|
||||||
|
Log::debug('Create rest of the things.');
|
||||||
$noBudgetLine = $this->createNoBudgetLine($accounts, $start, $end);
|
$noBudgetLine = $this->createNoBudgetLine($accounts, $start, $end);
|
||||||
$coveredByTagLine = $this->createTagsBalanceLine($accounts, $start, $end);
|
$coveredByTagLine = $this->createTagsBalanceLine($accounts, $start, $end);
|
||||||
$leftUnbalancedLine = $this->createLeftUnbalancedLine($noBudgetLine, $coveredByTagLine);
|
$leftUnbalancedLine = $this->createLeftUnbalancedLine($noBudgetLine, $coveredByTagLine);
|
||||||
@@ -81,9 +84,12 @@ class BalanceReportHelper implements BalanceReportHelperInterface
|
|||||||
$balance->addBalanceLine($leftUnbalancedLine);
|
$balance->addBalanceLine($leftUnbalancedLine);
|
||||||
$balance->setBalanceHeader($header);
|
$balance->setBalanceHeader($header);
|
||||||
|
|
||||||
|
Log::debug('Clear unused budgets.');
|
||||||
// remove budgets without expenses from balance lines:
|
// remove budgets without expenses from balance lines:
|
||||||
$balance = $this->removeUnusedBudgets($balance);
|
$balance = $this->removeUnusedBudgets($balance);
|
||||||
|
|
||||||
|
Log::debug('Return report.');
|
||||||
|
|
||||||
return $balance;
|
return $balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,27 +143,22 @@ class BalanceReportHelper implements BalanceReportHelperInterface
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Budget $budget
|
* @param BudgetLimit $budgetLimit
|
||||||
* @param LimitRepetition $repetition
|
* @param Collection $accounts
|
||||||
* @param Collection $accounts
|
|
||||||
*
|
*
|
||||||
* @return BalanceLine
|
* @return BalanceLine
|
||||||
*/
|
*/
|
||||||
private function createBalanceLine(Budget $budget, LimitRepetition $repetition, Collection $accounts): BalanceLine
|
private function createBalanceLine(BudgetLimit $budgetLimit, Collection $accounts): BalanceLine
|
||||||
{
|
{
|
||||||
$line = new BalanceLine;
|
$line = new BalanceLine;
|
||||||
$budget->amount = $repetition->amount;
|
$line->setBudget($budgetLimit->budget);
|
||||||
$line->setBudget($budget);
|
$line->setBudgetLimit($budgetLimit);
|
||||||
$line->setStartDate($repetition->startdate);
|
|
||||||
$line->setEndDate($repetition->enddate);
|
|
||||||
|
|
||||||
// loop accounts:
|
// loop accounts:
|
||||||
foreach ($accounts as $account) {
|
foreach ($accounts as $account) {
|
||||||
$balanceEntry = new BalanceEntry;
|
$balanceEntry = new BalanceEntry;
|
||||||
$balanceEntry->setAccount($account);
|
$balanceEntry->setAccount($account);
|
||||||
$spent = $this->budgetRepository->spentInPeriod(
|
$spent = $this->budgetRepository->spentInPeriod(new Collection([$budgetLimit->budget]), new Collection([$account]), $budgetLimit->start_date, $budgetLimit->end_date);
|
||||||
new Collection([$budget]), new Collection([$account]), $repetition->startdate, $repetition->enddate
|
|
||||||
);
|
|
||||||
$balanceEntry->setSpent($spent);
|
$balanceEntry->setSpent($spent);
|
||||||
$line->addBalanceEntry($balanceEntry);
|
$line->addBalanceEntry($balanceEntry);
|
||||||
}
|
}
|
||||||
@@ -212,7 +213,7 @@ class BalanceReportHelper implements BalanceReportHelperInterface
|
|||||||
$empty = new BalanceLine;
|
$empty = new BalanceLine;
|
||||||
|
|
||||||
foreach ($accounts as $account) {
|
foreach ($accounts as $account) {
|
||||||
$spent = $this->budgetRepository->spentInPeriodWithoutBudget(new Collection([$account]), $start, $end);
|
$spent = $this->budgetRepository->spentInPeriodWoBudget(new Collection([$account]), $start, $end);
|
||||||
// budget
|
// budget
|
||||||
$budgetEntry = new BalanceEntry;
|
$budgetEntry = new BalanceEntry;
|
||||||
$budgetEntry->setAccount($account);
|
$budgetEntry->setAccount($account);
|
||||||
|
@@ -18,7 +18,7 @@ use Carbon\Carbon;
|
|||||||
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
|
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
|
||||||
use FireflyIII\Helpers\Collection\BudgetLine;
|
use FireflyIII\Helpers\Collection\BudgetLine;
|
||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\Budget;
|
||||||
use FireflyIII\Models\LimitRepetition;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
@@ -43,6 +43,7 @@ class BudgetReportHelper implements BudgetReportHelperInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly 5.
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
* @param Carbon $end
|
* @param Carbon $end
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
@@ -56,13 +57,9 @@ class BudgetReportHelper implements BudgetReportHelperInterface
|
|||||||
|
|
||||||
/** @var Budget $budget */
|
/** @var Budget $budget */
|
||||||
foreach ($set as $budget) {
|
foreach ($set as $budget) {
|
||||||
$repetitions = $budget->limitrepetitions()->before($end)->after($start)->get();
|
$budgetLimits = $this->repository->getBudgetLimits($budget, $start, $end);
|
||||||
|
if ($budgetLimits->count() == 0) { // no budget limit(s) for this budget
|
||||||
// no repetition(s) for this budget:
|
$spent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);// spent for budget in time range
|
||||||
if ($repetitions->count() == 0) {
|
|
||||||
// spent for budget in time range:
|
|
||||||
$spent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);
|
|
||||||
|
|
||||||
if ($spent > 0) {
|
if ($spent > 0) {
|
||||||
$budgetLine = new BudgetLine;
|
$budgetLine = new BudgetLine;
|
||||||
$budgetLine->setBudget($budget)->setOverspent($spent);
|
$budgetLine->setBudget($budget)->setOverspent($spent);
|
||||||
@@ -70,26 +67,20 @@ class BudgetReportHelper implements BudgetReportHelperInterface
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// one or more repetitions for budget:
|
/** @var BudgetLimit $budgetLimit */
|
||||||
/** @var LimitRepetition $repetition */
|
foreach ($budgetLimits as $budgetLimit) { // one or more repetitions for budget
|
||||||
foreach ($repetitions as $repetition) {
|
$data = $this->calculateExpenses($budget, $budgetLimit, $accounts);
|
||||||
$data = $this->calculateExpenses($budget, $repetition, $accounts);
|
|
||||||
|
|
||||||
$budgetLine = new BudgetLine;
|
$budgetLine = new BudgetLine;
|
||||||
$budgetLine->setBudget($budget)->setRepetition($repetition)
|
$budgetLine->setBudget($budget)->setBudgetLimit($budgetLimit)
|
||||||
->setLeft($data['left'])->setSpent($data['expenses'])->setOverspent($data['overspent'])
|
->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);
|
->addLeft($data['left'])->addOverspent($data['overspent'])->addBudgetLine($budgetLine);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
$noBudget = $this->repository->spentInPeriodWoBudget($accounts, $start, $end); // stuff outside of budgets
|
||||||
// stuff outside of budgets:
|
|
||||||
|
|
||||||
$noBudget = $this->repository->spentInPeriodWithoutBudget($accounts, $start, $end);
|
|
||||||
$budgetLine = new BudgetLine;
|
$budgetLine = new BudgetLine;
|
||||||
$budgetLine->setOverspent($noBudget)->setSpent($noBudget);
|
$budgetLine->setOverspent($noBudget)->setSpent($noBudget);
|
||||||
$object->addOverspent($noBudget)->addBudgetLine($budgetLine);
|
$object->addOverspent($noBudget)->addBudgetLine($budgetLine);
|
||||||
@@ -128,19 +119,19 @@ class BudgetReportHelper implements BudgetReportHelperInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Budget $budget
|
* @param Budget $budget
|
||||||
* @param LimitRepetition $repetition
|
* @param BudgetLimit $budgetLimit
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function calculateExpenses(Budget $budget, LimitRepetition $repetition, Collection $accounts): array
|
private function calculateExpenses(Budget $budget, BudgetLimit $budgetLimit, Collection $accounts): array
|
||||||
{
|
{
|
||||||
$array = [];
|
$array = [];
|
||||||
$expenses = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $repetition->startdate, $repetition->enddate);
|
$expenses = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $budgetLimit->start_date, $budgetLimit->end_date);
|
||||||
$array['left'] = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? bcadd($repetition->amount, $expenses) : '0';
|
$array['left'] = bccomp(bcadd($budgetLimit->amount, $expenses), '0') === 1 ? bcadd($budgetLimit->amount, $expenses) : '0';
|
||||||
$array['spent'] = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? $expenses : '0';
|
$array['spent'] = bccomp(bcadd($budgetLimit->amount, $expenses), '0') === 1 ? $expenses : '0';
|
||||||
$array['overspent'] = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? '0' : bcadd($expenses, $repetition->amount);
|
$array['overspent'] = bccomp(bcadd($budgetLimit->amount, $expenses), '0') === 1 ? '0' : bcadd($expenses, $budgetLimit->amount);
|
||||||
$array['expenses'] = $expenses;
|
$array['expenses'] = $expenses;
|
||||||
|
|
||||||
return $array;
|
return $array;
|
||||||
|
@@ -53,6 +53,8 @@ class ReportHelper implements ReportHelperInterface
|
|||||||
* This method generates a full report for the given period on all
|
* This method generates a full report for the given period on all
|
||||||
* the users bills and their payments.
|
* 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.
|
* Excludes bills which have not had a payment on the mentioned accounts.
|
||||||
*
|
*
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
@@ -80,8 +82,6 @@ class ReportHelper implements ReportHelperInterface
|
|||||||
$billLine->setMin(strval($bill->amount_min));
|
$billLine->setMin(strval($bill->amount_min));
|
||||||
$billLine->setMax(strval($bill->amount_max));
|
$billLine->setMax(strval($bill->amount_max));
|
||||||
$billLine->setHit(false);
|
$billLine->setHit(false);
|
||||||
// is hit in period?
|
|
||||||
|
|
||||||
$entry = $journals->filter(
|
$entry = $journals->filter(
|
||||||
function (Transaction $transaction) use ($bill) {
|
function (Transaction $transaction) use ($bill) {
|
||||||
return $transaction->bill_id === $bill->id;
|
return $transaction->bill_id === $bill->id;
|
||||||
@@ -94,44 +94,15 @@ class ReportHelper implements ReportHelperInterface
|
|||||||
$billLine->setLastHitDate($first->date);
|
$billLine->setLastHitDate($first->date);
|
||||||
$billLine->setHit(true);
|
$billLine->setHit(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// bill is active, or bill is hit:
|
|
||||||
if ($billLine->isActive() || $billLine->isHit()) {
|
if ($billLine->isActive() || $billLine->isHit()) {
|
||||||
$collection->addBill($billLine);
|
$collection->addBill($billLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// do some extra filtering.
|
|
||||||
$collection->filterBills();
|
$collection->filterBills();
|
||||||
|
|
||||||
return $collection;
|
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
|
* @param Carbon $date
|
||||||
*
|
*
|
||||||
|
@@ -42,15 +42,6 @@ interface ReportHelperInterface
|
|||||||
*/
|
*/
|
||||||
public function getBillReport(Carbon $start, Carbon $end, Collection $accounts): BillCollection;
|
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
|
* @param Carbon $date
|
||||||
*
|
*
|
||||||
|
@@ -17,7 +17,6 @@ use Amount;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use ExpandedForm;
|
use ExpandedForm;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
|
||||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
use FireflyIII\Http\Requests\AccountFormRequest;
|
use FireflyIII\Http\Requests\AccountFormRequest;
|
||||||
use FireflyIII\Models\Account;
|
use FireflyIII\Models\Account;
|
||||||
@@ -28,8 +27,8 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
|
|||||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Input;
|
|
||||||
use Log;
|
use Log;
|
||||||
use Navigation;
|
use Navigation;
|
||||||
use Preferences;
|
use Preferences;
|
||||||
@@ -66,7 +65,7 @@ class AccountController extends Controller
|
|||||||
/**
|
/**
|
||||||
* @param string $what
|
* @param string $what
|
||||||
*
|
*
|
||||||
* @return View
|
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory|View
|
||||||
*/
|
*/
|
||||||
public function create(string $what = 'asset')
|
public function create(string $what = 'asset')
|
||||||
{
|
{
|
||||||
@@ -76,12 +75,14 @@ class AccountController extends Controller
|
|||||||
$defaultCurrency = Amount::getDefaultCurrency();
|
$defaultCurrency = Amount::getDefaultCurrency();
|
||||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
||||||
$subTitle = trans('firefly.make_new_' . $what . '_account');
|
$subTitle = trans('firefly.make_new_' . $what . '_account');
|
||||||
Session::flash(
|
$roles = [];
|
||||||
'preFilled',
|
foreach (config('firefly.accountRoles') as $role) {
|
||||||
[
|
$roles[$role] = strval(trans('firefly.account_role_' . $role));
|
||||||
'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").
|
// put previous url in session if not redirect from store (not "create another").
|
||||||
if (session('accounts.create.fromStore') !== true) {
|
if (session('accounts.create.fromStore') !== true) {
|
||||||
@@ -91,7 +92,7 @@ class AccountController extends Controller
|
|||||||
Session::flash('gaEventCategory', 'accounts');
|
Session::flash('gaEventCategory', 'accounts');
|
||||||
Session::flash('gaEventAction', 'create-' . $what);
|
Session::flash('gaEventAction', 'create-' . $what);
|
||||||
|
|
||||||
return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle', 'currencies'));
|
return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle', 'currencies', 'roles'));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,24 +118,32 @@ class AccountController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param Request $request
|
||||||
* @param ARI $repository
|
* @param ARI $repository
|
||||||
* @param Account $account
|
* @param Account $account
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
* @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;
|
$type = $account->accountType->type;
|
||||||
$typeName = config('firefly.shortNamesByFullName.' . $type);
|
$typeName = config('firefly.shortNamesByFullName.' . $type);
|
||||||
$name = $account->name;
|
$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);
|
$repository->destroy($account, $moveTo);
|
||||||
|
|
||||||
Session::flash('success', strval(trans('firefly.' . $typeName . '_deleted', ['name' => $name])));
|
Session::flash('success', strval(trans('firefly.' . $typeName . '_deleted', ['name' => $name])));
|
||||||
Preferences::mark();
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -151,6 +160,11 @@ class AccountController extends Controller
|
|||||||
/** @var CurrencyRepositoryInterface $repository */
|
/** @var CurrencyRepositoryInterface $repository */
|
||||||
$repository = app(CurrencyRepositoryInterface::class);
|
$repository = app(CurrencyRepositoryInterface::class);
|
||||||
$currencies = ExpandedForm::makeSelectList($repository->get());
|
$currencies = ExpandedForm::makeSelectList($repository->get());
|
||||||
|
$roles = [];
|
||||||
|
foreach (config('firefly.accountRoles') as $role) {
|
||||||
|
$roles[$role] = strval(trans('firefly.account_role_' . $role));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// put previous url in session if not redirect from store (not "return_to_edit").
|
// put previous url in session if not redirect from store (not "return_to_edit").
|
||||||
if (session('accounts.edit.fromUpdate') !== true) {
|
if (session('accounts.edit.fromUpdate') !== true) {
|
||||||
@@ -181,7 +195,7 @@ class AccountController extends Controller
|
|||||||
Session::flash('gaEventCategory', 'accounts');
|
Session::flash('gaEventCategory', 'accounts');
|
||||||
Session::flash('gaEventAction', 'edit-' . $what);
|
Session::flash('gaEventAction', 'edit-' . $what);
|
||||||
|
|
||||||
return view('accounts.edit', compact('currencies', 'account', 'subTitle', 'subTitleIcon', 'openingBalance', 'what'));
|
return view('accounts.edit', compact('currencies', 'account', 'subTitle', 'subTitleIcon', 'openingBalance', 'what', 'roles'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -192,8 +206,7 @@ class AccountController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(ARI $repository, string $what)
|
public function index(ARI $repository, string $what)
|
||||||
{
|
{
|
||||||
$what = $what ?? 'asset';
|
$what = $what ?? 'asset';
|
||||||
|
|
||||||
$subTitle = trans('firefly.' . $what . '_accounts');
|
$subTitle = trans('firefly.' . $what . '_accounts');
|
||||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
||||||
$types = config('firefly.accountTypesByIdentifier.' . $what);
|
$types = config('firefly.accountTypesByIdentifier.' . $what);
|
||||||
@@ -214,6 +227,7 @@ class AccountController extends Controller
|
|||||||
$account->lastActivityDate = $this->isInArray($activities, $account->id);
|
$account->lastActivityDate = $this->isInArray($activities, $account->id);
|
||||||
$account->startBalance = $this->isInArray($startBalances, $account->id);
|
$account->startBalance = $this->isInArray($startBalances, $account->id);
|
||||||
$account->endBalance = $this->isInArray($endBalances, $account->id);
|
$account->endBalance = $this->isInArray($endBalances, $account->id);
|
||||||
|
$account->difference = bcsub($account->endBalance, $account->startBalance);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -221,12 +235,13 @@ class AccountController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param Request $request
|
||||||
* @param JournalCollectorInterface $collector
|
* @param JournalCollectorInterface $collector
|
||||||
* @param Account $account
|
* @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) {
|
if ($account->accountType->type === AccountType::INITIAL_BALANCE) {
|
||||||
return $this->redirectToOriginalAccount($account);
|
return $this->redirectToOriginalAccount($account);
|
||||||
@@ -235,12 +250,13 @@ class AccountController extends Controller
|
|||||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
|
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
|
||||||
$subTitle = $account->name;
|
$subTitle = $account->name;
|
||||||
$range = Preferences::get('viewRange', '1M')->data;
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
|
|
||||||
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
|
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||||
$end = session('end', Navigation::endOfPeriod(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);
|
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||||
$chartUri = route('chart.account.single', [$account->id]);
|
$chartUri = route('chart.account.single', [$account->id]);
|
||||||
|
$accountType = $account->accountType->type;
|
||||||
|
|
||||||
// grab those journals:
|
// grab those journals:
|
||||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
|
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
|
||||||
$journals = $collector->getPaginatedJournals();
|
$journals = $collector->getPaginatedJournals();
|
||||||
@@ -249,24 +265,26 @@ class AccountController extends Controller
|
|||||||
// generate entries for each period (and cache those)
|
// generate entries for each period (and cache those)
|
||||||
$entries = $this->periodEntries($account);
|
$entries = $this->periodEntries($account);
|
||||||
|
|
||||||
return view('accounts.show', compact('account', 'entries', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri'));
|
return view('accounts.show', compact('account', 'accountType', 'entries', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param Request $request
|
||||||
* @param ARI $repository
|
* @param ARI $repository
|
||||||
* @param Account $account
|
* @param Account $account
|
||||||
*
|
*
|
||||||
* @return View
|
* @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')));
|
$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);
|
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||||
$chartUri = route('chart.account.all', [$account->id]);
|
$chartUri = route('chart.account.all', [$account->id]);
|
||||||
|
|
||||||
// replace with journal collector:
|
// 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);
|
$collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page);
|
||||||
$journals = $collector->getPaginatedJournals();
|
$journals = $collector->getPaginatedJournals();
|
||||||
$journals->setPath('accounts/show/' . $account->id . '/all');
|
$journals->setPath('accounts/show/' . $account->id . '/all');
|
||||||
@@ -280,30 +298,36 @@ class AccountController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param Request $request
|
||||||
* @param Account $account
|
* @param Account $account
|
||||||
* @param string $date
|
* @param string $date
|
||||||
*
|
*
|
||||||
* @return View
|
* @return View
|
||||||
*/
|
*/
|
||||||
public function showByDate(Account $account, string $date)
|
public function showByDate(Request $request, Account $account, string $date)
|
||||||
{
|
{
|
||||||
$carbon = new Carbon($date);
|
$carbon = new Carbon($date);
|
||||||
$range = Preferences::get('viewRange', '1M')->data;
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
$start = Navigation::startOfPeriod($carbon, $range);
|
$start = Navigation::startOfPeriod($carbon, $range);
|
||||||
$end = Navigation::endOfPeriod($carbon, $range);
|
$end = Navigation::endOfPeriod($carbon, $range);
|
||||||
$subTitle = $account->name . ' (' . Navigation::periodShow($start, $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);
|
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||||
$chartUri = route('chart.account.period', [$account->id, $carbon->format('Y-m-d')]);
|
$chartUri = route('chart.account.period', [$account->id, $carbon->format('Y-m-d')]);
|
||||||
|
$accountType = $account->accountType->type;
|
||||||
|
|
||||||
// replace with journal collector:
|
// 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);
|
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
|
||||||
$journals = $collector->getPaginatedJournals();
|
$journals = $collector->getPaginatedJournals();
|
||||||
$journals->setPath('accounts/show/' . $account->id . '/' . $date);
|
$journals->setPath('accounts/show/' . $account->id . '/' . $date);
|
||||||
|
|
||||||
|
// generate entries for each period (and cache those)
|
||||||
|
$entries = $this->periodEntries($account);
|
||||||
|
|
||||||
// same call, except "entries".
|
// same call, except "entries".
|
||||||
return view('accounts.show', compact('account', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri'));
|
return view('accounts.show', compact('account', 'accountType', 'entries', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -328,7 +352,7 @@ class AccountController extends Controller
|
|||||||
Preferences::set('frontPageAccounts', $frontPage);
|
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:
|
// set value so create routine will not overwrite URL:
|
||||||
Session::put('accounts.create.fromStore', true);
|
Session::put('accounts.create.fromStore', true);
|
||||||
|
|
||||||
@@ -354,7 +378,7 @@ class AccountController extends Controller
|
|||||||
Session::flash('success', strval(trans('firefly.updated_account', ['name' => $account->name])));
|
Session::flash('success', strval(trans('firefly.updated_account', ['name' => $account->name])));
|
||||||
Preferences::mark();
|
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:
|
// set value so edit routine will not overwrite URL:
|
||||||
Session::put('accounts.edit.fromUpdate', true);
|
Session::put('accounts.edit.fromUpdate', true);
|
||||||
|
|
||||||
@@ -379,7 +403,7 @@ class AccountController extends Controller
|
|||||||
return $array[$entryId];
|
return $array[$entryId];
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -430,7 +454,7 @@ class AccountController extends Controller
|
|||||||
$earned = $tasker->amountInInPeriod(new Collection([$account]), $assets, $end, $currentEnd);
|
$earned = $tasker->amountInInPeriod(new Collection([$account]), $assets, $end, $currentEnd);
|
||||||
$dateStr = $end->format('Y-m-d');
|
$dateStr = $end->format('Y-m-d');
|
||||||
$dateName = Navigation::periodShow($end, $range);
|
$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);
|
$end = Navigation::subtractPeriod($end, $range, 1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -58,23 +58,13 @@ class ConfigurationController extends Controller
|
|||||||
|
|
||||||
// all available configuration and their default value in case
|
// all available configuration and their default value in case
|
||||||
// they don't exist yet.
|
// they don't exist yet.
|
||||||
$singleUserMode = FireflyConfig::get('single_user_mode', config('firefly.configuration.single_user_mode'))->data;
|
$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;
|
||||||
$isDemoSite = FireflyConfig::get('is_demo_site', config('firefly.configuration.is_demo_site'))->data;
|
$siteOwner = env('SITE_OWNER');
|
||||||
$siteOwner = env('SITE_OWNER');
|
|
||||||
|
|
||||||
// email settings:
|
|
||||||
$sendErrorMessage = [
|
|
||||||
'mail_for_lockout' => FireflyConfig::get('mail_for_lockout', config('firefly.configuration.mail_for_lockout'))->data,
|
|
||||||
'mail_for_blocked_domain' => FireflyConfig::get('mail_for_blocked_domain', config('firefly.configuration.mail_for_blocked_domain'))->data,
|
|
||||||
'mail_for_blocked_email' => FireflyConfig::get('mail_for_blocked_email', config('firefly.configuration.mail_for_blocked_email'))->data,
|
|
||||||
'mail_for_bad_login' => FireflyConfig::get('mail_for_bad_login', config('firefly.configuration.mail_for_bad_login'))->data,
|
|
||||||
'mail_for_blocked_login' => FireflyConfig::get('mail_for_blocked_login', config('firefly.configuration.mail_for_blocked_login'))->data,
|
|
||||||
];
|
|
||||||
|
|
||||||
return view(
|
return view(
|
||||||
'admin.configuration.index',
|
'admin.configuration.index',
|
||||||
compact('subTitle', 'subTitleIcon', 'singleUserMode', 'mustConfirmAccount', 'isDemoSite', 'sendErrorMessage', 'siteOwner')
|
compact('subTitle', 'subTitleIcon', 'singleUserMode', 'isDemoSite', 'siteOwner')
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -91,16 +81,8 @@ class ConfigurationController extends Controller
|
|||||||
|
|
||||||
// store config values
|
// store config values
|
||||||
FireflyConfig::set('single_user_mode', $data['single_user_mode']);
|
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']);
|
FireflyConfig::set('is_demo_site', $data['is_demo_site']);
|
||||||
|
|
||||||
// email settings
|
|
||||||
FireflyConfig::set('mail_for_lockout', $data['mail_for_lockout']);
|
|
||||||
FireflyConfig::set('mail_for_blocked_domain', $data['mail_for_blocked_domain']);
|
|
||||||
FireflyConfig::set('mail_for_blocked_email', $data['mail_for_blocked_email']);
|
|
||||||
FireflyConfig::set('mail_for_bad_login', $data['mail_for_bad_login']);
|
|
||||||
FireflyConfig::set('mail_for_blocked_login', $data['mail_for_blocked_login']);
|
|
||||||
|
|
||||||
// flash message
|
// flash message
|
||||||
Session::flash('success', strval(trans('firefly.configuration_updated')));
|
Session::flash('success', strval(trans('firefly.configuration_updated')));
|
||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
@@ -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,7 +14,6 @@ declare(strict_types = 1);
|
|||||||
namespace FireflyIII\Http\Controllers\Admin;
|
namespace FireflyIII\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
|
||||||
use FireflyConfig;
|
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
use FireflyIII\Http\Requests\UserFormRequest;
|
use FireflyIII\Http\Requests\UserFormRequest;
|
||||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||||
@@ -81,30 +80,20 @@ class UserController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(UserRepositoryInterface $repository)
|
public function index(UserRepositoryInterface $repository)
|
||||||
{
|
{
|
||||||
$subTitle = strval(trans('firefly.user_administration'));
|
$subTitle = strval(trans('firefly.user_administration'));
|
||||||
$subTitleIcon = 'fa-users';
|
$subTitleIcon = 'fa-users';
|
||||||
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
|
$users = $repository->all();
|
||||||
$users = $repository->all();
|
|
||||||
|
|
||||||
// add meta stuff.
|
// add meta stuff.
|
||||||
$users->each(
|
$users->each(
|
||||||
function (User $user) use ($mustConfirmAccount) {
|
function (User $user) {
|
||||||
$list = ['user_confirmed', 'twoFactorAuthEnabled', 'twoFactorAuthSecret', 'registration_ip_address', 'confirmation_ip_address'];
|
$list = ['twoFactorAuthEnabled', 'twoFactorAuthSecret'];
|
||||||
$preferences = Preferences::getArrayForUser($user, $list);
|
$preferences = Preferences::getArrayForUser($user, $list);
|
||||||
|
|
||||||
$user->activated = true;
|
|
||||||
if (!($preferences['user_confirmed'] === true) && $mustConfirmAccount === true) {
|
|
||||||
$user->activated = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$user->isAdmin = $user->hasRole('owner');
|
$user->isAdmin = $user->hasRole('owner');
|
||||||
$is2faEnabled = $preferences['twoFactorAuthEnabled'] === true;
|
$is2faEnabled = $preferences['twoFactorAuthEnabled'] === true;
|
||||||
$has2faSecret = !is_null($preferences['twoFactorAuthSecret']);
|
$has2faSecret = !is_null($preferences['twoFactorAuthSecret']);
|
||||||
$user->has2FA = false;
|
$user->has2FA = ($is2faEnabled && $has2faSecret) ? true : false;
|
||||||
if ($is2faEnabled && $has2faSecret) {
|
$user->prefs = $preferences;
|
||||||
$user->has2FA = true;
|
|
||||||
}
|
|
||||||
$user->prefs = $preferences;
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -125,37 +114,12 @@ class UserController extends Controller
|
|||||||
$mainTitleIcon = 'fa-hand-spock-o';
|
$mainTitleIcon = 'fa-hand-spock-o';
|
||||||
$subTitle = strval(trans('firefly.single_user_administration', ['email' => $user->email]));
|
$subTitle = strval(trans('firefly.single_user_administration', ['email' => $user->email]));
|
||||||
$subTitleIcon = 'fa-user';
|
$subTitleIcon = 'fa-user';
|
||||||
|
$information = $repository->getUserData($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(
|
return view(
|
||||||
'admin.users.show',
|
'admin.users.show',
|
||||||
compact(
|
compact(
|
||||||
'title', 'mainTitleIcon', 'subTitle', 'subTitleIcon', 'information',
|
'title', 'mainTitleIcon', 'subTitle', 'subTitleIcon', 'information', 'user'
|
||||||
'user', 'registration', 'confirmation', 'registrationHost', 'confirmationHost'
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -13,24 +13,23 @@ declare(strict_types = 1);
|
|||||||
|
|
||||||
namespace FireflyIII\Http\Controllers;
|
namespace FireflyIII\Http\Controllers;
|
||||||
|
|
||||||
use Crypt;
|
|
||||||
use File;
|
use File;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Http\Requests\AttachmentFormRequest;
|
use FireflyIII\Http\Requests\AttachmentFormRequest;
|
||||||
use FireflyIII\Models\Attachment;
|
use FireflyIII\Models\Attachment;
|
||||||
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
|
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
|
||||||
use Input;
|
use Illuminate\Http\Response as LaravelResponse;
|
||||||
use Log;
|
|
||||||
use Preferences;
|
use Preferences;
|
||||||
use Response;
|
use Response;
|
||||||
use Session;
|
use Session;
|
||||||
use Storage;
|
|
||||||
use URL;
|
use URL;
|
||||||
use View;
|
use View;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class AttachmentController
|
* Class AttachmentController
|
||||||
*
|
*
|
||||||
|
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) // it's 13.
|
||||||
|
*
|
||||||
* @package FireflyIII\Http\Controllers
|
* @package FireflyIII\Http\Controllers
|
||||||
*/
|
*/
|
||||||
class AttachmentController extends Controller
|
class AttachmentController extends Controller
|
||||||
@@ -57,7 +56,7 @@ class AttachmentController extends Controller
|
|||||||
/**
|
/**
|
||||||
* @param Attachment $attachment
|
* @param Attachment $attachment
|
||||||
*
|
*
|
||||||
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
|
* @return View
|
||||||
*/
|
*/
|
||||||
public function delete(Attachment $attachment)
|
public function delete(Attachment $attachment)
|
||||||
{
|
{
|
||||||
@@ -90,25 +89,21 @@ class AttachmentController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Attachment $attachment
|
* @param AttachmentRepositoryInterface $repository
|
||||||
|
* @param Attachment $attachment
|
||||||
*
|
*
|
||||||
|
* @return mixed
|
||||||
* @throws FireflyException
|
* @throws FireflyException
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public function download(Attachment $attachment)
|
public function download(AttachmentRepositoryInterface $repository, Attachment $attachment)
|
||||||
{
|
{
|
||||||
// create a disk.
|
if ($repository->exists($attachment)) {
|
||||||
$disk = Storage::disk('upload');
|
$content = $repository->getContent($attachment);
|
||||||
$file = $attachment->fileName();
|
|
||||||
|
|
||||||
if ($disk->exists($file)) {
|
|
||||||
|
|
||||||
$quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\'));
|
$quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\'));
|
||||||
$content = Crypt::decrypt($disk->get($file));
|
|
||||||
|
|
||||||
Log::debug('Send file to user', ['file' => $quoted, 'size' => strlen($content)]);
|
/** @var LaravelResponse $response */
|
||||||
|
$response = response($content, 200);
|
||||||
return response($content, 200)
|
$response
|
||||||
->header('Content-Description', 'File Transfer')
|
->header('Content-Description', 'File Transfer')
|
||||||
->header('Content-Type', 'application/octet-stream')
|
->header('Content-Type', 'application/octet-stream')
|
||||||
->header('Content-Disposition', 'attachment; filename=' . $quoted)
|
->header('Content-Disposition', 'attachment; filename=' . $quoted)
|
||||||
@@ -119,6 +114,7 @@ class AttachmentController extends Controller
|
|||||||
->header('Pragma', 'public')
|
->header('Pragma', 'public')
|
||||||
->header('Content-Length', strlen($content));
|
->header('Content-Length', strlen($content));
|
||||||
|
|
||||||
|
return $response;
|
||||||
}
|
}
|
||||||
throw new FireflyException('Could not find the indicated attachment. The file is no longer there.');
|
throw new FireflyException('Could not find the indicated attachment. The file is no longer there.');
|
||||||
}
|
}
|
||||||
@@ -151,7 +147,6 @@ class AttachmentController extends Controller
|
|||||||
{
|
{
|
||||||
$image = 'images/page_green.png';
|
$image = 'images/page_green.png';
|
||||||
|
|
||||||
|
|
||||||
if ($attachment->mime == 'application/pdf') {
|
if ($attachment->mime == 'application/pdf') {
|
||||||
$image = 'images/page_white_acrobat.png';
|
$image = 'images/page_white_acrobat.png';
|
||||||
}
|
}
|
||||||
@@ -178,7 +173,7 @@ class AttachmentController extends Controller
|
|||||||
Session::flash('success', strval(trans('firefly.attachment_updated', ['name' => $attachment->filename])));
|
Session::flash('success', strval(trans('firefly.attachment_updated', ['name' => $attachment->filename])));
|
||||||
Preferences::mark();
|
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:
|
// set value so edit routine will not overwrite URL:
|
||||||
Session::put('attachments.edit.fromUpdate', true);
|
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;
|
namespace FireflyIII\Http\Controllers\Auth;
|
||||||
|
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
|
use FireflyIII\User;
|
||||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Password;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ForgotPasswordController
|
* Class ForgotPasswordController
|
||||||
@@ -33,4 +36,39 @@ class ForgotPasswordController extends Controller
|
|||||||
parent::__construct();
|
parent::__construct();
|
||||||
$this->middleware('guest');
|
$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)]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
48
app/Http/Controllers/Auth/LoginController.php
Executable file → Normal file
48
app/Http/Controllers/Auth/LoginController.php
Executable file → Normal file
@@ -14,9 +14,6 @@ namespace FireflyIII\Http\Controllers\Auth;
|
|||||||
|
|
||||||
use Config;
|
use Config;
|
||||||
use FireflyConfig;
|
use FireflyConfig;
|
||||||
use FireflyIII\Events\BlockedBadLogin;
|
|
||||||
use FireflyIII\Events\BlockedUserLogin;
|
|
||||||
use FireflyIII\Events\LockedOutUser;
|
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||||
@@ -53,9 +50,9 @@ class LoginController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Handle a login request to the application.
|
* 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)
|
public function login(Request $request)
|
||||||
{
|
{
|
||||||
@@ -64,8 +61,6 @@ class LoginController extends Controller
|
|||||||
if ($lockedOut) {
|
if ($lockedOut) {
|
||||||
$this->fireLockoutEvent($request);
|
$this->fireLockoutEvent($request);
|
||||||
|
|
||||||
event(new LockedOutUser($request->get('email'), $request->ip()));
|
|
||||||
|
|
||||||
return $this->sendLockoutResponse($request);
|
return $this->sendLockoutResponse($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,25 +71,8 @@ class LoginController extends Controller
|
|||||||
return $this->sendLoginResponse($request);
|
return $this->sendLoginResponse($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if user is blocked:
|
$errorMessage = $this->getBlockedError($credentials['email']);
|
||||||
$errorMessage = '';
|
|
||||||
/** @var User $foundUser */
|
|
||||||
$foundUser = User::where('email', $credentials['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' => $credentials['email']]));
|
|
||||||
event(new BlockedUserLogin($foundUser, $request->ip()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// simply a bad login.
|
|
||||||
if (is_null($foundUser)) {
|
|
||||||
event(new BlockedBadLogin($credentials['email'], $request->ip()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
if (!$lockedOut) {
|
||||||
$this->incrementLoginAttempts($request);
|
$this->incrementLoginAttempts($request);
|
||||||
}
|
}
|
||||||
@@ -159,4 +137,24 @@ class LoginController extends Controller
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $email
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
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]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $errorMessage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,8 +26,8 @@ use Illuminate\Support\Facades\Password;
|
|||||||
*
|
*
|
||||||
* @package FireflyIII\Http\Controllers\Auth
|
* @package FireflyIII\Http\Controllers\Auth
|
||||||
* @method getEmailSubject()
|
* @method getEmailSubject()
|
||||||
* @method getSendResetLinkEmailSuccessResponse()
|
* @method getSendResetLinkEmailSuccessResponse(string $response)
|
||||||
* @method getSendResetLinkEmailFailureResponse()
|
* @method getSendResetLinkEmailFailureResponse(string $response)
|
||||||
*/
|
*/
|
||||||
class PasswordController extends Controller
|
class PasswordController extends Controller
|
||||||
{
|
{
|
||||||
@@ -47,6 +47,7 @@ class PasswordController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a reset link to the given user.
|
* Send a reset link to the given user.
|
||||||
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's 7 but ok
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
*
|
*
|
||||||
|
61
app/Http/Controllers/Auth/RegisterController.php
Executable file → Normal file
61
app/Http/Controllers/Auth/RegisterController.php
Executable file → Normal file
@@ -15,18 +15,12 @@ namespace FireflyIII\Http\Controllers\Auth;
|
|||||||
use Auth;
|
use Auth;
|
||||||
use Config;
|
use Config;
|
||||||
use FireflyConfig;
|
use FireflyConfig;
|
||||||
use FireflyIII\Events\BlockedUseOfDomain;
|
|
||||||
use FireflyIII\Events\BlockedUseOfEmail;
|
|
||||||
use FireflyIII\Events\RegisteredUser;
|
use FireflyIII\Events\RegisteredUser;
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
use Illuminate\Foundation\Auth\RegistersUsers;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Mail\Message;
|
|
||||||
use Log;
|
|
||||||
use Mail;
|
|
||||||
use Session;
|
use Session;
|
||||||
use Swift_TransportException;
|
|
||||||
use Validator;
|
use Validator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,30 +72,6 @@ class RegisterController extends Controller
|
|||||||
$this->throwValidationException($request, $validator);
|
$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'));
|
|
||||||
|
|
||||||
event(new BlockedUseOfDomain($data['email'], $request->ip()));
|
|
||||||
$this->throwValidationException($request, $validator);
|
|
||||||
}
|
|
||||||
|
|
||||||
// is user a deleted user?
|
|
||||||
$hash = hash('sha256', $data['email']);
|
|
||||||
$configuration = FireflyConfig::get('deleted_users', []);
|
|
||||||
$set = $configuration->data;
|
|
||||||
Log::debug(sprintf('Hash of email is %s', $hash));
|
|
||||||
Log::debug('Hashes of deleted users: ', $set);
|
|
||||||
if (in_array($hash, $set)) {
|
|
||||||
$validator->getMessageBag()->add('email', (string)trans('validation.deleted_user'));
|
|
||||||
event(new BlockedUseOfEmail($data['email'], $request->ip()));
|
|
||||||
$this->throwValidationException($request, $validator);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$user = $this->create($request->all());
|
$user = $this->create($request->all());
|
||||||
|
|
||||||
// trigger user registration event:
|
// trigger user registration event:
|
||||||
@@ -129,9 +99,6 @@ class RegisterController extends Controller
|
|||||||
// is demo site?
|
// is demo site?
|
||||||
$isDemoSite = FireflyConfig::get('is_demo_site', Config::get('firefly.configuration.is_demo_site'))->data;
|
$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?
|
// is allowed to?
|
||||||
$singleUserMode = FireflyConfig::get('single_user_mode', Config::get('firefly.configuration.single_user_mode'))->data;
|
$singleUserMode = FireflyConfig::get('single_user_mode', Config::get('firefly.configuration.single_user_mode'))->data;
|
||||||
$userCount = User::count();
|
$userCount = User::count();
|
||||||
@@ -143,7 +110,7 @@ class RegisterController extends Controller
|
|||||||
|
|
||||||
$email = $request->old('email');
|
$email = $request->old('email');
|
||||||
|
|
||||||
return view('auth.register', compact('isDemoSite', 'email', 'mustConfirmAccount'));
|
return view('auth.register', compact('isDemoSite', 'email'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -179,30 +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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
0
app/Http/Controllers/Auth/ResetPasswordController.php
Executable file → Normal file
0
app/Http/Controllers/Auth/ResetPasswordController.php
Executable file → Normal file
@@ -76,6 +76,7 @@ class TwoFactorController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param TokenFormRequest $request
|
* @param TokenFormRequest $request
|
||||||
|
* @SuppressWarnings(PHPMD.UnusedFormalParameter) // it's unused but the class does some validation.
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
|
@@ -14,13 +14,13 @@ declare(strict_types = 1);
|
|||||||
namespace FireflyIII\Http\Controllers;
|
namespace FireflyIII\Http\Controllers;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
use FireflyIII\Http\Requests\BillFormRequest;
|
use FireflyIII\Http\Requests\BillFormRequest;
|
||||||
use FireflyIII\Models\Bill;
|
use FireflyIII\Models\Bill;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Input;
|
|
||||||
use Preferences;
|
use Preferences;
|
||||||
use Session;
|
use Session;
|
||||||
use URL;
|
use URL;
|
||||||
@@ -99,13 +99,20 @@ class BillController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function destroy(BillRepositoryInterface $repository, Bill $bill)
|
public function destroy(BillRepositoryInterface $repository, Bill $bill)
|
||||||
{
|
{
|
||||||
$name = $bill->name;
|
$name = $bill->name;
|
||||||
|
$billId = $bill->id;
|
||||||
$repository->destroy($bill);
|
$repository->destroy($bill);
|
||||||
|
|
||||||
Session::flash('success', strval(trans('firefly.deleted_bill', ['name' => $name])));
|
Session::flash('success', strval(trans('firefly.deleted_bill', ['name' => $name])));
|
||||||
Preferences::mark();
|
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 BillRepositoryInterface $repository
|
||||||
* @param Bill $bill
|
* @param Bill $bill
|
||||||
*
|
*
|
||||||
* @return View
|
* @return View
|
||||||
*/
|
*/
|
||||||
public function show(BillRepositoryInterface $repository, Bill $bill)
|
public function show(Request $request, BillRepositoryInterface $repository, Bill $bill)
|
||||||
{
|
{
|
||||||
/** @var Carbon $date */
|
/** @var Carbon $date */
|
||||||
$date = session('start');
|
$date = session('start');
|
||||||
$year = $date->year;
|
$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);
|
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||||
$yearAverage = $repository->getYearAverage($bill, $date);
|
$yearAverage = $repository->getYearAverage($bill, $date);
|
||||||
$overallAverage = $repository->getOverallAverage($bill);
|
$overallAverage = $repository->getOverallAverage($bill);
|
||||||
|
|
||||||
// use collector:
|
// use collector:
|
||||||
$collector = new JournalCollector(auth()->user());
|
/** @var JournalCollectorInterface $collector */
|
||||||
$collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->setPage($page)->setLimit($pageSize);
|
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||||
|
$collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->setLimit($pageSize)->setPage($page)->withBudgetInformation()
|
||||||
|
->withCategoryInformation();
|
||||||
$journals = $collector->getPaginatedJournals();
|
$journals = $collector->getPaginatedJournals();
|
||||||
$journals->setPath('/bills/show/' . $bill->id);
|
$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)])));
|
Session::flash('success', strval(trans('firefly.stored_new_bill', ['name' => e($bill->name)])));
|
||||||
Preferences::mark();
|
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:
|
// set value so create routine will not overwrite URL:
|
||||||
Session::put('bills.create.fromStore', true);
|
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)])));
|
Session::flash('success', strval(trans('firefly.updated_bill', ['name' => e($bill->name)])));
|
||||||
Preferences::mark();
|
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:
|
// set value so edit routine will not overwrite URL:
|
||||||
Session::put('bills.edit.fromUpdate', true);
|
Session::put('bills.edit.fromUpdate', true);
|
||||||
|
|
||||||
|
@@ -15,19 +15,18 @@ namespace FireflyIII\Http\Controllers;
|
|||||||
|
|
||||||
use Amount;
|
use Amount;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Config;
|
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
use FireflyIII\Http\Requests\BudgetFormRequest;
|
use FireflyIII\Http\Requests\BudgetFormRequest;
|
||||||
|
use FireflyIII\Http\Requests\BudgetIncomeRequest;
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\Budget;
|
||||||
use FireflyIII\Models\LimitRepetition;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
|
use FireflyIII\Support\CacheProperties;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Input;
|
|
||||||
use Log;
|
|
||||||
use Navigation;
|
|
||||||
use Preferences;
|
use Preferences;
|
||||||
use Response;
|
use Response;
|
||||||
use Session;
|
use Session;
|
||||||
@@ -42,6 +41,9 @@ use View;
|
|||||||
class BudgetController extends Controller
|
class BudgetController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/** @var BudgetRepositoryInterface */
|
||||||
|
private $repository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -55,6 +57,7 @@ class BudgetController extends Controller
|
|||||||
function ($request, $next) {
|
function ($request, $next) {
|
||||||
View::share('title', trans('firefly.budgets'));
|
View::share('title', trans('firefly.budgets'));
|
||||||
View::share('mainTitleIcon', 'fa-tasks');
|
View::share('mainTitleIcon', 'fa-tasks');
|
||||||
|
$this->repository = app(BudgetRepositoryInterface::class);
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
@@ -62,32 +65,25 @@ class BudgetController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param BudgetRepositoryInterface $repository
|
* @param Request $request
|
||||||
* @param Budget $budget
|
* @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 */
|
/** @var Carbon $start */
|
||||||
$start = session('start', Carbon::now()->startOfMonth());
|
$start = session('start', Carbon::now()->startOfMonth());
|
||||||
/** @var Carbon $end */
|
/** @var Carbon $end */
|
||||||
$end = session('end', Carbon::now()->endOfMonth());
|
$end = session('end', Carbon::now()->endOfMonth());
|
||||||
$viewRange = Preferences::get('viewRange', '1M')->data;
|
$budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount);
|
||||||
|
|
||||||
// is custom view range?
|
|
||||||
if (session('is_custom_range') === true) {
|
|
||||||
$viewRange = 'custom';
|
|
||||||
}
|
|
||||||
|
|
||||||
$limitRepetition = $repository->updateLimitAmount($budget, $start, $end, $viewRange, $amount);
|
|
||||||
if ($amount == 0) {
|
if ($amount == 0) {
|
||||||
$limitRepetition = null;
|
$budgetLimit = null;
|
||||||
}
|
}
|
||||||
Preferences::mark();
|
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]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,23 +122,28 @@ class BudgetController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Budget $budget
|
* @param Budget $budget
|
||||||
* @param BudgetRepositoryInterface $repository
|
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function destroy(Budget $budget, BudgetRepositoryInterface $repository)
|
public function destroy(Budget $budget)
|
||||||
{
|
{
|
||||||
|
|
||||||
$name = $budget->name;
|
$name = $budget->name;
|
||||||
$repository->destroy($budget);
|
$budgetId = $budget->id;
|
||||||
|
$this->repository->destroy($budget);
|
||||||
|
|
||||||
|
|
||||||
Session::flash('success', strval(trans('firefly.deleted_budget', ['name' => e($name)])));
|
Session::flash('success', strval(trans('firefly.deleted_budget', ['name' => e($name)])));
|
||||||
Preferences::mark();
|
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
|
* @return View
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public function index(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository)
|
public function index()
|
||||||
{
|
{
|
||||||
$repository->cleanupBudgets();
|
$this->repository->cleanupBudgets();
|
||||||
|
|
||||||
$budgets = $repository->getActiveBudgets();
|
$budgets = $this->repository->getActiveBudgets();
|
||||||
$inactive = $repository->getInactiveBudgets();
|
$inactive = $this->repository->getInactiveBudgets();
|
||||||
$spent = '0';
|
$start = session('start', new Carbon);
|
||||||
$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 */
|
|
||||||
$start = session('start', new Carbon);
|
|
||||||
/** @var Carbon $end */
|
|
||||||
$end = session('end', new Carbon);
|
$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);
|
$periodStart = $start->formatLocalized($this->monthAndDayFormat);
|
||||||
$periodEnd = $end->formatLocalized($this->monthAndDayFormat);
|
$periodEnd = $end->formatLocalized($this->monthAndDayFormat);
|
||||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
$budgetInformation = $this->collectBudgetInformation($budgets, $start, $end);
|
||||||
$startAsString = $start->format('Y-m-d');
|
$defaultCurrency = Amount::getDefaultCurrency();
|
||||||
$endAsString = $end->format('Y-m-d');
|
$available = $this->repository->getAvailableBudget($defaultCurrency, $start, $end);
|
||||||
Log::debug('Now at /budgets');
|
$spent = array_sum(array_column($budgetInformation, 'spent'));
|
||||||
|
$budgeted = array_sum(array_column($budgetInformation, 'budgeted'));
|
||||||
// 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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$defaultCurrency = Amount::getDefaultCurrency();
|
|
||||||
|
|
||||||
return view(
|
return view(
|
||||||
'budgets.index', compact(
|
'budgets.index',
|
||||||
'periodStart', 'periodEnd',
|
compact('available', 'periodStart', 'periodEnd', 'budgetInformation', 'inactive', 'budgets', 'spent', 'budgeted')
|
||||||
'period', 'range', 'budgetIncomeTotal',
|
|
||||||
'defaultCurrency', 'inactive', 'budgets',
|
|
||||||
'spent', 'budgeted'
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
* @return View
|
* @return View
|
||||||
*/
|
*/
|
||||||
public function noBudget()
|
public function noBudget(Request $request)
|
||||||
{
|
{
|
||||||
/** @var Carbon $start */
|
/** @var Carbon $start */
|
||||||
$start = session('start', Carbon::now()->startOfMonth());
|
$start = session('start', Carbon::now()->startOfMonth());
|
||||||
/** @var Carbon $end */
|
/** @var Carbon $end */
|
||||||
$end = session('end', Carbon::now()->endOfMonth());
|
$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);
|
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||||
$subTitle = trans(
|
$subTitle = trans(
|
||||||
'firefly.without_budget_between',
|
'firefly.without_budget_between',
|
||||||
@@ -263,7 +211,8 @@ class BudgetController extends Controller
|
|||||||
);
|
);
|
||||||
|
|
||||||
// collector
|
// 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();
|
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutBudget();
|
||||||
$journals = $collector->getPaginatedJournals();
|
$journals = $collector->getPaginatedJournals();
|
||||||
$journals->setPath('/budgets/list/noBudget');
|
$journals->setPath('/budgets/list/noBudget');
|
||||||
@@ -274,113 +223,102 @@ class BudgetController extends Controller
|
|||||||
/**
|
/**
|
||||||
* @return \Illuminate\Http\RedirectResponse
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function postUpdateIncome()
|
public function postUpdateIncome(BudgetIncomeRequest $request)
|
||||||
{
|
{
|
||||||
$range = Preferences::get('viewRange', '1M')->data;
|
$start = session('start', new Carbon);
|
||||||
/** @var Carbon $date */
|
$end = session('end', new Carbon);
|
||||||
$date = session('start', new Carbon);
|
$defaultCurrency = Amount::getDefaultCurrency();
|
||||||
$start = Navigation::startOfPeriod($date, $range);
|
$amount = $request->get('amount');
|
||||||
$end = Navigation::endOfPeriod($start, $range);
|
|
||||||
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
|
|
||||||
|
|
||||||
Preferences::set($key, intval(Input::get('amount')));
|
$this->repository->setAvailableBudget($defaultCurrency, $start, $end, $amount);
|
||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
return redirect(route('budgets.index'));
|
return redirect(route('budgets.index'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param BudgetRepositoryInterface $repository
|
* @param Request $request
|
||||||
* @param AccountRepositoryInterface $accountRepository
|
* @param Budget $budget
|
||||||
* @param Budget $budget
|
|
||||||
*
|
*
|
||||||
* @return View
|
* @return View
|
||||||
*/
|
*/
|
||||||
public function show(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget)
|
public function show(Request $request, Budget $budget)
|
||||||
{
|
{
|
||||||
/** @var Carbon $start */
|
/** @var Carbon $start */
|
||||||
$start = session('first', Carbon::create()->startOfYear());
|
$start = session('first', Carbon::create()->startOfYear());
|
||||||
$end = new Carbon;
|
$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);
|
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
$limits = $this->getLimits($budget, $start, $end);
|
||||||
$repetition = null;
|
$repetition = null;
|
||||||
// collector:
|
// collector:
|
||||||
$collector = new JournalCollector(auth()->user());
|
/** @var JournalCollectorInterface $collector */
|
||||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page);
|
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||||
|
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page)->withCategoryInformation();
|
||||||
$journals = $collector->getPaginatedJournals();
|
$journals = $collector->getPaginatedJournals();
|
||||||
$journals->setPath('/budgets/show/' . $budget->id);
|
$journals->setPath('/budgets/show/' . $budget->id);
|
||||||
|
|
||||||
|
|
||||||
$set = $budget->limitrepetitions()->orderBy('startdate', 'DESC')->get();
|
|
||||||
$subTitle = e($budget->name);
|
$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'));
|
return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Budget $budget
|
* @param Request $request
|
||||||
* @param LimitRepetition $repetition
|
* @param Budget $budget
|
||||||
|
* @param BudgetLimit $budgetLimit
|
||||||
*
|
*
|
||||||
* @return View
|
* @return View
|
||||||
* @throws FireflyException
|
* @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.');
|
throw new FireflyException('This budget limit is not part of this budget.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var BudgetRepositoryInterface $repository */
|
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||||
$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'));
|
|
||||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||||
$subTitle = trans(
|
$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:
|
||||||
$collector = new JournalCollector(auth()->user());
|
/** @var JournalCollectorInterface $collector */
|
||||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page);
|
$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 = $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);
|
$start = session('first', Carbon::create()->startOfYear());
|
||||||
$limits = new Collection([$repetition]);
|
$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 BudgetFormRequest $request
|
||||||
* @param BudgetRepositoryInterface $repository
|
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function store(BudgetFormRequest $request, BudgetRepositoryInterface $repository)
|
public function store(BudgetFormRequest $request)
|
||||||
{
|
{
|
||||||
$data = $request->getBudgetData();
|
$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)])));
|
Session::flash('success', strval(trans('firefly.stored_new_budget', ['name' => e($budget->name)])));
|
||||||
Preferences::mark();
|
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:
|
// set value so create routine will not overwrite URL:
|
||||||
Session::put('budgets.create.fromStore', true);
|
Session::put('budgets.create.fromStore', true);
|
||||||
|
|
||||||
@@ -393,21 +331,20 @@ class BudgetController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param BudgetFormRequest $request
|
* @param BudgetFormRequest $request
|
||||||
* @param BudgetRepositoryInterface $repository
|
* @param Budget $budget
|
||||||
* @param Budget $budget
|
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function update(BudgetFormRequest $request, BudgetRepositoryInterface $repository, Budget $budget)
|
public function update(BudgetFormRequest $request, Budget $budget)
|
||||||
{
|
{
|
||||||
$data = $request->getBudgetData();
|
$data = $request->getBudgetData();
|
||||||
$repository->update($budget, $data);
|
$this->repository->update($budget, $data);
|
||||||
|
|
||||||
Session::flash('success', strval(trans('firefly.updated_budget', ['name' => e($budget->name)])));
|
Session::flash('success', strval(trans('firefly.updated_budget', ['name' => e($budget->name)])));
|
||||||
Preferences::mark();
|
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:
|
// set value so edit routine will not overwrite URL:
|
||||||
Session::put('budgets.edit.fromUpdate', true);
|
Session::put('budgets.edit.fromUpdate', true);
|
||||||
|
|
||||||
@@ -424,19 +361,93 @@ class BudgetController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function updateIncome()
|
public function updateIncome()
|
||||||
{
|
{
|
||||||
$range = Preferences::get('viewRange', '1M')->data;
|
$start = session('start', new Carbon);
|
||||||
$format = strval(trans('config.month_and_day'));
|
$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,16 +14,15 @@ declare(strict_types = 1);
|
|||||||
namespace FireflyIII\Http\Controllers;
|
namespace FireflyIII\Http\Controllers;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
|
||||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
use FireflyIII\Http\Requests\CategoryFormRequest;
|
use FireflyIII\Http\Requests\CategoryFormRequest;
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\Category;
|
use FireflyIII\Models\Category;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
|
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Input;
|
|
||||||
use Navigation;
|
use Navigation;
|
||||||
use Preferences;
|
use Preferences;
|
||||||
use Session;
|
use Session;
|
||||||
@@ -89,22 +88,30 @@ class CategoryController extends Controller
|
|||||||
return view('categories.delete', compact('category', 'subTitle'));
|
return view('categories.delete', compact('category', 'subTitle'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param CRI $repository
|
* @param CategoryRepositoryInterface $repository
|
||||||
* @param Category $category
|
* @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;
|
$name = $category->name;
|
||||||
|
$categoryId = $category->id;
|
||||||
$repository->destroy($category);
|
$repository->destroy($category);
|
||||||
|
|
||||||
Session::flash('success', strval(trans('firefly.deleted_category', ['name' => e($name)])));
|
Session::flash('success', strval(trans('firefly.deleted_category', ['name' => e($name)])));
|
||||||
Preferences::mark();
|
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
|
* @return View
|
||||||
*/
|
*/
|
||||||
public function index(CRI $repository)
|
public function index(CategoryRepositoryInterface $repository)
|
||||||
{
|
{
|
||||||
$categories = $repository->getCategories();
|
$categories = $repository->getCategories();
|
||||||
|
|
||||||
@@ -157,7 +164,8 @@ class CategoryController extends Controller
|
|||||||
$end = session('end', Carbon::now()->startOfMonth());
|
$end = session('end', Carbon::now()->startOfMonth());
|
||||||
|
|
||||||
// new collector:
|
// new collector:
|
||||||
$collector = new JournalCollector(auth()->user());
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||||
$collector->setAllAssetAccounts()->setRange($start, $end)->withoutCategory();//->groupJournals();
|
$collector->setAllAssetAccounts()->setRange($start, $end)->withoutCategory();//->groupJournals();
|
||||||
$journals = $collector->getJournals();
|
$journals = $collector->getJournals();
|
||||||
$subTitle = trans(
|
$subTitle = trans(
|
||||||
@@ -169,110 +177,102 @@ class CategoryController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param CRI $repository
|
* @param Request $request
|
||||||
* @param AccountRepositoryInterface $accountRepository
|
* @param JournalCollectorInterface $collector
|
||||||
* @param Category $category
|
* @param Category $category
|
||||||
*
|
*
|
||||||
* @return View
|
* @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;
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
/** @var Carbon $start */
|
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||||
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
|
|
||||||
/** @var Carbon $end */
|
|
||||||
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
|
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
|
||||||
$hideCategory = true; // used in list.
|
$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);
|
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||||
$subTitle = $category->name;
|
$subTitle = $category->name;
|
||||||
$subTitleIcon = 'fa-bar-chart';
|
$subTitleIcon = 'fa-bar-chart';
|
||||||
|
$entries = $this->getGroupedEntries($category);
|
||||||
|
$method = 'default';
|
||||||
|
|
||||||
// use journal collector
|
// get journals
|
||||||
$collector = app(JournalCollectorInterface::class);
|
$collector->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category)->withBudgetInformation();
|
||||||
$collector->setPage($page)->setLimit($pageSize)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category);
|
|
||||||
$journals = $collector->getPaginatedJournals();
|
$journals = $collector->getPaginatedJournals();
|
||||||
$journals->setPath('categories/show/' . $category->id);
|
$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);
|
$start = $repository->firstUseDate($category);
|
||||||
if ($start->year == 1900) {
|
if ($start->year == 1900) {
|
||||||
$start = new Carbon;
|
$start = new Carbon;
|
||||||
}
|
}
|
||||||
$range = Preferences::get('viewRange', '1M')->data;
|
$end = Navigation::endOfPeriod(new Carbon, $range);
|
||||||
$start = Navigation::startOfPeriod($start, $range);
|
$subTitle = $category->name;
|
||||||
$end = Navigation::endOfX(new Carbon, $range);
|
$subTitleIcon = 'fa-bar-chart';
|
||||||
$entries = new Collection;
|
$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:
|
/** @var JournalCollectorInterface $collector */
|
||||||
$cache = new CacheProperties();
|
$collector = app(JournalCollectorInterface::class);
|
||||||
$cache->addProperty($start);
|
$collector->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setCategory($category)->withBudgetInformation();
|
||||||
$cache->addProperty($end);
|
$journals = $collector->getPaginatedJournals();
|
||||||
$cache->addProperty('category-show');
|
$journals->setPath('categories/show/' . $category->id . '/all');
|
||||||
$cache->addProperty($category->id);
|
|
||||||
|
|
||||||
|
return view('categories.show', compact('category', 'method', 'journals', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end'));
|
||||||
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'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param Request $request
|
||||||
* @param Category $category
|
* @param Category $category
|
||||||
* @param $date
|
* @param string $date
|
||||||
*
|
*
|
||||||
* @return View
|
* @return View
|
||||||
*/
|
*/
|
||||||
public function showByDate(Category $category, string $date)
|
public function showByDate(Request $request, Category $category, string $date)
|
||||||
{
|
{
|
||||||
$carbon = new Carbon($date);
|
$carbon = new Carbon($date);
|
||||||
$range = Preferences::get('viewRange', '1M')->data;
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
$start = Navigation::startOfPeriod($carbon, $range);
|
$start = Navigation::startOfPeriod($carbon, $range);
|
||||||
$end = Navigation::endOfPeriod($carbon, $range);
|
$end = Navigation::endOfPeriod($carbon, $range);
|
||||||
$subTitle = $category->name;
|
$subTitle = $category->name;
|
||||||
|
$subTitleIcon = 'fa-bar-chart';
|
||||||
$hideCategory = true; // used in list.
|
$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);
|
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||||
|
$entries = $this->getGroupedEntries($category);
|
||||||
|
$method = 'date';
|
||||||
|
|
||||||
// new collector:
|
/** @var JournalCollectorInterface $collector */
|
||||||
$collector = app(JournalCollectorInterface::class);
|
$collector = app(JournalCollectorInterface::class);
|
||||||
$collector->setPage($page)->setLimit($pageSize)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category);
|
$collector->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category)->withBudgetInformation();
|
||||||
$journals = $collector->getPaginatedJournals();
|
$journals = $collector->getPaginatedJournals();
|
||||||
$journals->setPath('categories/show/' . $category->id . '/' . $date);
|
$journals->setPath('categories/show/' . $category->id . '/' . $date);
|
||||||
|
|
||||||
|
return view('categories.show', compact('category', 'method', 'entries', 'journals', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end'));
|
||||||
return view('categories.show-by-date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param CategoryFormRequest $request
|
* @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();
|
$data = $request->getCategoryData();
|
||||||
$category = $repository->store($data);
|
$category = $repository->store($data);
|
||||||
@@ -280,7 +280,7 @@ class CategoryController extends Controller
|
|||||||
Session::flash('success', strval(trans('firefly.stored_category', ['name' => e($category->name)])));
|
Session::flash('success', strval(trans('firefly.stored_category', ['name' => e($category->name)])));
|
||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
if (intval(Input::get('create_another')) === 1) {
|
if (intval($request->get('create_another')) === 1) {
|
||||||
Session::put('categories.create.fromStore', true);
|
Session::put('categories.create.fromStore', true);
|
||||||
|
|
||||||
return redirect(route('categories.create'))->withInput();
|
return redirect(route('categories.create'))->withInput();
|
||||||
@@ -291,13 +291,13 @@ class CategoryController extends Controller
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param CategoryFormRequest $request
|
* @param CategoryFormRequest $request
|
||||||
* @param CRI $repository
|
* @param CategoryRepositoryInterface $repository
|
||||||
* @param Category $category
|
* @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();
|
$data = $request->getCategoryData();
|
||||||
$repository->update($category, $data);
|
$repository->update($category, $data);
|
||||||
@@ -305,7 +305,7 @@ class CategoryController extends Controller
|
|||||||
Session::flash('success', strval(trans('firefly.updated_category', ['name' => e($category->name)])));
|
Session::flash('success', strval(trans('firefly.updated_category', ['name' => e($category->name)])));
|
||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
if (intval(Input::get('return_to_edit')) === 1) {
|
if (intval($request->get('return_to_edit')) === 1) {
|
||||||
Session::put('categories.edit.fromUpdate', true);
|
Session::put('categories.edit.fromUpdate', true);
|
||||||
|
|
||||||
return redirect(route('categories.edit', [$category->id]));
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -128,7 +128,7 @@ class AccountController extends Controller
|
|||||||
$endBalance = $endBalances[$id] ?? '0';
|
$endBalance = $endBalances[$id] ?? '0';
|
||||||
$diff = bcsub($endBalance, $startBalance);
|
$diff = bcsub($endBalance, $startBalance);
|
||||||
if (bccomp($diff, '0') !== 0) {
|
if (bccomp($diff, '0') !== 0) {
|
||||||
$chartData[$account->name] = round($diff, 2);
|
$chartData[$account->name] = $diff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
arsort($chartData);
|
arsort($chartData);
|
||||||
@@ -391,7 +391,7 @@ class AccountController extends Controller
|
|||||||
$diff = bcsub($endBalance, $startBalance);
|
$diff = bcsub($endBalance, $startBalance);
|
||||||
$diff = bcmul($diff, '-1');
|
$diff = bcmul($diff, '-1');
|
||||||
if (bccomp($diff, '0') !== 0) {
|
if (bccomp($diff, '0') !== 0) {
|
||||||
$chartData[$account->name] = round($diff, 2);
|
$chartData[$account->name] = $diff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,11 +475,11 @@ class AccountController extends Controller
|
|||||||
];
|
];
|
||||||
$currentStart = clone $start;
|
$currentStart = clone $start;
|
||||||
$range = Steam::balanceInRange($account, $start, clone $end);
|
$range = Steam::balanceInRange($account, $start, clone $end);
|
||||||
$previous = round(array_values($range)[0], 2);
|
$previous = array_values($range)[0];
|
||||||
while ($currentStart <= $end) {
|
while ($currentStart <= $end) {
|
||||||
$format = $currentStart->format('Y-m-d');
|
$format = $currentStart->format('Y-m-d');
|
||||||
$label = $currentStart->formatLocalized(strval(trans('config.month_and_day')));
|
$label = $currentStart->formatLocalized(strval(trans('config.month_and_day')));
|
||||||
$balance = isset($range[$format]) ? round($range[$format], 2) : $previous;
|
$balance = isset($range[$format]) ? round($range[$format], 12) : $previous;
|
||||||
$previous = $balance;
|
$previous = $balance;
|
||||||
$currentStart->addDay();
|
$currentStart->addDay();
|
||||||
$currentSet['entries'][$label] = $balance;
|
$currentSet['entries'][$label] = $balance;
|
||||||
|
@@ -91,40 +91,24 @@ class BillController extends Controller
|
|||||||
return Response::json($cache->get());
|
return Response::json($cache->get());
|
||||||
}
|
}
|
||||||
|
|
||||||
$results = $collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->getJournals();
|
$results = $collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->getJournals();
|
||||||
$results = $results->sortBy(
|
$results = $results->sortBy(
|
||||||
function (Transaction $transaction) {
|
function (Transaction $transaction) {
|
||||||
return $transaction->date->format('U');
|
return $transaction->date->format('U');
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
$chartData = [
|
$chartData = [
|
||||||
[
|
['type' => 'bar', 'label' => trans('firefly.min-amount'), 'entries' => [],],
|
||||||
'type' => 'bar',
|
['type' => 'bar', 'label' => trans('firefly.max-amount'), 'entries' => [],],
|
||||||
'label' => trans('firefly.min-amount'),
|
['type' => 'line', 'label' => trans('firefly.journal-amount'), 'entries' => [],],
|
||||||
'entries' => [],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'type' => 'bar',
|
|
||||||
'label' => trans('firefly.max-amount'),
|
|
||||||
'entries' => [],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'type' => 'line',
|
|
||||||
'label' => trans('firefly.journal-amount'),
|
|
||||||
'entries' => [],
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @var Transaction $entry */
|
/** @var Transaction $entry */
|
||||||
foreach ($results as $entry) {
|
foreach ($results as $entry) {
|
||||||
$date = $entry->date->formatLocalized(strval(trans('config.month_and_day')));
|
$date = $entry->date->formatLocalized(strval(trans('config.month_and_day')));
|
||||||
// minimum amount of bill:
|
$chartData[0]['entries'][$date] = $bill->amount_min; // minimum amount of bill
|
||||||
$chartData[0]['entries'][$date] = $bill->amount_min;
|
$chartData[1]['entries'][$date] = $bill->amount_max; // maximum amount of bill
|
||||||
// maximum amount of bill:
|
$chartData[2]['entries'][$date] = bcmul($entry->transaction_amount, '-1'); // amount of journal
|
||||||
$chartData[1]['entries'][$date] = $bill->amount_max;
|
|
||||||
// amount of journal:
|
|
||||||
$chartData[2]['entries'][$date] = bcmul($entry->transaction_amount, '-1');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $this->generator->multiSet($chartData);
|
$data = $this->generator->multiSet($chartData);
|
||||||
|
@@ -14,11 +14,12 @@ declare(strict_types = 1);
|
|||||||
namespace FireflyIII\Http\Controllers\Chart;
|
namespace FireflyIII\Http\Controllers\Chart;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\Budget;
|
||||||
use FireflyIII\Models\LimitRepetition;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
@@ -31,6 +32,8 @@ use Response;
|
|||||||
/**
|
/**
|
||||||
* Class BudgetController
|
* Class BudgetController
|
||||||
*
|
*
|
||||||
|
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) // can't realy be helped.
|
||||||
|
*
|
||||||
* @package FireflyIII\Http\Controllers\Chart
|
* @package FireflyIII\Http\Controllers\Chart
|
||||||
*/
|
*/
|
||||||
class BudgetController extends Controller
|
class BudgetController extends Controller
|
||||||
@@ -39,26 +42,35 @@ class BudgetController extends Controller
|
|||||||
/** @var GeneratorInterface */
|
/** @var GeneratorInterface */
|
||||||
protected $generator;
|
protected $generator;
|
||||||
|
|
||||||
|
/** @var BudgetRepositoryInterface */
|
||||||
|
protected $repository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BudgetController constructor.
|
* BudgetController constructor.
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
$this->generator = app(GeneratorInterface::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
|
||||||
* @param Budget $budget
|
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
* @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;
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
$last = session('end', new Carbon);
|
$last = session('end', new Carbon);
|
||||||
|
|
||||||
@@ -73,7 +85,6 @@ class BudgetController extends Controller
|
|||||||
|
|
||||||
$final = clone $last;
|
$final = clone $last;
|
||||||
$final->addYears(2);
|
$final->addYears(2);
|
||||||
|
|
||||||
$budgetCollection = new Collection([$budget]);
|
$budgetCollection = new Collection([$budget]);
|
||||||
$last = Navigation::endOfX($last, $range, $final); // not to overshoot.
|
$last = Navigation::endOfX($last, $range, $final); // not to overshoot.
|
||||||
$entries = [];
|
$entries = [];
|
||||||
@@ -84,7 +95,7 @@ class BudgetController extends Controller
|
|||||||
$currentEnd = Navigation::endOfPeriod($first, $range);
|
$currentEnd = Navigation::endOfPeriod($first, $range);
|
||||||
// sub another day because reasons.
|
// sub another day because reasons.
|
||||||
$currentEnd->subDay();
|
$currentEnd->subDay();
|
||||||
$spent = $repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd);
|
$spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd);
|
||||||
$format = Navigation::periodShow($first, $range);
|
$format = Navigation::periodShow($first, $range);
|
||||||
$entries[$format] = bcmul($spent, '-1');
|
$entries[$format] = bcmul($spent, '-1');
|
||||||
$first = Navigation::addPeriod($first, $range, 0);
|
$first = Navigation::addPeriod($first, $range, 0);
|
||||||
@@ -100,31 +111,36 @@ class BudgetController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Shows the amount left in a specific budget limit.
|
* Shows the amount left in a specific budget limit.
|
||||||
*
|
*
|
||||||
* @param BudgetRepositoryInterface $repository
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
|
||||||
* @param Budget $budget
|
* @param Budget $budget
|
||||||
* @param LimitRepetition $repetition
|
* @param BudgetLimit $budgetLimit
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
* @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;
|
if ($budgetLimit->budget->id != $budget->id) {
|
||||||
$end = $repetition->enddate;
|
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 = new CacheProperties();
|
||||||
$cache->addProperty($start);
|
$cache->addProperty($start);
|
||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
$cache->addProperty('chart.budget.budget.limit');
|
$cache->addProperty('chart.budget.budget.limit');
|
||||||
$cache->addProperty($repetition->id);
|
$cache->addProperty($budgetLimit->id);
|
||||||
|
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get());
|
||||||
}
|
}
|
||||||
|
|
||||||
$entries = [];
|
$entries = [];
|
||||||
$amount = $repetition->amount;
|
$amount = $budgetLimit->amount;
|
||||||
$budgetCollection = new Collection([$budget]);
|
$budgetCollection = new Collection([$budget]);
|
||||||
while ($start <= $end) {
|
while ($start <= $end) {
|
||||||
$spent = $repository->spentInPeriod($budgetCollection, new Collection, $start, $start);
|
$spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $start, $start);
|
||||||
$amount = bcadd($amount, $spent);
|
$amount = bcadd($amount, $spent);
|
||||||
$format = $start->formatLocalized(strval(trans('config.month_and_day')));
|
$format = $start->formatLocalized(strval(trans('config.month_and_day')));
|
||||||
$entries[$format] = $amount;
|
$entries[$format] = $amount;
|
||||||
@@ -139,12 +155,12 @@ class BudgetController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows a budget list with spent/left/overspent.
|
* Shows a budget list with spent/left/overspent.
|
||||||
*
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
|
||||||
* @param BudgetRepositoryInterface $repository
|
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) // 46 lines, I'm fine with this.
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
*/
|
*/
|
||||||
public function frontpage(BudgetRepositoryInterface $repository)
|
public function frontpage()
|
||||||
{
|
{
|
||||||
$start = session('start', Carbon::now()->startOfMonth());
|
$start = session('start', Carbon::now()->startOfMonth());
|
||||||
$end = session('end', Carbon::now()->endOfMonth());
|
$end = session('end', Carbon::now()->endOfMonth());
|
||||||
@@ -156,58 +172,32 @@ class BudgetController extends Controller
|
|||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get());
|
||||||
}
|
}
|
||||||
$budgets = $repository->getActiveBudgets();
|
$budgets = $this->repository->getActiveBudgets();
|
||||||
$repetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
|
$chartData = [
|
||||||
$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.spent_in_budget')),
|
['label' => strval(trans('firefly.overspent')), 'entries' => [], 'type' => 'bar',],
|
||||||
'entries' => [],
|
|
||||||
'type' => 'bar',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'label' => strval(trans('firefly.left_to_spend')),
|
|
||||||
'entries' => [],
|
|
||||||
'type' => 'bar',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'label' => strval(trans('firefly.overspent')),
|
|
||||||
'entries' => [],
|
|
||||||
'type' => 'bar',
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
/** @var Budget $budget */
|
/** @var Budget $budget */
|
||||||
foreach ($budgets as $budget) {
|
foreach ($budgets as $budget) {
|
||||||
// get relevant repetitions:
|
// get relevant repetitions:
|
||||||
$reps = $this->filterRepetitions($repetitions, $budget, $start, $end);
|
$limits = $this->repository->getBudgetLimits($budget, $start, $end);
|
||||||
|
$expenses = $this->getExpensesForBudget($limits, $budget, $start, $end);
|
||||||
if ($reps->count() === 0) {
|
foreach ($expenses as $name => $row) {
|
||||||
$row = $this->spentInPeriodSingle($repository, $budget, $start, $end);
|
$chartData[0]['entries'][$name] = $row['spent'];
|
||||||
if (bccomp($row['spent'], '0') !== 0 || bccomp($row['repetition_left'], '0') !== 0) {
|
$chartData[1]['entries'][$name] = $row['left'];
|
||||||
$chartData[0]['entries'][$row['name']] = bcmul($row['spent'], '-1');
|
$chartData[2]['entries'][$name] = $row['overspent'];
|
||||||
$chartData[1]['entries'][$row['name']] = $row['repetition_left'];
|
|
||||||
$chartData[2]['entries'][$row['name']] = bcmul($row['repetition_overspent'], '-1');
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
$rows = $this->spentInPeriodMulti($repository, $budget, $reps);
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
if (bccomp($row['spent'], '0') !== 0 || bccomp($row['repetition_left'], '0') !== 0) {
|
|
||||||
$chartData[0]['entries'][$row['name']] = bcmul($row['spent'], '-1');
|
|
||||||
$chartData[1]['entries'][$row['name']] = $row['repetition_left'];
|
|
||||||
$chartData[2]['entries'][$row['name']] = bcmul($row['repetition_overspent'], '-1');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unset($rows, $row);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
// for no budget:
|
// for no budget:
|
||||||
$row = $this->spentInPeriodWithout($start, $end);
|
$spent = $this->spentInPeriodWithout($start, $end);
|
||||||
if (bccomp($row['spent'], '0') !== 0 || bccomp($row['repetition_left'], '0') !== 0) {
|
$name = strval(trans('firefly.no_budget'));
|
||||||
$chartData[0]['entries'][$row['name']] = bcmul($row['spent'], '-1');
|
if (bccomp($spent, '0') !== 0) {
|
||||||
$chartData[1]['entries'][$row['name']] = $row['repetition_left'];
|
$chartData[0]['entries'][$name] = bcmul($spent, '-1');
|
||||||
$chartData[2]['entries'][$row['name']] = bcmul($row['repetition_overspent'], '-1');
|
$chartData[1]['entries'][$name] = '0';
|
||||||
|
$chartData[2]['entries'][$name] = '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $this->generator->multiSet($chartData);
|
$data = $this->generator->multiSet($chartData);
|
||||||
@@ -218,15 +208,16 @@ class BudgetController extends Controller
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param BudgetRepositoryInterface $repository
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
|
||||||
* @param Budget $budget
|
*
|
||||||
* @param Carbon $start
|
* @param Budget $budget
|
||||||
* @param Carbon $end
|
* @param Carbon $start
|
||||||
* @param Collection $accounts
|
* @param Carbon $end
|
||||||
|
* @param Collection $accounts
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @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:
|
// chart properties for cache:
|
||||||
$cache = new CacheProperties();
|
$cache = new CacheProperties();
|
||||||
@@ -238,56 +229,22 @@ class BudgetController extends Controller
|
|||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get());
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the expenses
|
|
||||||
$budgeted = [];
|
|
||||||
$periods = Navigation::listOfPeriods($start, $end);
|
$periods = Navigation::listOfPeriods($start, $end);
|
||||||
$entries = $repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end);
|
$entries = $this->repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end); // get the expenses
|
||||||
$key = Navigation::preferredCarbonFormat($start, $end);
|
$budgeted = $this->getBudgetedInPeriod($budget, $start, $end);
|
||||||
$range = Navigation::preferredRangeFormat($start, $end);
|
|
||||||
|
|
||||||
// get the budget limits (if any)
|
|
||||||
$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 into one set of data:
|
// join them into one set of data:
|
||||||
$chartData = [
|
$chartData = [
|
||||||
[
|
['label' => strval(trans('firefly.spent')), 'type' => 'bar', 'entries' => [],],
|
||||||
'label' => strval(trans('firefly.spent')),
|
['label' => strval(trans('firefly.budgeted')), 'type' => 'bar', 'entries' => [],],
|
||||||
'type' => 'bar',
|
|
||||||
'entries' => [],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'label' => strval(trans('firefly.budgeted')),
|
|
||||||
'type' => 'bar',
|
|
||||||
'entries' => [],
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach (array_keys($periods) as $period) {
|
foreach (array_keys($periods) as $period) {
|
||||||
$label = $periods[$period];
|
$label = $periods[$period];
|
||||||
$spent = isset($entries[$budget->id]['entries'][$period]) ? $entries[$budget->id]['entries'][$period] : '0';
|
$spent = isset($entries[$budget->id]['entries'][$period]) ? $entries[$budget->id]['entries'][$period] : '0';
|
||||||
$limit = isset($entries[$period]) ? $budgeted[$period] : 0;
|
$limit = isset($budgeted[$period]) ? $budgeted[$period] : 0;
|
||||||
$chartData[0]['entries'][$label] = bcmul($spent, '-1');
|
$chartData[0]['entries'][$label] = round(bcmul($spent, '-1'), 12);
|
||||||
$chartData[1]['entries'][$label] = $limit;
|
$chartData[1]['entries'][$label] = $limit;
|
||||||
|
|
||||||
}
|
}
|
||||||
$data = $this->generator->multiSet($chartData);
|
$data = $this->generator->multiSet($chartData);
|
||||||
$cache->store($data);
|
$cache->store($data);
|
||||||
@@ -296,14 +253,13 @@ class BudgetController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param BudgetRepositoryInterface $repository
|
* @param Collection $accounts
|
||||||
* @param Collection $accounts
|
* @param Carbon $start
|
||||||
* @param Carbon $start
|
* @param Carbon $end
|
||||||
* @param Carbon $end
|
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @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:
|
// chart properties for cache:
|
||||||
$cache = new CacheProperties();
|
$cache = new CacheProperties();
|
||||||
@@ -317,7 +273,7 @@ class BudgetController extends Controller
|
|||||||
|
|
||||||
// the expenses:
|
// the expenses:
|
||||||
$periods = Navigation::listOfPeriods($start, $end);
|
$periods = Navigation::listOfPeriods($start, $end);
|
||||||
$entries = $repository->getNoBudgetPeriodReport($accounts, $start, $end);
|
$entries = $this->repository->getNoBudgetPeriodReport($accounts, $start, $end);
|
||||||
$chartData = [];
|
$chartData = [];
|
||||||
|
|
||||||
// join them:
|
// join them:
|
||||||
@@ -333,98 +289,115 @@ class BudgetController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $repetitions
|
* @param Budget $budget
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return 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 Budget $budget
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
* @param Carbon $end
|
* @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;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array with the following values:
|
|
||||||
* 0 =>
|
|
||||||
* 'name' => name of budget + repetition
|
|
||||||
* 'repetition_left' => left in budget repetition (always zero)
|
|
||||||
* 'repetition_overspent' => spent more than budget repetition? (always zero)
|
|
||||||
* 'spent' => actually spent in period for budget
|
|
||||||
* 1 => (etc)
|
|
||||||
*
|
|
||||||
* @param BudgetRepositoryInterface $repository
|
|
||||||
* @param Budget $budget
|
|
||||||
* @param Collection $repetitions
|
|
||||||
*
|
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function spentInPeriodMulti(BudgetRepositoryInterface $repository, Budget $budget, Collection $repetitions): array
|
private function getExpensesForBudget(Collection $limits, Budget $budget, Carbon $start, Carbon $end): array
|
||||||
{
|
{
|
||||||
$return = [];
|
$return = [];
|
||||||
$format = strval(trans('config.month_and_day'));
|
if ($limits->count() === 0) {
|
||||||
$name = $budget->name;
|
$spent = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end);
|
||||||
/** @var LimitRepetition $repetition */
|
if (bccomp($spent, '0') !== 0) {
|
||||||
foreach ($repetitions as $repetition) {
|
$return[$budget->name]['spent'] = bcmul($spent, '-1');
|
||||||
$expenses = $repository->spentInPeriod(new Collection([$budget]), new Collection, $repetition->startdate, $repetition->enddate);
|
$return[$budget->name]['left'] = 0;
|
||||||
|
$return[$budget->name]['overspent'] = 0;
|
||||||
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);
|
return $return;
|
||||||
$spent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcmul($amount, '-1') : $expenses;
|
|
||||||
$overspent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcadd($amount, $expenses) : '0';
|
|
||||||
$return[] = [
|
|
||||||
'name' => $name,
|
|
||||||
'repetition_left' => $left,
|
|
||||||
'repetition_overspent' => $overspent,
|
|
||||||
'spent' => $spent,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$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;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
|
||||||
|
*
|
||||||
* Returns an array with the following values:
|
* Returns an array with the following values:
|
||||||
* 'name' => name of budget
|
* 0 =>
|
||||||
* 'repetition_left' => left in budget repetition (always zero)
|
* 'name' => name of budget + repetition
|
||||||
* 'repetition_overspent' => spent more than budget repetition? (always zero)
|
* 'left' => left in budget repetition (always zero)
|
||||||
* 'spent' => actually spent in period for budget
|
* 'overspent' => spent more than budget repetition? (always zero)
|
||||||
|
* 'spent' => actually spent in period for budget
|
||||||
|
* 1 => (etc)
|
||||||
*
|
*
|
||||||
*
|
* @param Budget $budget
|
||||||
* @param BudgetRepositoryInterface $repository
|
* @param Collection $limits
|
||||||
* @param Budget $budget
|
|
||||||
* @param Carbon $start
|
|
||||||
* @param Carbon $end
|
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function spentInPeriodSingle(BudgetRepositoryInterface $repository, Budget $budget, Carbon $start, Carbon $end): array
|
private function spentInPeriodMulti(Budget $budget, Collection $limits): array
|
||||||
{
|
{
|
||||||
$spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end);
|
$return = [];
|
||||||
$array = [
|
$format = strval(trans('config.month_and_day'));
|
||||||
'name' => $budget->name,
|
$name = $budget->name;
|
||||||
'repetition_left' => '0',
|
/** @var BudgetLimit $budgetLimit */
|
||||||
'repetition_overspent' => '0',
|
foreach ($limits as $budgetLimit) {
|
||||||
'spent' => $spent,
|
$expenses = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $budgetLimit->start_date, $budgetLimit->end_date);
|
||||||
];
|
|
||||||
|
|
||||||
return $array;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -437,12 +410,13 @@ class BudgetController extends Controller
|
|||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
* @param Carbon $end
|
* @param Carbon $end
|
||||||
*
|
*
|
||||||
* @return array
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function spentInPeriodWithout(Carbon $start, Carbon $end): array
|
private function spentInPeriodWithout(Carbon $start, Carbon $end): string
|
||||||
{
|
{
|
||||||
// collector
|
// collector
|
||||||
$collector = new JournalCollector(auth()->user());
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||||
$types = [TransactionType::WITHDRAWAL];
|
$types = [TransactionType::WITHDRAWAL];
|
||||||
$collector->setAllAssetAccounts()->setTypes($types)->setRange($start, $end)->withoutBudget();
|
$collector->setAllAssetAccounts()->setTypes($types)->setRange($start, $end)->withoutBudget();
|
||||||
$journals = $collector->getJournals();
|
$journals = $collector->getJournals();
|
||||||
@@ -451,13 +425,7 @@ class BudgetController extends Controller
|
|||||||
foreach ($journals as $entry) {
|
foreach ($journals as $entry) {
|
||||||
$sum = bcadd($entry->transaction_amount, $sum);
|
$sum = bcadd($entry->transaction_amount, $sum);
|
||||||
}
|
}
|
||||||
$array = [
|
|
||||||
'name' => strval(trans('firefly.no_budget')),
|
|
||||||
'repetition_left' => '0',
|
|
||||||
'repetition_overspent' => '0',
|
|
||||||
'spent' => $sum,
|
|
||||||
];
|
|
||||||
|
|
||||||
return $array;
|
return $sum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,9 +17,11 @@ namespace FireflyIII\Http\Controllers\Chart;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||||
use FireflyIII\Generator\Report\Category\MonthReportGenerator;
|
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\Http\Controllers\Controller;
|
||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\Budget;
|
||||||
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
@@ -29,7 +31,6 @@ use Illuminate\Support\Collection;
|
|||||||
use Navigation;
|
use Navigation;
|
||||||
use Response;
|
use Response;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Separate controller because many helper functions are shared.
|
* Separate controller because many helper functions are shared.
|
||||||
*
|
*
|
||||||
@@ -75,49 +76,19 @@ class BudgetReportController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function accountExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end, string $others)
|
public function accountExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end, string $others)
|
||||||
{
|
{
|
||||||
/** @var bool $others */
|
/** @var MetaPieChartInterface $helper */
|
||||||
$others = intval($others) === 1;
|
$helper = app(MetaPieChartInterface::class);
|
||||||
$cache = new CacheProperties;
|
$helper->setAccounts($accounts);
|
||||||
$cache->addProperty('chart.budget.report.account-expense');
|
$helper->setBudgets($budgets);
|
||||||
$cache->addProperty($accounts);
|
$helper->setUser(auth()->user());
|
||||||
$cache->addProperty($budgets);
|
$helper->setStart($start);
|
||||||
$cache->addProperty($start);
|
$helper->setEnd($end);
|
||||||
$cache->addProperty($end);
|
$helper->setCollectOtherObjects(intval($others) === 1);
|
||||||
if ($cache->has()) {
|
$chartData = $helper->generate('expense', 'account');
|
||||||
return Response::json($cache->get());
|
$data = $this->generator->pieChart($chartData);
|
||||||
}
|
|
||||||
|
|
||||||
$names = [];
|
|
||||||
$set = $this->getExpenses($accounts, $budgets, $start, $end);
|
|
||||||
$grouped = $this->groupByOpposingAccount($set);
|
|
||||||
$chartData = [];
|
|
||||||
$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);
|
|
||||||
$chartData[$names[$accountId]] = $amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// also collect all transactions NOT in these budgets.
|
|
||||||
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');
|
|
||||||
$sum = bcsub($sum, $total);
|
|
||||||
$chartData[strval(trans('firefly.everything_else'))] = $sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = $this->generator->pieChart($chartData);
|
|
||||||
$cache->store($data);
|
|
||||||
|
|
||||||
return Response::json($data);
|
return Response::json($data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,47 +102,16 @@ class BudgetReportController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function budgetExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end, string $others)
|
public function budgetExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end, string $others)
|
||||||
{
|
{
|
||||||
/** @var bool $others */
|
/** @var MetaPieChartInterface $helper */
|
||||||
$others = intval($others) === 1;
|
$helper = app(MetaPieChartInterface::class);
|
||||||
$cache = new CacheProperties;
|
$helper->setAccounts($accounts);
|
||||||
$cache->addProperty('chart.budget.report.budget-expense');
|
$helper->setBudgets($budgets);
|
||||||
$cache->addProperty($accounts);
|
$helper->setUser(auth()->user());
|
||||||
$cache->addProperty($budgets);
|
$helper->setStart($start);
|
||||||
$cache->addProperty($start);
|
$helper->setEnd($end);
|
||||||
$cache->addProperty($end);
|
$helper->setCollectOtherObjects(intval($others) === 1);
|
||||||
if ($cache->has()) {
|
$chartData = $helper->generate('expense', 'budget');
|
||||||
return Response::json($cache->get());
|
$data = $this->generator->pieChart($chartData);
|
||||||
}
|
|
||||||
|
|
||||||
$names = [];
|
|
||||||
$set = $this->getExpenses($accounts, $budgets, $start, $end);
|
|
||||||
$grouped = $this->groupByBudget($set);
|
|
||||||
$total = '0';
|
|
||||||
$chartData = [];
|
|
||||||
|
|
||||||
foreach ($grouped as $budgetId => $amount) {
|
|
||||||
if (!isset($names[$budgetId])) {
|
|
||||||
$budget = $this->budgetRepository->find(intval($budgetId));
|
|
||||||
$names[$budgetId] = $budget->name;
|
|
||||||
}
|
|
||||||
$amount = bcmul($amount, '-1');
|
|
||||||
$total = bcadd($total, $amount);
|
|
||||||
$chartData[$names[$budgetId]] = $amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// also collect all transactions NOT in these budgets.
|
|
||||||
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');
|
|
||||||
$sum = bcsub($sum, $total);
|
|
||||||
$chartData[strval(trans('firefly.everything_else'))] = $sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = $this->generator->pieChart($chartData);
|
|
||||||
$cache->store($data);
|
|
||||||
|
|
||||||
return Response::json($data);
|
return Response::json($data);
|
||||||
}
|
}
|
||||||
@@ -195,7 +135,8 @@ class BudgetReportController extends Controller
|
|||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get());
|
||||||
}
|
}
|
||||||
|
/** @var BudgetRepositoryInterface $repository */
|
||||||
|
$repository = app(BudgetRepositoryInterface::class);
|
||||||
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
||||||
$function = Navigation::preferredEndOfPeriod($start, $end);
|
$function = Navigation::preferredEndOfPeriod($start, $end);
|
||||||
$chartData = [];
|
$chartData = [];
|
||||||
@@ -203,13 +144,30 @@ class BudgetReportController extends Controller
|
|||||||
|
|
||||||
// prep chart data:
|
// prep chart data:
|
||||||
foreach ($budgets as $budget) {
|
foreach ($budgets as $budget) {
|
||||||
$chartData[$budget->id] = [
|
$chartData[$budget->id] = [
|
||||||
'label' => $budget->name,
|
'label' => strval(trans('firefly.spent_in_specific_budget', ['budget' => $budget->name])),
|
||||||
'type' => 'bar',
|
'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' => [],
|
'entries' => [],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
$allBudgetLimits = $repository->getAllBudgetLimits($start, $end);
|
||||||
|
$sumOfExpenses = [];
|
||||||
|
$leftOfLimits = [];
|
||||||
while ($currentStart < $end) {
|
while ($currentStart < $end) {
|
||||||
$currentEnd = clone $currentStart;
|
$currentEnd = clone $currentStart;
|
||||||
$currentEnd = $currentEnd->$function();
|
$currentEnd = $currentEnd->$function();
|
||||||
@@ -218,7 +176,20 @@ class BudgetReportController extends Controller
|
|||||||
|
|
||||||
/** @var Budget $budget */
|
/** @var Budget $budget */
|
||||||
foreach ($budgets as $budget) {
|
foreach ($budgets as $budget) {
|
||||||
$chartData[$budget->id]['entries'][$label] = $expenses[$budget->id] ?? '0';
|
// 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 = clone $currentEnd;
|
||||||
$currentStart->addDay();
|
$currentStart->addDay();
|
||||||
@@ -230,6 +201,32 @@ class BudgetReportController extends Controller
|
|||||||
return Response::json($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 $accounts
|
||||||
@@ -241,7 +238,8 @@ class BudgetReportController extends Controller
|
|||||||
*/
|
*/
|
||||||
private function getExpenses(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end): Collection
|
private function getExpenses(Collection $accounts, Collection $budgets, 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])
|
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
|
||||||
->setBudgets($budgets)->withOpposingAccount()->disableFilter();
|
->setBudgets($budgets)->withOpposingAccount()->disableFilter();
|
||||||
$accountIds = $accounts->pluck('id')->toArray();
|
$accountIds = $accounts->pluck('id')->toArray();
|
||||||
@@ -289,4 +287,4 @@ class BudgetReportController extends Controller
|
|||||||
|
|
||||||
return $grouped;
|
return $grouped;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -65,7 +65,12 @@ class CategoryController extends Controller
|
|||||||
return Response::json($cache->get());
|
return Response::json($cache->get());
|
||||||
}
|
}
|
||||||
|
|
||||||
$start = $repository->firstUseDate($category);
|
$start = $repository->firstUseDate($category);
|
||||||
|
|
||||||
|
if ($start->year == 1900) {
|
||||||
|
$start = new Carbon;
|
||||||
|
}
|
||||||
|
|
||||||
$range = Preferences::get('viewRange', '1M')->data;
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
$start = Navigation::startOfPeriod($start, $range);
|
$start = Navigation::startOfPeriod($start, $range);
|
||||||
$end = new Carbon;
|
$end = new Carbon;
|
||||||
@@ -143,6 +148,7 @@ class CategoryController extends Controller
|
|||||||
$chartData[$category->name] = bcmul($spent, '-1');
|
$chartData[$category->name] = bcmul($spent, '-1');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$chartData[strval(trans('firefly.no_category'))] = bcmul($repository->spentInPeriodWithoutCategory(new Collection, $start, $end), '-1');
|
$chartData[strval(trans('firefly.no_category'))] = bcmul($repository->spentInPeriodWithoutCategory(new Collection, $start, $end), '-1');
|
||||||
|
|
||||||
// sort
|
// sort
|
||||||
|
@@ -17,7 +17,8 @@ namespace FireflyIII\Http\Controllers\Chart;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||||
use FireflyIII\Generator\Report\Category\MonthReportGenerator;
|
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\Http\Controllers\Controller;
|
||||||
use FireflyIII\Models\Category;
|
use FireflyIII\Models\Category;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
@@ -75,47 +76,16 @@ class CategoryReportController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function accountExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
|
public function accountExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
|
||||||
{
|
{
|
||||||
/** @var bool $others */
|
/** @var MetaPieChartInterface $helper */
|
||||||
$others = intval($others) === 1;
|
$helper = app(MetaPieChartInterface::class);
|
||||||
$cache = new CacheProperties;
|
$helper->setAccounts($accounts);
|
||||||
$cache->addProperty('chart.category.report.account-expense');
|
$helper->setCategories($categories);
|
||||||
$cache->addProperty($accounts);
|
$helper->setUser(auth()->user());
|
||||||
$cache->addProperty($categories);
|
$helper->setStart($start);
|
||||||
$cache->addProperty($start);
|
$helper->setEnd($end);
|
||||||
$cache->addProperty($end);
|
$helper->setCollectOtherObjects(intval($others) === 1);
|
||||||
if ($cache->has()) {
|
$chartData = $helper->generate('expense', 'account');
|
||||||
return Response::json($cache->get());
|
$data = $this->generator->pieChart($chartData);
|
||||||
}
|
|
||||||
|
|
||||||
$names = [];
|
|
||||||
$set = $this->getExpenses($accounts, $categories, $start, $end);
|
|
||||||
$grouped = $this->groupByOpposingAccount($set);
|
|
||||||
$chartData = [];
|
|
||||||
$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);
|
|
||||||
$chartData[$names[$accountId]] = $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');
|
|
||||||
$sum = bcsub($sum, $total);
|
|
||||||
$chartData[strval(trans('firefly.everything_else'))] = $sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = $this->generator->pieChart($chartData);
|
|
||||||
$cache->store($data);
|
|
||||||
|
|
||||||
return Response::json($data);
|
return Response::json($data);
|
||||||
}
|
}
|
||||||
@@ -131,46 +101,16 @@ class CategoryReportController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function accountIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
|
public function accountIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
|
||||||
{
|
{
|
||||||
/** @var bool $others */
|
/** @var MetaPieChartInterface $helper */
|
||||||
$others = intval($others) === 1;
|
$helper = app(MetaPieChartInterface::class);
|
||||||
$cache = new CacheProperties;
|
$helper->setAccounts($accounts);
|
||||||
$cache->addProperty('chart.category.report.account-income');
|
$helper->setCategories($categories);
|
||||||
$cache->addProperty($accounts);
|
$helper->setUser(auth()->user());
|
||||||
$cache->addProperty($categories);
|
$helper->setStart($start);
|
||||||
$cache->addProperty($start);
|
$helper->setEnd($end);
|
||||||
$cache->addProperty($end);
|
$helper->setCollectOtherObjects(intval($others) === 1);
|
||||||
if ($cache->has()) {
|
$chartData = $helper->generate('income', 'account');
|
||||||
return Response::json($cache->get());
|
$data = $this->generator->pieChart($chartData);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$names = [];
|
|
||||||
$set = $this->getIncome($accounts, $categories, $start, $end);
|
|
||||||
$grouped = $this->groupByOpposingAccount($set);
|
|
||||||
$chartData = [];
|
|
||||||
$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);
|
|
||||||
$chartData[$names[$accountId]] = $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'));
|
|
||||||
$sum = bcsub($sum, $total);
|
|
||||||
$chartData[strval(trans('firefly.everything_else'))] = $sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = $this->generator->pieChart($chartData);
|
|
||||||
$cache->store($data);
|
|
||||||
|
|
||||||
return Response::json($data);
|
return Response::json($data);
|
||||||
}
|
}
|
||||||
@@ -186,47 +126,16 @@ class CategoryReportController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function categoryExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
|
public function categoryExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
|
||||||
{
|
{
|
||||||
/** @var bool $others */
|
/** @var MetaPieChartInterface $helper */
|
||||||
$others = intval($others) === 1;
|
$helper = app(MetaPieChartInterface::class);
|
||||||
$cache = new CacheProperties;
|
$helper->setAccounts($accounts);
|
||||||
$cache->addProperty('chart.category.report.category-expense');
|
$helper->setCategories($categories);
|
||||||
$cache->addProperty($accounts);
|
$helper->setUser(auth()->user());
|
||||||
$cache->addProperty($categories);
|
$helper->setStart($start);
|
||||||
$cache->addProperty($start);
|
$helper->setEnd($end);
|
||||||
$cache->addProperty($end);
|
$helper->setCollectOtherObjects(intval($others) === 1);
|
||||||
if ($cache->has()) {
|
$chartData = $helper->generate('expense', 'category');
|
||||||
return Response::json($cache->get());
|
$data = $this->generator->pieChart($chartData);
|
||||||
}
|
|
||||||
|
|
||||||
$names = [];
|
|
||||||
$set = $this->getExpenses($accounts, $categories, $start, $end);
|
|
||||||
$grouped = $this->groupByCategory($set);
|
|
||||||
$total = '0';
|
|
||||||
$chartData = [];
|
|
||||||
|
|
||||||
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);
|
|
||||||
$chartData[$names[$categoryId]] = $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');
|
|
||||||
$sum = bcsub($sum, $total);
|
|
||||||
$chartData[strval(trans('firefly.everything_else'))] = $sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = $this->generator->pieChart($chartData);
|
|
||||||
$cache->store($data);
|
|
||||||
|
|
||||||
return Response::json($data);
|
return Response::json($data);
|
||||||
}
|
}
|
||||||
@@ -242,44 +151,17 @@ class CategoryReportController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function categoryIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
|
public function categoryIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
|
||||||
{
|
{
|
||||||
/** @var bool $others */
|
|
||||||
$others = intval($others) === 1;
|
|
||||||
$cache = new CacheProperties;
|
|
||||||
$cache->addProperty('chart.category.report.category-income');
|
|
||||||
$cache->addProperty($accounts);
|
|
||||||
$cache->addProperty($categories);
|
|
||||||
$cache->addProperty($start);
|
|
||||||
$cache->addProperty($end);
|
|
||||||
if ($cache->has()) {
|
|
||||||
return Response::json($cache->get());
|
|
||||||
}
|
|
||||||
|
|
||||||
$names = [];
|
/** @var MetaPieChartInterface $helper */
|
||||||
$set = $this->getIncome($accounts, $categories, $start, $end);
|
$helper = app(MetaPieChartInterface::class);
|
||||||
$grouped = $this->groupByCategory($set);
|
$helper->setAccounts($accounts);
|
||||||
$total = '0';
|
$helper->setCategories($categories);
|
||||||
$chartData = [];
|
$helper->setUser(auth()->user());
|
||||||
|
$helper->setStart($start);
|
||||||
foreach ($grouped as $categoryId => $amount) {
|
$helper->setEnd($end);
|
||||||
if (!isset($names[$categoryId])) {
|
$helper->setCollectOtherObjects(intval($others) === 1);
|
||||||
$category = $this->categoryRepository->find(intval($categoryId));
|
$chartData = $helper->generate('income', 'category');
|
||||||
$names[$categoryId] = $category->name;
|
$data = $this->generator->pieChart($chartData);
|
||||||
}
|
|
||||||
$total = bcadd($total, $amount);
|
|
||||||
$chartData[$names[$categoryId]] = $amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
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'));
|
|
||||||
$sum = bcsub($sum, $total);
|
|
||||||
$chartData[strval(trans('firefly.everything_else'))] = $sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = $this->generator->pieChart($chartData);
|
|
||||||
$cache->store($data);
|
|
||||||
|
|
||||||
return Response::json($data);
|
return Response::json($data);
|
||||||
}
|
}
|
||||||
@@ -314,14 +196,33 @@ class CategoryReportController extends Controller
|
|||||||
$chartData[$category->id . '-in'] = [
|
$chartData[$category->id . '-in'] = [
|
||||||
'label' => $category->name . ' (' . strtolower(strval(trans('firefly.income'))) . ')',
|
'label' => $category->name . ' (' . strtolower(strval(trans('firefly.income'))) . ')',
|
||||||
'type' => 'bar',
|
'type' => 'bar',
|
||||||
|
'yAxisID' => 'y-axis-0',
|
||||||
'entries' => [],
|
'entries' => [],
|
||||||
];
|
];
|
||||||
$chartData[$category->id . '-out'] = [
|
$chartData[$category->id . '-out'] = [
|
||||||
'label' => $category->name . ' (' . strtolower(strval(trans('firefly.expenses'))) . ')',
|
'label' => $category->name . ' (' . strtolower(strval(trans('firefly.expenses'))) . ')',
|
||||||
'type' => 'bar',
|
'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' => [],
|
'entries' => [],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
$sumOfIncome = [];
|
||||||
|
$sumOfExpense = [];
|
||||||
|
|
||||||
while ($currentStart < $end) {
|
while ($currentStart < $end) {
|
||||||
$currentEnd = clone $currentStart;
|
$currentEnd = clone $currentStart;
|
||||||
@@ -332,17 +233,40 @@ class CategoryReportController extends Controller
|
|||||||
|
|
||||||
/** @var Category $category */
|
/** @var Category $category */
|
||||||
foreach ($categories as $category) {
|
foreach ($categories as $category) {
|
||||||
$labelIn = $category->id . '-in';
|
$labelIn = $category->id . '-in';
|
||||||
$labelOut = $category->id . '-out';
|
$labelOut = $category->id . '-out';
|
||||||
// get sum, and get label:
|
$labelSumIn = $category->id . '-total-in';
|
||||||
$chartData[$labelIn]['entries'][$label] = $income[$category->id] ?? '0';
|
$labelSumOut = $category->id . '-total-out';
|
||||||
$chartData[$labelOut]['entries'][$label] = $expenses[$category->id] ?? '0';
|
$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 = clone $currentEnd;
|
||||||
$currentStart->addDay();
|
$currentStart->addDay();
|
||||||
}
|
}
|
||||||
|
// remove all empty entries to prevent cluttering:
|
||||||
$data = $this->generator->multiSet($chartData);
|
$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);
|
$cache->store($data);
|
||||||
|
|
||||||
return Response::json($data);
|
return Response::json($data);
|
||||||
@@ -359,7 +283,8 @@ class CategoryReportController extends Controller
|
|||||||
*/
|
*/
|
||||||
private function getExpenses(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): Collection
|
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])
|
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
|
||||||
->setCategories($categories)->withOpposingAccount()->disableFilter();
|
->setCategories($categories)->withOpposingAccount()->disableFilter();
|
||||||
$accountIds = $accounts->pluck('id')->toArray();
|
$accountIds = $accounts->pluck('id')->toArray();
|
||||||
@@ -379,7 +304,8 @@ class CategoryReportController extends Controller
|
|||||||
*/
|
*/
|
||||||
private function getIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): Collection
|
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])
|
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
|
||||||
->setCategories($categories)->withOpposingAccount();
|
->setCategories($categories)->withOpposingAccount();
|
||||||
$accountIds = $accounts->pluck('id')->toArray();
|
$accountIds = $accounts->pluck('id')->toArray();
|
||||||
@@ -427,4 +353,4 @@ class CategoryReportController extends Controller
|
|||||||
|
|
||||||
return $grouped;
|
return $grouped;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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;
|
namespace FireflyIII\Http\Controllers;
|
||||||
|
|
||||||
|
use FireflyConfig;
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
@@ -49,7 +50,10 @@ class Controller extends BaseController
|
|||||||
View::share('hideCategories', false);
|
View::share('hideCategories', false);
|
||||||
View::share('hideBills', false);
|
View::share('hideBills', false);
|
||||||
View::share('hideTags', 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:
|
// translations:
|
||||||
|
|
||||||
|
@@ -17,7 +17,6 @@ use Cache;
|
|||||||
use FireflyIII\Http\Requests\CurrencyFormRequest;
|
use FireflyIII\Http\Requests\CurrencyFormRequest;
|
||||||
use FireflyIII\Models\TransactionCurrency;
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||||
use Input;
|
|
||||||
use Log;
|
use Log;
|
||||||
use Preferences;
|
use Preferences;
|
||||||
use Session;
|
use Session;
|
||||||
@@ -170,7 +169,7 @@ class CurrencyController extends Controller
|
|||||||
|
|
||||||
|
|
||||||
if (!auth()->user()->hasRole('owner')) {
|
if (!auth()->user()->hasRole('owner')) {
|
||||||
Session::flash('warning', trans('firefly.ask_site_owner', ['site_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);
|
$currency = $repository->store($data);
|
||||||
Session::flash('success', trans('firefly.created_currency', ['name' => $currency->name]));
|
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);
|
Session::put('currencies.create.fromStore', true);
|
||||||
|
|
||||||
return redirect(route('currencies.create'))->withInput();
|
return redirect(route('currencies.create'))->withInput();
|
||||||
@@ -225,7 +224,7 @@ class CurrencyController extends Controller
|
|||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
|
|
||||||
if (intval(Input::get('return_to_edit')) === 1) {
|
if (intval($request->get('return_to_edit')) === 1) {
|
||||||
Session::put('currencies.edit.fromUpdate', true);
|
Session::put('currencies.edit.fromUpdate', true);
|
||||||
|
|
||||||
return redirect(route('currencies.edit', [$currency->id]));
|
return redirect(route('currencies.edit', [$currency->id]));
|
||||||
|
@@ -22,10 +22,11 @@ use FireflyIII\Http\Requests\ExportFormRequest;
|
|||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\ExportJob;
|
use FireflyIII\Models\ExportJob;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface;
|
||||||
use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface as EJRI;
|
use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface as EJRI;
|
||||||
|
use Illuminate\Http\Response as LaravelResponse;
|
||||||
use Preferences;
|
use Preferences;
|
||||||
use Response;
|
use Response;
|
||||||
use Storage;
|
|
||||||
use View;
|
use View;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,21 +60,23 @@ class ExportController extends Controller
|
|||||||
* @return \Symfony\Component\HttpFoundation\Response|\Illuminate\Contracts\Routing\ResponseFactory
|
* @return \Symfony\Component\HttpFoundation\Response|\Illuminate\Contracts\Routing\ResponseFactory
|
||||||
* @throws FireflyException
|
* @throws FireflyException
|
||||||
*/
|
*/
|
||||||
public function download(ExportJob $job)
|
public function download(ExportJobRepositoryInterface $repository, ExportJob $job)
|
||||||
{
|
{
|
||||||
$disk = Storage::disk('export');
|
|
||||||
$file = $job->key . '.zip';
|
$file = $job->key . '.zip';
|
||||||
$date = date('Y-m-d \a\t H-i-s');
|
$date = date('Y-m-d \a\t H-i-s');
|
||||||
$name = 'Export job on ' . $date . '.zip';
|
$name = 'Export job on ' . $date . '.zip';
|
||||||
$quoted = sprintf('"%s"', addcslashes($name, '"\\'));
|
$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.');
|
throw new FireflyException('Against all expectations, zip file "' . $file . '" does not exist.');
|
||||||
}
|
}
|
||||||
|
$content = $repository->getContent($job);
|
||||||
|
|
||||||
|
|
||||||
$job->change('export_downloaded');
|
$job->change('export_downloaded');
|
||||||
|
/** @var LaravelResponse $response */
|
||||||
return response($disk->get($file), 200)
|
$response = response($content, 200);
|
||||||
|
$response
|
||||||
->header('Content-Description', 'File Transfer')
|
->header('Content-Description', 'File Transfer')
|
||||||
->header('Content-Type', 'application/octet-stream')
|
->header('Content-Type', 'application/octet-stream')
|
||||||
->header('Content-Disposition', 'attachment; filename=' . $quoted)
|
->header('Content-Disposition', 'attachment; filename=' . $quoted)
|
||||||
@@ -82,7 +85,9 @@ class ExportController extends Controller
|
|||||||
->header('Expires', '0')
|
->header('Expires', '0')
|
||||||
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
|
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
|
||||||
->header('Pragma', 'public')
|
->header('Pragma', 'public')
|
||||||
->header('Content-Length', $disk->size($file));
|
->header('Content-Length', strlen($content));
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,8 +132,7 @@ class ExportController extends Controller
|
|||||||
* @param AccountRepositoryInterface $repository
|
* @param AccountRepositoryInterface $repository
|
||||||
* @param EJRI $jobs
|
* @param EJRI $jobs
|
||||||
*
|
*
|
||||||
* @return string
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public function postIndex(ExportFormRequest $request, AccountRepositoryInterface $repository, EJRI $jobs)
|
public function postIndex(ExportFormRequest $request, AccountRepositoryInterface $repository, EJRI $jobs)
|
||||||
{
|
{
|
||||||
@@ -143,7 +147,7 @@ class ExportController extends Controller
|
|||||||
'job' => $job,
|
'job' => $job,
|
||||||
];
|
];
|
||||||
|
|
||||||
$job->change('export_status_make_exporter');
|
$jobs->changeStatus($job, 'export_status_make_exporter');
|
||||||
|
|
||||||
/** @var ProcessorInterface $processor */
|
/** @var ProcessorInterface $processor */
|
||||||
$processor = app(ProcessorInterface::class, [$settings]);
|
$processor = app(ProcessorInterface::class, [$settings]);
|
||||||
@@ -151,47 +155,46 @@ class ExportController extends Controller
|
|||||||
/*
|
/*
|
||||||
* Collect journals:
|
* Collect journals:
|
||||||
*/
|
*/
|
||||||
$job->change('export_status_collecting_journals');
|
$jobs->changeStatus($job, 'export_status_collecting_journals');
|
||||||
$processor->collectJournals();
|
$processor->collectJournals();
|
||||||
$job->change('export_status_collected_journals');
|
$jobs->changeStatus($job, 'export_status_collected_journals');
|
||||||
/*
|
/*
|
||||||
* Transform to exportable entries:
|
* Transform to exportable entries:
|
||||||
*/
|
*/
|
||||||
$job->change('export_status_converting_to_export_format');
|
$jobs->changeStatus($job, 'export_status_converting_to_export_format');
|
||||||
$processor->convertJournals();
|
$processor->convertJournals();
|
||||||
$job->change('export_status_converted_to_export_format');
|
$jobs->changeStatus($job, 'export_status_converted_to_export_format');
|
||||||
/*
|
/*
|
||||||
* Transform to (temporary) file:
|
* Transform to (temporary) file:
|
||||||
*/
|
*/
|
||||||
$job->change('export_status_creating_journal_file');
|
$jobs->changeStatus($job, 'export_status_creating_journal_file');
|
||||||
$processor->exportJournals();
|
$processor->exportJournals();
|
||||||
$job->change('export_status_created_journal_file');
|
$jobs->changeStatus($job, 'export_status_created_journal_file');
|
||||||
/*
|
/*
|
||||||
* Collect attachments, if applicable.
|
* Collect attachments, if applicable.
|
||||||
*/
|
*/
|
||||||
if ($settings['includeAttachments']) {
|
if ($settings['includeAttachments']) {
|
||||||
$job->change('export_status_collecting_attachments');
|
$jobs->changeStatus($job, 'export_status_collecting_attachments');
|
||||||
$processor->collectAttachments();
|
$processor->collectAttachments();
|
||||||
$job->change('export_status_collected_attachments');
|
$jobs->changeStatus($job, 'export_status_collected_attachments');
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Collect old uploads
|
* Collect old uploads
|
||||||
*/
|
*/
|
||||||
if ($settings['includeOldUploads']) {
|
if ($settings['includeOldUploads']) {
|
||||||
$job->change('export_status_collecting_old_uploads');
|
$jobs->changeStatus($job, 'export_status_collecting_old_uploads');
|
||||||
$processor->collectOldUploads();
|
$processor->collectOldUploads();
|
||||||
$job->change('export_status_collected_old_uploads');
|
$jobs->changeStatus($job, 'export_status_collected_old_uploads');
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create ZIP file:
|
* Create ZIP file:
|
||||||
*/
|
*/
|
||||||
$job->change('export_status_creating_zip_file');
|
$jobs->changeStatus($job, 'export_status_creating_zip_file');
|
||||||
$processor->createZipFile();
|
$processor->createZipFile();
|
||||||
$job->change('export_status_created_zip_file');
|
$jobs->changeStatus($job, 'export_status_created_zip_file');
|
||||||
|
$jobs->changeStatus($job, 'export_status_finished');
|
||||||
$job->change('export_status_finished');
|
|
||||||
|
|
||||||
return Response::json('ok');
|
return Response::json('ok');
|
||||||
}
|
}
|
||||||
|
@@ -107,7 +107,7 @@ class HomeController extends Controller
|
|||||||
$journal->save();
|
$journal->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Session::forget(['start', 'end', 'viewRange', 'range', 'is_custom_range']);
|
||||||
|
|
||||||
Session::clear();
|
Session::clear();
|
||||||
Artisan::call('cache:clear');
|
Artisan::call('cache:clear');
|
||||||
@@ -174,9 +174,6 @@ class HomeController extends Controller
|
|||||||
'logout',
|
'logout',
|
||||||
'two-fac',
|
'two-fac',
|
||||||
'lost-two',
|
'lost-two',
|
||||||
'confirm',
|
|
||||||
'resend',
|
|
||||||
'do_confirm',
|
|
||||||
// test troutes
|
// test troutes
|
||||||
'test-flash',
|
'test-flash',
|
||||||
'all-routes',
|
'all-routes',
|
||||||
|
@@ -15,14 +15,16 @@ namespace FireflyIII\Http\Controllers;
|
|||||||
use Crypt;
|
use Crypt;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Http\Requests\ImportUploadRequest;
|
use FireflyIII\Http\Requests\ImportUploadRequest;
|
||||||
use FireflyIII\Import\ImportProcedure;
|
use FireflyIII\Import\ImportProcedureInterface;
|
||||||
use FireflyIII\Import\Setup\SetupInterface;
|
use FireflyIII\Import\Setup\SetupInterface;
|
||||||
use FireflyIII\Models\ImportJob;
|
use FireflyIII\Models\ImportJob;
|
||||||
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
|
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Response as LaravelResponse;
|
||||||
use Log;
|
use Log;
|
||||||
use Response;
|
use Response;
|
||||||
|
use Session;
|
||||||
use SplFileObject;
|
use SplFileObject;
|
||||||
use Storage;
|
use Storage;
|
||||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||||
@@ -119,15 +121,18 @@ class ImportController extends Controller
|
|||||||
$result = json_encode($config, JSON_PRETTY_PRINT);
|
$result = json_encode($config, JSON_PRETTY_PRINT);
|
||||||
$name = sprintf('"%s"', addcslashes('import-configuration-' . date('Y-m-d') . '.json', '"\\'));
|
$name = sprintf('"%s"', addcslashes('import-configuration-' . date('Y-m-d') . '.json', '"\\'));
|
||||||
|
|
||||||
return response($result, 200)
|
/** @var LaravelResponse $response */
|
||||||
->header('Content-disposition', 'attachment; filename=' . $name)
|
$response = response($result, 200);
|
||||||
->header('Content-Type', 'application/json')
|
$response->header('Content-disposition', 'attachment; filename=' . $name)
|
||||||
->header('Content-Description', 'File Transfer')
|
->header('Content-Type', 'application/json')
|
||||||
->header('Connection', 'Keep-Alive')
|
->header('Content-Description', 'File Transfer')
|
||||||
->header('Expires', '0')
|
->header('Connection', 'Keep-Alive')
|
||||||
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
|
->header('Expires', '0')
|
||||||
->header('Pragma', 'public')
|
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
|
||||||
->header('Content-Length', strlen($result));
|
->header('Pragma', 'public')
|
||||||
|
->header('Content-Length', strlen($result));
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -315,13 +320,13 @@ class ImportController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ImportJob $job
|
* @param ImportProcedureInterface $importProcedure
|
||||||
|
* @param ImportJob $job
|
||||||
*/
|
*/
|
||||||
public function start(ImportJob $job)
|
public function start(ImportProcedureInterface $importProcedure, ImportJob $job)
|
||||||
{
|
{
|
||||||
set_time_limit(0);
|
set_time_limit(0);
|
||||||
if ($job->status == 'settings_complete') {
|
if ($job->status == 'settings_complete') {
|
||||||
$importProcedure = new ImportProcedure;
|
|
||||||
$importProcedure->runImport($job);
|
$importProcedure->runImport($job);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -334,7 +339,7 @@ class ImportController extends Controller
|
|||||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||||
*/
|
*/
|
||||||
public function status(ImportJob $job)
|
public function status(ImportJob $job)
|
||||||
{
|
{ //
|
||||||
Log::debug('Now in status()', ['job' => $job->key]);
|
Log::debug('Now in status()', ['job' => $job->key]);
|
||||||
if (!$this->jobInCorrectStep($job, 'status')) {
|
if (!$this->jobInCorrectStep($job, 'status')) {
|
||||||
return $this->redirectToCorrectStep($job);
|
return $this->redirectToCorrectStep($job);
|
||||||
@@ -368,14 +373,33 @@ class ImportController extends Controller
|
|||||||
$content = $uploaded->fread($uploaded->getSize());
|
$content = $uploaded->fread($uploaded->getSize());
|
||||||
$contentEncrypted = Crypt::encrypt($content);
|
$contentEncrypted = Crypt::encrypt($content);
|
||||||
$disk = Storage::disk('upload');
|
$disk = Storage::disk('upload');
|
||||||
$disk->put($newName, $contentEncrypted);
|
|
||||||
|
|
||||||
Log::debug('Uploaded file', ['name' => $upload->getClientOriginalName(), 'size' => $upload->getSize(), 'mime' => $upload->getClientMimeType()]);
|
// 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.');
|
||||||
|
|
||||||
// store configuration file's content into the job's configuration
|
// also set up prepared configuration.
|
||||||
// thing.
|
$configuration = json_decode($stubsDisk->get('demo-configuration.json'), true);
|
||||||
// otherwise, leave it empty.
|
$job->configuration = $configuration;
|
||||||
if ($request->files->has('configuration_file')) {
|
$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.
|
||||||
|
// demo user's configuration upload is ignored completely.
|
||||||
|
if ($request->files->has('configuration_file') && !auth()->user()->hasRole('demo')) {
|
||||||
/** @var UploadedFile $configFile */
|
/** @var UploadedFile $configFile */
|
||||||
$configFile = $request->files->get('configuration_file');
|
$configFile = $request->files->get('configuration_file');
|
||||||
Log::debug(
|
Log::debug(
|
||||||
@@ -394,6 +418,9 @@ class ImportController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if user is demo user, replace config with prepared config:
|
||||||
|
|
||||||
|
|
||||||
return redirect(route('import.configure', [$job->key]));
|
return redirect(route('import.configure', [$job->key]));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
132
app/Http/Controllers/JavascriptController.php
Normal file
132
app/Http/Controllers/JavascriptController.php
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* JavascriptController.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\Http\Controllers;
|
||||||
|
|
||||||
|
use Amount;
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use Navigation;
|
||||||
|
use Preferences;
|
||||||
|
use Session;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class JavascriptController
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Http\Controllers
|
||||||
|
*/
|
||||||
|
class JavascriptController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function variables()
|
||||||
|
{
|
||||||
|
$picker = $this->getDateRangePicker();
|
||||||
|
$start = Session::get('start');
|
||||||
|
$end = Session::get('end');
|
||||||
|
$linkTitle = sprintf('%s - %s', $start->formatLocalized($this->monthAndDayFormat), $end->formatLocalized($this->monthAndDayFormat));
|
||||||
|
$firstDate = session('first')->format('Y-m-d');
|
||||||
|
$localeconv = localeconv();
|
||||||
|
$accounting = Amount::getJsConfig($localeconv);
|
||||||
|
$localeconv = localeconv();
|
||||||
|
$defaultCurrency = Amount::getDefaultCurrency();
|
||||||
|
$localeconv['frac_digits'] = $defaultCurrency->decimal_places;
|
||||||
|
$pref = Preferences::get('language', config('firefly.default_language', 'en_US'));
|
||||||
|
$lang = $pref->data;
|
||||||
|
$data = [
|
||||||
|
'picker' => $picker,
|
||||||
|
'linkTitle' => $linkTitle,
|
||||||
|
'firstDate' => $firstDate,
|
||||||
|
'currencyCode' => Amount::getCurrencyCode(),
|
||||||
|
'currencySymbol' => Amount::getCurrencySymbol(),
|
||||||
|
'accounting' => $accounting,
|
||||||
|
'localeconv' => $localeconv,
|
||||||
|
'language' => $lang,
|
||||||
|
];
|
||||||
|
|
||||||
|
return response()
|
||||||
|
->view('javascript.variables', $data, 200)
|
||||||
|
->header('Content-Type', 'text/javascript');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
private function getDateRangePicker(): array
|
||||||
|
{
|
||||||
|
$viewRange = Preferences::get('viewRange', '1M')->data;
|
||||||
|
$start = Session::get('start');
|
||||||
|
$end = Session::get('end');
|
||||||
|
|
||||||
|
$prevStart = clone $start;
|
||||||
|
$prevEnd = clone $start;
|
||||||
|
$nextStart = clone $end;
|
||||||
|
$nextEnd = clone $end;
|
||||||
|
if ($viewRange === 'custom') {
|
||||||
|
$days = $start->diffInDays($end);
|
||||||
|
$prevStart->subDays($days);
|
||||||
|
$nextEnd->addDays($days);
|
||||||
|
unset($days);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($viewRange !== 'custom') {
|
||||||
|
$prevStart = Navigation::subtractPeriod($start, $viewRange);// subtract for previous period
|
||||||
|
$prevEnd = Navigation::endOfPeriod($prevStart, $viewRange);
|
||||||
|
$nextStart = Navigation::addPeriod($start, $viewRange, 0); // add for previous period
|
||||||
|
$nextEnd = Navigation::endOfPeriod($nextStart, $viewRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ranges = [];
|
||||||
|
$ranges['current'] = [$start->format('Y-m-d'), $end->format('Y-m-d')];
|
||||||
|
$ranges['previous'] = [$prevStart->format('Y-m-d'), $prevEnd->format('Y-m-d')];
|
||||||
|
$ranges['next'] = [$nextStart->format('Y-m-d'), $nextEnd->format('Y-m-d')];
|
||||||
|
|
||||||
|
switch ($viewRange) {
|
||||||
|
default:
|
||||||
|
throw new FireflyException('The date picker does not yet support "' . $viewRange . '".');
|
||||||
|
case '1D':
|
||||||
|
case 'custom':
|
||||||
|
$format = (string)trans('config.month_and_day');
|
||||||
|
break;
|
||||||
|
case '3M':
|
||||||
|
$format = (string)trans('config.quarter_in_year');
|
||||||
|
break;
|
||||||
|
case '6M':
|
||||||
|
$format = (string)trans('config.half_year');
|
||||||
|
break;
|
||||||
|
case '1Y':
|
||||||
|
$format = (string)trans('config.year');
|
||||||
|
break;
|
||||||
|
case '1M':
|
||||||
|
$format = (string)trans('config.month');
|
||||||
|
break;
|
||||||
|
case '1W':
|
||||||
|
$format = (string)trans('config.week_in_year');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$current = $start->formatLocalized($format);
|
||||||
|
$next = $nextStart->formatLocalized($format);
|
||||||
|
$prev = $prevStart->formatLocalized($format);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'start' => $start->format('Y-m-d'),
|
||||||
|
'end' => $end->format('Y-m-d'),
|
||||||
|
'current' => $current,
|
||||||
|
'previous' => $prev,
|
||||||
|
'next' => $next,
|
||||||
|
'ranges' => $ranges,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -15,15 +15,17 @@ namespace FireflyIII\Http\Controllers;
|
|||||||
use Amount;
|
use Amount;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use Input;
|
use Illuminate\Http\Request;
|
||||||
use Preferences;
|
use Preferences;
|
||||||
use Response;
|
use Response;
|
||||||
|
|
||||||
@@ -43,11 +45,13 @@ class JsonController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @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'));
|
$keys = array_keys(config('firefly.rule-actions'));
|
||||||
$actions = [];
|
$actions = [];
|
||||||
foreach ($keys as $key) {
|
foreach ($keys as $key) {
|
||||||
@@ -59,6 +63,43 @@ class JsonController extends Controller
|
|||||||
return Response::json(['html' => $view]);
|
return Response::json(['html' => $view]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a JSON list of all accounts.
|
||||||
|
*
|
||||||
|
* @param AccountRepositoryInterface $repository
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function allAccounts(AccountRepositoryInterface $repository)
|
||||||
|
{
|
||||||
|
$return = array_unique(
|
||||||
|
$repository->getAccountsByType(
|
||||||
|
[AccountType::REVENUE, AccountType::EXPENSE, AccountType::BENEFICIARY, AccountType::DEFAULT, AccountType::ASSET]
|
||||||
|
)->pluck('name')->toArray()
|
||||||
|
);
|
||||||
|
sort($return);
|
||||||
|
|
||||||
|
return Response::json($return);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param JournalCollectorInterface $collector
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function allTransactionJournals(JournalCollectorInterface $collector)
|
||||||
|
{
|
||||||
|
$collector->setLimit(100)->setPage(1);
|
||||||
|
$return = array_unique($collector->getJournals()->pluck('description')->toArray());
|
||||||
|
sort($return);
|
||||||
|
|
||||||
|
return Response::json($return);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param BillRepositoryInterface $repository
|
* @param BillRepositoryInterface $repository
|
||||||
*
|
*
|
||||||
@@ -156,19 +197,29 @@ class JsonController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of categories.
|
* @param BudgetRepositoryInterface $repository
|
||||||
*
|
|
||||||
* @param CRI $repository
|
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*/
|
*/
|
||||||
public function categories(CRI $repository)
|
public function budgets(BudgetRepositoryInterface $repository)
|
||||||
{
|
{
|
||||||
$list = $repository->getCategories();
|
$return = array_unique($repository->getBudgets()->pluck('name')->toArray());
|
||||||
$return = [];
|
sort($return);
|
||||||
foreach ($list as $entry) {
|
|
||||||
$return[] = $entry->name;
|
return Response::json($return);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of categories.
|
||||||
|
*
|
||||||
|
* @param CategoryRepositoryInterface $repository
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function categories(CategoryRepositoryInterface $repository)
|
||||||
|
{
|
||||||
|
$return = array_unique($repository->getCategories()->pluck('name')->toArray());
|
||||||
|
sort($return);
|
||||||
|
|
||||||
return Response::json($return);
|
return Response::json($return);
|
||||||
}
|
}
|
||||||
@@ -193,14 +244,10 @@ class JsonController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function expenseAccounts(AccountRepositoryInterface $repository)
|
public function expenseAccounts(AccountRepositoryInterface $repository)
|
||||||
{
|
{
|
||||||
$list = $repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY]);
|
$return = array_unique($repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY])->pluck('name')->toArray());
|
||||||
$return = [];
|
sort($return);
|
||||||
foreach ($list as $entry) {
|
|
||||||
$return[] = $entry->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Response::json($return);
|
return Response::json($return);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -211,14 +258,10 @@ class JsonController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function revenueAccounts(AccountRepositoryInterface $repository)
|
public function revenueAccounts(AccountRepositoryInterface $repository)
|
||||||
{
|
{
|
||||||
$list = $repository->getAccountsByType([AccountType::REVENUE]);
|
$return = array_unique($repository->getAccountsByType([AccountType::REVENUE])->pluck('name')->toArray());
|
||||||
$return = [];
|
sort($return);
|
||||||
foreach ($list as $entry) {
|
|
||||||
$return[] = $entry->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Response::json($return);
|
return Response::json($return);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -230,11 +273,8 @@ class JsonController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function tags(TagRepositoryInterface $tagRepository)
|
public function tags(TagRepositoryInterface $tagRepository)
|
||||||
{
|
{
|
||||||
$list = $tagRepository->get();
|
$return = array_unique($tagRepository->get()->pluck('tag')->toArray());
|
||||||
$return = [];
|
sort($return);
|
||||||
foreach ($list as $entry) {
|
|
||||||
$return[] = $entry->tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Response::json($return);
|
return Response::json($return);
|
||||||
|
|
||||||
@@ -269,38 +309,44 @@ class JsonController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $what
|
* @param JournalCollectorInterface $collector
|
||||||
|
* @param string $what
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*/
|
*/
|
||||||
public function transactionJournals($what)
|
public function transactionJournals(JournalCollectorInterface $collector, string $what)
|
||||||
{
|
{
|
||||||
$descriptions = [];
|
$type = config('firefly.transactionTypesByWhat.' . $what);
|
||||||
$type = config('firefly.transactionTypesByWhat.' . $what);
|
$types = [$type];
|
||||||
$types = [$type];
|
|
||||||
|
|
||||||
// use journal collector instead:
|
|
||||||
$collector = new JournalCollector(auth()->user());
|
|
||||||
$collector->setTypes($types)->setLimit(100)->setPage(1);
|
$collector->setTypes($types)->setLimit(100)->setPage(1);
|
||||||
$journals = $collector->getJournals();
|
$return = array_unique($collector->getJournals()->pluck('description')->toArray());
|
||||||
foreach ($journals as $j) {
|
sort($return);
|
||||||
$descriptions[] = $j->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
$descriptions = array_unique($descriptions);
|
return Response::json($return);
|
||||||
sort($descriptions);
|
|
||||||
|
|
||||||
return Response::json($descriptions);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function transactionTypes(JournalRepositoryInterface $repository)
|
||||||
|
{
|
||||||
|
$return = array_unique($repository->getTransactionTypes()->pluck('type')->toArray());
|
||||||
|
sort($return);
|
||||||
|
|
||||||
|
return Response::json($return);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @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'));
|
$keys = array_keys(config('firefly.rule-triggers'));
|
||||||
$triggers = [];
|
$triggers = [];
|
||||||
foreach ($keys as $key) {
|
foreach ($keys as $key) {
|
||||||
|
@@ -117,7 +117,7 @@ class NewUserController extends Controller
|
|||||||
'virtualBalance' => 0,
|
'virtualBalance' => 0,
|
||||||
'active' => true,
|
'active' => true,
|
||||||
'accountRole' => 'defaultAsset',
|
'accountRole' => 'defaultAsset',
|
||||||
'openingBalance' => round($request->input('bank_balance'), 2),
|
'openingBalance' => round($request->input('bank_balance'), 12),
|
||||||
'openingBalanceDate' => new Carbon,
|
'openingBalanceDate' => new Carbon,
|
||||||
'openingBalanceCurrency' => intval($request->input('amount_currency_id_bank_balance')),
|
'openingBalanceCurrency' => intval($request->input('amount_currency_id_bank_balance')),
|
||||||
];
|
];
|
||||||
@@ -142,7 +142,7 @@ class NewUserController extends Controller
|
|||||||
'virtualBalance' => 0,
|
'virtualBalance' => 0,
|
||||||
'active' => true,
|
'active' => true,
|
||||||
'accountRole' => 'savingAsset',
|
'accountRole' => 'savingAsset',
|
||||||
'openingBalance' => round($request->input('savings_balance'), 2),
|
'openingBalance' => round($request->input('savings_balance'), 12),
|
||||||
'openingBalanceDate' => new Carbon,
|
'openingBalanceDate' => new Carbon,
|
||||||
'openingBalanceCurrency' => intval($request->input('amount_currency_id_savings_balance')),
|
'openingBalanceCurrency' => intval($request->input('amount_currency_id_savings_balance')),
|
||||||
];
|
];
|
||||||
@@ -163,7 +163,7 @@ class NewUserController extends Controller
|
|||||||
'name' => 'Credit card',
|
'name' => 'Credit card',
|
||||||
'iban' => null,
|
'iban' => null,
|
||||||
'accountType' => 'asset',
|
'accountType' => 'asset',
|
||||||
'virtualBalance' => round($request->get('credit_card_limit'), 2),
|
'virtualBalance' => round($request->get('credit_card_limit'), 12),
|
||||||
'active' => true,
|
'active' => true,
|
||||||
'accountRole' => 'ccAsset',
|
'accountRole' => 'ccAsset',
|
||||||
'openingBalance' => null,
|
'openingBalance' => null,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user