mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-09-21 19:47:48 +00:00
Compare commits
597 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
665a52b106 | ||
|
f68d33870b | ||
|
49d13b12a5 | ||
|
674ab7e41f | ||
|
5b3eb3ba82 | ||
|
e28d3f3b5a | ||
|
870d8b5008 | ||
|
6aa240e9a3 | ||
|
b55a4047d0 | ||
|
a07d87318c | ||
|
dd6555c903 | ||
|
e8c40b6044 | ||
|
91c17a0b2a | ||
|
2ed0ea0243 | ||
|
df2f92433a | ||
|
68b3fc72bf | ||
|
df5bb14758 | ||
|
e24199bbbe | ||
|
2c6099556b | ||
|
f98215a5da | ||
|
b6b6888493 | ||
|
f8e5b9be43 | ||
|
692210214f | ||
|
cff2546c0c | ||
|
f0cc1200f3 | ||
|
e83a9af455 | ||
|
fb84f9d9cf | ||
|
118a2515e1 | ||
|
63e891b0f7 | ||
|
9891080b57 | ||
|
bc7a7e55af | ||
|
9b8a029de1 | ||
|
11b5575422 | ||
|
06336aa580 | ||
|
aa8c1d6e9c | ||
|
9a4d5d8abf | ||
|
3c5631bca3 | ||
|
84f85f87b2 | ||
|
c35db5976f | ||
|
8d1fcf988c | ||
|
bbf8f2dd69 | ||
|
2f6436e34f | ||
|
9cbb03107d | ||
|
ee44d7fb2e | ||
|
6a0fcd9cf0 | ||
|
43cfe3b858 | ||
|
dba83c1c03 | ||
|
053e139d00 | ||
|
04f791a839 | ||
|
a4029c9490 | ||
|
d471dfec43 | ||
|
494bbd46d0 | ||
|
1525b9ad06 | ||
|
19847ee80b | ||
|
fddf1f146c | ||
|
5f19cb1c0c | ||
|
aaeae992e1 | ||
|
a0e7be9d45 | ||
|
78faf7e14c | ||
|
98f84c2c37 | ||
|
278805043e | ||
|
dc5215e41e | ||
|
7e11691ea4 | ||
|
c83dfc44d6 | ||
|
68a01b1735 | ||
|
0307b58d17 | ||
|
9d1508049e | ||
|
42322055f9 | ||
|
5de8fce156 | ||
|
29ff92f833 | ||
|
359007c5bf | ||
|
bbe40518e4 | ||
|
b26f3c0cc6 | ||
|
552b4b67a6 | ||
|
7fbf359efd | ||
|
052b804855 | ||
|
67cc611495 | ||
|
7e2c24b82e | ||
|
4a6bbd4dca | ||
|
166bfba5b9 | ||
|
87b8ac5f4e | ||
|
46b4100291 | ||
|
83090ade94 | ||
|
44b7a42d5a | ||
|
1a5617d430 | ||
|
fa818e0924 | ||
|
f87b531fe1 | ||
|
82fd0c4d37 | ||
|
26ceb9e3be | ||
|
743458b853 | ||
|
920e5f6fbe | ||
|
fc0dd22769 | ||
|
c7c61ce280 | ||
|
5f5a603f71 | ||
|
d611858883 | ||
|
dbc9ce76a0 | ||
|
62386e2e40 | ||
|
5aa7ab5e37 | ||
|
be1b57eab7 | ||
|
ad5c8b41e2 | ||
|
09752b25ad | ||
|
3f317f961b | ||
|
d6d6a62fda | ||
|
528833b831 | ||
|
95e2bca4a6 | ||
|
dfd3327108 | ||
|
653befbcd4 | ||
|
33202efcdc | ||
|
77a37411ba | ||
|
491f221741 | ||
|
e8e125a598 | ||
|
cb01ae0ac5 | ||
|
a09469c01f | ||
|
662f398b08 | ||
|
d7aef627b8 | ||
|
87bcf293aa | ||
|
0171831ebb | ||
|
50cc455f0f | ||
|
47a7729358 | ||
|
6d508e61a0 | ||
|
8ef9223d84 | ||
|
e58ea4d0c7 | ||
|
9629ba916e | ||
|
aad4df1596 | ||
|
30ed25f80a | ||
|
8b7000681c | ||
|
221adbf3db | ||
|
39129d8cd0 | ||
|
2c2d444906 | ||
|
04a1d785bf | ||
|
c0a0aa4652 | ||
|
21c24fd7f0 | ||
|
beb358f8ee | ||
|
e3cd11ec2e | ||
|
ee08fc2421 | ||
|
ae30f7920b | ||
|
f4786c3ec8 | ||
|
8b9e4d2539 | ||
|
d7ca7ed632 | ||
|
1346b25fc7 | ||
|
15d9314503 | ||
|
93f4006c9e | ||
|
16182fec6c | ||
|
99606ba936 | ||
|
be1ed56d42 | ||
|
d7fb6f83b8 | ||
|
665f1f470a | ||
|
7867f26120 | ||
|
8e195bf811 | ||
|
e3403dc87f | ||
|
5844e95488 | ||
|
cb3a1f2ff6 | ||
|
f95edb06a9 | ||
|
86d13060bc | ||
|
0b8c2f6f8d | ||
|
589cafc64f | ||
|
448dc7b5c3 | ||
|
10cfde6cab | ||
|
e48eb2ce2f | ||
|
20a30a2d1d | ||
|
2e5b8418ae | ||
|
054fd229d2 | ||
|
9bdf2c8877 | ||
|
04f858a355 | ||
|
a9bf405a65 | ||
|
463c866cfa | ||
|
03fe0c8fff | ||
|
102d9f3510 | ||
|
3cf9a40c42 | ||
|
ff33267296 | ||
|
f0dab5bdb9 | ||
|
209a907c61 | ||
|
c7984d4363 | ||
|
a388313e1c | ||
|
a3933cb307 | ||
|
89c82d9be9 | ||
|
6e6311a1a1 | ||
|
69b334ed8a | ||
|
a84b6f377f | ||
|
ee5db4e4cc | ||
|
20f3322e5a | ||
|
1e84bc3743 | ||
|
7f198cbd50 | ||
|
1c7d5ccefc | ||
|
41dc4d994e | ||
|
490733bdd1 | ||
|
ed8cf8c431 | ||
|
a7e1f85c4d | ||
|
4b8919420d | ||
|
9afc5d67a4 | ||
|
72afaddd7c | ||
|
8ae3182e1c | ||
|
9d7e16b390 | ||
|
3a2ed202ad | ||
|
9e4b9b98ab | ||
|
e65fb7d995 | ||
|
7197830edc | ||
|
687603ae84 | ||
|
23fc25f926 | ||
|
e0c1f07f92 | ||
|
9424aa3378 | ||
|
57e6e0945f | ||
|
d87a033f29 | ||
|
10481895e6 | ||
|
813432f386 | ||
|
aba78c9776 | ||
|
8dc56bcee0 | ||
|
32b6e030ef | ||
|
a27f5d2474 | ||
|
e2fe8cfb75 | ||
|
9f1c346365 | ||
|
3e56803e95 | ||
|
fc5feb054d | ||
|
8fc233e436 | ||
|
22303eb2ff | ||
|
d6477ff9ff | ||
|
a0f6f13650 | ||
|
1dd213d587 | ||
|
569e51afbe | ||
|
ff06a4ed0d | ||
|
ea3fdb0668 | ||
|
bd917f6484 | ||
|
ee96311222 | ||
|
135153ff9c | ||
|
fdb98133c8 | ||
|
ad4e2c0a85 | ||
|
e38cb263aa | ||
|
0922136389 | ||
|
2a05517a30 | ||
|
a0ee924aeb | ||
|
1595afce68 | ||
|
4919d730c8 | ||
|
1407d6cc66 | ||
|
55b092e7a0 | ||
|
93afb754f3 | ||
|
81c0ea8f5c | ||
|
6faacd3781 | ||
|
ea1e9e407f | ||
|
d4f5aa1578 | ||
|
1cbbf3fa5d | ||
|
dad3dc71ca | ||
|
afb1e9f230 | ||
|
c33dd1ecee | ||
|
7e31a29b12 | ||
|
9a69ce309e | ||
|
b99bfcd02e | ||
|
adb16e4560 | ||
|
953c38563b | ||
|
89ee9c058a | ||
|
bac7a73555 | ||
|
5fb6ff230b | ||
|
605a718418 | ||
|
994542c75d | ||
|
d5fdce02fa | ||
|
d3637de0c3 | ||
|
63acbb222d | ||
|
5e1fe157b6 | ||
|
8e811da967 | ||
|
fb23108f4e | ||
|
a0220eb5f8 | ||
|
c16cbd5bc8 | ||
|
2d0673e1bc | ||
|
a90e00d577 | ||
|
8db96025a3 | ||
|
595596d73f | ||
|
240797e92a | ||
|
6cafb91680 | ||
|
852ce3e32f | ||
|
6b9c9458fa | ||
|
970ce917b0 | ||
|
db46d450bf | ||
|
db806b92dd | ||
|
6765f08b07 | ||
|
99d75ba14b | ||
|
0e8ce5680c | ||
|
fd01b54a14 | ||
|
ce6253bbd7 | ||
|
4fd33f19c6 | ||
|
01ae278f09 | ||
|
7907c71e47 | ||
|
4e44733dcc | ||
|
dd1d7bb02a | ||
|
6f933b3bd1 | ||
|
243530b750 | ||
|
ea984281b0 | ||
|
92cd3d60b9 | ||
|
5920ccee04 | ||
|
569fec4610 | ||
|
5770edcde2 | ||
|
1fb0a64f31 | ||
|
fe66d089ad | ||
|
cef10b4d4e | ||
|
b45aa6446d | ||
|
61749312b2 | ||
|
842bb2c5a6 | ||
|
4358fe99b1 | ||
|
3fda8b3714 | ||
|
09f3274adc | ||
|
029224f708 | ||
|
e87cce12f8 | ||
|
7660667153 | ||
|
222b3008d5 | ||
|
398cf0b312 | ||
|
5ad8be2483 | ||
|
6fe319702d | ||
|
32c89f9a98 | ||
|
64f983786e | ||
|
16438b416d | ||
|
52cd292a69 | ||
|
53b501df19 | ||
|
477acafc4c | ||
|
ab9146b7c6 | ||
|
4d15913e18 | ||
|
63a91811e2 | ||
|
9df4da6173 | ||
|
643927799b | ||
|
2652e23089 | ||
|
b70c5ae41e | ||
|
c53d9b2855 | ||
|
2f6712be87 | ||
|
37f78985ae | ||
|
34ac9bc71a | ||
|
19ff2484fd | ||
|
b7ef2211d9 | ||
|
c5206a4559 | ||
|
894296f63d | ||
|
61526df245 | ||
|
92549c4485 | ||
|
6014892d4d | ||
|
9515ce6807 | ||
|
1adb0f2f0e | ||
|
00b1b54347 | ||
|
282ce041e1 | ||
|
3215c4ee4b | ||
|
fce00c95c9 | ||
|
0a69ab1dc2 | ||
|
94771d250a | ||
|
b8395a1dbb | ||
|
0436cd1584 | ||
|
2c774bb94c | ||
|
b879d90b0d | ||
|
ba52b5c328 | ||
|
bf61de13f4 | ||
|
b26e67ae07 | ||
|
edafd16c75 | ||
|
c62e3dcb78 | ||
|
68be58c9f2 | ||
|
a25a499ed4 | ||
|
97f67912f4 | ||
|
e2f3788ff5 | ||
|
fd1f06c2cb | ||
|
2db8d25038 | ||
|
d618ddc8c5 | ||
|
79aa0afc97 | ||
|
e53d294c1c | ||
|
aedc3fdff9 | ||
|
b67dfeced2 | ||
|
65dbfcba5c | ||
|
8f14c78ba1 | ||
|
be1b64bb78 | ||
|
a34c285820 | ||
|
298f0c194d | ||
|
f5ad569aba | ||
|
c70df01532 | ||
|
03e115e066 | ||
|
b88fce35ad | ||
|
d214e9ff49 | ||
|
bd8382c005 | ||
|
18cb815e45 | ||
|
800e8aca6e | ||
|
63a6e0754b | ||
|
4abc271805 | ||
|
22cca3858c | ||
|
b816fbdf82 | ||
|
d400060b8b | ||
|
c88cfa2d9d | ||
|
b060027304 | ||
|
d4787aca9c | ||
|
69ae2e93f8 | ||
|
82a37c62c1 | ||
|
ca69970242 | ||
|
fac3853d95 | ||
|
9dd2f447cc | ||
|
ecbd7ca95b | ||
|
0c52d54d7d | ||
|
78276ac66b | ||
|
ca0a22d755 | ||
|
47361fd77a | ||
|
1af6fd5d74 | ||
|
51df78ad2d | ||
|
84a3e68eb5 | ||
|
b2904e09d8 | ||
|
fc42a621e4 | ||
|
a1c4fb73cd | ||
|
c18f19db97 | ||
|
ebc712f6b5 | ||
|
ef0057d88d | ||
|
a8fdfdd5e0 | ||
|
89f7e1ba2a | ||
|
6508b4058f | ||
|
bec5d93af6 | ||
|
1125fcd6c5 | ||
|
ff43a547a6 | ||
|
ca8684146c | ||
|
44bea62383 | ||
|
8dc2faa5e5 | ||
|
f4867d1d09 | ||
|
8d172801b5 | ||
|
db6e6dfe4a | ||
|
61007a95a6 | ||
|
bf138670e8 | ||
|
0e59f7433c | ||
|
9a033ac62a | ||
|
789412ee6a | ||
|
91125d20ef | ||
|
292b3672e2 | ||
|
14ef6b753c | ||
|
55fcb97a10 | ||
|
6b8ec544c1 | ||
|
c0aad385cd | ||
|
ecfb5e4711 | ||
|
46c9967a68 | ||
|
176c44e2b9 | ||
|
5957cf5ff8 | ||
|
436a270524 | ||
|
7a1c14b766 | ||
|
721c4aa888 | ||
|
638aab4eea | ||
|
9aa44f7458 | ||
|
1e887a2a8d | ||
|
78571ad121 | ||
|
8e5ec79097 | ||
|
fc351f36b1 | ||
|
80204147f7 | ||
|
d599b8e5ea | ||
|
208ef9d664 | ||
|
c14da542d1 | ||
|
451832cb2b | ||
|
1409aeb8ed | ||
|
342f24cfe4 | ||
|
8aedfd5153 | ||
|
33b099456f | ||
|
42cb40102f | ||
|
2fbeaaccd3 | ||
|
57fb75bef4 | ||
|
5c0e22cd31 | ||
|
879f74e521 | ||
|
e5a2e1a8c7 | ||
|
884d6c59a2 | ||
|
c206a95d55 | ||
|
8b4ef4e2da | ||
|
bac5238589 | ||
|
ae05d4d51d | ||
|
9d22bbee1c | ||
|
1d6007b848 | ||
|
3157339e44 | ||
|
b31d5eefd1 | ||
|
e47398dc71 | ||
|
757d472704 | ||
|
9d0ae99602 | ||
|
0d9ceb6fde | ||
|
cb665d4016 | ||
|
9673dc96d9 | ||
|
2295091427 | ||
|
45f4395f26 | ||
|
d9aa074330 | ||
|
33c20c8dc4 | ||
|
8101f910f0 | ||
|
8fb6c1a0c8 | ||
|
978e3e615c | ||
|
b3b8981b4b | ||
|
015064e5af | ||
|
7ef8ff60a5 | ||
|
4c3f54699e | ||
|
3bf5040324 | ||
|
ed9e5b31fa | ||
|
506e97e12d | ||
|
9365f9ab60 | ||
|
dd1e9ecb32 | ||
|
d3a2bf174d | ||
|
311020ff2e | ||
|
a6df3ac1fb | ||
|
d313b50e39 | ||
|
b23eb07018 | ||
|
2116486fe0 | ||
|
eed8fe22c6 | ||
|
1cec91e4bf | ||
|
8decf8ab9f | ||
|
79b0c20adb | ||
|
65647ca822 | ||
|
18fafcd45f | ||
|
b7723f4487 | ||
|
6418df87bb | ||
|
daa88c5be1 | ||
|
707b70b136 | ||
|
f9b3b6f7d3 | ||
|
b295eef970 | ||
|
f13dace7ad | ||
|
8dfc40ed3e | ||
|
2e637031ac | ||
|
de9ef20014 | ||
|
4f50689d0e | ||
|
082392f9e0 | ||
|
aca2ef08f1 | ||
|
90fcec4ca8 | ||
|
c90db28b9e | ||
|
56b617bd57 | ||
|
06d58c16d8 | ||
|
b1a807139a | ||
|
ac07736040 | ||
|
c68ba8d510 | ||
|
470802c93b | ||
|
c58745b6ce | ||
|
9eea4749f0 | ||
|
251206fb75 | ||
|
b53a6c5703 | ||
|
8c6972d12d | ||
|
fe0d88b7b3 | ||
|
658b6de9c7 | ||
|
4ea89c3811 | ||
|
2d3d71f46a | ||
|
80a6f431b6 | ||
|
0644729148 | ||
|
07da48e5ea | ||
|
b3f565819b | ||
|
af4abfbed9 | ||
|
eb0011cb46 | ||
|
444439fdab | ||
|
a0e66b913b | ||
|
96c780c804 | ||
|
4f1f46aa93 | ||
|
1d9f76ee5a | ||
|
a7ceda7eea | ||
|
baec4c4d70 | ||
|
4fa850048c | ||
|
8163655a24 | ||
|
4cabf8d2e3 | ||
|
db2f08fa96 | ||
|
a68be2fed7 | ||
|
c3e9aea3a7 | ||
|
40c38af766 | ||
|
fc2cee7a54 | ||
|
3d4feff7de | ||
|
f63c6875cd | ||
|
115b72149c | ||
|
a7e8118c83 | ||
|
8569cc5ac6 | ||
|
7a0ffce36f | ||
|
c573d95ec5 | ||
|
d3e1fba4e0 | ||
|
e7dd087b52 | ||
|
c7cb79906c | ||
|
3c3f80c5a0 | ||
|
e20fb3c3ac | ||
|
e3986a4dd4 | ||
|
a3926e3996 | ||
|
e737683efb | ||
|
0157c4ed65 | ||
|
058ade266d | ||
|
e0adb4c397 | ||
|
35aa61bb23 | ||
|
27236d19cf | ||
|
063ca3121a | ||
|
b8521c6875 | ||
|
e3798d6462 | ||
|
387a3e541f | ||
|
42a3b411e4 | ||
|
25c7ec4175 | ||
|
7ec11765da | ||
|
d57ae126b7 | ||
|
87f133555c | ||
|
db9883d851 | ||
|
8dad5ff7db | ||
|
fc36f9cd8c | ||
|
563c668e3f | ||
|
b3df1f3d26 | ||
|
1ac3f7af3b | ||
|
6cbb86aed7 | ||
|
e459a8c88b | ||
|
ca003b7d4d | ||
|
03f873a6d3 | ||
|
5dbec53847 | ||
|
49dfad9b0c | ||
|
4ee0462d1b | ||
|
3f78be4471 | ||
|
d93fad39ac | ||
|
aa3429bb0e | ||
|
d88246f2f6 | ||
|
201db34936 | ||
|
47709dfc7c | ||
|
2aaafc54ee | ||
|
e211881691 | ||
|
0d32f16041 | ||
|
2082e8d462 | ||
|
bfc95cfc57 | ||
|
35aeb7e04a | ||
|
adcddb09cd |
@@ -41,6 +41,8 @@ SHOW_INCOMPLETE_TRANSLATIONS=false
|
|||||||
|
|
||||||
CACHE_PREFIX=firefly
|
CACHE_PREFIX=firefly
|
||||||
|
|
||||||
|
EXCHANGE_RATE_SERVICE=fixerio
|
||||||
|
|
||||||
GOOGLE_MAPS_API_KEY=
|
GOOGLE_MAPS_API_KEY=
|
||||||
ANALYTICS_ID=
|
ANALYTICS_ID=
|
||||||
SITE_OWNER=mail@example.com
|
SITE_OWNER=mail@example.com
|
||||||
|
@@ -3,6 +3,7 @@ APP_DEBUG=true
|
|||||||
APP_FORCE_SSL=false
|
APP_FORCE_SSL=false
|
||||||
APP_FORCE_ROOT=
|
APP_FORCE_ROOT=
|
||||||
APP_KEY=TestTestTestTestTestTestTestTest
|
APP_KEY=TestTestTestTestTestTestTestTest
|
||||||
|
APP_LOG=daily
|
||||||
APP_LOG_LEVEL=debug
|
APP_LOG_LEVEL=debug
|
||||||
APP_URL=http://localhost
|
APP_URL=http://localhost
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ REDIS_HOST=127.0.0.1
|
|||||||
REDIS_PASSWORD=null
|
REDIS_PASSWORD=null
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
|
|
||||||
MAIL_DRIVER=smtp
|
MAIL_DRIVER=log
|
||||||
MAIL_HOST=mailtrap.io
|
MAIL_HOST=mailtrap.io
|
||||||
MAIL_PORT=2525
|
MAIL_PORT=2525
|
||||||
MAIL_FROM=changeme@example.com
|
MAIL_FROM=changeme@example.com
|
||||||
|
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@@ -17,3 +17,7 @@ Take the time to read the [installation guide FAQ](https://firefly-iii.github.io
|
|||||||
## Pull requests
|
## Pull requests
|
||||||
|
|
||||||
I can only accept pull requests against the `develop` branch, never the `master` branch.
|
I can only accept pull requests against the `develop` branch, never the `master` branch.
|
||||||
|
|
||||||
|
## Translations :us: :fr: :de:
|
||||||
|
|
||||||
|
If you see a spelling error, grammatical error or a weird translation in your language, please join [our CrowdIn](https://crowdin.com/project/firefly-iii) project. There, you can submit your translations and fixes. The GitHub repository will download these automatically and they will be included in the next release.
|
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,14 +1,7 @@
|
|||||||
/node_modules
|
/node_modules
|
||||||
/public/storage
|
/public/storage
|
||||||
/vendor
|
/vendor
|
||||||
/.idea
|
|
||||||
Homestead.json
|
Homestead.json
|
||||||
Homestead.yaml
|
Homestead.yaml
|
||||||
.env
|
.env
|
||||||
_development
|
|
||||||
.env.local
|
|
||||||
result.html
|
|
||||||
test-import.sh
|
|
||||||
test-import-report.txt
|
|
||||||
public/google*.html
|
public/google*.html
|
||||||
.env.backup
|
|
||||||
|
@@ -232,27 +232,52 @@ opt/app/app/Console/Kernel.php
|
|||||||
opt/app/app/Exceptions/Handler.php
|
opt/app/app/Exceptions/Handler.php
|
||||||
opt/app/app/Generator/Chart/Basic/ChartJsGenerator.php
|
opt/app/app/Generator/Chart/Basic/ChartJsGenerator.php
|
||||||
opt/app/app/Generator/Chart/Basic/GeneratorInterface.php
|
opt/app/app/Generator/Chart/Basic/GeneratorInterface.php
|
||||||
|
opt/app/app/Generator/Report/ReportGeneratorFactory.php
|
||||||
|
opt/app/app/Generator/Report/ReportGeneratorInterface.php
|
||||||
|
opt/app/app/Generator/Report/Standard/MonthReportGenerator.php
|
||||||
opt/app/app/Helpers/Attachments/AttachmentHelper.php
|
opt/app/app/Helpers/Attachments/AttachmentHelper.php
|
||||||
opt/app/app/Helpers/Attachments/AttachmentHelperInterface.php
|
opt/app/app/Helpers/Attachments/AttachmentHelperInterface.php
|
||||||
|
opt/app/app/Helpers/Collection/Balance.php
|
||||||
|
opt/app/app/Helpers/Collection/BalanceEntry.php
|
||||||
|
opt/app/app/Helpers/Collection/BalanceHeader.php
|
||||||
|
opt/app/app/Helpers/Collection/BalanceLine.php
|
||||||
|
opt/app/app/Helpers/Collection/Bill.php
|
||||||
opt/app/app/Helpers/Collector/JournalCollector.php
|
opt/app/app/Helpers/Collector/JournalCollector.php
|
||||||
opt/app/app/Helpers/Collector/JournalCollectorInterface.php
|
opt/app/app/Helpers/Collector/JournalCollectorInterface.php
|
||||||
opt/app/app/Helpers/FiscalHelper.php
|
opt/app/app/Helpers/FiscalHelper.php
|
||||||
opt/app/app/Helpers/FiscalHelperInterface.php
|
opt/app/app/Helpers/FiscalHelperInterface.php
|
||||||
|
opt/app/app/Helpers/Report/BalanceReportHelper.php
|
||||||
|
opt/app/app/Helpers/Report/BalanceReportHelperInterface.php
|
||||||
|
opt/app/app/Helpers/Report/BudgetReportHelper.php
|
||||||
|
opt/app/app/Helpers/Report/BudgetReportHelperInterface.php
|
||||||
opt/app/app/Helpers/Report/ReportHelper.php
|
opt/app/app/Helpers/Report/ReportHelper.php
|
||||||
opt/app/app/Helpers/Report/ReportHelperInterface.php
|
opt/app/app/Helpers/Report/ReportHelperInterface.php
|
||||||
|
opt/app/app/Http/Controllers/AccountController.php
|
||||||
opt/app/app/Http/Controllers/Auth/LoginController.php
|
opt/app/app/Http/Controllers/Auth/LoginController.php
|
||||||
|
opt/app/app/Http/Controllers/BillController.php
|
||||||
opt/app/app/Http/Controllers/BudgetController.php
|
opt/app/app/Http/Controllers/BudgetController.php
|
||||||
|
opt/app/app/Http/Controllers/CategoryController.php
|
||||||
opt/app/app/Http/Controllers/Chart/AccountController.php
|
opt/app/app/Http/Controllers/Chart/AccountController.php
|
||||||
opt/app/app/Http/Controllers/Chart/BudgetController.php
|
opt/app/app/Http/Controllers/Chart/BudgetController.php
|
||||||
opt/app/app/Http/Controllers/Chart/CategoryController.php
|
opt/app/app/Http/Controllers/Chart/CategoryController.php
|
||||||
opt/app/app/Http/Controllers/Controller.php
|
opt/app/app/Http/Controllers/Controller.php
|
||||||
opt/app/app/Http/Controllers/HomeController.php
|
opt/app/app/Http/Controllers/HomeController.php
|
||||||
|
opt/app/app/Http/Controllers/ImportController.php
|
||||||
opt/app/app/Http/Controllers/JavascriptController.php
|
opt/app/app/Http/Controllers/JavascriptController.php
|
||||||
opt/app/app/Http/Controllers/JsonController.php
|
opt/app/app/Http/Controllers/JsonController.php
|
||||||
opt/app/app/Http/Controllers/NewUserController.php
|
opt/app/app/Http/Controllers/NewUserController.php
|
||||||
|
opt/app/app/Http/Controllers/PiggyBankController.php
|
||||||
opt/app/app/Http/Controllers/ProfileController.php
|
opt/app/app/Http/Controllers/ProfileController.php
|
||||||
|
opt/app/app/Http/Controllers/Report/AccountController.php
|
||||||
|
opt/app/app/Http/Controllers/Report/BalanceController.php
|
||||||
|
opt/app/app/Http/Controllers/Report/BudgetController.php
|
||||||
|
opt/app/app/Http/Controllers/Report/CategoryController.php
|
||||||
|
opt/app/app/Http/Controllers/Report/OperationsController.php
|
||||||
opt/app/app/Http/Controllers/ReportController.php
|
opt/app/app/Http/Controllers/ReportController.php
|
||||||
|
opt/app/app/Http/Controllers/RuleController.php
|
||||||
|
opt/app/app/Http/Controllers/TagController.php
|
||||||
opt/app/app/Http/Controllers/Transaction/SingleController.php
|
opt/app/app/Http/Controllers/Transaction/SingleController.php
|
||||||
|
opt/app/app/Http/Controllers/TransactionController.php
|
||||||
opt/app/app/Http/Kernel.php
|
opt/app/app/Http/Kernel.php
|
||||||
opt/app/app/Http/Middleware/Authenticate.php
|
opt/app/app/Http/Middleware/Authenticate.php
|
||||||
opt/app/app/Http/Middleware/AuthenticateTwoFactor.php
|
opt/app/app/Http/Middleware/AuthenticateTwoFactor.php
|
||||||
@@ -263,10 +288,16 @@ opt/app/app/Http/Middleware/RedirectIfAuthenticated.php
|
|||||||
opt/app/app/Http/Middleware/Sandstorm.php
|
opt/app/app/Http/Middleware/Sandstorm.php
|
||||||
opt/app/app/Http/Middleware/StartFireflySession.php
|
opt/app/app/Http/Middleware/StartFireflySession.php
|
||||||
opt/app/app/Http/Middleware/VerifyCsrfToken.php
|
opt/app/app/Http/Middleware/VerifyCsrfToken.php
|
||||||
|
opt/app/app/Http/Requests/BudgetFormRequest.php
|
||||||
|
opt/app/app/Http/Requests/BudgetIncomeRequest.php
|
||||||
|
opt/app/app/Http/Requests/CategoryFormRequest.php
|
||||||
opt/app/app/Http/Requests/JournalFormRequest.php
|
opt/app/app/Http/Requests/JournalFormRequest.php
|
||||||
opt/app/app/Http/Requests/NewUserFormRequest.php
|
opt/app/app/Http/Requests/NewUserFormRequest.php
|
||||||
|
opt/app/app/Http/Requests/PiggyBankFormRequest.php
|
||||||
opt/app/app/Http/Requests/ProfileFormRequest.php
|
opt/app/app/Http/Requests/ProfileFormRequest.php
|
||||||
|
opt/app/app/Http/Requests/ReportFormRequest.php
|
||||||
opt/app/app/Http/Requests/Request.php
|
opt/app/app/Http/Requests/Request.php
|
||||||
|
opt/app/app/Http/Requests/TagFormRequest.php
|
||||||
opt/app/app/Http/breadcrumbs.php
|
opt/app/app/Http/breadcrumbs.php
|
||||||
opt/app/app/Jobs/Job.php
|
opt/app/app/Jobs/Job.php
|
||||||
opt/app/app/Jobs/MailError.php
|
opt/app/app/Jobs/MailError.php
|
||||||
@@ -279,9 +310,15 @@ opt/app/app/Models/Budget.php
|
|||||||
opt/app/app/Models/BudgetLimit.php
|
opt/app/app/Models/BudgetLimit.php
|
||||||
opt/app/app/Models/Category.php
|
opt/app/app/Models/Category.php
|
||||||
opt/app/app/Models/Configuration.php
|
opt/app/app/Models/Configuration.php
|
||||||
|
opt/app/app/Models/Note.php
|
||||||
opt/app/app/Models/PiggyBank.php
|
opt/app/app/Models/PiggyBank.php
|
||||||
|
opt/app/app/Models/PiggyBankRepetition.php
|
||||||
opt/app/app/Models/Preference.php
|
opt/app/app/Models/Preference.php
|
||||||
opt/app/app/Models/Role.php
|
opt/app/app/Models/Role.php
|
||||||
|
opt/app/app/Models/Rule.php
|
||||||
|
opt/app/app/Models/RuleAction.php
|
||||||
|
opt/app/app/Models/RuleGroup.php
|
||||||
|
opt/app/app/Models/RuleTrigger.php
|
||||||
opt/app/app/Models/Tag.php
|
opt/app/app/Models/Tag.php
|
||||||
opt/app/app/Models/Transaction.php
|
opt/app/app/Models/Transaction.php
|
||||||
opt/app/app/Models/TransactionCurrency.php
|
opt/app/app/Models/TransactionCurrency.php
|
||||||
@@ -317,14 +354,24 @@ opt/app/app/Repositories/Budget/BudgetRepository.php
|
|||||||
opt/app/app/Repositories/Budget/BudgetRepositoryInterface.php
|
opt/app/app/Repositories/Budget/BudgetRepositoryInterface.php
|
||||||
opt/app/app/Repositories/Category/CategoryRepository.php
|
opt/app/app/Repositories/Category/CategoryRepository.php
|
||||||
opt/app/app/Repositories/Category/CategoryRepositoryInterface.php
|
opt/app/app/Repositories/Category/CategoryRepositoryInterface.php
|
||||||
|
opt/app/app/Repositories/Currency/CurrencyRepository.php
|
||||||
|
opt/app/app/Repositories/Currency/CurrencyRepositoryInterface.php
|
||||||
opt/app/app/Repositories/Journal/JournalRepository.php
|
opt/app/app/Repositories/Journal/JournalRepository.php
|
||||||
opt/app/app/Repositories/Journal/JournalRepositoryInterface.php
|
opt/app/app/Repositories/Journal/JournalRepositoryInterface.php
|
||||||
opt/app/app/Repositories/PiggyBank/PiggyBankRepository.php
|
opt/app/app/Repositories/PiggyBank/PiggyBankRepository.php
|
||||||
opt/app/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php
|
opt/app/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php
|
||||||
|
opt/app/app/Repositories/Rule/RuleRepository.php
|
||||||
|
opt/app/app/Repositories/Rule/RuleRepositoryInterface.php
|
||||||
|
opt/app/app/Repositories/RuleGroup/RuleGroupRepository.php
|
||||||
|
opt/app/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php
|
||||||
opt/app/app/Repositories/Tag/TagRepository.php
|
opt/app/app/Repositories/Tag/TagRepository.php
|
||||||
opt/app/app/Repositories/Tag/TagRepositoryInterface.php
|
opt/app/app/Repositories/Tag/TagRepositoryInterface.php
|
||||||
|
opt/app/app/Repositories/User/UserRepository.php
|
||||||
opt/app/app/Repositories/User/UserRepositoryInterface.php
|
opt/app/app/Repositories/User/UserRepositoryInterface.php
|
||||||
opt/app/app/Support/Amount.php
|
opt/app/app/Support/Amount.php
|
||||||
|
opt/app/app/Support/Binder/AccountList.php
|
||||||
|
opt/app/app/Support/Binder/BinderInterface.php
|
||||||
|
opt/app/app/Support/Binder/Date.php
|
||||||
opt/app/app/Support/CacheProperties.php
|
opt/app/app/Support/CacheProperties.php
|
||||||
opt/app/app/Support/Domain.php
|
opt/app/app/Support/Domain.php
|
||||||
opt/app/app/Support/ExpandedForm.php
|
opt/app/app/Support/ExpandedForm.php
|
||||||
@@ -386,25 +433,40 @@ opt/app/database/seeds/PermissionSeeder.php
|
|||||||
opt/app/database/seeds/TransactionCurrencySeeder.php
|
opt/app/database/seeds/TransactionCurrencySeeder.php
|
||||||
opt/app/database/seeds/TransactionTypeSeeder.php
|
opt/app/database/seeds/TransactionTypeSeeder.php
|
||||||
opt/app/public/css/bootstrap-multiselect.css
|
opt/app/public/css/bootstrap-multiselect.css
|
||||||
|
opt/app/public/css/bootstrap-sortable.css
|
||||||
opt/app/public/css/bootstrap-tagsinput.css
|
opt/app/public/css/bootstrap-tagsinput.css
|
||||||
opt/app/public/css/bootstrap-tour.min.css
|
opt/app/public/css/bootstrap-tour.min.css
|
||||||
opt/app/public/css/daterangepicker.css
|
opt/app/public/css/daterangepicker.css
|
||||||
opt/app/public/css/firefly.css
|
opt/app/public/css/firefly.css
|
||||||
opt/app/public/css/jquery-ui/jquery-ui.structure.min.css
|
opt/app/public/css/jquery-ui/jquery-ui.structure.min.css
|
||||||
opt/app/public/css/jquery-ui/jquery-ui.theme.min.css
|
opt/app/public/css/jquery-ui/jquery-ui.theme.min.css
|
||||||
|
opt/app/public/favicon-16x16.png
|
||||||
|
opt/app/public/favicon-32x32.png
|
||||||
opt/app/public/index.php
|
opt/app/public/index.php
|
||||||
|
opt/app/public/js/ff/accounts/edit.js
|
||||||
|
opt/app/public/js/ff/bills/create.js
|
||||||
opt/app/public/js/ff/budgets/index.js
|
opt/app/public/js/ff/budgets/index.js
|
||||||
|
opt/app/public/js/ff/categories/index.js
|
||||||
opt/app/public/js/ff/charts.defaults.js
|
opt/app/public/js/ff/charts.defaults.js
|
||||||
opt/app/public/js/ff/charts.js
|
opt/app/public/js/ff/charts.js
|
||||||
opt/app/public/js/ff/firefly.js
|
opt/app/public/js/ff/firefly.js
|
||||||
opt/app/public/js/ff/guest.js
|
opt/app/public/js/ff/guest.js
|
||||||
opt/app/public/js/ff/help.js
|
opt/app/public/js/ff/help.js
|
||||||
opt/app/public/js/ff/index.js
|
opt/app/public/js/ff/index.js
|
||||||
|
opt/app/public/js/ff/piggy-banks/create.js
|
||||||
|
opt/app/public/js/ff/piggy-banks/index.js
|
||||||
|
opt/app/public/js/ff/reports/default/all.js
|
||||||
|
opt/app/public/js/ff/reports/default/month.js
|
||||||
opt/app/public/js/ff/reports/index.js
|
opt/app/public/js/ff/reports/index.js
|
||||||
|
opt/app/public/js/ff/rules/index.js
|
||||||
|
opt/app/public/js/ff/tags/create-edit.js
|
||||||
|
opt/app/public/js/ff/tags/index.js
|
||||||
|
opt/app/public/js/ff/transactions/list.js
|
||||||
opt/app/public/js/ff/transactions/single/create.js
|
opt/app/public/js/ff/transactions/single/create.js
|
||||||
opt/app/public/js/lib/Chart.bundle.min.js
|
opt/app/public/js/lib/Chart.bundle.min.js
|
||||||
opt/app/public/js/lib/accounting.min.js
|
opt/app/public/js/lib/accounting.min.js
|
||||||
opt/app/public/js/lib/bootstrap-multiselect.js
|
opt/app/public/js/lib/bootstrap-multiselect.js
|
||||||
|
opt/app/public/js/lib/bootstrap-sortable.js
|
||||||
opt/app/public/js/lib/bootstrap-tagsinput.min.js
|
opt/app/public/js/lib/bootstrap-tagsinput.min.js
|
||||||
opt/app/public/js/lib/bootstrap-tour.min.js
|
opt/app/public/js/lib/bootstrap-tour.min.js
|
||||||
opt/app/public/js/lib/bootstrap3-typeahead.min.js
|
opt/app/public/js/lib/bootstrap3-typeahead.min.js
|
||||||
@@ -417,6 +479,7 @@ opt/app/public/lib/adminlte/css/AdminLTE.min.css
|
|||||||
opt/app/public/lib/adminlte/css/skins/skin-blue-light.min.css
|
opt/app/public/lib/adminlte/css/skins/skin-blue-light.min.css
|
||||||
opt/app/public/lib/adminlte/js/app.min.js
|
opt/app/public/lib/adminlte/js/app.min.js
|
||||||
opt/app/public/lib/bootstrap/css/bootstrap.min.css
|
opt/app/public/lib/bootstrap/css/bootstrap.min.css
|
||||||
|
opt/app/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2
|
||||||
opt/app/public/lib/bootstrap/js/bootstrap.min.js
|
opt/app/public/lib/bootstrap/js/bootstrap.min.js
|
||||||
opt/app/public/lib/font-awesome/css/font-awesome.min.css
|
opt/app/public/lib/font-awesome/css/font-awesome.min.css
|
||||||
opt/app/public/lib/font-awesome/fonts/fontawesome-webfont.woff2
|
opt/app/public/lib/font-awesome/fonts/fontawesome-webfont.woff2
|
||||||
@@ -425,9 +488,19 @@ opt/app/resources/lang/en_US/config.php
|
|||||||
opt/app/resources/lang/en_US/firefly.php
|
opt/app/resources/lang/en_US/firefly.php
|
||||||
opt/app/resources/lang/en_US/form.php
|
opt/app/resources/lang/en_US/form.php
|
||||||
opt/app/resources/lang/en_US/help.php
|
opt/app/resources/lang/en_US/help.php
|
||||||
|
opt/app/resources/lang/en_US/list.php
|
||||||
opt/app/resources/lang/en_US/validation.php
|
opt/app/resources/lang/en_US/validation.php
|
||||||
|
opt/app/resources/views/accounts/delete.twig
|
||||||
|
opt/app/resources/views/accounts/edit.twig
|
||||||
|
opt/app/resources/views/accounts/index.twig
|
||||||
opt/app/resources/views/auth/login.twig
|
opt/app/resources/views/auth/login.twig
|
||||||
|
opt/app/resources/views/bills/create.twig
|
||||||
|
opt/app/resources/views/bills/index.twig
|
||||||
|
opt/app/resources/views/budgets/create.twig
|
||||||
|
opt/app/resources/views/budgets/income.twig
|
||||||
opt/app/resources/views/budgets/index.twig
|
opt/app/resources/views/budgets/index.twig
|
||||||
|
opt/app/resources/views/categories/create.twig
|
||||||
|
opt/app/resources/views/categories/index.twig
|
||||||
opt/app/resources/views/emails/error-html.twig
|
opt/app/resources/views/emails/error-html.twig
|
||||||
opt/app/resources/views/emails/error-text.twig
|
opt/app/resources/views/emails/error-text.twig
|
||||||
opt/app/resources/views/emails/footer-html.twig
|
opt/app/resources/views/emails/footer-html.twig
|
||||||
@@ -437,18 +510,31 @@ opt/app/resources/views/emails/header-text.twig
|
|||||||
opt/app/resources/views/error.twig
|
opt/app/resources/views/error.twig
|
||||||
opt/app/resources/views/form/amount.twig
|
opt/app/resources/views/form/amount.twig
|
||||||
opt/app/resources/views/form/balance.twig
|
opt/app/resources/views/form/balance.twig
|
||||||
|
opt/app/resources/views/form/checkbox.twig
|
||||||
opt/app/resources/views/form/date.twig
|
opt/app/resources/views/form/date.twig
|
||||||
opt/app/resources/views/form/feedback.twig
|
opt/app/resources/views/form/feedback.twig
|
||||||
|
opt/app/resources/views/form/file.twig
|
||||||
opt/app/resources/views/form/help.twig
|
opt/app/resources/views/form/help.twig
|
||||||
|
opt/app/resources/views/form/integer.twig
|
||||||
|
opt/app/resources/views/form/location.twig
|
||||||
|
opt/app/resources/views/form/multiRadio.twig
|
||||||
opt/app/resources/views/form/options.twig
|
opt/app/resources/views/form/options.twig
|
||||||
opt/app/resources/views/form/select.twig
|
opt/app/resources/views/form/select.twig
|
||||||
|
opt/app/resources/views/form/tags.twig
|
||||||
opt/app/resources/views/form/text.twig
|
opt/app/resources/views/form/text.twig
|
||||||
|
opt/app/resources/views/form/textarea.twig
|
||||||
|
opt/app/resources/views/import/index.twig
|
||||||
opt/app/resources/views/index.twig
|
opt/app/resources/views/index.twig
|
||||||
opt/app/resources/views/javascript/variables.twig
|
opt/app/resources/views/javascript/variables.twig
|
||||||
opt/app/resources/views/json/tour.twig
|
opt/app/resources/views/json/tour.twig
|
||||||
opt/app/resources/views/layout/default.twig
|
opt/app/resources/views/layout/default.twig
|
||||||
opt/app/resources/views/layout/guest.twig
|
opt/app/resources/views/layout/guest.twig
|
||||||
|
opt/app/resources/views/list/accounts.twig
|
||||||
|
opt/app/resources/views/list/bills.twig
|
||||||
|
opt/app/resources/views/list/categories.twig
|
||||||
|
opt/app/resources/views/list/journals-tasker.twig
|
||||||
opt/app/resources/views/list/journals-tiny-tasker.twig
|
opt/app/resources/views/list/journals-tiny-tasker.twig
|
||||||
|
opt/app/resources/views/list/piggy-banks.twig
|
||||||
opt/app/resources/views/new-user/index.twig
|
opt/app/resources/views/new-user/index.twig
|
||||||
opt/app/resources/views/partials/boxes.twig
|
opt/app/resources/views/partials/boxes.twig
|
||||||
opt/app/resources/views/partials/control-bar.twig
|
opt/app/resources/views/partials/control-bar.twig
|
||||||
@@ -456,11 +542,25 @@ opt/app/resources/views/partials/favicons.twig
|
|||||||
opt/app/resources/views/partials/flashes.twig
|
opt/app/resources/views/partials/flashes.twig
|
||||||
opt/app/resources/views/partials/menu-sidebar.twig
|
opt/app/resources/views/partials/menu-sidebar.twig
|
||||||
opt/app/resources/views/partials/page-header.twig
|
opt/app/resources/views/partials/page-header.twig
|
||||||
|
opt/app/resources/views/piggy-banks/create.twig
|
||||||
|
opt/app/resources/views/piggy-banks/index.twig
|
||||||
opt/app/resources/views/profile/change-password.twig
|
opt/app/resources/views/profile/change-password.twig
|
||||||
opt/app/resources/views/profile/delete-account.twig
|
opt/app/resources/views/profile/delete-account.twig
|
||||||
opt/app/resources/views/profile/index.twig
|
opt/app/resources/views/profile/index.twig
|
||||||
|
opt/app/resources/views/reports/default/month.twig
|
||||||
opt/app/resources/views/reports/index.twig
|
opt/app/resources/views/reports/index.twig
|
||||||
opt/app/resources/views/reports/options/no-options.twig
|
opt/app/resources/views/reports/options/no-options.twig
|
||||||
|
opt/app/resources/views/reports/partials/accounts.twig
|
||||||
|
opt/app/resources/views/reports/partials/balance.twig
|
||||||
|
opt/app/resources/views/reports/partials/bills.twig
|
||||||
|
opt/app/resources/views/reports/partials/budgets.twig
|
||||||
|
opt/app/resources/views/reports/partials/categories.twig
|
||||||
|
opt/app/resources/views/reports/partials/income-expenses.twig
|
||||||
|
opt/app/resources/views/reports/partials/operations.twig
|
||||||
|
opt/app/resources/views/rules/index.twig
|
||||||
|
opt/app/resources/views/tags/create.twig
|
||||||
|
opt/app/resources/views/tags/index.twig
|
||||||
|
opt/app/resources/views/transactions/index.twig
|
||||||
opt/app/resources/views/transactions/single/create.twig
|
opt/app/resources/views/transactions/single/create.twig
|
||||||
opt/app/routes/api.php
|
opt/app/routes/api.php
|
||||||
opt/app/routes/console.php
|
opt/app/routes/console.php
|
||||||
@@ -612,6 +712,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Http/Kernel.php
|
|||||||
opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Logging/Log.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Logging/Log.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Mail/MailQueue.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Mail/MailQueue.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Mail/Mailer.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Mail/Mailer.php
|
||||||
|
opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Pagination/LengthAwarePaginator.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Pagination/Paginator.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Pagination/Paginator.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Pipeline/Pipeline.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Pipeline/Pipeline.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/Factory.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/Factory.php
|
||||||
@@ -682,6 +783,8 @@ opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/Conc
|
|||||||
opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/HasMany.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/HasMany.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/HasOneOrMany.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/HasOneOrMany.php
|
||||||
|
opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/MorphMany.php
|
||||||
|
opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/MorphOneOrMany.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/Relation.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/Relation.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Scope.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Scope.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/SoftDeletes.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/SoftDeletes.php
|
||||||
@@ -808,8 +911,11 @@ opt/app/vendor/laravel/framework/src/Illuminate/Notifications/Notifiable.php
|
|||||||
opt/app/vendor/laravel/framework/src/Illuminate/Notifications/NotificationServiceProvider.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Notifications/NotificationServiceProvider.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Notifications/RoutesNotifications.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Notifications/RoutesNotifications.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Pagination/AbstractPaginator.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Pagination/AbstractPaginator.php
|
||||||
|
opt/app/vendor/laravel/framework/src/Illuminate/Pagination/LengthAwarePaginator.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Pagination/PaginationServiceProvider.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Pagination/PaginationServiceProvider.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Pagination/Paginator.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Pagination/Paginator.php
|
||||||
|
opt/app/vendor/laravel/framework/src/Illuminate/Pagination/UrlWindow.php
|
||||||
|
opt/app/vendor/laravel/framework/src/Illuminate/Pagination/resources/views/default.blade.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Pipeline/PipelineServiceProvider.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Pipeline/PipelineServiceProvider.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php
|
||||||
@@ -884,12 +990,14 @@ opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/App.php
|
|||||||
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Auth.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Auth.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Cache.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Cache.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Config.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Config.php
|
||||||
|
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Cookie.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Crypt.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Crypt.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/DB.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/DB.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Event.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Event.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Gate.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Gate.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Input.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Input.php
|
||||||
|
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Lang.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Log.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Log.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Mail.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Mail.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Request.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Request.php
|
||||||
@@ -914,6 +1022,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Support/ViewErrorBag.php
|
|||||||
opt/app/vendor/laravel/framework/src/Illuminate/Support/helpers.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Support/helpers.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Translation/FileLoader.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Translation/FileLoader.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Translation/LoaderInterface.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Translation/LoaderInterface.php
|
||||||
|
opt/app/vendor/laravel/framework/src/Illuminate/Translation/MessageSelector.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Translation/TranslationServiceProvider.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Translation/TranslationServiceProvider.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Translation/Translator.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Translation/Translator.php
|
||||||
opt/app/vendor/laravel/framework/src/Illuminate/Validation/Concerns/FormatsMessages.php
|
opt/app/vendor/laravel/framework/src/Illuminate/Validation/Concerns/FormatsMessages.php
|
||||||
@@ -1214,14 +1323,17 @@ opt/app/vendor/twig/twig/lib/Twig/Node/Expression.php
|
|||||||
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Array.php
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Array.php
|
||||||
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php
|
||||||
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary.php
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary.php
|
||||||
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Add.php
|
||||||
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php
|
||||||
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php
|
||||||
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php
|
||||||
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php
|
||||||
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Less.php
|
||||||
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php
|
||||||
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php
|
||||||
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php
|
||||||
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Or.php
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Or.php
|
||||||
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php
|
||||||
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Call.php
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Call.php
|
||||||
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Conditional.php
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Conditional.php
|
||||||
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Constant.php
|
opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Constant.php
|
||||||
|
@@ -16,7 +16,7 @@ const pkgdef :Spk.PackageDefinition = (
|
|||||||
manifest = (
|
manifest = (
|
||||||
appTitle = (defaultText = "Firefly III"),
|
appTitle = (defaultText = "Firefly III"),
|
||||||
appVersion = 1,
|
appVersion = 1,
|
||||||
appMarketingVersion = (defaultText = "3.4.3"),
|
appMarketingVersion = (defaultText = "3.4.6"),
|
||||||
actions = [
|
actions = [
|
||||||
# Define your "new document" handlers here.
|
# Define your "new document" handlers here.
|
||||||
( nounPhrase = (defaultText = "administration"),
|
( nounPhrase = (defaultText = "administration"),
|
||||||
@@ -97,7 +97,7 @@ const pkgdef :Spk.PackageDefinition = (
|
|||||||
# `spk dev` will write a list of all the files your app uses to this file.
|
# `spk dev` will write a list of all the files your app uses to this file.
|
||||||
# You should review it later, before shipping your app.
|
# You should review it later, before shipping your app.
|
||||||
|
|
||||||
alwaysInclude = [],
|
alwaysInclude = ["app","bootstrap","config","database","public","resources","routes"],
|
||||||
# Fill this list with more names of files or directories that should be
|
# Fill this list with more names of files or directories that should be
|
||||||
# included in your package, even if not listed in sandstorm-files.list.
|
# included in your package, even if not listed in sandstorm-files.list.
|
||||||
# Use this to force-include stuff that you know you need but which may
|
# Use this to force-include stuff that you know you need but which may
|
||||||
|
@@ -9,7 +9,6 @@ cache:
|
|||||||
- $HOME/.composer/cache
|
- $HOME/.composer/cache
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- if [[ "$(php -v | grep 'PHP 7')" ]]; then phpenv config-rm xdebug.ini; fi
|
|
||||||
- rm composer.lock
|
- rm composer.lock
|
||||||
- composer update --no-scripts
|
- composer update --no-scripts
|
||||||
- cp .env.testing .env
|
- cp .env.testing .env
|
||||||
@@ -18,9 +17,13 @@ install:
|
|||||||
- php artisan env
|
- php artisan env
|
||||||
- cp .env.testing .env
|
- cp .env.testing .env
|
||||||
- mv storage/database/databasecopy.sqlite storage/database/database.sqlite
|
- mv storage/database/databasecopy.sqlite storage/database/database.sqlite
|
||||||
|
- mkdir -p build/logs
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- phpunit
|
- phpunit -c phpunit.xml
|
||||||
|
|
||||||
|
#after_success:
|
||||||
|
# - travis_retry php vendor/bin/coveralls -x storage/build/clover.xml
|
||||||
|
|
||||||
# safelist
|
# safelist
|
||||||
branches:
|
branches:
|
||||||
|
106
CHANGELOG.md
106
CHANGELOG.md
@@ -2,6 +2,85 @@
|
|||||||
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.4.3] - 2017-05-03
|
||||||
|
### Added
|
||||||
|
- Added support for Slovenian
|
||||||
|
- Removed support for Spanish. No translations whatsoever by the guy who requested it.
|
||||||
|
- Removed support for Russian. Same thing.
|
||||||
|
- Removed support for Croatian. Same thing.
|
||||||
|
- Removed support for Chinese Traditional, Hong Kong. Same thing.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- The journal collector, an internal piece of code to collect transactions, now uses a slightly different method of collecting journals. This may cause problems.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Issue #638 as reported by [worldworm](https://github.com/worldworm).
|
||||||
|
- Possible fix for #624
|
||||||
|
|
||||||
|
## [4.4.2] - 2017-04-27
|
||||||
|
### Fixed
|
||||||
|
- Fixed a bug where the opening balance could not be stored.
|
||||||
|
|
||||||
|
## [4.4.1] - 2017-04-27
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Support for deployment on Heroku
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Bug in new-user routine.
|
||||||
|
|
||||||
|
## [4.4.0] - 2017-04-23
|
||||||
|
### Added
|
||||||
|
- Firefly III can now handle foreign currencies better, including some code to get the exchange rate live from the web.
|
||||||
|
- Can now make rules for attachments, see #608, as suggested by dzaikos.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed #629, reported by forcaeluz
|
||||||
|
- Fixed #630, reported by welbert
|
||||||
|
- And more various bug fixes.
|
||||||
|
|
||||||
|
## [4.3.8] - 2017-04-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Better overview / show pages.
|
||||||
|
- #628, as reported by [xzaz](https://github.com/xzaz).
|
||||||
|
- Greatly expanded test coverage
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- #619, as reported by [dfiel](https://github.com/dfiel).
|
||||||
|
- #620, as reported by [forcaeluz](https://github.com/forcaeluz).
|
||||||
|
- Attempt to fix #624, as reported by [TheSerenin](https://github.com/TheSerenin).
|
||||||
|
- Favicon link is relative now, fixed by [welbert](https://github.com/welbert).
|
||||||
|
- Some search bugs
|
||||||
|
|
||||||
|
## [4.3.7] - 2017-03-06
|
||||||
|
### Added
|
||||||
|
- Nice user friendly views for empty lists.
|
||||||
|
- Extended contribution guidelines.
|
||||||
|
- First version of financial report filtered on tags.
|
||||||
|
- Suggested monthly savings for piggy banks, by [Zsub](https://github.com/Zsub)
|
||||||
|
- Better test coverage.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Slightly changed tag overview.
|
||||||
|
- Consistent icon for bill in list.
|
||||||
|
- Slightly changed account overview.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Removed IDE specific views from .gitignore, issue #598
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Force key generation during installation.
|
||||||
|
- The `date` function takes the fieldname where a date is stored, not the literal date by [Zsub](https://github.com/Zsub)
|
||||||
|
- Improved budget frontpage chart, as suggested by [skibbipl](https://github.com/skibbipl)
|
||||||
|
- Issue #602 and #607, as reported by [skibbipl](https://github.com/skibbipl) and [dzaikos](https://github.com/dzaikos).
|
||||||
|
- Issue #605, as reported by [Zsub](https://github.com/Zsub).
|
||||||
|
- Issue #599, as reported by [leander091](https://github.com/leander091).
|
||||||
|
- Issue #610, as reported by [skibbipl](https://github.com/skibbipl).
|
||||||
|
- Issue #611, as reported by [ragnarkarlsson](https://github.com/ragnarkarlsson).
|
||||||
|
- Issue #612, as reported by [ragnarkarlsson](https://github.com/ragnarkarlsson).
|
||||||
|
- Issue #614, as reported by [worldworm](https://github.com/worldworm).
|
||||||
|
- Various other bug fixes.
|
||||||
|
|
||||||
## [4.3.6] - 2017-02-20
|
## [4.3.6] - 2017-02-20
|
||||||
### Fixed
|
### Fixed
|
||||||
@@ -159,13 +238,6 @@ An intermediate release because something in the Twig and Twigbridge libraries i
|
|||||||
- Updated all email messages.
|
- Updated all email messages.
|
||||||
- Made some fonts local
|
- Made some fonts local
|
||||||
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
- Initial release.
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
- Initial release.
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Issue #408
|
- Issue #408
|
||||||
- Various issues with split journals
|
- Various issues with split journals
|
||||||
@@ -174,11 +246,6 @@ An intermediate release because something in the Twig and Twigbridge libraries i
|
|||||||
- Issue #422, thx [xzaz](https://github.com/xzaz)
|
- Issue #422, thx [xzaz](https://github.com/xzaz)
|
||||||
- Various import bugs, such as #416 ([zjean](https://github.com/zjean))
|
- Various import bugs, such as #416 ([zjean](https://github.com/zjean))
|
||||||
|
|
||||||
|
|
||||||
### Security
|
|
||||||
- Initial release.
|
|
||||||
|
|
||||||
|
|
||||||
## [4.1.7] - 2016-11-19
|
## [4.1.7] - 2016-11-19
|
||||||
### Added
|
### Added
|
||||||
- Check for database table presence in console commands.
|
- Check for database table presence in console commands.
|
||||||
@@ -301,15 +368,6 @@ An intermediate release because something in the Twig and Twigbridge libraries i
|
|||||||
- New Presidents Choice specific to fix #307
|
- New Presidents Choice specific to fix #307
|
||||||
- Added some trimming (#335)
|
- Added some trimming (#335)
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Initial release.
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
- Initial release.
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
- Initial release.
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fixed a bug where incoming transactions would not be properly filtered in several reports.
|
- Fixed a bug where incoming transactions would not be properly filtered in several reports.
|
||||||
- #334 by [cyberkov](https://github.com/cyberkov)
|
- #334 by [cyberkov](https://github.com/cyberkov)
|
||||||
@@ -317,12 +375,6 @@ An intermediate release because something in the Twig and Twigbridge libraries i
|
|||||||
- #336
|
- #336
|
||||||
- #338 found by [roberthorlings](https://github.com/roberthorlings)
|
- #338 found by [roberthorlings](https://github.com/roberthorlings)
|
||||||
|
|
||||||
### Security
|
|
||||||
- Initial release.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.0.0] - 2015-09-26
|
## [4.0.0] - 2015-09-26
|
||||||
### Added
|
### Added
|
||||||
- Upgraded to Laravel 5.3, most other libraries upgraded as well.
|
- Upgraded to Laravel 5.3, most other libraries upgraded as well.
|
||||||
|
1
Procfile
Normal file
1
Procfile
Normal file
@@ -0,0 +1 @@
|
|||||||
|
web: vendor/bin/heroku-php-nginx -C nginx_app.conf public/
|
12
README.md
12
README.md
@@ -1,6 +1,6 @@
|
|||||||
# Firefly III: A personal finances manager
|
# Firefly III: A personal finances manager
|
||||||
|
|
||||||
[](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)
|
[](https://secure.php.net/downloads.php) [](https://packagist.org/packages/grumpydictator/firefly-iii) [](https://creativecommons.org/licenses/by-sa/4.0/) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA)
|
||||||
|
|
||||||
[](https://i.nder.be/h2b37243) [](https://i.nder.be/hv70pbwc)
|
[](https://i.nder.be/h2b37243) [](https://i.nder.be/hv70pbwc)
|
||||||
|
|
||||||
@@ -10,11 +10,15 @@
|
|||||||
|
|
||||||
## Try it out!
|
## Try it out!
|
||||||
|
|
||||||
Try out Firefly III on the [demo site](https://firefly-iii.nder.be/).
|
[](https://heroku.com/deploy?template=https://github.com/firefly-iii/firefly-iii/tree/master)
|
||||||
|
|
||||||
|
Firefly III can be run on Heroku. Register for a free Heroku account and instantly run Firefly III on your very own cloud instance.
|
||||||
|
|
||||||
|
There is also a [demo site](https://firefly-iii.nder.be) with an example financial administration already present.
|
||||||
|
|
||||||
## 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/using-installing.html).
|
||||||
|
|
||||||
## More about Firefly III
|
## More about Firefly III
|
||||||
|
|
||||||
@@ -34,3 +38,5 @@ Firefly is pretty awesome. [You can read more about Firefly III, and its feature
|
|||||||
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 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).
|
||||||
|
|
||||||
|
[](https://travis-ci.org/firefly-iii/firefly-iii) [](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/?branch=master) [](https://coveralls.io/github/firefly-iii/firefly-iii?branch=master)
|
||||||
|
59
app.json
Normal file
59
app.json
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"name": "Firefly III",
|
||||||
|
"description": "A free and open source personal finances manager",
|
||||||
|
"repository": "https://github.com/firefly-iii/firefly-iii",
|
||||||
|
"logo": "https://raw.githubusercontent.com/firefly-iii/firefly-iii/master/public/mstile-150x150.png",
|
||||||
|
"keywords": [
|
||||||
|
"finance",
|
||||||
|
"finances",
|
||||||
|
"manager",
|
||||||
|
"management",
|
||||||
|
"euro",
|
||||||
|
"dollar",
|
||||||
|
"laravel",
|
||||||
|
"money",
|
||||||
|
"currency",
|
||||||
|
"financials",
|
||||||
|
"financial",
|
||||||
|
"budgets",
|
||||||
|
"administration",
|
||||||
|
"tool",
|
||||||
|
"tooling",
|
||||||
|
"help",
|
||||||
|
"helper",
|
||||||
|
"assistant",
|
||||||
|
"planning",
|
||||||
|
"organizing",
|
||||||
|
"bills",
|
||||||
|
"personal finance",
|
||||||
|
"budgets",
|
||||||
|
"budgeting",
|
||||||
|
"budgeting tool",
|
||||||
|
"budgeting application",
|
||||||
|
"transactions",
|
||||||
|
"self hosted",
|
||||||
|
"self-hosted",
|
||||||
|
"transfers",
|
||||||
|
"management"
|
||||||
|
],
|
||||||
|
"website": "https://firefly-iii.github.io/",
|
||||||
|
"addons": [
|
||||||
|
{
|
||||||
|
"plan": "heroku-postgresql"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"postdeploy": "export APP_KEY=$(php artisan --no-ansi key:generate --show)"
|
||||||
|
},
|
||||||
|
"buildpacks": [
|
||||||
|
{
|
||||||
|
"url": "heroku/php"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"APP_KEY": {
|
||||||
|
"description": "This key is used to encrypt your data.",
|
||||||
|
"value": "base64:If1gJN4pyycXTq+WS5TjneDympKuu+8SKvTl6RZnhJg="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,63 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* ConfigureLogging.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\Bootstrap;
|
|
||||||
|
|
||||||
use Illuminate\Contracts\Foundation\Application;
|
|
||||||
use Illuminate\Foundation\Bootstrap\ConfigureLogging as IlluminateConfigureLogging;
|
|
||||||
use Illuminate\Log\Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ConfigureLogging
|
|
||||||
*
|
|
||||||
* @package FireflyIII\Bootstrap
|
|
||||||
*/
|
|
||||||
class ConfigureLogging extends IlluminateConfigureLogging
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configure the Monolog handlers for the application.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
|
||||||
* @param \Illuminate\Log\Writer $log
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function configureDailyHandler(Application $app, Writer $log)
|
|
||||||
{
|
|
||||||
$config = $app->make('config');
|
|
||||||
|
|
||||||
$maxFiles = $config->get('app.log_max_files');
|
|
||||||
|
|
||||||
$log->useDailyFiles(
|
|
||||||
$app->storagePath() . '/logs/firefly-iii.log', is_null($maxFiles) ? 5 : $maxFiles,
|
|
||||||
$config->get('app.log_level', 'debug')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configure the Monolog handlers for the application.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
|
||||||
* @param \Illuminate\Log\Writer $log
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function configureSingleHandler(Application $app, Writer $log)
|
|
||||||
{
|
|
||||||
$log->useFiles(
|
|
||||||
$app->storagePath() . '/logs/firefly-iii.log',
|
|
||||||
$app->make('config')->get('app.log_level', 'debug')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -84,7 +84,7 @@ class CreateImport extends Command
|
|||||||
$job = $jobRepository->create($type);
|
$job = $jobRepository->create($type);
|
||||||
$this->line(sprintf('Created job "%s"...', $job->key));
|
$this->line(sprintf('Created job "%s"...', $job->key));
|
||||||
|
|
||||||
Artisan::call('firefly:encrypt', ['file' => $file, 'key' => $job->key]);
|
Artisan::call('firefly:encrypt-file', ['file' => $file, 'key' => $job->key]);
|
||||||
$this->line('Stored import data...');
|
$this->line('Stored import data...');
|
||||||
|
|
||||||
$job->configuration = $configurationData;
|
$job->configuration = $configurationData;
|
||||||
|
@@ -58,7 +58,12 @@ class Import extends Command
|
|||||||
{
|
{
|
||||||
Log::debug('Start start-import command');
|
Log::debug('Start start-import command');
|
||||||
$jobKey = $this->argument('key');
|
$jobKey = $this->argument('key');
|
||||||
$job = ImportJob::whereKey($jobKey)->first();
|
$job = ImportJob::where('key', $jobKey)->first();
|
||||||
|
if (is_null($job)) {
|
||||||
|
$this->error(sprintf('No job found with key "%s"', $jobKey));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!$this->isValid($job)) {
|
if (!$this->isValid($job)) {
|
||||||
Log::error('Job is not valid for some reason. Exit.');
|
Log::error('Job is not valid for some reason. Exit.');
|
||||||
|
|
||||||
|
@@ -15,13 +15,22 @@ namespace FireflyIII\Console\Commands;
|
|||||||
|
|
||||||
|
|
||||||
use DB;
|
use DB;
|
||||||
|
use FireflyIII\Models\Account;
|
||||||
|
use FireflyIII\Models\AccountMeta;
|
||||||
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Models\LimitRepetition;
|
use FireflyIII\Models\LimitRepetition;
|
||||||
|
use FireflyIII\Models\PiggyBankEvent;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
|
use FireflyIII\Models\TransactionType;
|
||||||
|
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Database\Query\JoinClause;
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
use Log;
|
use Log;
|
||||||
|
use Preferences;
|
||||||
use Schema;
|
use Schema;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,8 +69,15 @@ class UpgradeDatabase extends Command
|
|||||||
{
|
{
|
||||||
$this->setTransactionIdentifier();
|
$this->setTransactionIdentifier();
|
||||||
$this->migrateRepetitions();
|
$this->migrateRepetitions();
|
||||||
|
$this->repairPiggyBanks();
|
||||||
|
$this->updateAccountCurrencies();
|
||||||
|
$this->updateJournalCurrencies();
|
||||||
|
$this->info('Firefly III database is up to date.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate budget repetitions to new format.
|
||||||
|
*/
|
||||||
private function migrateRepetitions()
|
private function migrateRepetitions()
|
||||||
{
|
{
|
||||||
if (!Schema::hasTable('budget_limits')) {
|
if (!Schema::hasTable('budget_limits')) {
|
||||||
@@ -69,7 +85,9 @@ class UpgradeDatabase extends Command
|
|||||||
}
|
}
|
||||||
// get all budget limits with end_date NULL
|
// get all budget limits with end_date NULL
|
||||||
$set = BudgetLimit::whereNull('end_date')->get();
|
$set = BudgetLimit::whereNull('end_date')->get();
|
||||||
|
if ($set->count() > 0) {
|
||||||
$this->line(sprintf('Found %d budget limit(s) to update', $set->count()));
|
$this->line(sprintf('Found %d budget limit(s) to update', $set->count()));
|
||||||
|
}
|
||||||
/** @var BudgetLimit $budgetLimit */
|
/** @var BudgetLimit $budgetLimit */
|
||||||
foreach ($set as $budgetLimit) {
|
foreach ($set as $budgetLimit) {
|
||||||
// get limit repetition (should be just one):
|
// get limit repetition (should be just one):
|
||||||
@@ -84,6 +102,37 @@ class UpgradeDatabase extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure there are only transfers linked to piggy bank events.
|
||||||
|
*/
|
||||||
|
private function repairPiggyBanks()
|
||||||
|
{
|
||||||
|
// if table does not exist, return false
|
||||||
|
if (!Schema::hasTable('piggy_bank_events')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$set = PiggyBankEvent::with(['PiggyBank', 'TransactionJournal', 'TransactionJournal.TransactionType'])->get();
|
||||||
|
/** @var PiggyBankEvent $event */
|
||||||
|
foreach ($set as $event) {
|
||||||
|
|
||||||
|
if (is_null($event->transaction_journal_id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/** @var TransactionJournal $journal */
|
||||||
|
$journal = $event->transactionJournal()->first();
|
||||||
|
if (is_null($journal)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = $journal->transactionType->type;
|
||||||
|
if ($type !== TransactionType::TRANSFER) {
|
||||||
|
$event->transaction_journal_id = null;
|
||||||
|
$event->save();
|
||||||
|
$this->line(sprintf('Piggy bank #%d was referenced by an invalid event. This has been fixed.', $event->piggy_bank_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is strangely complex, because the HAVING modifier is a no-no. And subqueries in Laravel are weird.
|
* This is strangely complex, because the HAVING modifier is a no-no. And subqueries in Laravel are weird.
|
||||||
*/
|
*/
|
||||||
@@ -112,6 +161,57 @@ class UpgradeDatabase extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function updateAccountCurrencies()
|
||||||
|
{
|
||||||
|
$accounts = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||||
|
->whereIn('account_types.type', [AccountType::DEFAULT, AccountType::ASSET])->get(['accounts.*']);
|
||||||
|
|
||||||
|
/** @var Account $account */
|
||||||
|
foreach ($accounts as $account) {
|
||||||
|
// get users preference, fall back to system pref.
|
||||||
|
$defaultCurrencyCode = Preferences::getForUser($account->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data;
|
||||||
|
$defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first();
|
||||||
|
$accountCurrency = intval($account->getMeta('currency_id'));
|
||||||
|
$openingBalance = $account->getOpeningBalance();
|
||||||
|
$openingBalanceCurrency = intval($openingBalance->transaction_currency_id);
|
||||||
|
|
||||||
|
// both 0? set to default currency:
|
||||||
|
if ($accountCurrency === 0 && $openingBalanceCurrency === 0) {
|
||||||
|
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $defaultCurrency->id]);
|
||||||
|
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// opening balance 0, account not zero? just continue:
|
||||||
|
if ($accountCurrency > 0 && $openingBalanceCurrency === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// account is set to 0, opening balance is not?
|
||||||
|
if ($accountCurrency === 0 && $openingBalanceCurrency > 0) {
|
||||||
|
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $openingBalanceCurrency]);
|
||||||
|
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// both are equal, just continue:
|
||||||
|
if ($accountCurrency === $openingBalanceCurrency) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// do not match:
|
||||||
|
if ($accountCurrency !== $openingBalanceCurrency) {
|
||||||
|
// update opening balance:
|
||||||
|
$openingBalance->transaction_currency_id = $accountCurrency;
|
||||||
|
$openingBalance->save();
|
||||||
|
$this->line(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* grab all positive transactiosn from this journal that are not deleted. for each one, grab the negative opposing one
|
* grab all positive transactiosn from this journal that are not deleted. for each one, grab the negative opposing one
|
||||||
* which has 0 as an identifier and give it the same identifier.
|
* which has 0 as an identifier and give it the same identifier.
|
||||||
@@ -151,9 +251,87 @@ class UpgradeDatabase extends Command
|
|||||||
$opposing->save();
|
$opposing->save();
|
||||||
$processed[] = $transaction->id;
|
$processed[] = $transaction->id;
|
||||||
$processed[] = $opposing->id;
|
$processed[] = $opposing->id;
|
||||||
$this->line(sprintf('Database upgrade for journal #%d, transactions #%d and #%d', $journalId, $transaction->id, $opposing->id));
|
|
||||||
}
|
}
|
||||||
$identifier++;
|
$identifier++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes sure that withdrawals, deposits and transfers have
|
||||||
|
* a currency setting matching their respective accounts
|
||||||
|
*/
|
||||||
|
private function updateJournalCurrencies()
|
||||||
|
{
|
||||||
|
$types = [
|
||||||
|
TransactionType::WITHDRAWAL => '<',
|
||||||
|
TransactionType::DEPOSIT => '>',
|
||||||
|
];
|
||||||
|
$repository = app(CurrencyRepositoryInterface::class);
|
||||||
|
$notification = '%s #%d uses %s but should use %s. It has been updated. Please verify this in Firefly III.';
|
||||||
|
$transfer = 'Transfer #%d has been updated to use the correct currencies. Please verify this in Firefly III.';
|
||||||
|
|
||||||
|
foreach ($types as $type => $operator) {
|
||||||
|
$set = TransactionJournal
|
||||||
|
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')->leftJoin(
|
||||||
|
'transactions', function (JoinClause $join) use ($operator) {
|
||||||
|
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', $operator, '0');
|
||||||
|
}
|
||||||
|
)
|
||||||
|
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
|
||||||
|
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
|
||||||
|
->where('transaction_types.type', $type)
|
||||||
|
->where('account_meta.name', 'currency_id')
|
||||||
|
->where('transaction_journals.transaction_currency_id', '!=', DB::raw('account_meta.data'))
|
||||||
|
->get(['transaction_journals.*', 'account_meta.data as expected_currency_id', 'transactions.amount as transaction_amount']);
|
||||||
|
/** @var TransactionJournal $journal */
|
||||||
|
foreach ($set as $journal) {
|
||||||
|
$expectedCurrency = $repository->find(intval($journal->expected_currency_id));
|
||||||
|
$line = sprintf($notification, $type, $journal->id, $journal->transactionCurrency->code, $expectedCurrency->code);
|
||||||
|
|
||||||
|
$journal->setMeta('foreign_amount', $journal->transaction_amount);
|
||||||
|
$journal->setMeta('foreign_currency_id', $journal->transaction_currency_id);
|
||||||
|
$journal->transaction_currency_id = $expectedCurrency->id;
|
||||||
|
$journal->save();
|
||||||
|
$this->line($line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* For transfers it's slightly different. Both source and destination
|
||||||
|
* must match the respective currency preference. So we must verify ALL
|
||||||
|
* transactions.
|
||||||
|
*/
|
||||||
|
$set = TransactionJournal
|
||||||
|
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||||
|
->where('transaction_types.type', TransactionType::TRANSFER)
|
||||||
|
->get(['transaction_journals.*']);
|
||||||
|
/** @var TransactionJournal $journal */
|
||||||
|
foreach ($set as $journal) {
|
||||||
|
$updated = false;
|
||||||
|
/** @var Transaction $sourceTransaction */
|
||||||
|
$sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first();
|
||||||
|
$sourceCurrency = $repository->find(intval($sourceTransaction->account->getMeta('currency_id')));
|
||||||
|
|
||||||
|
if ($sourceCurrency->id !== $journal->transaction_currency_id) {
|
||||||
|
$updated = true;
|
||||||
|
$journal->transaction_currency_id = $sourceCurrency->id;
|
||||||
|
$journal->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// destination
|
||||||
|
$destinationTransaction = $journal->transactions()->where('amount', '>', 0)->first();
|
||||||
|
$destinationCurrency = $repository->find(intval($destinationTransaction->account->getMeta('currency_id')));
|
||||||
|
|
||||||
|
if ($destinationCurrency->id !== $journal->transaction_currency_id) {
|
||||||
|
$updated = true;
|
||||||
|
$journal->deleteMeta('foreign_amount');
|
||||||
|
$journal->deleteMeta('foreign_currency_id');
|
||||||
|
$journal->setMeta('foreign_amount', $destinationTransaction->amount);
|
||||||
|
$journal->setMeta('foreign_currency_id', $destinationCurrency->id);
|
||||||
|
}
|
||||||
|
if ($updated) {
|
||||||
|
$line = sprintf($transfer, $journal->id);
|
||||||
|
$this->line($line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -93,6 +93,7 @@ class VerifyDatabase extends Command
|
|||||||
|
|
||||||
// report on journals with the wrong types of accounts.
|
// report on journals with the wrong types of accounts.
|
||||||
$this->reportIncorrectJournals();
|
$this->reportIncorrectJournals();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,7 +132,7 @@ class VerifyDatabase extends Command
|
|||||||
/** @var Budget $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.',
|
'User #%d (%s) has budget #%d ("%s") which has no budget limits.',
|
||||||
$entry->user_id, $entry->email, $entry->id, $entry->name
|
$entry->user_id, $entry->email, $entry->id, $entry->name
|
||||||
);
|
);
|
||||||
$this->line($line);
|
$this->line($line);
|
||||||
@@ -277,7 +278,7 @@ class VerifyDatabase extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
$line = sprintf(
|
$line = sprintf(
|
||||||
'Notice: User #%d (%s) has %s #%d ("%s") which has no transactions.',
|
'User #%d (%s) has %s #%d ("%s") which has no transactions.',
|
||||||
$entry->user_id, $entry->email, $name, $entry->id, $objName
|
$entry->user_id, $entry->email, $name, $entry->id, $objName
|
||||||
);
|
);
|
||||||
$this->line($line);
|
$this->line($line);
|
||||||
|
@@ -41,7 +41,6 @@ class Kernel extends ConsoleKernel
|
|||||||
= [
|
= [
|
||||||
'Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables',
|
'Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables',
|
||||||
'Illuminate\Foundation\Bootstrap\LoadConfiguration',
|
'Illuminate\Foundation\Bootstrap\LoadConfiguration',
|
||||||
//'FireflyIII\Bootstrap\ConfigureLogging',
|
|
||||||
'Illuminate\Foundation\Bootstrap\HandleExceptions',
|
'Illuminate\Foundation\Bootstrap\HandleExceptions',
|
||||||
'Illuminate\Foundation\Bootstrap\RegisterFacades',
|
'Illuminate\Foundation\Bootstrap\RegisterFacades',
|
||||||
'Illuminate\Foundation\Bootstrap\SetRequestForConsole',
|
'Illuminate\Foundation\Bootstrap\SetRequestForConsole',
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Events;
|
namespace FireflyIII\Events;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -26,7 +26,9 @@ class StoredTransactionJournal extends Event
|
|||||||
|
|
||||||
use SerializesModels;
|
use SerializesModels;
|
||||||
|
|
||||||
|
/** @var TransactionJournal */
|
||||||
public $journal;
|
public $journal;
|
||||||
|
/** @var int */
|
||||||
public $piggyBankId;
|
public $piggyBankId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -26,6 +26,7 @@ class UpdatedTransactionJournal extends Event
|
|||||||
|
|
||||||
use SerializesModels;
|
use SerializesModels;
|
||||||
|
|
||||||
|
/** @var TransactionJournal */
|
||||||
public $journal;
|
public $journal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Exceptions;
|
namespace FireflyIII\Exceptions;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Exceptions;
|
namespace FireflyIII\Exceptions;
|
||||||
|
|
||||||
use ErrorException;
|
use ErrorException;
|
||||||
@@ -97,6 +98,7 @@ class Handler extends ExceptionHandler
|
|||||||
'file' => $exception->getFile(),
|
'file' => $exception->getFile(),
|
||||||
'line' => $exception->getLine(),
|
'line' => $exception->getLine(),
|
||||||
'code' => $exception->getCode(),
|
'code' => $exception->getCode(),
|
||||||
|
'version' => config('firefly.version'),
|
||||||
];
|
];
|
||||||
|
|
||||||
// create job that will mail.
|
// create job that will mail.
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Exceptions;
|
namespace FireflyIII\Exceptions;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Exceptions;
|
namespace FireflyIII\Exceptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -14,7 +14,6 @@ declare(strict_types = 1);
|
|||||||
namespace FireflyIII\Export\Collector;
|
namespace FireflyIII\Export\Collector;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Crypt;
|
|
||||||
use DB;
|
use DB;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use Illuminate\Database\Query\JoinClause;
|
use Illuminate\Database\Query\JoinClause;
|
||||||
|
@@ -13,7 +13,6 @@ declare(strict_types = 1);
|
|||||||
|
|
||||||
namespace FireflyIII\Export\Entry;
|
namespace FireflyIII\Export\Entry;
|
||||||
|
|
||||||
use Crypt;
|
|
||||||
use Steam;
|
use Steam;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -81,6 +81,9 @@ class ChartJsGenerator implements GeneratorInterface
|
|||||||
if (isset($set['fill'])) {
|
if (isset($set['fill'])) {
|
||||||
$currentSet['fill'] = $set['fill'];
|
$currentSet['fill'] = $set['fill'];
|
||||||
}
|
}
|
||||||
|
if (isset($set['currency_symbol'])) {
|
||||||
|
$currentSet['currency_symbol'] = $set['currency_symbol'];
|
||||||
|
}
|
||||||
|
|
||||||
$chartData['datasets'][] = $currentSet;
|
$chartData['datasets'][] = $currentSet;
|
||||||
}
|
}
|
||||||
@@ -105,6 +108,10 @@ class ChartJsGenerator implements GeneratorInterface
|
|||||||
],
|
],
|
||||||
'labels' => [],
|
'labels' => [],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// sort by value, keep keys.
|
||||||
|
asort($data);
|
||||||
|
|
||||||
$index = 0;
|
$index = 0;
|
||||||
foreach ($data as $key => $value) {
|
foreach ($data as $key => $value) {
|
||||||
|
|
||||||
|
@@ -18,6 +18,9 @@ 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\JournalCollectorInterface;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
|
use FireflyIII\Helpers\Filter\OpposingAccountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\TransferFilter;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -141,52 +144,10 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Collection $collection
|
|
||||||
* @param int $sortFlag
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getAverages(Collection $collection, int $sortFlag): array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
/** @var Transaction $transaction */
|
|
||||||
foreach ($collection as $transaction) {
|
|
||||||
// opposing name and ID:
|
|
||||||
$opposingId = $transaction->opposing_account_id;
|
|
||||||
|
|
||||||
// is not set?
|
|
||||||
if (!isset($result[$opposingId])) {
|
|
||||||
$name = $transaction->opposing_account_name;
|
|
||||||
$result[$opposingId] = [
|
|
||||||
'name' => $name,
|
|
||||||
'count' => 1,
|
|
||||||
'id' => $opposingId,
|
|
||||||
'average' => $transaction->transaction_amount,
|
|
||||||
'sum' => $transaction->transaction_amount,
|
|
||||||
];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$result[$opposingId]['count']++;
|
|
||||||
$result[$opposingId]['sum'] = bcadd($result[$opposingId]['sum'], $transaction->transaction_amount);
|
|
||||||
$result[$opposingId]['average'] = bcdiv($result[$opposingId]['sum'], strval($result[$opposingId]['count']));
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort result by average:
|
|
||||||
$average = [];
|
|
||||||
foreach ($result as $key => $row) {
|
|
||||||
$average[$key] = floatval($row['average']);
|
|
||||||
}
|
|
||||||
|
|
||||||
array_multisort($average, $sortFlag, $result);
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
private function getExpenses(): Collection
|
protected function getExpenses(): Collection
|
||||||
{
|
{
|
||||||
if ($this->expenses->count() > 0) {
|
if ($this->expenses->count() > 0) {
|
||||||
Log::debug('Return previous set of expenses.');
|
Log::debug('Return previous set of expenses.');
|
||||||
@@ -198,44 +159,18 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
|||||||
$collector = app(JournalCollectorInterface::class);
|
$collector = app(JournalCollectorInterface::class);
|
||||||
$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();
|
||||||
|
$collector->removeFilter(TransferFilter::class);
|
||||||
|
|
||||||
|
$collector->addFilter(OpposingAccountFilter::class);
|
||||||
|
$collector->addFilter(PositiveAmountFilter::class);
|
||||||
|
|
||||||
$accountIds = $this->accounts->pluck('id')->toArray();
|
|
||||||
$transactions = $collector->getJournals();
|
$transactions = $collector->getJournals();
|
||||||
$transactions = self::filterExpenses($transactions, $accountIds);
|
|
||||||
$this->expenses = $transactions;
|
$this->expenses = $transactions;
|
||||||
|
|
||||||
return $transactions;
|
return $transactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Collection
|
|
||||||
*/
|
|
||||||
private function getTopExpenses(): Collection
|
|
||||||
{
|
|
||||||
$transactions = $this->getExpenses()->sortBy('transaction_amount');
|
|
||||||
|
|
||||||
return $transactions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Collection $collection
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function summarizeByAccount(Collection $collection): array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
/** @var Transaction $transaction */
|
|
||||||
foreach ($collection as $transaction) {
|
|
||||||
$accountId = $transaction->account_id;
|
|
||||||
$result[$accountId] = $result[$accountId] ?? '0';
|
|
||||||
$result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $collection
|
* @param Collection $collection
|
||||||
*
|
*
|
||||||
|
@@ -18,6 +18,10 @@ 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\JournalCollectorInterface;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
|
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\OpposingAccountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\TransferFilter;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -151,52 +155,10 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Collection $collection
|
|
||||||
* @param int $sortFlag
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getAverages(Collection $collection, int $sortFlag): array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
/** @var Transaction $transaction */
|
|
||||||
foreach ($collection as $transaction) {
|
|
||||||
// opposing name and ID:
|
|
||||||
$opposingId = $transaction->opposing_account_id;
|
|
||||||
|
|
||||||
// is not set?
|
|
||||||
if (!isset($result[$opposingId])) {
|
|
||||||
$name = $transaction->opposing_account_name;
|
|
||||||
$result[$opposingId] = [
|
|
||||||
'name' => $name,
|
|
||||||
'count' => 1,
|
|
||||||
'id' => $opposingId,
|
|
||||||
'average' => $transaction->transaction_amount,
|
|
||||||
'sum' => $transaction->transaction_amount,
|
|
||||||
];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$result[$opposingId]['count']++;
|
|
||||||
$result[$opposingId]['sum'] = bcadd($result[$opposingId]['sum'], $transaction->transaction_amount);
|
|
||||||
$result[$opposingId]['average'] = bcdiv($result[$opposingId]['sum'], strval($result[$opposingId]['count']));
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort result by average:
|
|
||||||
$average = [];
|
|
||||||
foreach ($result as $key => $row) {
|
|
||||||
$average[$key] = floatval($row['average']);
|
|
||||||
}
|
|
||||||
|
|
||||||
array_multisort($average, $sortFlag, $result);
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
private function getExpenses(): Collection
|
protected function getExpenses(): Collection
|
||||||
{
|
{
|
||||||
if ($this->expenses->count() > 0) {
|
if ($this->expenses->count() > 0) {
|
||||||
Log::debug('Return previous set of expenses.');
|
Log::debug('Return previous set of expenses.');
|
||||||
@@ -208,11 +170,13 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
|||||||
$collector = app(JournalCollectorInterface::class);
|
$collector = app(JournalCollectorInterface::class);
|
||||||
$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();
|
||||||
|
$collector->removeFilter(TransferFilter::class);
|
||||||
|
|
||||||
|
$collector->addFilter(OpposingAccountFilter::class);
|
||||||
|
$collector->addFilter(PositiveAmountFilter::class);
|
||||||
|
|
||||||
$accountIds = $this->accounts->pluck('id')->toArray();
|
|
||||||
$transactions = $collector->getJournals();
|
$transactions = $collector->getJournals();
|
||||||
$transactions = self::filterExpenses($transactions, $accountIds);
|
|
||||||
$this->expenses = $transactions;
|
$this->expenses = $transactions;
|
||||||
|
|
||||||
return $transactions;
|
return $transactions;
|
||||||
@@ -221,7 +185,7 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
|||||||
/**
|
/**
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
private function getIncome(): Collection
|
protected function getIncome(): Collection
|
||||||
{
|
{
|
||||||
if ($this->income->count() > 0) {
|
if ($this->income->count() > 0) {
|
||||||
return $this->income;
|
return $this->income;
|
||||||
@@ -232,93 +196,16 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
|||||||
$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();
|
||||||
$accountIds = $this->accounts->pluck('id')->toArray();
|
|
||||||
|
$collector->addFilter(OpposingAccountFilter::class);
|
||||||
|
$collector->addFilter(NegativeAmountFilter::class);
|
||||||
|
|
||||||
$transactions = $collector->getJournals();
|
$transactions = $collector->getJournals();
|
||||||
$transactions = self::filterIncome($transactions, $accountIds);
|
|
||||||
$this->income = $transactions;
|
$this->income = $transactions;
|
||||||
|
|
||||||
return $transactions;
|
return $transactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
|
|
||||||
* @param array $spent
|
|
||||||
* @param array $earned
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getObjectSummary(array $spent, array $earned): array
|
|
||||||
{
|
|
||||||
$return = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int $accountId
|
|
||||||
* @var string $entry
|
|
||||||
*/
|
|
||||||
foreach ($spent as $objectId => $entry) {
|
|
||||||
if (!isset($return[$objectId])) {
|
|
||||||
$return[$objectId] = ['spent' => 0, 'earned' => 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
$return[$objectId]['spent'] = $entry;
|
|
||||||
}
|
|
||||||
unset($entry);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int $accountId
|
|
||||||
* @var string $entry
|
|
||||||
*/
|
|
||||||
foreach ($earned as $objectId => $entry) {
|
|
||||||
if (!isset($return[$objectId])) {
|
|
||||||
$return[$objectId] = ['spent' => 0, 'earned' => 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
$return[$objectId]['earned'] = $entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Collection
|
|
||||||
*/
|
|
||||||
private function getTopExpenses(): Collection
|
|
||||||
{
|
|
||||||
$transactions = $this->getExpenses()->sortBy('transaction_amount');
|
|
||||||
|
|
||||||
return $transactions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Collection
|
|
||||||
*/
|
|
||||||
private function getTopIncome(): Collection
|
|
||||||
{
|
|
||||||
$transactions = $this->getIncome()->sortByDesc('transaction_amount');
|
|
||||||
|
|
||||||
return $transactions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Collection $collection
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function summarizeByAccount(Collection $collection): array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
/** @var Transaction $transaction */
|
|
||||||
foreach ($collection as $transaction) {
|
|
||||||
$accountId = $transaction->account_id;
|
|
||||||
$result[$accountId] = $result[$accountId] ?? '0';
|
|
||||||
$result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $collection
|
* @param Collection $collection
|
||||||
*
|
*
|
||||||
|
@@ -17,7 +17,7 @@ namespace FireflyIII\Generator\Report\Category;
|
|||||||
/**
|
/**
|
||||||
* Class MultiYearReportGenerator
|
* Class MultiYearReportGenerator
|
||||||
*
|
*
|
||||||
* @package FireflyIII\Generator\Report\Audit
|
* @package FireflyIII\Generator\Report\Category
|
||||||
*/
|
*/
|
||||||
class MultiYearReportGenerator extends MonthReportGenerator
|
class MultiYearReportGenerator extends MonthReportGenerator
|
||||||
{
|
{
|
||||||
|
@@ -17,7 +17,7 @@ namespace FireflyIII\Generator\Report\Category;
|
|||||||
/**
|
/**
|
||||||
* Class YearReportGenerator
|
* Class YearReportGenerator
|
||||||
*
|
*
|
||||||
* @package FireflyIII\Generator\Report\Audit
|
* @package FireflyIII\Generator\Report\Category
|
||||||
*/
|
*/
|
||||||
class YearReportGenerator extends MonthReportGenerator
|
class YearReportGenerator extends MonthReportGenerator
|
||||||
{
|
{
|
||||||
|
@@ -49,7 +49,7 @@ class ReportGeneratorFactory
|
|||||||
$class = sprintf('FireflyIII\Generator\Report\%s\%sReportGenerator', $type, $period);
|
$class = sprintf('FireflyIII\Generator\Report\%s\%sReportGenerator', $type, $period);
|
||||||
if (class_exists($class)) {
|
if (class_exists($class)) {
|
||||||
/** @var ReportGeneratorInterface $obj */
|
/** @var ReportGeneratorInterface $obj */
|
||||||
$obj = new $class;
|
$obj = app($class);
|
||||||
$obj->setStartDate($start);
|
$obj->setStartDate($start);
|
||||||
$obj->setEndDate($end);
|
$obj->setEndDate($end);
|
||||||
|
|
||||||
|
@@ -25,57 +25,122 @@ use Log;
|
|||||||
*/
|
*/
|
||||||
class Support
|
class Support
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $collection
|
|
||||||
* @param array $accounts
|
|
||||||
*
|
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public static function filterExpenses(Collection $collection, array $accounts): Collection
|
public function getTopExpenses(): Collection
|
||||||
{
|
{
|
||||||
return self::filterTransactions($collection, $accounts, 1);
|
$transactions = $this->getExpenses()->sortBy('transaction_amount');
|
||||||
|
|
||||||
|
return $transactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getTopIncome(): Collection
|
||||||
|
{
|
||||||
|
$transactions = $this->getIncome()->sortByDesc('transaction_amount');
|
||||||
|
|
||||||
|
return $transactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $collection
|
* @param Collection $collection
|
||||||
* @param array $accounts
|
* @param int $sortFlag
|
||||||
*
|
*
|
||||||
* @return Collection
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function filterIncome(Collection $collection, array $accounts): Collection
|
protected function getAverages(Collection $collection, int $sortFlag): array
|
||||||
{
|
{
|
||||||
return self::filterTransactions($collection, $accounts, -1);
|
$result = [];
|
||||||
|
/** @var Transaction $transaction */
|
||||||
|
foreach ($collection as $transaction) {
|
||||||
|
// opposing name and ID:
|
||||||
|
$opposingId = $transaction->opposing_account_id;
|
||||||
|
|
||||||
|
// is not set?
|
||||||
|
if (!isset($result[$opposingId])) {
|
||||||
|
$name = $transaction->opposing_account_name;
|
||||||
|
$result[$opposingId] = [
|
||||||
|
'name' => $name,
|
||||||
|
'count' => 1,
|
||||||
|
'id' => $opposingId,
|
||||||
|
'average' => $transaction->transaction_amount,
|
||||||
|
'sum' => $transaction->transaction_amount,
|
||||||
|
];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$result[$opposingId]['count']++;
|
||||||
|
$result[$opposingId]['sum'] = bcadd($result[$opposingId]['sum'], $transaction->transaction_amount);
|
||||||
|
$result[$opposingId]['average'] = bcdiv($result[$opposingId]['sum'], strval($result[$opposingId]['count']));
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort result by average:
|
||||||
|
$average = [];
|
||||||
|
foreach ($result as $key => $row) {
|
||||||
|
$average[$key] = floatval($row['average']);
|
||||||
|
}
|
||||||
|
|
||||||
|
array_multisort($average, $sortFlag, $result);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
|
||||||
|
* @param array $spent
|
||||||
|
* @param array $earned
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getObjectSummary(array $spent, array $earned): array
|
||||||
|
{
|
||||||
|
$return = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int $accountId
|
||||||
|
* @var string $entry
|
||||||
|
*/
|
||||||
|
foreach ($spent as $objectId => $entry) {
|
||||||
|
if (!isset($return[$objectId])) {
|
||||||
|
$return[$objectId] = ['spent' => 0, 'earned' => 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
$return[$objectId]['spent'] = $entry;
|
||||||
|
}
|
||||||
|
unset($entry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int $accountId
|
||||||
|
* @var string $entry
|
||||||
|
*/
|
||||||
|
foreach ($earned as $objectId => $entry) {
|
||||||
|
if (!isset($return[$objectId])) {
|
||||||
|
$return[$objectId] = ['spent' => 0, 'earned' => 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
$return[$objectId]['earned'] = $entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $collection
|
* @param Collection $collection
|
||||||
* @param array $accounts
|
|
||||||
* @param int $modifier
|
|
||||||
*
|
*
|
||||||
* @return Collection
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function filterTransactions(Collection $collection, array $accounts, int $modifier): Collection
|
protected function summarizeByAccount(Collection $collection): array
|
||||||
{
|
{
|
||||||
$result = $collection->filter(
|
$result = [];
|
||||||
function (Transaction $transaction) use ($accounts, $modifier) {
|
/** @var Transaction $transaction */
|
||||||
$opposing = $transaction->opposing_account_id;
|
foreach ($collection as $transaction) {
|
||||||
// remove internal transfer
|
$accountId = $transaction->account_id;
|
||||||
if (in_array($opposing, $accounts)) {
|
$result[$accountId] = $result[$accountId] ?? '0';
|
||||||
Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id));
|
$result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]);
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
// remove positive amount
|
|
||||||
if (bccomp($transaction->transaction_amount, '0') === $modifier) {
|
|
||||||
Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount));
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $transaction;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
227
app/Generator/Report/Tag/MonthReportGenerator.php
Normal file
227
app/Generator/Report/Tag/MonthReportGenerator.php
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* MonthReportGenerator.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\Generator\Report\Tag;
|
||||||
|
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
||||||
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
|
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\OpposingAccountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\TransferFilter;
|
||||||
|
use FireflyIII\Models\Tag;
|
||||||
|
use FireflyIII\Models\Transaction;
|
||||||
|
use FireflyIII\Models\TransactionType;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class MonthReportGenerator
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Generator\Report\Tag
|
||||||
|
*/
|
||||||
|
class MonthReportGenerator implements ReportGeneratorInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var Collection */
|
||||||
|
private $accounts;
|
||||||
|
/** @var Carbon */
|
||||||
|
private $end;
|
||||||
|
/** @var Collection */
|
||||||
|
private $expenses;
|
||||||
|
/** @var Collection */
|
||||||
|
private $income;
|
||||||
|
/** @var Carbon */
|
||||||
|
private $start;
|
||||||
|
/** @var Collection */
|
||||||
|
private $tags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MonthReportGenerator constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->expenses = new Collection;
|
||||||
|
$this->income = new Collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function generate(): string
|
||||||
|
{
|
||||||
|
$accountIds = join(',', $this->accounts->pluck('id')->toArray());
|
||||||
|
$tagTags = join(',', $this->tags->pluck('tag')->toArray());
|
||||||
|
$reportType = 'tag';
|
||||||
|
$expenses = $this->getExpenses();
|
||||||
|
$income = $this->getIncome();
|
||||||
|
$accountSummary = $this->getObjectSummary($this->summarizeByAccount($expenses), $this->summarizeByAccount($income));
|
||||||
|
$tagSummary = $this->getObjectSummary($this->summarizeByTag($expenses), $this->summarizeByTag($income));
|
||||||
|
$averageExpenses = $this->getAverages($expenses, SORT_ASC);
|
||||||
|
$averageIncome = $this->getAverages($income, SORT_DESC);
|
||||||
|
$topExpenses = $this->getTopExpenses();
|
||||||
|
$topIncome = $this->getTopIncome();
|
||||||
|
|
||||||
|
|
||||||
|
// render!
|
||||||
|
return view(
|
||||||
|
'reports.tag.month', compact(
|
||||||
|
'accountIds', 'tagTags', 'reportType', 'accountSummary', 'tagSummary', 'averageExpenses', 'averageIncome', 'topIncome',
|
||||||
|
'topExpenses'
|
||||||
|
)
|
||||||
|
)->with('start', $this->start)->with('end', $this->end)->with('tags', $this->tags)->with('accounts', $this->accounts)->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $accounts
|
||||||
|
*
|
||||||
|
* @return ReportGeneratorInterface
|
||||||
|
*/
|
||||||
|
public function setAccounts(Collection $accounts): ReportGeneratorInterface
|
||||||
|
{
|
||||||
|
$this->accounts = $accounts;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $budgets
|
||||||
|
*
|
||||||
|
* @return ReportGeneratorInterface
|
||||||
|
*/
|
||||||
|
public function setBudgets(Collection $budgets): ReportGeneratorInterface
|
||||||
|
{
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $categories
|
||||||
|
*
|
||||||
|
* @return ReportGeneratorInterface
|
||||||
|
*/
|
||||||
|
public function setCategories(Collection $categories): ReportGeneratorInterface
|
||||||
|
{
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Carbon $date
|
||||||
|
*
|
||||||
|
* @return ReportGeneratorInterface
|
||||||
|
*/
|
||||||
|
public function setEndDate(Carbon $date): ReportGeneratorInterface
|
||||||
|
{
|
||||||
|
$this->end = $date;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Carbon $date
|
||||||
|
*
|
||||||
|
* @return ReportGeneratorInterface
|
||||||
|
*/
|
||||||
|
public function setStartDate(Carbon $date): ReportGeneratorInterface
|
||||||
|
{
|
||||||
|
$this->start = $date;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $tags
|
||||||
|
*
|
||||||
|
* @return ReportGeneratorInterface
|
||||||
|
*/
|
||||||
|
public function setTags(Collection $tags): ReportGeneratorInterface
|
||||||
|
{
|
||||||
|
$this->tags = $tags;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
protected function getExpenses(): Collection
|
||||||
|
{
|
||||||
|
if ($this->expenses->count() > 0) {
|
||||||
|
Log::debug('Return previous set of expenses.');
|
||||||
|
|
||||||
|
return $this->expenses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
|
||||||
|
->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
|
||||||
|
->setTags($this->tags)->withOpposingAccount();
|
||||||
|
$collector->removeFilter(TransferFilter::class);
|
||||||
|
|
||||||
|
$collector->addFilter(OpposingAccountFilter::class);
|
||||||
|
$collector->addFilter(PositiveAmountFilter::class);
|
||||||
|
|
||||||
|
$transactions = $collector->getJournals();
|
||||||
|
|
||||||
|
$this->expenses = $transactions;
|
||||||
|
|
||||||
|
return $transactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
protected function getIncome(): Collection
|
||||||
|
{
|
||||||
|
if ($this->income->count() > 0) {
|
||||||
|
return $this->income;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
|
||||||
|
->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
|
||||||
|
->setTags($this->tags)->withOpposingAccount();
|
||||||
|
|
||||||
|
$collector->addFilter(OpposingAccountFilter::class);
|
||||||
|
$collector->addFilter(NegativeAmountFilter::class);
|
||||||
|
|
||||||
|
$transactions = $collector->getJournals();
|
||||||
|
$this->income = $transactions;
|
||||||
|
|
||||||
|
return $transactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $collection
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function summarizeByTag(Collection $collection): array
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
/** @var Transaction $transaction */
|
||||||
|
foreach ($collection as $transaction) {
|
||||||
|
$journal = $transaction->transactionJournal;
|
||||||
|
$journalTags = $journal->tags;
|
||||||
|
/** @var Tag $journalTag */
|
||||||
|
foreach ($journalTags as $journalTag) {
|
||||||
|
$journalTagId = $journalTag->id;
|
||||||
|
$result[$journalTagId] = $result[$journalTagId] ?? '0';
|
||||||
|
$result[$journalTagId] = bcadd($transaction->transaction_amount, $result[$journalTagId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
25
app/Generator/Report/Tag/MultiYearReportGenerator.php
Normal file
25
app/Generator/Report/Tag/MultiYearReportGenerator.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* MultiYearReportGenerator.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\Generator\Report\Tag;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class MultiYearReportGenerator
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Generator\Report\Tag
|
||||||
|
*/
|
||||||
|
class MultiYearReportGenerator extends MonthReportGenerator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Doesn't do anything different.
|
||||||
|
*/
|
||||||
|
}
|
26
app/Generator/Report/Tag/YearReportGenerator.php
Normal file
26
app/Generator/Report/Tag/YearReportGenerator.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* YearReportGenerator.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\Generator\Report\Tag;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class YearReportGenerator
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Generator\Report\Tag
|
||||||
|
*/
|
||||||
|
class YearReportGenerator extends MonthReportGenerator
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Doesn't do anything different.
|
||||||
|
*/
|
||||||
|
}
|
@@ -14,59 +14,91 @@ declare(strict_types = 1);
|
|||||||
namespace FireflyIII\Handlers\Events;
|
namespace FireflyIII\Handlers\Events;
|
||||||
|
|
||||||
use FireflyIII\Events\StoredTransactionJournal;
|
use FireflyIII\Events\StoredTransactionJournal;
|
||||||
use FireflyIII\Models\PiggyBank;
|
|
||||||
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\Repositories\Journal\JournalRepositoryInterface as JRI;
|
||||||
|
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface as PRI;
|
||||||
|
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface as RGRI;
|
||||||
use FireflyIII\Rules\Processor;
|
use FireflyIII\Rules\Processor;
|
||||||
use FireflyIII\Support\Events\BillScanner;
|
use FireflyIII\Support\Events\BillScanner;
|
||||||
use Log;
|
use Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*
|
||||||
* Class StoredJournalEventHandler
|
* Class StoredJournalEventHandler
|
||||||
*
|
*
|
||||||
* @package FireflyIII\Handlers\Events
|
* @package FireflyIII\Handlers\Events
|
||||||
*/
|
*/
|
||||||
class StoredJournalEventHandler
|
class StoredJournalEventHandler
|
||||||
{
|
{
|
||||||
|
/** @var JRI */
|
||||||
|
public $journalRepository;
|
||||||
|
/** @var PRI */
|
||||||
|
public $repository;
|
||||||
|
|
||||||
|
/** @var RGRI */
|
||||||
|
public $ruleGroupRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StoredJournalEventHandler constructor.
|
||||||
|
*/
|
||||||
|
public function __construct(PRI $repository, JRI $journalRepository, RGRI $ruleGroupRepository)
|
||||||
|
{
|
||||||
|
$this->repository = $repository;
|
||||||
|
$this->journalRepository = $journalRepository;
|
||||||
|
$this->ruleGroupRepository = $ruleGroupRepository;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method connects a new transfer to a piggy bank.
|
* This method connects a new transfer to a piggy bank.
|
||||||
*
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
* @param StoredTransactionJournal $event
|
* @param StoredTransactionJournal $event
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function connectToPiggyBank(StoredTransactionJournal $event): bool
|
public function connectToPiggyBank(StoredTransactionJournal $event): bool
|
||||||
{
|
{
|
||||||
/** @var TransactionJournal $journal */
|
|
||||||
$journal = $event->journal;
|
$journal = $event->journal;
|
||||||
$piggyBankId = $event->piggyBankId;
|
$piggyBankId = $event->piggyBankId;
|
||||||
Log::debug(sprintf('Trying to connect journal %d to piggy bank %d.', $journal->id, $piggyBankId));
|
$piggyBank = $this->repository->find($piggyBankId);
|
||||||
|
|
||||||
/*
|
// is a transfer?
|
||||||
* Verify existence of piggy bank:
|
if (!$this->journalRepository->isTransfer($journal)) {
|
||||||
*/
|
Log::info(sprintf('Will not connect %s #%d to a piggy bank.', $journal->transactionType->type, $journal->id));
|
||||||
if (!$this->verifyExistence($event)) {
|
|
||||||
Log::error(sprintf('No such piggy bank or no repetition on %s', $journal->date->format('Y-m-d')));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// piggy exists?
|
||||||
* Get relevant data:
|
if (is_null($piggyBank->id)) {
|
||||||
*/
|
Log::error(sprintf('There is no piggy bank with ID #%d', $piggyBankId));
|
||||||
$piggyBank = $journal->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
|
|
||||||
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
|
return true;
|
||||||
$amount = $this->getExactAmount($journal, $piggyBank, $repetition);
|
}
|
||||||
$repetition->currentamount = bcadd($repetition->currentamount, $amount);
|
|
||||||
$repetition->save();
|
// repetition exists?
|
||||||
|
$repetition = $this->repository->getRepetition($piggyBank, $journal->date);
|
||||||
|
if (is_null($repetition->id)) {
|
||||||
|
Log::error(sprintf('No piggy bank repetition on %s!', $journal->date->format('Y-m-d')));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the amount
|
||||||
|
$amount = $this->repository->getExactAmount($piggyBank, $repetition, $journal);
|
||||||
|
if (bccomp($amount, '0') === 0) {
|
||||||
|
Log::debug('Amount is zero, will not create event.');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update amount
|
||||||
|
$this->repository->addAmountToRepetition($repetition, $amount);
|
||||||
|
$event = $this->repository->createEventWithJournal($piggyBank, $amount, $journal);
|
||||||
|
|
||||||
/** @var PiggyBankEvent $event */
|
|
||||||
$event = PiggyBankEvent::create(
|
|
||||||
['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]
|
|
||||||
);
|
|
||||||
Log::debug(sprintf('Created piggy bank event #%d', $event->id));
|
Log::debug(sprintf('Created piggy bank event #%d', $event->id));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -83,16 +115,11 @@ class StoredJournalEventHandler
|
|||||||
{
|
{
|
||||||
// get all the user's rule groups, with the rules, order by 'order'.
|
// get all the user's rule groups, with the rules, order by 'order'.
|
||||||
$journal = $storedJournalEvent->journal;
|
$journal = $storedJournalEvent->journal;
|
||||||
$groups = $journal->user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
|
$groups = $this->ruleGroupRepository->getActiveGroups($journal->user);
|
||||||
//
|
|
||||||
/** @var RuleGroup $group */
|
/** @var RuleGroup $group */
|
||||||
foreach ($groups as $group) {
|
foreach ($groups as $group) {
|
||||||
$rules = $group->rules()
|
$rules = $this->ruleGroupRepository->getActiveStoreRules($group);
|
||||||
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
|
|
||||||
->where('rule_triggers.trigger_type', 'user_action')
|
|
||||||
->where('rule_triggers.trigger_value', 'store-journal')
|
|
||||||
->where('rules.active', 1)
|
|
||||||
->get(['rules.*']);
|
|
||||||
/** @var Rule $rule */
|
/** @var Rule $rule */
|
||||||
foreach ($rules as $rule) {
|
foreach ($rules as $rule) {
|
||||||
|
|
||||||
@@ -100,9 +127,8 @@ class StoredJournalEventHandler
|
|||||||
$processor->handleTransactionJournal($journal);
|
$processor->handleTransactionJournal($journal);
|
||||||
|
|
||||||
if ($rule->stop_processing) {
|
if ($rule->stop_processing) {
|
||||||
return true;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,81 +149,4 @@ 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -17,16 +17,29 @@ namespace FireflyIII\Handlers\Events;
|
|||||||
use FireflyIII\Events\UpdatedTransactionJournal;
|
use FireflyIII\Events\UpdatedTransactionJournal;
|
||||||
use FireflyIII\Models\Rule;
|
use FireflyIII\Models\Rule;
|
||||||
use FireflyIII\Models\RuleGroup;
|
use FireflyIII\Models\RuleGroup;
|
||||||
|
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||||
use FireflyIII\Rules\Processor;
|
use FireflyIII\Rules\Processor;
|
||||||
use FireflyIII\Support\Events\BillScanner;
|
use FireflyIII\Support\Events\BillScanner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*
|
||||||
* Class UpdatedJournalEventHandler
|
* Class UpdatedJournalEventHandler
|
||||||
*
|
*
|
||||||
* @package FireflyIII\Handlers\Events
|
* @package FireflyIII\Handlers\Events
|
||||||
*/
|
*/
|
||||||
class UpdatedJournalEventHandler
|
class UpdatedJournalEventHandler
|
||||||
{
|
{
|
||||||
|
/** @var RuleGroupRepositoryInterface */
|
||||||
|
public $repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StoredJournalEventHandler constructor.
|
||||||
|
*/
|
||||||
|
public function __construct(RuleGroupRepositoryInterface $ruleGroupRepository)
|
||||||
|
{
|
||||||
|
$this->repository = $ruleGroupRepository;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method will check all the rules when a journal is updated.
|
* This method will check all the rules when a journal is updated.
|
||||||
@@ -39,16 +52,11 @@ class UpdatedJournalEventHandler
|
|||||||
{
|
{
|
||||||
// get all the user's rule groups, with the rules, order by 'order'.
|
// get all the user's rule groups, with the rules, order by 'order'.
|
||||||
$journal = $updatedJournalEvent->journal;
|
$journal = $updatedJournalEvent->journal;
|
||||||
$groups = $journal->user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
|
$groups = $this->repository->getActiveGroups($journal->user);
|
||||||
//
|
|
||||||
/** @var RuleGroup $group */
|
/** @var RuleGroup $group */
|
||||||
foreach ($groups as $group) {
|
foreach ($groups as $group) {
|
||||||
$rules = $group->rules()
|
$rules = $this->repository->getActiveUpdateRules($group);
|
||||||
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
|
|
||||||
->where('rule_triggers.trigger_type', 'user_action')
|
|
||||||
->where('rule_triggers.trigger_value', 'update-journal')
|
|
||||||
->where('rules.active', 1)
|
|
||||||
->get(['rules.*']);
|
|
||||||
/** @var Rule $rule */
|
/** @var Rule $rule */
|
||||||
foreach ($rules as $rule) {
|
foreach ($rules as $rule) {
|
||||||
$processor = Processor::make($rule);
|
$processor = Processor::make($rule);
|
||||||
|
@@ -15,8 +15,9 @@ namespace FireflyIII\Handlers\Events;
|
|||||||
|
|
||||||
use FireflyIII\Events\RegisteredUser;
|
use FireflyIII\Events\RegisteredUser;
|
||||||
use FireflyIII\Events\RequestedNewPassword;
|
use FireflyIII\Events\RequestedNewPassword;
|
||||||
|
use FireflyIII\Mail\RegisteredUser as RegisteredUserMail;
|
||||||
|
use FireflyIII\Mail\RequestedNewPassword as RequestedNewPasswordMail;
|
||||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||||
use Illuminate\Mail\Message;
|
|
||||||
use Log;
|
use Log;
|
||||||
use Mail;
|
use Mail;
|
||||||
use Swift_TransportException;
|
use Swift_TransportException;
|
||||||
@@ -68,14 +69,12 @@ class UserEventHandler
|
|||||||
|
|
||||||
// send email.
|
// send email.
|
||||||
try {
|
try {
|
||||||
Mail::send(
|
Mail::to($email)->send(new RequestedNewPasswordMail($url, $ipAddress));
|
||||||
['emails.password-html', 'emails.password-text'], ['url' => $url, 'ip' => $ipAddress], function (Message $message) use ($email) {
|
// @codeCoverageIgnoreStart
|
||||||
$message->to($email, $email)->subject('Your password reset request');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (Swift_TransportException $e) {
|
} catch (Swift_TransportException $e) {
|
||||||
Log::error($e->getMessage());
|
Log::error($e->getMessage());
|
||||||
}
|
}
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -93,22 +92,21 @@ class UserEventHandler
|
|||||||
|
|
||||||
$sendMail = env('SEND_REGISTRATION_MAIL', true);
|
$sendMail = env('SEND_REGISTRATION_MAIL', true);
|
||||||
if (!$sendMail) {
|
if (!$sendMail) {
|
||||||
return true;
|
return true; // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
// get the email address
|
// get the email address
|
||||||
$email = $event->user->email;
|
$email = $event->user->email;
|
||||||
$address = route('index');
|
$address = route('index');
|
||||||
$ipAddress = $event->ipAddress;
|
$ipAddress = $event->ipAddress;
|
||||||
|
|
||||||
// send email.
|
// send email.
|
||||||
try {
|
try {
|
||||||
Mail::send(
|
Mail::to($email)->send(new RegisteredUserMail($address, $ipAddress));
|
||||||
['emails.registered-html', 'emails.registered-text'], ['address' => $address, 'ip' => $ipAddress], function (Message $message) use ($email) {
|
// @codeCoverageIgnoreStart
|
||||||
$message->to($email, $email)->subject('Welcome to Firefly III!');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (Swift_TransportException $e) {
|
} catch (Swift_TransportException $e) {
|
||||||
Log::error($e->getMessage());
|
Log::error($e->getMessage());
|
||||||
}
|
}
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -10,15 +10,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Helpers\Attachments;
|
namespace FireflyIII\Helpers\Attachments;
|
||||||
|
|
||||||
use Crypt;
|
use Crypt;
|
||||||
use FireflyIII\Models\Attachment;
|
use FireflyIII\Models\Attachment;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\MessageBag;
|
use Illuminate\Support\MessageBag;
|
||||||
use Storage;
|
use Storage;
|
||||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||||
|
use Log;
|
||||||
/**
|
/**
|
||||||
* Class AttachmentHelper
|
* Class AttachmentHelper
|
||||||
*
|
*
|
||||||
@@ -27,6 +29,8 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
|
|||||||
class AttachmentHelper implements AttachmentHelperInterface
|
class AttachmentHelper implements AttachmentHelperInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/** @var Collection */
|
||||||
|
public $attachments;
|
||||||
/** @var MessageBag */
|
/** @var MessageBag */
|
||||||
public $errors;
|
public $errors;
|
||||||
/** @var MessageBag */
|
/** @var MessageBag */
|
||||||
@@ -48,6 +52,7 @@ class AttachmentHelper implements AttachmentHelperInterface
|
|||||||
$this->allowedMimes = (array)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->attachments = new Collection;
|
||||||
$this->uploadDisk = Storage::disk('upload');
|
$this->uploadDisk = Storage::disk('upload');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +68,14 @@ class AttachmentHelper implements AttachmentHelperInterface
|
|||||||
return $path;
|
return $path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getAttachments(): Collection
|
||||||
|
{
|
||||||
|
return $this->attachments;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return MessageBag
|
* @return MessageBag
|
||||||
*/
|
*/
|
||||||
@@ -109,7 +122,7 @@ class AttachmentHelper implements AttachmentHelperInterface
|
|||||||
$md5 = md5_file($file->getRealPath());
|
$md5 = md5_file($file->getRealPath());
|
||||||
$name = $file->getClientOriginalName();
|
$name = $file->getClientOriginalName();
|
||||||
$class = get_class($model);
|
$class = get_class($model);
|
||||||
$count = auth()->user()->attachments()->where('md5', $md5)->where('attachable_id', $model->id)->where('attachable_type', $class)->count();
|
$count = $model->user->attachments()->where('md5', $md5)->where('attachable_id', $model->id)->where('attachable_type', $class)->count();
|
||||||
|
|
||||||
if ($count > 0) {
|
if ($count > 0) {
|
||||||
$msg = (string)trans('validation.file_already_attached', ['name' => $name]);
|
$msg = (string)trans('validation.file_already_attached', ['name' => $name]);
|
||||||
@@ -136,7 +149,7 @@ class AttachmentHelper implements AttachmentHelperInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
$attachment = new Attachment; // create Attachment object.
|
$attachment = new Attachment; // create Attachment object.
|
||||||
$attachment->user()->associate(auth()->user());
|
$attachment->user()->associate($model->user);
|
||||||
$attachment->attachable()->associate($model);
|
$attachment->attachable()->associate($model);
|
||||||
$attachment->md5 = md5_file($file->getRealPath());
|
$attachment->md5 = md5_file($file->getRealPath());
|
||||||
$attachment->filename = $file->getClientOriginalName();
|
$attachment->filename = $file->getClientOriginalName();
|
||||||
@@ -155,6 +168,7 @@ class AttachmentHelper implements AttachmentHelperInterface
|
|||||||
|
|
||||||
$attachment->uploaded = 1; // update attachment
|
$attachment->uploaded = 1; // update attachment
|
||||||
$attachment->save();
|
$attachment->save();
|
||||||
|
$this->attachments->push($attachment);
|
||||||
|
|
||||||
$name = e($file->getClientOriginalName()); // add message:
|
$name = e($file->getClientOriginalName()); // add message:
|
||||||
$msg = (string)trans('validation.file_attached', ['name' => $name]);
|
$msg = (string)trans('validation.file_attached', ['name' => $name]);
|
||||||
@@ -187,6 +201,7 @@ class AttachmentHelper implements AttachmentHelperInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
* @param UploadedFile $file
|
* @param UploadedFile $file
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
@@ -217,7 +232,7 @@ class AttachmentHelper implements AttachmentHelperInterface
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!$this->validSize($file)) {
|
if (!$this->validSize($file)) {
|
||||||
return false;
|
return false; // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
if ($this->hasFile($file, $model)) {
|
if ($this->hasFile($file, $model)) {
|
||||||
return false;
|
return false;
|
||||||
|
@@ -10,10 +10,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Helpers\Attachments;
|
namespace FireflyIII\Helpers\Attachments;
|
||||||
|
|
||||||
use FireflyIII\Models\Attachment;
|
use FireflyIII\Models\Attachment;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\MessageBag;
|
use Illuminate\Support\MessageBag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,6 +33,11 @@ interface AttachmentHelperInterface
|
|||||||
*/
|
*/
|
||||||
public function getAttachmentLocation(Attachment $attachment): string;
|
public function getAttachmentLocation(Attachment $attachment): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getAttachments(): Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return MessageBag
|
* @return MessageBag
|
||||||
*/
|
*/
|
||||||
@@ -44,6 +51,8 @@ interface AttachmentHelperInterface
|
|||||||
/**
|
/**
|
||||||
* @param Model $model
|
* @param Model $model
|
||||||
*
|
*
|
||||||
|
* @param null|array $files
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function saveAttachmentsForModel(Model $model, array $files = null): bool;
|
public function saveAttachmentsForModel(Model $model, array $files = null): bool;
|
||||||
|
@@ -12,13 +12,18 @@ declare(strict_types = 1);
|
|||||||
namespace FireflyIII\Helpers\Chart;
|
namespace FireflyIII\Helpers\Chart;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Generator\Report\Support;
|
|
||||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
|
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\OpposingAccountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\TransferFilter;
|
||||||
|
use FireflyIII\Models\Tag;
|
||||||
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;
|
||||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Steam;
|
use Steam;
|
||||||
@@ -46,19 +51,20 @@ class MetaPieChart implements MetaPieChartInterface
|
|||||||
'account' => ['opposing_account_id'],
|
'account' => ['opposing_account_id'],
|
||||||
'budget' => ['transaction_journal_budget_id', 'transaction_budget_id'],
|
'budget' => ['transaction_journal_budget_id', 'transaction_budget_id'],
|
||||||
'category' => ['transaction_journal_category_id', 'transaction_category_id'],
|
'category' => ['transaction_journal_category_id', 'transaction_category_id'],
|
||||||
|
'tag' => [],
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @var array */
|
/** @var array */
|
||||||
protected $repositories
|
protected $repositories
|
||||||
= [
|
= [
|
||||||
'account' => AccountRepositoryInterface::class,
|
'account' => AccountRepositoryInterface::class,
|
||||||
'budget' => BudgetRepositoryInterface::class,
|
'budget' => BudgetRepositoryInterface::class,
|
||||||
'category' => CategoryRepositoryInterface::class,
|
'category' => CategoryRepositoryInterface::class,
|
||||||
|
'tag' => TagRepositoryInterface::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
/** @var Carbon */
|
/** @var Carbon */
|
||||||
protected $start;
|
protected $start;
|
||||||
|
/** @var Collection */
|
||||||
|
protected $tags;
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $total = '0';
|
protected $total = '0';
|
||||||
/** @var User */
|
/** @var User */
|
||||||
@@ -69,6 +75,7 @@ class MetaPieChart implements MetaPieChartInterface
|
|||||||
$this->accounts = new Collection;
|
$this->accounts = new Collection;
|
||||||
$this->budgets = new Collection;
|
$this->budgets = new Collection;
|
||||||
$this->categories = new Collection;
|
$this->categories = new Collection;
|
||||||
|
$this->tags = new Collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,6 +106,7 @@ class MetaPieChart implements MetaPieChartInterface
|
|||||||
if ($this->collectOtherObjects && $direction === 'income') {
|
if ($this->collectOtherObjects && $direction === 'income') {
|
||||||
/** @var JournalCollectorInterface $collector */
|
/** @var JournalCollectorInterface $collector */
|
||||||
$collector = app(JournalCollectorInterface::class);
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setUser($this->user);
|
||||||
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::DEPOSIT]);
|
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::DEPOSIT]);
|
||||||
$journals = $collector->getJournals();
|
$journals = $collector->getJournals();
|
||||||
$sum = strval($journals->sum('transaction_amount'));
|
$sum = strval($journals->sum('transaction_amount'));
|
||||||
@@ -111,6 +119,8 @@ class MetaPieChart implements MetaPieChartInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
*
|
*
|
||||||
* @return MetaPieChartInterface
|
* @return MetaPieChartInterface
|
||||||
@@ -123,6 +133,8 @@ class MetaPieChart implements MetaPieChartInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*
|
||||||
* @param Collection $budgets
|
* @param Collection $budgets
|
||||||
*
|
*
|
||||||
* @return MetaPieChartInterface
|
* @return MetaPieChartInterface
|
||||||
@@ -135,6 +147,8 @@ class MetaPieChart implements MetaPieChartInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*
|
||||||
* @param Collection $categories
|
* @param Collection $categories
|
||||||
*
|
*
|
||||||
* @return MetaPieChartInterface
|
* @return MetaPieChartInterface
|
||||||
@@ -147,6 +161,8 @@ class MetaPieChart implements MetaPieChartInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*
|
||||||
* @param bool $collectOtherObjects
|
* @param bool $collectOtherObjects
|
||||||
*
|
*
|
||||||
* @return MetaPieChartInterface
|
* @return MetaPieChartInterface
|
||||||
@@ -159,6 +175,8 @@ class MetaPieChart implements MetaPieChartInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*
|
||||||
* @param Carbon $end
|
* @param Carbon $end
|
||||||
*
|
*
|
||||||
* @return MetaPieChartInterface
|
* @return MetaPieChartInterface
|
||||||
@@ -171,6 +189,8 @@ class MetaPieChart implements MetaPieChartInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
*
|
*
|
||||||
* @return MetaPieChartInterface
|
* @return MetaPieChartInterface
|
||||||
@@ -183,6 +203,22 @@ class MetaPieChart implements MetaPieChartInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*
|
||||||
|
* @param Collection $tags
|
||||||
|
*
|
||||||
|
* @return MetaPieChartInterface
|
||||||
|
*/
|
||||||
|
public function setTags(Collection $tags): MetaPieChartInterface
|
||||||
|
{
|
||||||
|
$this->tags = $tags;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*
|
||||||
* @param User $user
|
* @param User $user
|
||||||
*
|
*
|
||||||
* @return MetaPieChartInterface
|
* @return MetaPieChartInterface
|
||||||
@@ -194,23 +230,32 @@ class MetaPieChart implements MetaPieChartInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getTransactions(string $direction)
|
/**
|
||||||
|
* @param string $direction
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
protected function getTransactions(string $direction): Collection
|
||||||
{
|
{
|
||||||
$types = [TransactionType::DEPOSIT, TransactionType::TRANSFER];
|
|
||||||
$modifier = -1;
|
|
||||||
if ($direction === 'expense') {
|
|
||||||
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
|
||||||
$modifier = 1;
|
|
||||||
}
|
|
||||||
/** @var JournalCollectorInterface $collector */
|
/** @var JournalCollectorInterface $collector */
|
||||||
$collector = app(JournalCollectorInterface::class);
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$types = [TransactionType::DEPOSIT, TransactionType::TRANSFER];
|
||||||
|
$collector->addFilter(NegativeAmountFilter::class);
|
||||||
|
if ($direction === 'expense') {
|
||||||
|
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
||||||
|
$collector->addFilter(PositiveAmountFilter::class);
|
||||||
|
$collector->removeFilter(NegativeAmountFilter::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
$collector->setUser($this->user);
|
||||||
$collector->setAccounts($this->accounts);
|
$collector->setAccounts($this->accounts);
|
||||||
$collector->setRange($this->start, $this->end);
|
$collector->setRange($this->start, $this->end);
|
||||||
$collector->setTypes($types);
|
$collector->setTypes($types);
|
||||||
$collector->withOpposingAccount();
|
$collector->withOpposingAccount();
|
||||||
|
$collector->addFilter(OpposingAccountFilter::class);
|
||||||
|
|
||||||
if ($direction === 'income') {
|
if ($direction === 'income') {
|
||||||
$collector->disableFilter();
|
$collector->removeFilter(TransferFilter::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->budgets->count() > 0) {
|
if ($this->budgets->count() > 0) {
|
||||||
@@ -219,12 +264,13 @@ class MetaPieChart implements MetaPieChartInterface
|
|||||||
if ($this->categories->count() > 0) {
|
if ($this->categories->count() > 0) {
|
||||||
$collector->setCategories($this->categories);
|
$collector->setCategories($this->categories);
|
||||||
}
|
}
|
||||||
|
if ($this->tags->count() > 0) {
|
||||||
|
$collector->setTags($this->tags);
|
||||||
|
$collector->withCategoryInformation();
|
||||||
|
$collector->withBudgetInformation();
|
||||||
|
}
|
||||||
|
|
||||||
$accountIds = $this->accounts->pluck('id')->toArray();
|
return $collector->getJournals();
|
||||||
$transactions = $collector->getJournals();
|
|
||||||
$set = Support::filterTransactions($transactions, $accountIds, $modifier);
|
|
||||||
|
|
||||||
return $set;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -233,8 +279,13 @@ class MetaPieChart implements MetaPieChartInterface
|
|||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function groupByFields(Collection $set, array $fields)
|
protected function groupByFields(Collection $set, array $fields): array
|
||||||
{
|
{
|
||||||
|
if (count($fields) === 0 && $this->tags->count() > 0) {
|
||||||
|
// do a special group on tags:
|
||||||
|
return $this->groupByTag($set);
|
||||||
|
}
|
||||||
|
|
||||||
$grouped = [];
|
$grouped = [];
|
||||||
/** @var Transaction $transaction */
|
/** @var Transaction $transaction */
|
||||||
foreach ($set as $transaction) {
|
foreach ($set as $transaction) {
|
||||||
@@ -261,10 +312,11 @@ class MetaPieChart implements MetaPieChartInterface
|
|||||||
$chartData = [];
|
$chartData = [];
|
||||||
$names = [];
|
$names = [];
|
||||||
$repository = app($this->repositories[$type]);
|
$repository = app($this->repositories[$type]);
|
||||||
|
$repository->setUser($this->user);
|
||||||
foreach ($array as $objectId => $amount) {
|
foreach ($array as $objectId => $amount) {
|
||||||
if (!isset($names[$objectId])) {
|
if (!isset($names[$objectId])) {
|
||||||
$object = $repository->find(intval($objectId));
|
$object = $repository->find(intval($objectId));
|
||||||
$names[$objectId] = $object->name;
|
$names[$objectId] = $object->name ?? $object->tag;
|
||||||
}
|
}
|
||||||
$amount = Steam::positive($amount);
|
$amount = Steam::positive($amount);
|
||||||
$this->total = bcadd($this->total, $amount);
|
$this->total = bcadd($this->total, $amount);
|
||||||
@@ -274,4 +326,27 @@ class MetaPieChart implements MetaPieChartInterface
|
|||||||
return $chartData;
|
return $chartData;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $set
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function groupByTag(Collection $set): array
|
||||||
|
{
|
||||||
|
$grouped = [];
|
||||||
|
/** @var Transaction $transaction */
|
||||||
|
foreach ($set as $transaction) {
|
||||||
|
$journal = $transaction->transactionJournal;
|
||||||
|
$tags = $journal->tags;
|
||||||
|
/** @var Tag $tag */
|
||||||
|
foreach ($tags as $tag) {
|
||||||
|
$tagId = $tag->id;
|
||||||
|
$grouped[$tagId] = $grouped[$tagId] ?? '0';
|
||||||
|
$grouped[$tagId] = bcadd($transaction->transaction_amount, $grouped[$tagId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $grouped;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -72,6 +72,13 @@ interface MetaPieChartInterface
|
|||||||
*/
|
*/
|
||||||
public function setStart(Carbon $start): MetaPieChartInterface;
|
public function setStart(Carbon $start): MetaPieChartInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $tags
|
||||||
|
*
|
||||||
|
* @return MetaPieChartInterface
|
||||||
|
*/
|
||||||
|
public function setTags(Collection $tags): MetaPieChartInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param User $user
|
* @param User $user
|
||||||
*
|
*
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Helpers\Collection;
|
namespace FireflyIII\Helpers\Collection;
|
||||||
|
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Helpers\Collection;
|
namespace FireflyIII\Helpers\Collection;
|
||||||
|
|
||||||
use FireflyIII\Models\Account as AccountModel;
|
use FireflyIII\Models\Account as AccountModel;
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Helpers\Collection;
|
namespace FireflyIII\Helpers\Collection;
|
||||||
|
|
||||||
use FireflyIII\Models\Account as AccountModel;
|
use FireflyIII\Models\Account as AccountModel;
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Helpers\Collection;
|
namespace FireflyIII\Helpers\Collection;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Helpers\Collection;
|
namespace FireflyIII\Helpers\Collection;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Helpers\Collection;
|
namespace FireflyIII\Helpers\Collection;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Helpers\Collection;
|
namespace FireflyIII\Helpers\Collection;
|
||||||
|
|
||||||
use FireflyIII\Models\Category as CategoryModel;
|
use FireflyIII\Models\Category as CategoryModel;
|
||||||
|
@@ -18,12 +18,17 @@ use Carbon\Carbon;
|
|||||||
use Crypt;
|
use Crypt;
|
||||||
use DB;
|
use DB;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Helpers\Filter\FilterInterface;
|
||||||
|
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\OpposingAccountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\TransferFilter;
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\Budget;
|
||||||
use FireflyIII\Models\Category;
|
use FireflyIII\Models\Category;
|
||||||
use FireflyIII\Models\Tag;
|
use FireflyIII\Models\Tag;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionType;
|
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Contracts\Encryption\DecryptException;
|
use Illuminate\Contracts\Encryption\DecryptException;
|
||||||
@@ -74,6 +79,9 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
private $filterInternalTransfers;
|
private $filterInternalTransfers;
|
||||||
/** @var bool */
|
/** @var bool */
|
||||||
private $filterTransfers = false;
|
private $filterTransfers = false;
|
||||||
|
/** @var array */
|
||||||
|
private $filters = [InternalTransferFilter::class];
|
||||||
|
|
||||||
/** @var bool */
|
/** @var bool */
|
||||||
private $joinedBudget = false;
|
private $joinedBudget = false;
|
||||||
/** @var bool */
|
/** @var bool */
|
||||||
@@ -95,6 +103,22 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
/** @var User */
|
/** @var User */
|
||||||
private $user;
|
private $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $filter
|
||||||
|
*
|
||||||
|
* @return JournalCollectorInterface
|
||||||
|
*/
|
||||||
|
public function addFilter(string $filter): JournalCollectorInterface
|
||||||
|
{
|
||||||
|
$interfaces = class_implements($filter);
|
||||||
|
if (in_array(FilterInterface::class, $interfaces)) {
|
||||||
|
Log::debug(sprintf('Enabled filter %s', $filter));
|
||||||
|
$this->filters[] = $filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return int
|
* @return int
|
||||||
* @throws FireflyException
|
* @throws FireflyException
|
||||||
@@ -119,36 +143,6 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
return $this->count;
|
return $this->count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return JournalCollectorInterface
|
|
||||||
*/
|
|
||||||
public function disableFilter(): JournalCollectorInterface
|
|
||||||
{
|
|
||||||
$this->filterTransfers = false;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return JournalCollectorInterface
|
|
||||||
*/
|
|
||||||
public function disableInternalFilter(): JournalCollectorInterface
|
|
||||||
{
|
|
||||||
$this->filterInternalTransfers = false;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return JournalCollectorInterface
|
|
||||||
*/
|
|
||||||
public function enableInternalFilter(): JournalCollectorInterface
|
|
||||||
{
|
|
||||||
$this->filterInternalTransfers = true;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
@@ -157,14 +151,9 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
$this->run = true;
|
$this->run = true;
|
||||||
/** @var Collection $set */
|
/** @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()));
|
|
||||||
$set = $this->filterTransfers($set);
|
|
||||||
Log::debug(sprintf('Count of set after filterTransfers() is %d', $set->count()));
|
|
||||||
|
|
||||||
// possibly filter "internal" transfers:
|
|
||||||
$set = $this->filterInternalTransfers($set);
|
|
||||||
Log::debug(sprintf('Count of set after filterInternalTransfers() is %d', $set->count()));
|
|
||||||
|
|
||||||
|
// run all filters:
|
||||||
|
$set = $this->filter($set);
|
||||||
|
|
||||||
// loop for decryption.
|
// loop for decryption.
|
||||||
$set->each(
|
$set->each(
|
||||||
@@ -204,6 +193,22 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
return $journals;
|
return $journals;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $filter
|
||||||
|
*
|
||||||
|
* @return JournalCollectorInterface
|
||||||
|
*/
|
||||||
|
public function removeFilter(string $filter): JournalCollectorInterface
|
||||||
|
{
|
||||||
|
$key = array_search($filter, $this->filters, true);
|
||||||
|
if (!($key === false)) {
|
||||||
|
Log::debug(sprintf('Removed filter %s', $filter));
|
||||||
|
unset($this->filters[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
*
|
*
|
||||||
@@ -219,6 +224,7 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($accounts->count() > 1) {
|
if ($accounts->count() > 1) {
|
||||||
|
$this->addFilter(TransferFilter::class);
|
||||||
$this->filterTransfers = true;
|
$this->filterTransfers = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,6 +248,7 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($accounts->count() > 1) {
|
if ($accounts->count() > 1) {
|
||||||
|
$this->addFilter(TransferFilter::class);
|
||||||
$this->filterTransfers = true;
|
$this->filterTransfers = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,6 +437,20 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $tags
|
||||||
|
*
|
||||||
|
* @return JournalCollectorInterface
|
||||||
|
*/
|
||||||
|
public function setTags(Collection $tags): JournalCollectorInterface
|
||||||
|
{
|
||||||
|
$this->joinTagTables();
|
||||||
|
$tagIds = $tags->pluck('id')->toArray();
|
||||||
|
$this->query->whereIn('tag_transaction_journal.tag_id', $tagIds);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $types
|
* @param array $types
|
||||||
*
|
*
|
||||||
@@ -450,7 +471,9 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
*/
|
*/
|
||||||
public function setUser(User $user)
|
public function setUser(User $user)
|
||||||
{
|
{
|
||||||
|
Log::debug(sprintf('Journal collector now collecting for user #%d', $user->id));
|
||||||
$this->user = $user;
|
$this->user = $user;
|
||||||
|
$this->startQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -458,6 +481,7 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
*/
|
*/
|
||||||
public function startQuery()
|
public function startQuery()
|
||||||
{
|
{
|
||||||
|
Log::debug('journalCollector::startQuery');
|
||||||
/** @var EloquentBuilder $query */
|
/** @var EloquentBuilder $query */
|
||||||
$query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
$query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||||
->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id')
|
->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id')
|
||||||
@@ -546,79 +570,23 @@ class JournalCollector implements JournalCollectorInterface
|
|||||||
*
|
*
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
private function filterInternalTransfers(Collection $set): Collection
|
private function filter(Collection $set): Collection
|
||||||
{
|
{
|
||||||
if ($this->filterInternalTransfers === false) {
|
// create all possible filters:
|
||||||
Log::debug('Did NO filtering for internal transfers on given set.');
|
$filters = [
|
||||||
|
InternalTransferFilter::class => new InternalTransferFilter($this->accountIds),
|
||||||
return $set;
|
OpposingAccountFilter::class => new OpposingAccountFilter($this->accountIds),
|
||||||
|
TransferFilter::class => new TransferFilter,
|
||||||
|
PositiveAmountFilter::class => new PositiveAmountFilter,
|
||||||
|
NegativeAmountFilter::class => new NegativeAmountFilter,
|
||||||
|
];
|
||||||
|
Log::debug(sprintf('Will run %d filters on the set.', count($this->filters)));
|
||||||
|
foreach ($this->filters as $enabled) {
|
||||||
|
if (isset($filters[$enabled])) {
|
||||||
|
Log::debug(sprintf('Before filter %s: %d', $enabled, $set->count()));
|
||||||
|
$set = $filters[$enabled]->filter($set);
|
||||||
|
Log::debug(sprintf('After filter %s: %d', $enabled, $set->count()));
|
||||||
}
|
}
|
||||||
if ($this->joinedOpposing === false) {
|
|
||||||
Log::info('Cannot filter internal transfers because no opposing information is present.');
|
|
||||||
|
|
||||||
return $set;
|
|
||||||
}
|
|
||||||
|
|
||||||
$accountIds = $this->accountIds;
|
|
||||||
$set = $set->filter(
|
|
||||||
function (Transaction $transaction) use ($accountIds) {
|
|
||||||
// both id's in $accountids?
|
|
||||||
if (in_array($transaction->account_id, $accountIds) && in_array($transaction->opposing_account_id, $accountIds)) {
|
|
||||||
Log::debug(
|
|
||||||
sprintf(
|
|
||||||
'Transaction #%d has #%d and #%d in set, so removed',
|
|
||||||
$transaction->id, $transaction->account_id, $transaction->opposing_account_id
|
|
||||||
), $accountIds
|
|
||||||
);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $transaction;
|
|
||||||
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return $set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the set of accounts used by the collector includes more than one asset
|
|
||||||
* account, chances are the set include double entries: transfers get selected
|
|
||||||
* on both the source, and then again on the destination account.
|
|
||||||
*
|
|
||||||
* This method filters them out by removing transfers that have been selected twice.
|
|
||||||
*
|
|
||||||
* @param Collection $set
|
|
||||||
*
|
|
||||||
* @return Collection
|
|
||||||
*/
|
|
||||||
private function filterTransfers(Collection $set): Collection
|
|
||||||
{
|
|
||||||
if ($this->filterTransfers) {
|
|
||||||
$count = [];
|
|
||||||
$new = new Collection;
|
|
||||||
/** @var Transaction $transaction */
|
|
||||||
foreach ($set as $transaction) {
|
|
||||||
if ($transaction->transaction_type_type !== TransactionType::TRANSFER) {
|
|
||||||
$new->push($transaction);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// make property string:
|
|
||||||
$journalId = $transaction->transaction_journal_id;
|
|
||||||
$amount = Steam::positive($transaction->transaction_amount);
|
|
||||||
$accountIds = [intval($transaction->account_id), intval($transaction->opposing_account_id)];
|
|
||||||
sort($accountIds);
|
|
||||||
$key = $journalId . '-' . join(',', $accountIds) . '-' . $amount;
|
|
||||||
Log::debug(sprintf('Key is %s', $key));
|
|
||||||
if (!isset($count[$key])) {
|
|
||||||
// not yet counted? add to new set and count it:
|
|
||||||
$new->push($transaction);
|
|
||||||
$count[$key] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $new;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $set;
|
return $set;
|
||||||
|
@@ -28,26 +28,18 @@ use Illuminate\Support\Collection;
|
|||||||
*/
|
*/
|
||||||
interface JournalCollectorInterface
|
interface JournalCollectorInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @param string $filter
|
||||||
|
*
|
||||||
|
* @return JournalCollectorInterface
|
||||||
|
*/
|
||||||
|
public function addFilter(string $filter): JournalCollectorInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function count(): int;
|
public function count(): int;
|
||||||
|
|
||||||
/**
|
|
||||||
* @return JournalCollectorInterface
|
|
||||||
*/
|
|
||||||
public function disableFilter(): JournalCollectorInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return JournalCollectorInterface
|
|
||||||
*/
|
|
||||||
public function disableInternalFilter(): JournalCollectorInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return JournalCollectorInterface
|
|
||||||
*/
|
|
||||||
public function enableInternalFilter(): JournalCollectorInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
@@ -58,6 +50,13 @@ interface JournalCollectorInterface
|
|||||||
*/
|
*/
|
||||||
public function getPaginatedJournals(): LengthAwarePaginator;
|
public function getPaginatedJournals(): LengthAwarePaginator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $filter
|
||||||
|
*
|
||||||
|
* @return JournalCollectorInterface
|
||||||
|
*/
|
||||||
|
public function removeFilter(string $filter): JournalCollectorInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
*
|
*
|
||||||
@@ -141,6 +140,13 @@ interface JournalCollectorInterface
|
|||||||
*/
|
*/
|
||||||
public function setTag(Tag $tag): JournalCollectorInterface;
|
public function setTag(Tag $tag): JournalCollectorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $tags
|
||||||
|
*
|
||||||
|
* @return JournalCollectorInterface
|
||||||
|
*/
|
||||||
|
public function setTags(Collection $tags): JournalCollectorInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $types
|
* @param array $types
|
||||||
*
|
*
|
||||||
|
56
app/Helpers/Filter/AmountFilter.php
Normal file
56
app/Helpers/Filter/AmountFilter.php
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* AmountFilter.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\Filter;
|
||||||
|
|
||||||
|
use FireflyIII\Models\Transaction;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AmountFilter
|
||||||
|
*
|
||||||
|
* This filter removes transactions with either a positive amount ($parameters = 1) or a negative amount
|
||||||
|
* ($parameter = -1). This is helpful when a Collection has you with both transactions in a journal.
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Helpers\Filter
|
||||||
|
*/
|
||||||
|
class AmountFilter implements FilterInterface
|
||||||
|
{
|
||||||
|
/** @var int */
|
||||||
|
private $modifier = 0;
|
||||||
|
|
||||||
|
public function __construct(int $modifier)
|
||||||
|
{
|
||||||
|
$this->modifier = $modifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $set
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function filter(Collection $set): Collection
|
||||||
|
{
|
||||||
|
return $set->filter(
|
||||||
|
function (Transaction $transaction) {
|
||||||
|
// remove by amount
|
||||||
|
if (bccomp($transaction->transaction_amount, '0') === $this->modifier) {
|
||||||
|
Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount));
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $transaction;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
34
app/Helpers/Filter/EmptyFilter.php
Normal file
34
app/Helpers/Filter/EmptyFilter.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* EmptyFilter.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\Filter;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class EmptyFilter
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Helpers\Filter
|
||||||
|
*/
|
||||||
|
class EmptyFilter implements FilterInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $set
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function filter(Collection $set): Collection
|
||||||
|
{
|
||||||
|
return $set;
|
||||||
|
}
|
||||||
|
}
|
26
app/Helpers/Filter/FilterInterface.php
Normal file
26
app/Helpers/Filter/FilterInterface.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* FilterInterface.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\Filter;
|
||||||
|
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
interface FilterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Collection $set
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function filter(Collection $set): Collection;
|
||||||
|
|
||||||
|
}
|
73
app/Helpers/Filter/InternalTransferFilter.php
Normal file
73
app/Helpers/Filter/InternalTransferFilter.php
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* InternalTransferFilter.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\Filter;
|
||||||
|
|
||||||
|
use FireflyIII\Models\Transaction;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class InternalTransferFilter
|
||||||
|
*
|
||||||
|
* This filter removes any filters that are from A to B or from B to A given a set of
|
||||||
|
* account id's (in $parameters) where A and B are mentioned. So transfers between the mentioned
|
||||||
|
* accounts will be removed.
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Helpers\Filter
|
||||||
|
*/
|
||||||
|
class InternalTransferFilter implements FilterInterface
|
||||||
|
{
|
||||||
|
/** @var array */
|
||||||
|
private $accounts = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* InternalTransferFilter constructor.
|
||||||
|
*
|
||||||
|
* @param array $accounts
|
||||||
|
*/
|
||||||
|
public function __construct(array $accounts)
|
||||||
|
{
|
||||||
|
$this->accounts = $accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $set
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function filter(Collection $set): Collection
|
||||||
|
{
|
||||||
|
return $set->filter(
|
||||||
|
function (Transaction $transaction) {
|
||||||
|
if (is_null($transaction->opposing_account_id)) {
|
||||||
|
return $transaction;
|
||||||
|
}
|
||||||
|
// both id's in $parameters?
|
||||||
|
if (in_array($transaction->account_id, $this->accounts) && in_array($transaction->opposing_account_id, $this->accounts)) {
|
||||||
|
Log::debug(
|
||||||
|
sprintf(
|
||||||
|
'Transaction #%d has #%d and #%d in set, so removed',
|
||||||
|
$transaction->id, $transaction->account_id, $transaction->opposing_account_id
|
||||||
|
), $this->accounts
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $transaction;
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
47
app/Helpers/Filter/NegativeAmountFilter.php
Normal file
47
app/Helpers/Filter/NegativeAmountFilter.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* NegativeAmountFilter.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\Filter;
|
||||||
|
|
||||||
|
use FireflyIII\Models\Transaction;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class NegativeAmountFilter
|
||||||
|
*
|
||||||
|
* This filter removes entries with a negative amount (the original modifier is -1).
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Helpers\Filter
|
||||||
|
*/
|
||||||
|
class NegativeAmountFilter implements FilterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Collection $set
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function filter(Collection $set): Collection
|
||||||
|
{
|
||||||
|
return $set->filter(
|
||||||
|
function (Transaction $transaction) {
|
||||||
|
// remove by 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;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
63
app/Helpers/Filter/OpposingAccountFilter.php
Normal file
63
app/Helpers/Filter/OpposingAccountFilter.php
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* OpposingAccountFilter.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\Filter;
|
||||||
|
|
||||||
|
|
||||||
|
use FireflyIII\Models\Transaction;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class OpposingAccountFilter
|
||||||
|
*
|
||||||
|
* This filter is similar to the internal transfer filter but only removes transactions when the opposing account is
|
||||||
|
* amongst $parameters (list of account ID's).
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Helpers\Filter
|
||||||
|
*/
|
||||||
|
class OpposingAccountFilter implements FilterInterface
|
||||||
|
{
|
||||||
|
/** @var array */
|
||||||
|
private $accounts = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* InternalTransferFilter constructor.
|
||||||
|
*
|
||||||
|
* @param array $accounts
|
||||||
|
*/
|
||||||
|
public function __construct(array $accounts)
|
||||||
|
{
|
||||||
|
$this->accounts = $accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $set
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function filter(Collection $set): Collection
|
||||||
|
{
|
||||||
|
return $set->filter(
|
||||||
|
function (Transaction $transaction) {
|
||||||
|
$opposing = $transaction->opposing_account_id;
|
||||||
|
// remove internal transfer
|
||||||
|
if (in_array($opposing, $this->accounts)) {
|
||||||
|
Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id), $this->accounts);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $transaction;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
50
app/Helpers/Filter/PositiveAmountFilter.php
Normal file
50
app/Helpers/Filter/PositiveAmountFilter.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* PositiveAmountFilter.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\Filter;
|
||||||
|
|
||||||
|
use FireflyIII\Models\Transaction;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class PositiveAmountFilter
|
||||||
|
*
|
||||||
|
* This filter removes entries with a negative amount (the original modifier is -1).
|
||||||
|
*
|
||||||
|
* This filter removes transactions with either a positive amount ($parameters = 1) or a negative amount
|
||||||
|
* ($parameter = -1). This is helpful when a Collection has you with both transactions in a journal.
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Helpers\Filter
|
||||||
|
*/
|
||||||
|
class PositiveAmountFilter implements FilterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Collection $set
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function filter(Collection $set): Collection
|
||||||
|
{
|
||||||
|
return $set->filter(
|
||||||
|
function (Transaction $transaction) {
|
||||||
|
// remove by 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;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
58
app/Helpers/Filter/TransferFilter.php
Normal file
58
app/Helpers/Filter/TransferFilter.php
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* TransferFilter.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\Filter;
|
||||||
|
|
||||||
|
use FireflyIII\Models\Transaction;
|
||||||
|
use FireflyIII\Models\TransactionType;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Steam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TransferFilter
|
||||||
|
*
|
||||||
|
* This filter removes any transfers that are in the collection twice (from A to B and from B to A).
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Helpers\Filter
|
||||||
|
*/
|
||||||
|
class TransferFilter implements FilterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Collection $set
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function filter(Collection $set): Collection
|
||||||
|
{
|
||||||
|
$count = [];
|
||||||
|
$new = new Collection;
|
||||||
|
/** @var Transaction $transaction */
|
||||||
|
foreach ($set as $transaction) {
|
||||||
|
if ($transaction->transaction_type_type !== TransactionType::TRANSFER) {
|
||||||
|
$new->push($transaction);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// make property string:
|
||||||
|
$journalId = $transaction->transaction_journal_id;
|
||||||
|
$amount = Steam::positive($transaction->transaction_amount);
|
||||||
|
$accountIds = [intval($transaction->account_id), intval($transaction->opposing_account_id)];
|
||||||
|
sort($accountIds);
|
||||||
|
$key = $journalId . '-' . join(',', $accountIds) . '-' . $amount;
|
||||||
|
if (!isset($count[$key])) {
|
||||||
|
// not yet counted? add to new set and count it:
|
||||||
|
$new->push($transaction);
|
||||||
|
$count[$key] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
}
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Helpers\Help;
|
namespace FireflyIII\Helpers\Help;
|
||||||
|
|
||||||
use Cache;
|
use Cache;
|
||||||
@@ -43,12 +44,12 @@ class Help implements HelpInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $language
|
|
||||||
* @param string $route
|
* @param string $route
|
||||||
|
* @param string $language
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getFromGithub(string $language, string $route): string
|
public function getFromGithub(string $route, string $language): string
|
||||||
{
|
{
|
||||||
|
|
||||||
$uri = sprintf('https://raw.githubusercontent.com/firefly-iii/help/master/%s/%s.md', $language, $route);
|
$uri = sprintf('https://raw.githubusercontent.com/firefly-iii/help/master/%s/%s.md', $language, $route);
|
||||||
@@ -123,6 +124,7 @@ class Help implements HelpInterface
|
|||||||
if (strlen($content) > 0) {
|
if (strlen($content) > 0) {
|
||||||
Log::debug(sprintf('Will store entry in cache: %s', $key));
|
Log::debug(sprintf('Will store entry in cache: %s', $key));
|
||||||
Cache::put($key, $content, 10080); // a week.
|
Cache::put($key, $content, 10080); // a week.
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log::info(sprintf('Will not cache %s because content is empty.', $key));
|
Log::info(sprintf('Will not cache %s because content is empty.', $key));
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Helpers\Help;
|
namespace FireflyIII\Helpers\Help;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,12 +30,12 @@ interface HelpInterface
|
|||||||
public function getFromCache(string $route, string $language): string;
|
public function getFromCache(string $route, string $language): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $language
|
|
||||||
* @param string $route
|
* @param string $route
|
||||||
|
* @param string $language
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getFromGithub(string $language, string $route): string;
|
public function getFromGithub(string $route, string $language): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $route
|
* @param string $route
|
||||||
|
199
app/Helpers/Report/PopupReport.php
Normal file
199
app/Helpers/Report/PopupReport.php
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* PopupReport.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\Report;
|
||||||
|
|
||||||
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
|
use FireflyIII\Models\Account;
|
||||||
|
use FireflyIII\Models\Budget;
|
||||||
|
use FireflyIII\Models\Category;
|
||||||
|
use FireflyIII\Models\Transaction;
|
||||||
|
use FireflyIII\Models\TransactionType;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class PopupReport
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Helpers\Report
|
||||||
|
*/
|
||||||
|
class PopupReport implements PopupReportInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $account
|
||||||
|
* @param $attributes
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function balanceDifference($account, $attributes): Collection
|
||||||
|
{
|
||||||
|
// row that displays difference
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector
|
||||||
|
->setAccounts(new Collection([$account]))
|
||||||
|
->setTypes([TransactionType::WITHDRAWAL])
|
||||||
|
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||||
|
->withoutBudget();
|
||||||
|
$journals = $collector->getJournals();
|
||||||
|
|
||||||
|
|
||||||
|
return $journals->filter(
|
||||||
|
function (Transaction $transaction) {
|
||||||
|
$tags = $transaction->transactionJournal->tags()->where('tagMode', 'balancingAct')->count();
|
||||||
|
if ($tags === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Budget $budget
|
||||||
|
* @param Account $account
|
||||||
|
* @param array $attributes
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function balanceForBudget(Budget $budget, Account $account, array $attributes): Collection
|
||||||
|
{
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setBudget($budget);
|
||||||
|
$journals = $collector->getJournals();
|
||||||
|
|
||||||
|
return $journals;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Account $account
|
||||||
|
* @param array $attributes
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function balanceForNoBudget(Account $account, array $attributes): Collection
|
||||||
|
{
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector
|
||||||
|
->setAccounts(new Collection([$account]))
|
||||||
|
->setTypes([TransactionType::WITHDRAWAL])
|
||||||
|
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||||
|
->withoutBudget();
|
||||||
|
|
||||||
|
return $collector->getJournals();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Budget $budget
|
||||||
|
* @param array $attributes
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function byBudget(Budget $budget, array $attributes): Collection
|
||||||
|
{
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
|
||||||
|
$collector->setAccounts($attributes['accounts'])->setRange($attributes['startDate'], $attributes['endDate']);
|
||||||
|
|
||||||
|
if (is_null($budget->id)) {
|
||||||
|
$collector->setTypes([TransactionType::WITHDRAWAL])->withoutBudget();
|
||||||
|
}
|
||||||
|
if (!is_null($budget->id)) {
|
||||||
|
$collector->setBudget($budget);
|
||||||
|
}
|
||||||
|
$journals = $collector->getJournals();
|
||||||
|
|
||||||
|
return $journals;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Category $category
|
||||||
|
* @param array $attributes
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function byCategory(Category $category, array $attributes): Collection
|
||||||
|
{
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAccounts($attributes['accounts'])->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
|
||||||
|
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||||
|
->setCategory($category);
|
||||||
|
$journals = $collector->getJournals();
|
||||||
|
|
||||||
|
return $journals;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Account $account
|
||||||
|
* @param array $attributes
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function byExpenses(Account $account, array $attributes): Collection
|
||||||
|
{
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
|
||||||
|
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])
|
||||||
|
->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]);
|
||||||
|
$journals = $collector->getJournals();
|
||||||
|
|
||||||
|
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
|
||||||
|
|
||||||
|
// filter for transfers and withdrawals TO the given $account
|
||||||
|
$journals = $journals->filter(
|
||||||
|
function (Transaction $transaction) use ($report) {
|
||||||
|
// get the destinations:
|
||||||
|
$sources = $transaction->transactionJournal->sourceAccountList()->pluck('id')->toArray();
|
||||||
|
|
||||||
|
// do these intersect with the current list?
|
||||||
|
return !empty(array_intersect($report, $sources));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $journals;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Account $account
|
||||||
|
* @param array $attributes
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function byIncome(Account $account, array $attributes): Collection
|
||||||
|
{
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])
|
||||||
|
->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]);
|
||||||
|
$journals = $collector->getJournals();
|
||||||
|
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
|
||||||
|
|
||||||
|
// filter the set so the destinations outside of $attributes['accounts'] are not included.
|
||||||
|
$journals = $journals->filter(
|
||||||
|
function (Transaction $transaction) use ($report) {
|
||||||
|
// get the destinations:
|
||||||
|
$destinations = $transaction->destinationAccountList($transaction->transactionJournal)->pluck('id')->toArray();
|
||||||
|
|
||||||
|
// do these intersect with the current list?
|
||||||
|
return !empty(array_intersect($report, $destinations));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $journals;
|
||||||
|
}
|
||||||
|
}
|
83
app/Helpers/Report/PopupReportInterface.php
Normal file
83
app/Helpers/Report/PopupReportInterface.php
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* PopupReportInterface.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\Report;
|
||||||
|
|
||||||
|
use FireflyIII\Models\Account;
|
||||||
|
use FireflyIII\Models\Budget;
|
||||||
|
use FireflyIII\Models\Category;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface PopupReportInterface
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Helpers\Report
|
||||||
|
*/
|
||||||
|
interface PopupReportInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $account
|
||||||
|
* @param $attributes
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function balanceDifference($account, $attributes): Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Budget $budget
|
||||||
|
* @param Account $account
|
||||||
|
* @param array $attributes
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function balanceForBudget(Budget $budget, Account $account, array $attributes): Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Account $account
|
||||||
|
* @param array $attributes
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function balanceForNoBudget(Account $account, array $attributes): Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Budget $budget
|
||||||
|
* @param array $attributes
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function byBudget(Budget $budget, array $attributes): Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Category $category
|
||||||
|
* @param array $attributes
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function byCategory(Category $category, array $attributes): Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Account $account
|
||||||
|
* @param array $attributes
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function byExpenses(Account $account, array $attributes): Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Account $account
|
||||||
|
* @param array $attributes
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function byIncome(Account $account, array $attributes): Collection;
|
||||||
|
}
|
@@ -22,9 +22,10 @@ use FireflyIII\Http\Requests\AccountFormRequest;
|
|||||||
use FireflyIII\Models\Account;
|
use FireflyIII\Models\Account;
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
|
||||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -69,7 +70,8 @@ class AccountController extends Controller
|
|||||||
{
|
{
|
||||||
/** @var CurrencyRepositoryInterface $repository */
|
/** @var CurrencyRepositoryInterface $repository */
|
||||||
$repository = app(CurrencyRepositoryInterface::class);
|
$repository = app(CurrencyRepositoryInterface::class);
|
||||||
$currencies = ExpandedForm::makeSelectList($repository->get());
|
$allCurrencies = $repository->get();
|
||||||
|
$currencySelectList = ExpandedForm::makeSelectList($allCurrencies);
|
||||||
$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');
|
||||||
@@ -90,7 +92,7 @@ class AccountController extends Controller
|
|||||||
$request->session()->flash('gaEventCategory', 'accounts');
|
$request->session()->flash('gaEventCategory', 'accounts');
|
||||||
$request->session()->flash('gaEventAction', 'create-' . $what);
|
$request->session()->flash('gaEventAction', 'create-' . $what);
|
||||||
|
|
||||||
return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle', 'currencies', 'roles'));
|
return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle', 'currencySelectList', 'allCurrencies', 'roles'));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,13 +148,13 @@ class AccountController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function edit(Request $request, Account $account)
|
public function edit(Request $request, Account $account)
|
||||||
{
|
{
|
||||||
|
/** @var CurrencyRepositoryInterface $repository */
|
||||||
|
$repository = app(CurrencyRepositoryInterface::class);
|
||||||
$what = config('firefly.shortNamesByFullName')[$account->accountType->type];
|
$what = config('firefly.shortNamesByFullName')[$account->accountType->type];
|
||||||
$subTitle = trans('firefly.edit_' . $what . '_account', ['name' => $account->name]);
|
$subTitle = trans('firefly.edit_' . $what . '_account', ['name' => $account->name]);
|
||||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
||||||
/** @var CurrencyRepositoryInterface $repository */
|
$allCurrencies = $repository->get();
|
||||||
$repository = app(CurrencyRepositoryInterface::class);
|
$currencySelectList = ExpandedForm::makeSelectList($allCurrencies);
|
||||||
$currencies = ExpandedForm::makeSelectList($repository->get());
|
|
||||||
$roles = [];
|
$roles = [];
|
||||||
foreach (config('firefly.accountRoles') as $role) {
|
foreach (config('firefly.accountRoles') as $role) {
|
||||||
$roles[$role] = strval(trans('firefly.account_role_' . $role));
|
$roles[$role] = strval(trans('firefly.account_role_' . $role));
|
||||||
@@ -172,6 +174,7 @@ class AccountController extends Controller
|
|||||||
$openingBalanceAmount = $account->getOpeningBalanceAmount() === '0' ? '' : $openingBalanceAmount;
|
$openingBalanceAmount = $account->getOpeningBalanceAmount() === '0' ? '' : $openingBalanceAmount;
|
||||||
$openingBalanceDate = $account->getOpeningBalanceDate();
|
$openingBalanceDate = $account->getOpeningBalanceDate();
|
||||||
$openingBalanceDate = $openingBalanceDate->year === 1900 ? null : $openingBalanceDate->format('Y-m-d');
|
$openingBalanceDate = $openingBalanceDate->year === 1900 ? null : $openingBalanceDate->format('Y-m-d');
|
||||||
|
$currency = $repository->find(intval($account->getMeta('currency_id')));
|
||||||
|
|
||||||
$preFilled = [
|
$preFilled = [
|
||||||
'accountNumber' => $account->getMeta('accountNumber'),
|
'accountNumber' => $account->getMeta('accountNumber'),
|
||||||
@@ -182,13 +185,18 @@ class AccountController extends Controller
|
|||||||
'openingBalanceDate' => $openingBalanceDate,
|
'openingBalanceDate' => $openingBalanceDate,
|
||||||
'openingBalance' => $openingBalanceAmount,
|
'openingBalance' => $openingBalanceAmount,
|
||||||
'virtualBalance' => $account->virtual_balance,
|
'virtualBalance' => $account->virtual_balance,
|
||||||
'currency_id' => $account->getMeta('currency_id'),
|
'currency_id' => $currency->id,
|
||||||
|
|
||||||
];
|
];
|
||||||
$request->session()->flash('preFilled', $preFilled);
|
$request->session()->flash('preFilled', $preFilled);
|
||||||
$request->session()->flash('gaEventCategory', 'accounts');
|
$request->session()->flash('gaEventCategory', 'accounts');
|
||||||
$request->session()->flash('gaEventAction', 'edit-' . $what);
|
$request->session()->flash('gaEventAction', 'edit-' . $what);
|
||||||
|
|
||||||
return view('accounts.edit', compact('currencies', 'account', 'subTitle', 'subTitleIcon', 'openingBalance', 'what', 'roles'));
|
return view(
|
||||||
|
'accounts.edit', compact(
|
||||||
|
'allCurrencies', 'currencySelectList', 'account', 'currency', 'subTitle', 'subTitleIcon', 'openingBalance', 'what', 'roles'
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -227,101 +235,99 @@ class AccountController extends Controller
|
|||||||
return view('accounts.index', compact('what', 'subTitleIcon', 'subTitle', 'accounts'));
|
return view('accounts.index', compact('what', 'subTitleIcon', 'subTitle', 'accounts'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param JournalCollectorInterface $collector
|
* @param JournalRepositoryInterface $repository
|
||||||
* @param Account $account
|
* @param Account $account
|
||||||
|
* @param string $moment
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||||
*/
|
*/
|
||||||
public function show(Request $request, JournalCollectorInterface $collector, Account $account)
|
public function show(Request $request, JournalRepositoryInterface $repository, Account $account, string $moment = '')
|
||||||
{
|
{
|
||||||
if ($account->accountType->type === AccountType::INITIAL_BALANCE) {
|
if ($account->accountType->type === AccountType::INITIAL_BALANCE) {
|
||||||
return $this->redirectToOriginalAccount($account);
|
return $this->redirectToOriginalAccount($account);
|
||||||
}
|
}
|
||||||
// show journals from current period only:
|
/** @var CurrencyRepositoryInterface $currencyRepos */
|
||||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
|
$currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||||
$subTitle = $account->name;
|
|
||||||
$range = Preferences::get('viewRange', '1M')->data;
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
|
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
|
||||||
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
|
|
||||||
$page = intval($request->get('page')) === 0 ? 1 : intval($request->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;
|
$start = null;
|
||||||
|
$end = null;
|
||||||
|
$periods = new Collection;
|
||||||
|
$currency = $currencyRepos->find(intval($account->getMeta('currency_id')));
|
||||||
|
|
||||||
// grab those journals:
|
// prep for "all" view.
|
||||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
|
if ($moment === 'all') {
|
||||||
$journals = $collector->getPaginatedJournals();
|
$subTitle = trans('firefly.all_journals_for_account', ['name' => $account->name]);
|
||||||
$journals->setPath('accounts/show/' . $account->id);
|
|
||||||
|
|
||||||
// generate entries for each period (and cache those)
|
|
||||||
$entries = $this->periodEntries($account);
|
|
||||||
|
|
||||||
return view('accounts.show', compact('account', 'accountType', 'entries', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Request $request
|
|
||||||
* @param AccountRepositoryInterface $repository
|
|
||||||
* @param Account $account
|
|
||||||
*
|
|
||||||
* @return View
|
|
||||||
*/
|
|
||||||
public function showAll(Request $request, AccountRepositoryInterface $repository, Account $account)
|
|
||||||
{
|
|
||||||
$subTitle = sprintf('%s (%s)', $account->name, strtolower(trans('firefly.everything')));
|
|
||||||
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
|
|
||||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
|
||||||
$chartUri = route('chart.account.all', [$account->id]);
|
$chartUri = route('chart.account.all', [$account->id]);
|
||||||
|
$first = $repository->first();
|
||||||
// replace with journal collector:
|
$start = $first->date ?? new Carbon;
|
||||||
/** @var JournalCollectorInterface $collector */
|
$end = new Carbon;
|
||||||
$collector = app(JournalCollectorInterface::class);
|
|
||||||
$collector->setUser(auth()->user());
|
|
||||||
$collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page);
|
|
||||||
$journals = $collector->getPaginatedJournals();
|
|
||||||
$journals->setPath('accounts/show/' . $account->id . '/all');
|
|
||||||
|
|
||||||
// get oldest and newest journal for account:
|
|
||||||
$start = $repository->oldestJournalDate($account);
|
|
||||||
$end = $repository->newestJournalDate($account);
|
|
||||||
|
|
||||||
// same call, except "entries".
|
|
||||||
return view('accounts.show', compact('account', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// prep for "specific date" view.
|
||||||
* @param Request $request
|
if (strlen($moment) > 0 && $moment !== 'all') {
|
||||||
* @param Account $account
|
$start = new Carbon($moment);
|
||||||
* @param string $date
|
$end = Navigation::endOfPeriod($start, $range);
|
||||||
*
|
$subTitle = trans(
|
||||||
* @return View
|
'firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
|
||||||
*/
|
'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||||
public function showByDate(Request $request, Account $account, string $date)
|
);
|
||||||
{
|
$chartUri = route('chart.account.period', [$account->id, $start->format('Y-m-d')]);
|
||||||
$carbon = new Carbon($date);
|
$periods = $this->getPeriodOverview($account);
|
||||||
$range = Preferences::get('viewRange', '1M')->data;
|
}
|
||||||
$start = Navigation::startOfPeriod($carbon, $range);
|
|
||||||
$end = Navigation::endOfPeriod($carbon, $range);
|
|
||||||
$subTitle = $account->name . ' (' . Navigation::periodShow($start, $range) . ')';
|
|
||||||
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
|
|
||||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
|
||||||
$chartUri = route('chart.account.period', [$account->id, $carbon->format('Y-m-d')]);
|
|
||||||
$accountType = $account->accountType->type;
|
|
||||||
|
|
||||||
// replace with journal collector:
|
// prep for current period
|
||||||
/** @var JournalCollectorInterface $collector */
|
if (strlen($moment) === 0) {
|
||||||
|
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||||
|
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||||
|
$subTitle = trans(
|
||||||
|
'firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
|
||||||
|
'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||||
|
);
|
||||||
|
$periods = $this->getPeriodOverview($account);
|
||||||
|
}
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
$loop = 0;
|
||||||
|
// grab journals, but be prepared to jump a period back to get the right ones:
|
||||||
|
Log::info('Now at loop start.');
|
||||||
|
while ($count === 0 && $loop < 3) {
|
||||||
|
$loop++;
|
||||||
$collector = app(JournalCollectorInterface::class);
|
$collector = app(JournalCollectorInterface::class);
|
||||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
|
Log::info('Count is zero, search for journals.');
|
||||||
|
$collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page);
|
||||||
|
if (!is_null($start)) {
|
||||||
|
$collector->setRange($start, $end);
|
||||||
|
}
|
||||||
$journals = $collector->getPaginatedJournals();
|
$journals = $collector->getPaginatedJournals();
|
||||||
$journals->setPath('accounts/show/' . $account->id . '/' . $date);
|
$journals->setPath('accounts/show/' . $account->id . '/' . $moment);
|
||||||
|
$count = $journals->getCollection()->count();
|
||||||
|
if ($count === 0) {
|
||||||
|
$start->subDay();
|
||||||
|
$start = Navigation::startOfPeriod($start, $range);
|
||||||
|
$end = Navigation::endOfPeriod($start, $range);
|
||||||
|
Log::info(sprintf('Count is still zero, go back in time to "%s" and "%s"!', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// generate entries for each period (and cache those)
|
if ($moment != 'all' && $loop > 1) {
|
||||||
$entries = $this->periodEntries($account);
|
$subTitle = trans(
|
||||||
|
'firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
|
||||||
|
'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// same call, except "entries".
|
|
||||||
return view('accounts.show', compact('account', 'accountType', 'entries', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri'));
|
return view(
|
||||||
|
'accounts.show',
|
||||||
|
compact('account', 'currency', 'moment', 'periods', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -335,7 +341,6 @@ class AccountController extends Controller
|
|||||||
{
|
{
|
||||||
$data = $request->getAccountData();
|
$data = $request->getAccountData();
|
||||||
$account = $repository->store($data);
|
$account = $repository->store($data);
|
||||||
|
|
||||||
$request->session()->flash('success', strval(trans('firefly.stored_new_account', ['name' => $account->name])));
|
$request->session()->flash('success', strval(trans('firefly.stored_new_account', ['name' => $account->name])));
|
||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
@@ -409,13 +414,10 @@ class AccountController extends Controller
|
|||||||
*
|
*
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
private function periodEntries(Account $account): Collection
|
private function getPeriodOverview(Account $account): Collection
|
||||||
{
|
{
|
||||||
/** @var AccountRepositoryInterface $repository */
|
/** @var AccountRepositoryInterface $repository */
|
||||||
$repository = app(AccountRepositoryInterface::class);
|
$repository = app(AccountRepositoryInterface::class);
|
||||||
/** @var AccountTaskerInterface $tasker */
|
|
||||||
$tasker = app(AccountTaskerInterface::class);
|
|
||||||
|
|
||||||
$start = $repository->oldestJournalDate($account);
|
$start = $repository->oldestJournalDate($account);
|
||||||
$range = Preferences::get('viewRange', '1M')->data;
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
$start = Navigation::startOfPeriod($start, $range);
|
$start = Navigation::startOfPeriod($start, $range);
|
||||||
@@ -430,25 +432,39 @@ class AccountController extends Controller
|
|||||||
$cache->addProperty($account->id);
|
$cache->addProperty($account->id);
|
||||||
|
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
Log::debug('Entries are cached, return cache.');
|
return $cache->get(); // @codeCoverageIgnore
|
||||||
|
|
||||||
return $cache->get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// only include asset accounts when this account is an asset:
|
|
||||||
$assets = new Collection;
|
|
||||||
if (in_array($account->accountType->type, [AccountType::ASSET, AccountType::DEFAULT])) {
|
|
||||||
$assets = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]);
|
|
||||||
}
|
|
||||||
Log::debug('Going to get period expenses and incomes.');
|
Log::debug('Going to get period expenses and incomes.');
|
||||||
while ($end >= $start) {
|
while ($end >= $start) {
|
||||||
$end = Navigation::startOfPeriod($end, $range);
|
$end = Navigation::startOfPeriod($end, $range);
|
||||||
$currentEnd = Navigation::endOfPeriod($end, $range);
|
$currentEnd = Navigation::endOfPeriod($end, $range);
|
||||||
$spent = $tasker->amountOutInPeriod(new Collection([$account]), $assets, $end, $currentEnd);
|
|
||||||
$earned = $tasker->amountInInPeriod(new Collection([$account]), $assets, $end, $currentEnd);
|
// try a collector for income:
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAccounts(new Collection([$account]))->setRange($end, $currentEnd)
|
||||||
|
->setTypes([TransactionType::DEPOSIT])
|
||||||
|
->withOpposingAccount();
|
||||||
|
$earned = strval($collector->getJournals()->sum('transaction_amount'));
|
||||||
|
|
||||||
|
// try a collector for expenses:
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAccounts(new Collection([$account]))->setRange($end, $currentEnd)
|
||||||
|
->setTypes([TransactionType::WITHDRAWAL])
|
||||||
|
->withOpposingAccount();
|
||||||
|
$spent = strval($collector->getJournals()->sum('transaction_amount'));
|
||||||
$dateStr = $end->format('Y-m-d');
|
$dateStr = $end->format('Y-m-d');
|
||||||
$dateName = Navigation::periodShow($end, $range);
|
$dateName = Navigation::periodShow($end, $range);
|
||||||
$entries->push([$dateStr, $dateName, $spent, $earned, clone $end]);
|
$entries->push(
|
||||||
|
[
|
||||||
|
'string' => $dateStr,
|
||||||
|
'name' => $dateName,
|
||||||
|
'spent' => $spent,
|
||||||
|
'earned' => $earned,
|
||||||
|
'date' => clone $end]
|
||||||
|
);
|
||||||
$end = Navigation::subtractPeriod($end, $range, 1);
|
$end = Navigation::subtractPeriod($end, $range, 1);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -476,7 +492,7 @@ class AccountController extends Controller
|
|||||||
$opposingTransaction = $journal->transactions()->where('transactions.id', '!=', $transaction->id)->first();
|
$opposingTransaction = $journal->transactions()->where('transactions.id', '!=', $transaction->id)->first();
|
||||||
|
|
||||||
if (is_null($opposingTransaction)) {
|
if (is_null($opposingTransaction)) {
|
||||||
throw new FireflyException('Expected an opposing transaction. This account has none. BEEP, error.');
|
throw new FireflyException('Expected an opposing transaction. This account has none. BEEP, error.'); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect(route('accounts.show', [$opposingTransaction->account_id]));
|
return redirect(route('accounts.show', [$opposingTransaction->account_id]));
|
||||||
|
@@ -18,6 +18,7 @@ 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;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
|
use Log;
|
||||||
use Preferences;
|
use Preferences;
|
||||||
use Session;
|
use Session;
|
||||||
use View;
|
use View;
|
||||||
@@ -127,31 +128,31 @@ class UserController extends Controller
|
|||||||
* @param UserFormRequest $request
|
* @param UserFormRequest $request
|
||||||
* @param User $user
|
* @param User $user
|
||||||
*
|
*
|
||||||
|
* @param UserRepositoryInterface $repository
|
||||||
|
*
|
||||||
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
*/
|
*/
|
||||||
public function update(UserFormRequest $request, User $user)
|
public function update(UserFormRequest $request, User $user, UserRepositoryInterface $repository)
|
||||||
{
|
{
|
||||||
|
Log::debug('Actually here');
|
||||||
$data = $request->getUserData();
|
$data = $request->getUserData();
|
||||||
|
|
||||||
// update password
|
// update password
|
||||||
if (strlen($data['password']) > 0) {
|
if (strlen($data['password']) > 0) {
|
||||||
$user->password = bcrypt($data['password']);
|
$repository->changePassword($user, $data['password']);
|
||||||
$user->save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// change blocked status and code:
|
$repository->changeStatus($user, $data['blocked'], $data['blocked_code']);
|
||||||
$user->blocked = $data['blocked'];
|
|
||||||
$user->blocked_code = $data['blocked_code'];
|
|
||||||
$user->save();
|
|
||||||
|
|
||||||
Session::flash('success', strval(trans('firefly.updated_user', ['email' => $user->email])));
|
Session::flash('success', strval(trans('firefly.updated_user', ['email' => $user->email])));
|
||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
if (intval($request->get('return_to_edit')) === 1) {
|
if (intval($request->get('return_to_edit')) === 1) {
|
||||||
// set value so edit routine will not overwrite URL:
|
// @codeCoverageIgnoreStart
|
||||||
Session::put('users.edit.fromUpdate', true);
|
Session::put('users.edit.fromUpdate', true);
|
||||||
|
|
||||||
return redirect(route('admin.users.edit', [$user->id]))->withInput(['return_to_edit' => 1]);
|
return redirect(route('admin.users.edit', [$user->id]))->withInput(['return_to_edit' => 1]);
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
// redirect to previous URL.
|
// redirect to previous URL.
|
||||||
|
@@ -176,10 +176,11 @@ class AttachmentController extends Controller
|
|||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
if (intval($request->get('return_to_edit')) === 1) {
|
if (intval($request->get('return_to_edit')) === 1) {
|
||||||
// set value so edit routine will not overwrite URL:
|
// @codeCoverageIgnoreStart
|
||||||
$request->session()->put('attachments.edit.fromUpdate', true);
|
$request->session()->put('attachments.edit.fromUpdate', true);
|
||||||
|
|
||||||
return redirect(route('attachments.edit', [$attachment->id]))->withInput(['return_to_edit' => 1]);
|
return redirect(route('attachments.edit', [$attachment->id]))->withInput(['return_to_edit' => 1]);
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
// redirect to previous URL.
|
// redirect to previous URL.
|
||||||
|
@@ -13,6 +13,7 @@ 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\Repositories\User\UserRepositoryInterface;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -42,23 +43,22 @@ class ForgotPasswordController extends Controller
|
|||||||
*
|
*
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
*
|
*
|
||||||
|
* @param UserRepositoryInterface $repository
|
||||||
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function sendResetLinkEmail(Request $request)
|
public function sendResetLinkEmail(Request $request, UserRepositoryInterface $repository)
|
||||||
{
|
{
|
||||||
$this->validate($request, ['email' => 'required|email']);
|
$this->validate($request, ['email' => 'required|email']);
|
||||||
|
|
||||||
// verify if the user is not a demo user. If so, we give him back an error.
|
// 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();
|
$user = User::where('email', $request->get('email'))->first();
|
||||||
if (!is_null($user) && $user->hasRole('demo')) {
|
|
||||||
return back()->withErrors(
|
if (!is_null($user) && $repository->hasRole($user, 'demo')) {
|
||||||
['email' => trans('firefly.cannot_reset_demo_user')]
|
return back()->withErrors(['email' => trans('firefly.cannot_reset_demo_user')]);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = $this->broker()->sendResetLink(
|
$response = $this->broker()->sendResetLink($request->only('email'));
|
||||||
$request->only('email')
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($response === Password::RESET_LINK_SENT) {
|
if ($response === Password::RESET_LINK_SENT) {
|
||||||
return back()->with('status', trans($response));
|
return back()->with('status', trans($response));
|
||||||
@@ -67,8 +67,6 @@ class ForgotPasswordController extends Controller
|
|||||||
// If an error was returned by the password broker, we will get this message
|
// 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
|
// 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.
|
// to where the users came from so they can attempt this process again.
|
||||||
return back()->withErrors(
|
return back()->withErrors(['email' => trans($response)]); // @codeCoverageIgnore
|
||||||
['email' => trans($response)]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,8 @@ use Illuminate\Http\Request;
|
|||||||
use Lang;
|
use Lang;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*
|
||||||
* Class LoginController
|
* Class LoginController
|
||||||
*
|
*
|
||||||
* @package FireflyIII\Http\Controllers\Auth
|
* @package FireflyIII\Http\Controllers\Auth
|
||||||
@@ -110,10 +112,14 @@ class LoginController extends Controller
|
|||||||
*
|
*
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
*
|
*
|
||||||
|
* @param CookieJar $cookieJar
|
||||||
|
*
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function showLoginForm(Request $request)
|
public function showLoginForm(Request $request, CookieJar $cookieJar)
|
||||||
{
|
{
|
||||||
|
// forget 2fa cookie:
|
||||||
|
$cookie = $cookieJar->forever('twoFactorAuthenticated', 'false');
|
||||||
// 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();
|
||||||
@@ -125,7 +131,7 @@ class LoginController extends Controller
|
|||||||
$email = $request->old('email');
|
$email = $request->old('email');
|
||||||
$remember = $request->old('remember');
|
$remember = $request->old('remember');
|
||||||
|
|
||||||
return view('auth.login', compact('allowRegistration', 'email', 'remember'));
|
return view('auth.login', compact('allowRegistration', 'email', 'remember'))->withCookie($cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -22,6 +22,8 @@ use Illuminate\Support\Facades\Password;
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*
|
||||||
* Class PasswordController
|
* Class PasswordController
|
||||||
*
|
*
|
||||||
* @package FireflyIII\Http\Controllers\Auth
|
* @package FireflyIII\Http\Controllers\Auth
|
||||||
|
@@ -24,6 +24,8 @@ use Session;
|
|||||||
use Validator;
|
use Validator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*
|
||||||
* Class RegisterController
|
* Class RegisterController
|
||||||
*
|
*
|
||||||
* @package FireflyIII\Http\Controllers\Auth
|
* @package FireflyIII\Http\Controllers\Auth
|
||||||
@@ -122,12 +124,15 @@ class RegisterController extends Controller
|
|||||||
*/
|
*/
|
||||||
protected function create(array $data)
|
protected function create(array $data)
|
||||||
{
|
{
|
||||||
return User::create(
|
/** @var User $user */
|
||||||
|
$user = User::create(
|
||||||
[
|
[
|
||||||
'email' => $data['email'],
|
'email' => $data['email'],
|
||||||
'password' => bcrypt($data['password']),
|
'password' => bcrypt($data['password']),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -16,6 +16,8 @@ use FireflyIII\Http\Controllers\Controller;
|
|||||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*
|
||||||
* Class ResetPasswordController
|
* Class ResetPasswordController
|
||||||
*
|
*
|
||||||
* @package FireflyIII\Http\Controllers\Auth
|
* @package FireflyIII\Http\Controllers\Auth
|
||||||
|
@@ -41,11 +41,12 @@ class TwoFactorController extends Controller
|
|||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
|
||||||
// to make sure the validator in the next step gets the secret, we push it in session
|
// to make sure the validator in the next step gets the secret, we push it in session
|
||||||
$secret = Preferences::get('twoFactorAuthSecret', null)->data;
|
$secretPreference = Preferences::get('twoFactorAuthSecret', null);
|
||||||
|
$secret = is_null($secretPreference) ? null : $secretPreference->data;
|
||||||
$title = strval(trans('firefly.two_factor_title'));
|
$title = strval(trans('firefly.two_factor_title'));
|
||||||
|
|
||||||
// make sure the user has two factor configured:
|
// make sure the user has two factor configured:
|
||||||
$has2FA = Preferences::get('twoFactorAuthEnabled', null)->data;
|
$has2FA = Preferences::get('twoFactorAuthEnabled', false)->data;
|
||||||
if (is_null($has2FA) || $has2FA === false) {
|
if (is_null($has2FA) || $has2FA === false) {
|
||||||
return redirect(route('index'));
|
return redirect(route('index'));
|
||||||
}
|
}
|
||||||
@@ -79,9 +80,11 @@ class TwoFactorController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param TokenFormRequest $request
|
* @param TokenFormRequest $request
|
||||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter) // it's unused but the class does some validation.
|
* @param CookieJar $cookieJar
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
|
* @SuppressWarnings(PHPMD.UnusedFormalParameter) // it's unused but the class does some validation.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public function postIndex(TokenFormRequest $request, CookieJar $cookieJar)
|
public function postIndex(TokenFormRequest $request, CookieJar $cookieJar)
|
||||||
{
|
{
|
||||||
|
@@ -240,10 +240,11 @@ class BillController extends Controller
|
|||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
if (intval($request->get('create_another')) === 1) {
|
if (intval($request->get('create_another')) === 1) {
|
||||||
// set value so create routine will not overwrite URL:
|
// @codeCoverageIgnoreStart
|
||||||
$request->session()->put('bills.create.fromStore', true);
|
$request->session()->put('bills.create.fromStore', true);
|
||||||
|
|
||||||
return redirect(route('bills.create'))->withInput();
|
return redirect(route('bills.create'))->withInput();
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
// redirect to previous URL.
|
// redirect to previous URL.
|
||||||
@@ -267,10 +268,11 @@ class BillController extends Controller
|
|||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
if (intval($request->get('return_to_edit')) === 1) {
|
if (intval($request->get('return_to_edit')) === 1) {
|
||||||
// set value so edit routine will not overwrite URL:
|
// @codeCoverageIgnoreStart
|
||||||
$request->session()->put('bills.edit.fromUpdate', true);
|
$request->session()->put('bills.edit.fromUpdate', true);
|
||||||
|
|
||||||
return redirect(route('bills.edit', [$bill->id]))->withInput(['return_to_edit' => 1]);
|
return redirect(route('bills.edit', [$bill->id]))->withInput(['return_to_edit' => 1]);
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect($this->getPreviousUri('bills.edit.uri'));
|
return redirect($this->getPreviousUri('bills.edit.uri'));
|
||||||
|
@@ -22,11 +22,15 @@ use FireflyIII\Http\Requests\BudgetIncomeRequest;
|
|||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\Budget;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Log;
|
||||||
|
use Navigation;
|
||||||
use Preferences;
|
use Preferences;
|
||||||
use Response;
|
use Response;
|
||||||
use View;
|
use View;
|
||||||
@@ -105,6 +109,7 @@ class BudgetController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param Request $request
|
||||||
* @param Budget $budget
|
* @param Budget $budget
|
||||||
*
|
*
|
||||||
* @return View
|
* @return View
|
||||||
@@ -187,33 +192,87 @@ class BudgetController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
|
* @param JournalRepositoryInterface $repository
|
||||||
|
* @param string $moment
|
||||||
*
|
*
|
||||||
* @return View
|
* @return View
|
||||||
*/
|
*/
|
||||||
public function noBudget(Request $request)
|
public function noBudget(Request $request, JournalRepositoryInterface $repository, string $moment = '')
|
||||||
{
|
{
|
||||||
/** @var Carbon $start */
|
// default values:
|
||||||
$start = session('start', Carbon::now()->startOfMonth());
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
/** @var Carbon $end */
|
$start = null;
|
||||||
$end = session('end', Carbon::now()->endOfMonth());
|
$end = null;
|
||||||
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
$periods = new Collection;
|
||||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
|
||||||
|
// prep for "all" view.
|
||||||
|
if ($moment === 'all') {
|
||||||
|
$subTitle = trans('firefly.all_journals_without_budget');
|
||||||
|
$first = $repository->first();
|
||||||
|
$start = $first->date ?? new Carbon;
|
||||||
|
$end = new Carbon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prep for "specific date" view.
|
||||||
|
if (strlen($moment) > 0 && $moment !== 'all') {
|
||||||
|
$start = new Carbon($moment);
|
||||||
|
$end = Navigation::endOfPeriod($start, $range);
|
||||||
$subTitle = trans(
|
$subTitle = trans(
|
||||||
'firefly.without_budget_between',
|
'firefly.without_budget_between',
|
||||||
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||||
);
|
);
|
||||||
|
$periods = $this->getPeriodOverview();
|
||||||
|
}
|
||||||
|
|
||||||
// collector
|
// prep for current period
|
||||||
|
if (strlen($moment) === 0) {
|
||||||
|
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||||
|
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||||
|
$periods = $this->getPeriodOverview();
|
||||||
|
$subTitle = trans(
|
||||||
|
'firefly.without_budget_between',
|
||||||
|
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||||
|
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
$loop = 0;
|
||||||
|
// grab journals, but be prepared to jump a period back to get the right ones:
|
||||||
|
Log::info('Now at no-budget loop start.');
|
||||||
|
while ($count === 0 && $loop < 3) {
|
||||||
|
$loop++;
|
||||||
|
Log::info('Count is zero, search for journals.');
|
||||||
/** @var JournalCollectorInterface $collector */
|
/** @var JournalCollectorInterface $collector */
|
||||||
$collector = app(JournalCollectorInterface::class);
|
$collector = app(JournalCollectorInterface::class);
|
||||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutBudget();
|
$collector->setAllAssetAccounts()->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setLimit($pageSize)->setPage($page)
|
||||||
|
->withoutBudget()->withOpposingAccount();
|
||||||
$journals = $collector->getPaginatedJournals();
|
$journals = $collector->getPaginatedJournals();
|
||||||
$journals->setPath('/budgets/list/noBudget');
|
$journals->setPath('/budgets/list/no-budget');
|
||||||
|
$count = $journals->getCollection()->count();
|
||||||
|
if ($count === 0) {
|
||||||
|
$start->subDay();
|
||||||
|
$start = Navigation::startOfPeriod($start, $range);
|
||||||
|
$end = Navigation::endOfPeriod($start, $range);
|
||||||
|
Log::info(sprintf('Count is still zero, go back in time to "%s" and "%s"!', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return view('budgets.no-budget', compact('journals', 'subTitle'));
|
if ($moment != 'all' && $loop > 1) {
|
||||||
|
$subTitle = trans(
|
||||||
|
'firefly.without_budget_between',
|
||||||
|
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('budgets.no-budget', compact('journals', 'subTitle', 'moment', 'periods', 'start', 'end'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param BudgetIncomeRequest $request
|
||||||
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function postUpdateIncome(BudgetIncomeRequest $request)
|
public function postUpdateIncome(BudgetIncomeRequest $request)
|
||||||
@@ -252,7 +311,7 @@ class BudgetController extends Controller
|
|||||||
$journals->setPath('/budgets/show/' . $budget->id);
|
$journals->setPath('/budgets/show/' . $budget->id);
|
||||||
|
|
||||||
|
|
||||||
$subTitle = e($budget->name);
|
$subTitle = trans('firefly.all_journals_for_budget', ['name' => $budget->name]);
|
||||||
|
|
||||||
return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle'));
|
return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle'));
|
||||||
}
|
}
|
||||||
@@ -312,10 +371,11 @@ class BudgetController extends Controller
|
|||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
if (intval($request->get('create_another')) === 1) {
|
if (intval($request->get('create_another')) === 1) {
|
||||||
// set value so create routine will not overwrite URL:
|
// @codeCoverageIgnoreStart
|
||||||
$request->session()->put('budgets.create.fromStore', true);
|
$request->session()->put('budgets.create.fromStore', true);
|
||||||
|
|
||||||
return redirect(route('budgets.create'))->withInput();
|
return redirect(route('budgets.create'))->withInput();
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect($this->getPreviousUri('budgets.create.uri'));
|
return redirect($this->getPreviousUri('budgets.create.uri'));
|
||||||
@@ -336,10 +396,11 @@ class BudgetController extends Controller
|
|||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
if (intval($request->get('return_to_edit')) === 1) {
|
if (intval($request->get('return_to_edit')) === 1) {
|
||||||
// set value so edit routine will not overwrite URL:
|
// @codeCoverageIgnoreStart
|
||||||
$request->session()->put('budgets.edit.fromUpdate', true);
|
$request->session()->put('budgets.edit.fromUpdate', true);
|
||||||
|
|
||||||
return redirect(route('budgets.edit', [$budget->id]))->withInput(['return_to_edit' => 1]);
|
return redirect(route('budgets.edit', [$budget->id]))->withInput(['return_to_edit' => 1]);
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect($this->getPreviousUri('budgets.edit.uri'));
|
return redirect($this->getPreviousUri('budgets.edit.uri'));
|
||||||
@@ -402,7 +463,6 @@ class BudgetController extends Controller
|
|||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Budget $budget
|
* @param Budget $budget
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
@@ -420,7 +480,7 @@ class BudgetController extends Controller
|
|||||||
$cache->addProperty('get-limits');
|
$cache->addProperty('get-limits');
|
||||||
|
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return $cache->get();
|
return $cache->get(); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var AccountRepositoryInterface $accountRepository */
|
/** @var AccountRepositoryInterface $accountRepository */
|
||||||
@@ -439,4 +499,57 @@ class BudgetController extends Controller
|
|||||||
return $set;
|
return $set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
private function getPeriodOverview(): Collection
|
||||||
|
{
|
||||||
|
$repository = app(JournalRepositoryInterface::class);
|
||||||
|
$first = $repository->first();
|
||||||
|
$start = $first->date ?? new Carbon;
|
||||||
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
|
$start = Navigation::startOfPeriod($start, $range);
|
||||||
|
$end = Navigation::endOfX(new Carbon, $range);
|
||||||
|
$entries = new Collection;
|
||||||
|
|
||||||
|
// properties for cache
|
||||||
|
$cache = new CacheProperties;
|
||||||
|
$cache->addProperty($start);
|
||||||
|
$cache->addProperty($end);
|
||||||
|
$cache->addProperty('no-budget-period-entries');
|
||||||
|
|
||||||
|
if ($cache->has()) {
|
||||||
|
return $cache->get(); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug('Going to get period expenses and incomes.');
|
||||||
|
while ($end >= $start) {
|
||||||
|
$end = Navigation::startOfPeriod($end, $range);
|
||||||
|
$currentEnd = Navigation::endOfPeriod($end, $range);
|
||||||
|
|
||||||
|
// count journals without budget in this period:
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutBudget()->withOpposingAccount()->setTypes([TransactionType::WITHDRAWAL]);
|
||||||
|
$set = $collector->getJournals();
|
||||||
|
$sum = $set->sum('transaction_amount');
|
||||||
|
$journals = $set->count();
|
||||||
|
$dateStr = $end->format('Y-m-d');
|
||||||
|
$dateName = Navigation::periodShow($end, $range);
|
||||||
|
$entries->push(
|
||||||
|
[
|
||||||
|
'string' => $dateStr,
|
||||||
|
'name' => $dateName,
|
||||||
|
'count' => $journals,
|
||||||
|
'sum' => $sum,
|
||||||
|
'date' => clone $end,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$end = Navigation::subtractPeriod($end, $range, 1);
|
||||||
|
}
|
||||||
|
$cache->store($entries);
|
||||||
|
|
||||||
|
return $entries;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -15,16 +15,21 @@ namespace FireflyIII\Http\Controllers;
|
|||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
|
use FireflyIII\Helpers\Filter\InternalTransferFilter;
|
||||||
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\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Log;
|
||||||
use Navigation;
|
use Navigation;
|
||||||
use Preferences;
|
use Preferences;
|
||||||
|
use Steam;
|
||||||
use View;
|
use View;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -150,118 +155,171 @@ class CategoryController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param JournalRepositoryInterface $repository
|
||||||
|
* @param string $moment
|
||||||
|
*
|
||||||
* @return View
|
* @return View
|
||||||
*/
|
*/
|
||||||
public function noCategory()
|
public function noCategory(Request $request, JournalRepositoryInterface $repository, string $moment = '')
|
||||||
{
|
{
|
||||||
/** @var Carbon $start */
|
// default values:
|
||||||
$start = session('start', Carbon::now()->startOfMonth());
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
/** @var Carbon $end */
|
$start = null;
|
||||||
$end = session('end', Carbon::now()->startOfMonth());
|
$end = null;
|
||||||
|
$periods = new Collection;
|
||||||
|
|
||||||
// new collector:
|
// prep for "all" view.
|
||||||
/** @var JournalCollectorInterface $collector */
|
if ($moment === 'all') {
|
||||||
$collector = app(JournalCollectorInterface::class);
|
$subTitle = trans('firefly.all_journals_without_category');
|
||||||
$collector->setAllAssetAccounts()->setRange($start, $end)->withoutCategory();//->groupJournals();
|
$first = $repository->first();
|
||||||
$journals = $collector->getJournals();
|
$start = $first->date ?? new Carbon;
|
||||||
|
$end = new Carbon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prep for "specific date" view.
|
||||||
|
if (strlen($moment) > 0 && $moment !== 'all') {
|
||||||
|
$start = new Carbon($moment);
|
||||||
|
$end = Navigation::endOfPeriod($start, $range);
|
||||||
$subTitle = trans(
|
$subTitle = trans(
|
||||||
'firefly.without_category_between',
|
'firefly.without_category_between',
|
||||||
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||||
);
|
);
|
||||||
|
$periods = $this->getNoCategoryPeriodOverview();
|
||||||
return view('categories.no-category', compact('journals', 'subTitle'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// prep for current period
|
||||||
* @param Request $request
|
if (strlen($moment) === 0) {
|
||||||
* @param JournalCollectorInterface $collector
|
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||||
* @param Category $category
|
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||||
*
|
$periods = $this->getNoCategoryPeriodOverview();
|
||||||
* @return View
|
$subTitle = trans(
|
||||||
*/
|
'firefly.without_category_between',
|
||||||
public function show(Request $request, JournalCollectorInterface $collector, Category $category)
|
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||||
{
|
);
|
||||||
$range = Preferences::get('viewRange', '1M')->data;
|
}
|
||||||
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
|
|
||||||
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
|
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||||
$hideCategory = true; // used in list.
|
|
||||||
$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;
|
|
||||||
$subTitleIcon = 'fa-bar-chart';
|
|
||||||
$entries = $this->getGroupedEntries($category);
|
|
||||||
$method = 'default';
|
|
||||||
|
|
||||||
// get journals
|
$count = 0;
|
||||||
$collector->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category)->withBudgetInformation();
|
$loop = 0;
|
||||||
|
// grab journals, but be prepared to jump a period back to get the right ones:
|
||||||
|
Log::info('Now at no-cat loop start.');
|
||||||
|
while ($count === 0 && $loop < 3) {
|
||||||
|
$loop++;
|
||||||
|
Log::info('Count is zero, search for journals.');
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutCategory()->withOpposingAccount();
|
||||||
|
$collector->removeFilter(InternalTransferFilter::class);
|
||||||
$journals = $collector->getPaginatedJournals();
|
$journals = $collector->getPaginatedJournals();
|
||||||
$journals->setPath('categories/show/' . $category->id);
|
$journals->setPath('/categories/list/no-category');
|
||||||
|
$count = $journals->getCollection()->count();
|
||||||
|
if ($count === 0) {
|
||||||
|
$start->subDay();
|
||||||
|
$start = Navigation::startOfPeriod($start, $range);
|
||||||
|
$end = Navigation::endOfPeriod($start, $range);
|
||||||
|
Log::info(sprintf('Count is still zero, go back in time to "%s" and "%s"!', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($moment != 'all' && $loop > 1) {
|
||||||
|
$subTitle = trans(
|
||||||
|
'firefly.without_category_between',
|
||||||
|
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return view('categories.show', compact('category', 'method', 'journals', 'entries', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end'));
|
return view('categories.no-category', compact('journals', 'subTitle', 'moment', 'periods', 'start', 'end'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param CategoryRepositoryInterface $repository
|
* @param CategoryRepositoryInterface $repository
|
||||||
* @param Category $category
|
* @param Category $category
|
||||||
|
* @param string $moment
|
||||||
*
|
*
|
||||||
* @return View
|
* @return View
|
||||||
*/
|
*/
|
||||||
public function showAll(Request $request, CategoryRepositoryInterface $repository, Category $category)
|
public function show(Request $request, CategoryRepositoryInterface $repository, Category $category, string $moment = '')
|
||||||
{
|
{
|
||||||
|
// default values:
|
||||||
|
$subTitle = $category->name;
|
||||||
|
$subTitleIcon = 'fa-bar-chart';
|
||||||
|
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||||
|
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||||
|
$count = 0;
|
||||||
|
$loop = 0;
|
||||||
$range = Preferences::get('viewRange', '1M')->data;
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
|
$start = null;
|
||||||
|
$end = null;
|
||||||
|
$periods = new Collection;
|
||||||
|
|
||||||
|
|
||||||
|
// prep for "all" view.
|
||||||
|
if ($moment === 'all') {
|
||||||
|
$subTitle = trans('firefly.all_journals_for_category', ['name' => $category->name]);
|
||||||
$start = $repository->firstUseDate($category);
|
$start = $repository->firstUseDate($category);
|
||||||
if ($start->year == 1900) {
|
$end = new Carbon;
|
||||||
$start = new Carbon;
|
|
||||||
}
|
}
|
||||||
$end = Navigation::endOfPeriod(new Carbon, $range);
|
|
||||||
$subTitle = $category->name;
|
|
||||||
$subTitleIcon = 'fa-bar-chart';
|
|
||||||
$hideCategory = true; // used in list.
|
|
||||||
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
|
|
||||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
|
||||||
$method = 'all';
|
|
||||||
|
|
||||||
|
// prep for "specific date" view.
|
||||||
|
if (strlen($moment) > 0 && $moment !== 'all') {
|
||||||
|
$start = new Carbon($moment);
|
||||||
|
$end = Navigation::endOfPeriod($start, $range);
|
||||||
|
$subTitle = trans(
|
||||||
|
'firefly.journals_in_period_for_category',
|
||||||
|
['name' => $category->name,
|
||||||
|
'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||||
|
);
|
||||||
|
$periods = $this->getPeriodOverview($category);
|
||||||
|
}
|
||||||
|
|
||||||
|
// prep for current period
|
||||||
|
if (strlen($moment) === 0) {
|
||||||
|
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||||
|
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||||
|
$periods = $this->getPeriodOverview($category);
|
||||||
|
$subTitle = trans(
|
||||||
|
'firefly.journals_in_period_for_category',
|
||||||
|
['name' => $category->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
|
||||||
|
'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// grab journals, but be prepared to jump a period back to get the right ones:
|
||||||
|
Log::info('Now at category loop start.');
|
||||||
|
while ($count === 0 && $loop < 3) {
|
||||||
|
$loop++;
|
||||||
|
Log::info('Count is zero, search for journals.');
|
||||||
/** @var JournalCollectorInterface $collector */
|
/** @var JournalCollectorInterface $collector */
|
||||||
$collector = app(JournalCollectorInterface::class);
|
$collector = app(JournalCollectorInterface::class);
|
||||||
$collector->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setCategory($category)->withBudgetInformation();
|
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withOpposingAccount()
|
||||||
|
->setCategory($category)->withBudgetInformation()->withCategoryInformation();
|
||||||
|
$collector->removeFilter(InternalTransferFilter::class);
|
||||||
$journals = $collector->getPaginatedJournals();
|
$journals = $collector->getPaginatedJournals();
|
||||||
$journals->setPath('categories/show/' . $category->id . '/all');
|
$journals->setPath('categories/show/' . $category->id);
|
||||||
|
$count = $journals->getCollection()->count();
|
||||||
return view('categories.show', compact('category', 'method', 'journals', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end'));
|
if ($count === 0) {
|
||||||
|
$start->subDay();
|
||||||
|
$start = Navigation::startOfPeriod($start, $range);
|
||||||
|
$end = Navigation::endOfPeriod($start, $range);
|
||||||
|
Log::info(sprintf('Count is still zero, go back in time to "%s" and "%s"!', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if ($moment != 'all' && $loop > 1) {
|
||||||
* @param Request $request
|
$subTitle = trans(
|
||||||
* @param Category $category
|
'firefly.journals_in_period_for_category',
|
||||||
* @param string $date
|
['name' => $category->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
|
||||||
*
|
'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||||
* @return View
|
);
|
||||||
*/
|
|
||||||
public function showByDate(Request $request, Category $category, string $date)
|
|
||||||
{
|
|
||||||
$carbon = new Carbon($date);
|
|
||||||
$range = Preferences::get('viewRange', '1M')->data;
|
|
||||||
$start = Navigation::startOfPeriod($carbon, $range);
|
|
||||||
$end = Navigation::endOfPeriod($carbon, $range);
|
|
||||||
$subTitle = $category->name;
|
|
||||||
$subTitleIcon = 'fa-bar-chart';
|
|
||||||
$hideCategory = true; // used in list.
|
|
||||||
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
|
|
||||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
|
||||||
$entries = $this->getGroupedEntries($category);
|
|
||||||
$method = 'date';
|
|
||||||
|
|
||||||
/** @var JournalCollectorInterface $collector */
|
|
||||||
$collector = app(JournalCollectorInterface::class);
|
|
||||||
$collector->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category)->withBudgetInformation();
|
|
||||||
$journals = $collector->getPaginatedJournals();
|
|
||||||
$journals->setPath('categories/show/' . $category->id . '/' . $date);
|
|
||||||
|
|
||||||
return view('categories.show', compact('category', 'method', 'entries', 'journals', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return view('categories.show', compact('category', 'moment', 'journals', 'periods', 'subTitle', 'subTitleIcon', 'start', 'end'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param CategoryFormRequest $request
|
* @param CategoryFormRequest $request
|
||||||
* @param CategoryRepositoryInterface $repository
|
* @param CategoryRepositoryInterface $repository
|
||||||
@@ -277,9 +335,11 @@ class CategoryController extends Controller
|
|||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
if (intval($request->get('create_another')) === 1) {
|
if (intval($request->get('create_another')) === 1) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
$request->session()->put('categories.create.fromStore', true);
|
$request->session()->put('categories.create.fromStore', true);
|
||||||
|
|
||||||
return redirect(route('categories.create'))->withInput();
|
return redirect(route('categories.create'))->withInput();
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect(route('categories.index'));
|
return redirect(route('categories.index'));
|
||||||
@@ -302,20 +362,100 @@ class CategoryController extends Controller
|
|||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
if (intval($request->get('return_to_edit')) === 1) {
|
if (intval($request->get('return_to_edit')) === 1) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
$request->session()->put('categories.edit.fromUpdate', true);
|
$request->session()->put('categories.edit.fromUpdate', true);
|
||||||
|
|
||||||
return redirect(route('categories.edit', [$category->id]));
|
return redirect(route('categories.edit', [$category->id]));
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect($this->getPreviousUri('categories.edit.uri'));
|
return redirect($this->getPreviousUri('categories.edit.uri'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
private function getNoCategoryPeriodOverview(): Collection
|
||||||
|
{
|
||||||
|
$repository = app(JournalRepositoryInterface::class);
|
||||||
|
$first = $repository->first();
|
||||||
|
$start = $first->date ?? new Carbon;
|
||||||
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
|
$start = Navigation::startOfPeriod($start, $range);
|
||||||
|
$end = Navigation::endOfX(new Carbon, $range);
|
||||||
|
$entries = new Collection;
|
||||||
|
|
||||||
|
// properties for cache
|
||||||
|
$cache = new CacheProperties;
|
||||||
|
$cache->addProperty($start);
|
||||||
|
$cache->addProperty($end);
|
||||||
|
$cache->addProperty('no-budget-period-entries');
|
||||||
|
|
||||||
|
if ($cache->has()) {
|
||||||
|
return $cache->get(); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug(sprintf('Going to get period expenses and incomes between %s and %s.', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||||
|
while ($end >= $start) {
|
||||||
|
Log::debug('Loop!');
|
||||||
|
$end = Navigation::startOfPeriod($end, $range);
|
||||||
|
$currentEnd = Navigation::endOfPeriod($end, $range);
|
||||||
|
|
||||||
|
// count journals without budget in this period:
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutCategory()
|
||||||
|
->withOpposingAccount();
|
||||||
|
$collector->removeFilter(InternalTransferFilter::class);
|
||||||
|
$count = $collector->getJournals()->count();
|
||||||
|
|
||||||
|
// amount transferred
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutCategory()
|
||||||
|
->withOpposingAccount()->setTypes([TransactionType::TRANSFER]);
|
||||||
|
$collector->removeFilter(InternalTransferFilter::class);
|
||||||
|
$transferred = Steam::positive($collector->getJournals()->sum('transaction_amount'));
|
||||||
|
|
||||||
|
// amount spent
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutCategory()->withOpposingAccount()->setTypes([TransactionType::WITHDRAWAL]);
|
||||||
|
$spent = $collector->getJournals()->sum('transaction_amount');
|
||||||
|
|
||||||
|
// amount earned
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutCategory()->withOpposingAccount()->setTypes([TransactionType::DEPOSIT]);
|
||||||
|
$earned = $collector->getJournals()->sum('transaction_amount');
|
||||||
|
|
||||||
|
$dateStr = $end->format('Y-m-d');
|
||||||
|
$dateName = Navigation::periodShow($end, $range);
|
||||||
|
$entries->push(
|
||||||
|
[
|
||||||
|
'string' => $dateStr,
|
||||||
|
'name' => $dateName,
|
||||||
|
'count' => $count,
|
||||||
|
'spent' => $spent,
|
||||||
|
'earned' => $earned,
|
||||||
|
'transferred' => $transferred,
|
||||||
|
'date' => clone $end,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$end = Navigation::subtractPeriod($end, $range, 1);
|
||||||
|
}
|
||||||
|
Log::debug('End of loops');
|
||||||
|
$cache->store($entries);
|
||||||
|
|
||||||
|
return $entries;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Category $category
|
* @param Category $category
|
||||||
*
|
*
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
private function getGroupedEntries(Category $category): Collection
|
private function getPeriodOverview(Category $category): Collection
|
||||||
{
|
{
|
||||||
/** @var CategoryRepositoryInterface $repository */
|
/** @var CategoryRepositoryInterface $repository */
|
||||||
$repository = app(CategoryRepositoryInterface::class);
|
$repository = app(CategoryRepositoryInterface::class);
|
||||||
@@ -339,7 +479,7 @@ class CategoryController extends Controller
|
|||||||
$cache->addProperty($category->id);
|
$cache->addProperty($category->id);
|
||||||
|
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return $cache->get();
|
return $cache->get(); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
while ($end >= $first) {
|
while ($end >= $first) {
|
||||||
$end = Navigation::startOfPeriod($end, $range);
|
$end = Navigation::startOfPeriod($end, $range);
|
||||||
@@ -348,7 +488,26 @@ class CategoryController extends Controller
|
|||||||
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $end, $currentEnd);
|
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $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, clone $end]);
|
|
||||||
|
// amount transferred
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->setCategory($category)
|
||||||
|
->withOpposingAccount()->setTypes([TransactionType::TRANSFER]);
|
||||||
|
$collector->removeFilter(InternalTransferFilter::class);
|
||||||
|
$transferred = Steam::positive($collector->getJournals()->sum('transaction_amount'));
|
||||||
|
|
||||||
|
$entries->push(
|
||||||
|
[
|
||||||
|
'string' => $dateStr,
|
||||||
|
'name' => $dateName,
|
||||||
|
'spent' => $spent,
|
||||||
|
'earned' => $earned,
|
||||||
|
'sum' => bcadd($earned, $spent),
|
||||||
|
'transferred' => $transferred,
|
||||||
|
'date' => clone $end,
|
||||||
|
]
|
||||||
|
);
|
||||||
$end = Navigation::subtractPeriod($end, $range, 1);
|
$end = Navigation::subtractPeriod($end, $range, 1);
|
||||||
}
|
}
|
||||||
$cache->store($entries);
|
$cache->store($entries);
|
||||||
|
@@ -26,6 +26,7 @@ use FireflyIII\Models\TransactionType;
|
|||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Log;
|
use Log;
|
||||||
@@ -61,15 +62,12 @@ class AccountController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function all(Account $account)
|
public function all(Account $account)
|
||||||
{
|
{
|
||||||
$cache = new CacheProperties();
|
$cache = new CacheProperties;
|
||||||
$cache->addProperty('chart.account.all');
|
$cache->addProperty('chart.account.all');
|
||||||
$cache->addProperty($account->id);
|
$cache->addProperty($account->id);
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
Log::debug('Return chart.account.all from cache.');
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
|
|
||||||
return Response::json($cache->get());
|
|
||||||
}
|
}
|
||||||
Log::debug('Regenerate chart.account.all from scratch.');
|
|
||||||
|
|
||||||
/** @var AccountRepositoryInterface $repository */
|
/** @var AccountRepositoryInterface $repository */
|
||||||
$repository = app(AccountRepositoryInterface::class);
|
$repository = app(AccountRepositoryInterface::class);
|
||||||
@@ -112,7 +110,7 @@ class AccountController extends Controller
|
|||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
$cache->addProperty('chart.account.expense-accounts');
|
$cache->addProperty('chart.account.expense-accounts');
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
$start->subDay();
|
$start->subDay();
|
||||||
|
|
||||||
@@ -139,14 +137,13 @@ class AccountController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param JournalCollectorInterface $collector
|
|
||||||
* @param Account $account
|
* @param Account $account
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
* @param Carbon $end
|
* @param Carbon $end
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*/
|
*/
|
||||||
public function expenseBudget(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
|
public function expenseBudget(Account $account, Carbon $start, Carbon $end)
|
||||||
{
|
{
|
||||||
$cache = new CacheProperties;
|
$cache = new CacheProperties;
|
||||||
$cache->addProperty($account->id);
|
$cache->addProperty($account->id);
|
||||||
@@ -154,12 +151,10 @@ class AccountController extends Controller
|
|||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
$cache->addProperty('chart.account.expense-budget');
|
$cache->addProperty('chart.account.expense-budget');
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
$collector->setAccounts(new Collection([$account]))
|
$collector = app(JournalCollectorInterface::class);
|
||||||
->setRange($start, $end)
|
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withBudgetInformation()->setTypes([TransactionType::WITHDRAWAL]);
|
||||||
->withBudgetInformation()
|
|
||||||
->setTypes([TransactionType::WITHDRAWAL]);
|
|
||||||
$transactions = $collector->getJournals();
|
$transactions = $collector->getJournals();
|
||||||
$chartData = [];
|
$chartData = [];
|
||||||
$result = [];
|
$result = [];
|
||||||
@@ -185,14 +180,27 @@ class AccountController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param JournalCollectorInterface $collector
|
* @param AccountRepositoryInterface $repository
|
||||||
|
* @param Account $account
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function expenseBudgetAll(AccountRepositoryInterface $repository, Account $account)
|
||||||
|
{
|
||||||
|
$start = $repository->oldestJournalDate($account);
|
||||||
|
$end = Carbon::now();
|
||||||
|
|
||||||
|
return $this->expenseBudget($account, $start, $end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* @param Account $account
|
* @param Account $account
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
* @param Carbon $end
|
* @param Carbon $end
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*/
|
*/
|
||||||
public function expenseCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
|
public function expenseCategory(Account $account, Carbon $start, Carbon $end)
|
||||||
{
|
{
|
||||||
$cache = new CacheProperties;
|
$cache = new CacheProperties;
|
||||||
$cache->addProperty($account->id);
|
$cache->addProperty($account->id);
|
||||||
@@ -200,9 +208,10 @@ class AccountController extends Controller
|
|||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
$cache->addProperty('chart.account.expense-category');
|
$cache->addProperty('chart.account.expense-category');
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::WITHDRAWAL]);
|
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::WITHDRAWAL]);
|
||||||
$transactions = $collector->getJournals();
|
$transactions = $collector->getJournals();
|
||||||
$result = [];
|
$result = [];
|
||||||
@@ -228,6 +237,20 @@ class AccountController extends Controller
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AccountRepositoryInterface $repository
|
||||||
|
* @param Account $account
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function expenseCategoryAll(AccountRepositoryInterface $repository, Account $account)
|
||||||
|
{
|
||||||
|
$start = $repository->oldestJournalDate($account);
|
||||||
|
$end = Carbon::now();
|
||||||
|
|
||||||
|
return $this->expenseCategory($account, $start, $end);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the balances for all the user's frontpage accounts.
|
* Shows the balances for all the user's frontpage accounts.
|
||||||
*
|
*
|
||||||
@@ -254,14 +277,13 @@ class AccountController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param JournalCollectorInterface $collector
|
|
||||||
* @param Account $account
|
* @param Account $account
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
* @param Carbon $end
|
* @param Carbon $end
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*/
|
*/
|
||||||
public function incomeCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
|
public function incomeCategory(Account $account, Carbon $start, Carbon $end)
|
||||||
{
|
{
|
||||||
$cache = new CacheProperties;
|
$cache = new CacheProperties;
|
||||||
$cache->addProperty($account->id);
|
$cache->addProperty($account->id);
|
||||||
@@ -269,10 +291,11 @@ class AccountController extends Controller
|
|||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
$cache->addProperty('chart.account.income-category');
|
$cache->addProperty('chart.account.income-category');
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
// grab all journals:
|
// grab all journals:
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::DEPOSIT]);
|
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::DEPOSIT]);
|
||||||
$transactions = $collector->getJournals();
|
$transactions = $collector->getJournals();
|
||||||
$result = [];
|
$result = [];
|
||||||
@@ -298,20 +321,28 @@ class AccountController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param AccountRepositoryInterface $repository
|
||||||
* @param Account $account
|
* @param Account $account
|
||||||
* @param string $date
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function incomeCategoryAll(AccountRepositoryInterface $repository, Account $account)
|
||||||
|
{
|
||||||
|
$start = $repository->oldestJournalDate($account);
|
||||||
|
$end = Carbon::now();
|
||||||
|
|
||||||
|
return $this->incomeCategory($account, $start, $end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Account $account
|
||||||
|
* @param Carbon $start
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
* @throws FireflyException
|
* @throws FireflyException
|
||||||
*/
|
*/
|
||||||
public function period(Account $account, string $date)
|
public function period(Account $account, Carbon $start)
|
||||||
{
|
{
|
||||||
try {
|
|
||||||
$start = new Carbon($date);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
Log::error($e->getMessage());
|
|
||||||
throw new FireflyException('"' . e($date) . '" does not seem to be a valid date. Should be in the format YYYY-MM-DD');
|
|
||||||
}
|
|
||||||
$range = Preferences::get('viewRange', '1M')->data;
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
$end = Navigation::endOfPeriod($start, $range);
|
$end = Navigation::endOfPeriod($start, $range);
|
||||||
$cache = new CacheProperties();
|
$cache = new CacheProperties();
|
||||||
@@ -320,7 +351,7 @@ class AccountController extends Controller
|
|||||||
$cache->addProperty('chart.account.period');
|
$cache->addProperty('chart.account.period');
|
||||||
$cache->addProperty($account->id);
|
$cache->addProperty($account->id);
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
$format = (string)trans('config.month_and_day');
|
$format = (string)trans('config.month_and_day');
|
||||||
@@ -375,7 +406,7 @@ class AccountController extends Controller
|
|||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
$cache->addProperty('chart.account.revenue-accounts');
|
$cache->addProperty('chart.account.revenue-accounts');
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
$accounts = $repository->getAccountsByType([AccountType::REVENUE]);
|
$accounts = $repository->getAccountsByType([AccountType::REVENUE]);
|
||||||
|
|
||||||
@@ -421,7 +452,7 @@ class AccountController extends Controller
|
|||||||
$cache->addProperty('chart.account.single');
|
$cache->addProperty('chart.account.single');
|
||||||
$cache->addProperty($account->id);
|
$cache->addProperty($account->id);
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
$format = (string)trans('config.month_and_day');
|
$format = (string)trans('config.month_and_day');
|
||||||
@@ -461,16 +492,19 @@ class AccountController extends Controller
|
|||||||
$cache->addProperty('chart.account.account-balance-chart');
|
$cache->addProperty('chart.account.account-balance-chart');
|
||||||
$cache->addProperty($accounts);
|
$cache->addProperty($accounts);
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
Log::debug('Return chart.account.account-balance-chart from cache.');
|
return $cache->get(); // @codeCoverageIgnore
|
||||||
|
|
||||||
return $cache->get();
|
|
||||||
}
|
}
|
||||||
Log::debug('Regenerate chart.account.account-balance-chart from scratch.');
|
Log::debug('Regenerate chart.account.account-balance-chart from scratch.');
|
||||||
|
|
||||||
|
/** @var CurrencyRepositoryInterface $repository */
|
||||||
|
$repository = app(CurrencyRepositoryInterface::class);
|
||||||
|
|
||||||
$chartData = [];
|
$chartData = [];
|
||||||
foreach ($accounts as $account) {
|
foreach ($accounts as $account) {
|
||||||
|
$currency = $repository->find(intval($account->getMeta('currency_id')));
|
||||||
$currentSet = [
|
$currentSet = [
|
||||||
'label' => $account->name,
|
'label' => $account->name,
|
||||||
|
'currency_symbol' => $currency->symbol,
|
||||||
'entries' => [],
|
'entries' => [],
|
||||||
];
|
];
|
||||||
$currentStart = clone $start;
|
$currentStart = clone $start;
|
||||||
|
@@ -60,7 +60,7 @@ class BillController extends Controller
|
|||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
$cache->addProperty('chart.bill.frontpage');
|
$cache->addProperty('chart.bill.frontpage');
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
$paid = $repository->getBillsPaidInRange($start, $end); // will be a negative amount.
|
$paid = $repository->getBillsPaidInRange($start, $end); // will be a negative amount.
|
||||||
@@ -88,7 +88,7 @@ class BillController extends Controller
|
|||||||
$cache->addProperty('chart.bill.single');
|
$cache->addProperty('chart.bill.single');
|
||||||
$cache->addProperty($bill->id);
|
$cache->addProperty($bill->id);
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
$results = $collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->getJournals();
|
$results = $collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->getJournals();
|
||||||
|
@@ -18,11 +18,14 @@ use FireflyIII\Exceptions\FireflyException;
|
|||||||
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\Budget;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
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\Budget\BudgetRepositoryInterface;
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Navigation;
|
use Navigation;
|
||||||
@@ -80,7 +83,7 @@ class BudgetController extends Controller
|
|||||||
$cache->addProperty('chart.budget.budget');
|
$cache->addProperty('chart.budget.budget');
|
||||||
|
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
$final = clone $last;
|
$final = clone $last;
|
||||||
@@ -133,7 +136,7 @@ class BudgetController extends Controller
|
|||||||
$cache->addProperty($budgetLimit->id);
|
$cache->addProperty($budgetLimit->id);
|
||||||
|
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
$entries = [];
|
$entries = [];
|
||||||
@@ -153,6 +156,144 @@ class BudgetController extends Controller
|
|||||||
return Response::json($data);
|
return Response::json($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Budget $budget
|
||||||
|
* @param BudgetLimit|null $budgetLimit
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function expenseAsset(Budget $budget, BudgetLimit $budgetLimit = null)
|
||||||
|
{
|
||||||
|
$cache = new CacheProperties;
|
||||||
|
$cache->addProperty($budget->id);
|
||||||
|
$cache->addProperty($budgetLimit->id ?? 0);
|
||||||
|
$cache->addProperty('chart.budget.expense-asset');
|
||||||
|
if ($cache->has()) {
|
||||||
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAllAssetAccounts()->setTypes([TransactionType::WITHDRAWAL])->setBudget($budget);
|
||||||
|
if (!is_null($budgetLimit->id)) {
|
||||||
|
$collector->setRange($budgetLimit->start_date, $budgetLimit->end_date);
|
||||||
|
}
|
||||||
|
|
||||||
|
$transactions = $collector->getJournals();
|
||||||
|
$result = [];
|
||||||
|
$chartData = [];
|
||||||
|
/** @var Transaction $transaction */
|
||||||
|
foreach ($transactions as $transaction) {
|
||||||
|
$assetId = intval($transaction->account_id);
|
||||||
|
$result[$assetId] = $result[$assetId] ?? '0';
|
||||||
|
$result[$assetId] = bcadd($transaction->transaction_amount, $result[$assetId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$names = $this->getAccountNames(array_keys($result));
|
||||||
|
foreach ($result as $assetId => $amount) {
|
||||||
|
$chartData[$names[$assetId]] = $amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $this->generator->pieChart($chartData);
|
||||||
|
$cache->store($data);
|
||||||
|
|
||||||
|
return Response::json($data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Budget $budget
|
||||||
|
* @param BudgetLimit|null $budgetLimit
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function expenseCategory(Budget $budget, BudgetLimit $budgetLimit = null)
|
||||||
|
{
|
||||||
|
$cache = new CacheProperties;
|
||||||
|
$cache->addProperty($budget->id);
|
||||||
|
$cache->addProperty($budgetLimit->id ?? 0);
|
||||||
|
$cache->addProperty('chart.budget.expense-category');
|
||||||
|
if ($cache->has()) {
|
||||||
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAllAssetAccounts()->setTypes([TransactionType::WITHDRAWAL])->setBudget($budget)->withCategoryInformation();
|
||||||
|
if (!is_null($budgetLimit->id)) {
|
||||||
|
$collector->setRange($budgetLimit->start_date, $budgetLimit->end_date);
|
||||||
|
}
|
||||||
|
|
||||||
|
$transactions = $collector->getJournals();
|
||||||
|
$result = [];
|
||||||
|
$chartData = [];
|
||||||
|
/** @var Transaction $transaction */
|
||||||
|
foreach ($transactions as $transaction) {
|
||||||
|
$jrnlCatId = intval($transaction->transaction_journal_category_id);
|
||||||
|
$transCatId = intval($transaction->transaction_category_id);
|
||||||
|
$categoryId = max($jrnlCatId, $transCatId);
|
||||||
|
$result[$categoryId] = $result[$categoryId] ?? '0';
|
||||||
|
$result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$names = $this->getCategoryNames(array_keys($result));
|
||||||
|
foreach ($result as $categoryId => $amount) {
|
||||||
|
$chartData[$names[$categoryId]] = $amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $this->generator->pieChart($chartData);
|
||||||
|
$cache->store($data);
|
||||||
|
|
||||||
|
return Response::json($data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Budget $budget
|
||||||
|
* @param BudgetLimit|null $budgetLimit
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function expenseExpense(Budget $budget, BudgetLimit $budgetLimit = null)
|
||||||
|
{
|
||||||
|
$cache = new CacheProperties;
|
||||||
|
$cache->addProperty($budget->id);
|
||||||
|
$cache->addProperty($budgetLimit->id ?? 0);
|
||||||
|
$cache->addProperty('chart.budget.expense-expense');
|
||||||
|
if ($cache->has()) {
|
||||||
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAllAssetAccounts()->setTypes([TransactionType::WITHDRAWAL])->setBudget($budget)->withOpposingAccount();
|
||||||
|
if (!is_null($budgetLimit->id)) {
|
||||||
|
$collector->setRange($budgetLimit->start_date, $budgetLimit->end_date);
|
||||||
|
}
|
||||||
|
|
||||||
|
$transactions = $collector->getJournals();
|
||||||
|
$result = [];
|
||||||
|
$chartData = [];
|
||||||
|
/** @var Transaction $transaction */
|
||||||
|
foreach ($transactions as $transaction) {
|
||||||
|
$opposingId = intval($transaction->opposing_account_id);
|
||||||
|
$result[$opposingId] = $result[$opposingId] ?? '0';
|
||||||
|
$result[$opposingId] = bcadd($transaction->transaction_amount, $result[$opposingId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$names = $this->getAccountNames(array_keys($result));
|
||||||
|
foreach ($result as $opposingId => $amount) {
|
||||||
|
$name = $names[$opposingId] ?? 'no name';
|
||||||
|
$chartData[$name] = $amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $this->generator->pieChart($chartData);
|
||||||
|
$cache->store($data);
|
||||||
|
|
||||||
|
return Response::json($data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows a budget list with spent/left/overspent.
|
* Shows a budget list with spent/left/overspent.
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
|
||||||
@@ -170,7 +311,7 @@ class BudgetController extends Controller
|
|||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
$cache->addProperty('chart.budget.frontpage');
|
$cache->addProperty('chart.budget.frontpage');
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
$budgets = $this->repository->getActiveBudgets();
|
$budgets = $this->repository->getActiveBudgets();
|
||||||
$chartData = [
|
$chartData = [
|
||||||
@@ -227,7 +368,7 @@ class BudgetController extends Controller
|
|||||||
$cache->addProperty($budget->id);
|
$cache->addProperty($budget->id);
|
||||||
$cache->addProperty('chart.budget.period');
|
$cache->addProperty('chart.budget.period');
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
$periods = Navigation::listOfPeriods($start, $end);
|
$periods = Navigation::listOfPeriods($start, $end);
|
||||||
$entries = $this->repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end); // get the expenses
|
$entries = $this->repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end); // get the expenses
|
||||||
@@ -268,7 +409,7 @@ class BudgetController extends Controller
|
|||||||
$cache->addProperty($accounts);
|
$cache->addProperty($accounts);
|
||||||
$cache->addProperty('chart.budget.no-budget');
|
$cache->addProperty('chart.budget.no-budget');
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
// the expenses:
|
// the expenses:
|
||||||
@@ -288,6 +429,28 @@ class BudgetController extends Controller
|
|||||||
return Response::json($data);
|
return Response::json($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $accountIds
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getAccountNames(array $accountIds): array
|
||||||
|
{
|
||||||
|
/** @var AccountRepositoryInterface $repository */
|
||||||
|
$repository = app(AccountRepositoryInterface::class);
|
||||||
|
$accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT, AccountType::EXPENSE]);
|
||||||
|
$grouped = $accounts->groupBy('id')->toArray();
|
||||||
|
$return = [];
|
||||||
|
foreach ($accountIds as $accountId) {
|
||||||
|
if (isset($grouped[$accountId])) {
|
||||||
|
$return[$accountId] = $grouped[$accountId][0]['name'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$return[0] = '(no name)';
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Budget $budget
|
* @param Budget $budget
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
@@ -314,6 +477,30 @@ class BudgetController extends Controller
|
|||||||
return $budgeted;
|
return $budgeted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Small helper function for some of the charts.
|
||||||
|
*
|
||||||
|
* @param array $categoryIds
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getCategoryNames(array $categoryIds): array
|
||||||
|
{
|
||||||
|
/** @var CategoryRepositoryInterface $repository */
|
||||||
|
$repository = app(CategoryRepositoryInterface::class);
|
||||||
|
$categories = $repository->getCategories();
|
||||||
|
$grouped = $categories->groupBy('id')->toArray();
|
||||||
|
$return = [];
|
||||||
|
foreach ($categoryIds as $categoryId) {
|
||||||
|
if (isset($grouped[$categoryId])) {
|
||||||
|
$return[$categoryId] = $grouped[$categoryId][0]['name'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$return[0] = trans('firefly.noCategory');
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's 6 but ok.
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's 6 but ok.
|
||||||
@@ -386,9 +573,14 @@ class BudgetController extends Controller
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* amount: amount of budget limit
|
||||||
|
* left: amount of budget limit min spent, or 0 when < 0.
|
||||||
|
* spent: spent, or amount of budget limit when > amount
|
||||||
|
*/
|
||||||
$amount = $budgetLimit->amount;
|
$amount = $budgetLimit->amount;
|
||||||
$left = bccomp(bcadd($amount, $expenses), '0') < 1 ? '0' : bcadd($amount, $expenses);
|
$left = bccomp(bcadd($amount, $expenses), '0') < 1 ? '0' : bcadd($amount, $expenses);
|
||||||
$spent = $expenses;
|
$spent = bccomp($expenses, $amount) === 1 ? $expenses : bcmul($amount, '-1');
|
||||||
$overspent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcadd($amount, $expenses) : '0';
|
$overspent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcadd($amount, $expenses) : '0';
|
||||||
$return[$name] = [
|
$return[$name] = [
|
||||||
'left' => $left,
|
'left' => $left,
|
||||||
|
@@ -16,15 +16,16 @@ 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\Helpers\Chart\MetaPieChartInterface;
|
use FireflyIII\Helpers\Chart\MetaPieChartInterface;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
|
use FireflyIII\Helpers\Filter\OpposingAccountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\TransferFilter;
|
||||||
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\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\Budget\BudgetRepositoryInterface;
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -41,8 +42,6 @@ use Response;
|
|||||||
class BudgetReportController extends Controller
|
class BudgetReportController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
/** @var AccountRepositoryInterface */
|
|
||||||
private $accountRepository;
|
|
||||||
/** @var BudgetRepositoryInterface */
|
/** @var BudgetRepositoryInterface */
|
||||||
private $budgetRepository;
|
private $budgetRepository;
|
||||||
/** @var GeneratorInterface */
|
/** @var GeneratorInterface */
|
||||||
@@ -58,7 +57,6 @@ class BudgetReportController extends Controller
|
|||||||
function ($request, $next) {
|
function ($request, $next) {
|
||||||
$this->generator = app(GeneratorInterface::class);
|
$this->generator = app(GeneratorInterface::class);
|
||||||
$this->budgetRepository = app(BudgetRepositoryInterface::class);
|
$this->budgetRepository = app(BudgetRepositoryInterface::class);
|
||||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
@@ -131,10 +129,8 @@ class BudgetReportController extends Controller
|
|||||||
$cache->addProperty($start);
|
$cache->addProperty($start);
|
||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
/** @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 = [];
|
||||||
@@ -163,7 +159,7 @@ class BudgetReportController extends Controller
|
|||||||
'entries' => [],
|
'entries' => [],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$allBudgetLimits = $repository->getAllBudgetLimits($start, $end);
|
$allBudgetLimits = $this->budgetRepository->getAllBudgetLimits($start, $end);
|
||||||
$sumOfExpenses = [];
|
$sumOfExpenses = [];
|
||||||
$leftOfLimits = [];
|
$leftOfLimits = [];
|
||||||
while ($currentStart < $end) {
|
while ($currentStart < $end) {
|
||||||
@@ -203,6 +199,7 @@ class BudgetReportController extends Controller
|
|||||||
* Returns the budget limits belonging to the given budget and valid on the given day.
|
* Returns the budget limits belonging to the given budget and valid on the given day.
|
||||||
*
|
*
|
||||||
* @param Collection $budgetLimits
|
* @param Collection $budgetLimits
|
||||||
|
* @param Budget $budget
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
* @param Carbon $end
|
* @param Carbon $end
|
||||||
*
|
*
|
||||||
@@ -239,12 +236,15 @@ class BudgetReportController extends Controller
|
|||||||
/** @var JournalCollectorInterface $collector */
|
/** @var JournalCollectorInterface $collector */
|
||||||
$collector = app(JournalCollectorInterface::class);
|
$collector = app(JournalCollectorInterface::class);
|
||||||
$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();
|
||||||
$accountIds = $accounts->pluck('id')->toArray();
|
$collector->removeFilter(TransferFilter::class);
|
||||||
$transactions = $collector->getJournals();
|
|
||||||
$set = MonthReportGenerator::filterExpenses($transactions, $accountIds);
|
|
||||||
|
|
||||||
return $set;
|
$collector->addFilter(OpposingAccountFilter::class);
|
||||||
|
$collector->addFilter(PositiveAmountFilter::class);
|
||||||
|
|
||||||
|
$transactions = $collector->getJournals();
|
||||||
|
|
||||||
|
return $transactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -268,21 +268,4 @@ class BudgetReportController extends Controller
|
|||||||
return $grouped;
|
return $grouped;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Collection $set
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function groupByOpposingAccount(Collection $set): array
|
|
||||||
{
|
|
||||||
$grouped = [];
|
|
||||||
/** @var Transaction $transaction */
|
|
||||||
foreach ($set as $transaction) {
|
|
||||||
$accountId = $transaction->opposing_account_id;
|
|
||||||
$grouped[$accountId] = $grouped[$accountId] ?? '0';
|
|
||||||
$grouped[$accountId] = bcadd($transaction->transaction_amount, $grouped[$accountId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $grouped;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,7 @@ use FireflyIII\Http\Controllers\Controller;
|
|||||||
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\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Navigation;
|
use Navigation;
|
||||||
@@ -50,19 +50,19 @@ class CategoryController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Show an overview for a category for all time, per month/week/year.
|
* Show an overview for a category for all time, per month/week/year.
|
||||||
*
|
*
|
||||||
* @param CRI $repository
|
* @param CategoryRepositoryInterface $repository
|
||||||
* @param AccountRepositoryInterface $accountRepository
|
* @param AccountRepositoryInterface $accountRepository
|
||||||
* @param Category $category
|
* @param Category $category
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
*/
|
*/
|
||||||
public function all(CRI $repository, AccountRepositoryInterface $accountRepository, Category $category)
|
public function all(CategoryRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Category $category)
|
||||||
{
|
{
|
||||||
$cache = new CacheProperties;
|
$cache = new CacheProperties;
|
||||||
$cache->addProperty('chart.category.all');
|
$cache->addProperty('chart.category.all');
|
||||||
$cache->addProperty($category->id);
|
$cache->addProperty($category->id);
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
$start = $repository->firstUseDate($category);
|
$start = $repository->firstUseDate($category);
|
||||||
@@ -86,15 +86,23 @@ class CategoryController extends Controller
|
|||||||
'entries' => [],
|
'entries' => [],
|
||||||
'type' => 'bar',
|
'type' => 'bar',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'label' => strval(trans('firefly.sum')),
|
||||||
|
'entries' => [],
|
||||||
|
'type' => 'line',
|
||||||
|
'fill' => false,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
while ($start <= $end) {
|
while ($start <= $end) {
|
||||||
$currentEnd = Navigation::endOfPeriod($start, $range);
|
$currentEnd = Navigation::endOfPeriod($start, $range);
|
||||||
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $currentEnd);
|
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $currentEnd);
|
||||||
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $currentEnd);
|
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $currentEnd);
|
||||||
|
$sum = bcadd($spent, $earned);
|
||||||
$label = Navigation::periodShow($start, $range);
|
$label = Navigation::periodShow($start, $range);
|
||||||
$chartData[0]['entries'][$label] = bcmul($spent, '-1');
|
$chartData[0]['entries'][$label] = round(bcmul($spent, '-1'), 12);
|
||||||
$chartData[1]['entries'][$label] = $earned;
|
$chartData[1]['entries'][$label] = round($earned, 12);
|
||||||
|
$chartData[2]['entries'][$label] = round($sum, 12);
|
||||||
$start = Navigation::addPeriod($start, $range, 0);
|
$start = Navigation::addPeriod($start, $range, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,27 +114,12 @@ class CategoryController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param CRI $repository
|
* @param CategoryRepositoryInterface $repository
|
||||||
* @param Category $category
|
|
||||||
*
|
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
|
||||||
*/
|
|
||||||
public function currentPeriod(CRI $repository, Category $category)
|
|
||||||
{
|
|
||||||
$start = clone session('start', Carbon::now()->startOfMonth());
|
|
||||||
$end = session('end', Carbon::now()->endOfMonth());
|
|
||||||
$data = $this->makePeriodChart($repository, $category, $start, $end);
|
|
||||||
|
|
||||||
return Response::json($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param CRI $repository
|
|
||||||
* @param AccountRepositoryInterface $accountRepository
|
* @param AccountRepositoryInterface $accountRepository
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*/
|
*/
|
||||||
public function frontpage(CRI $repository, AccountRepositoryInterface $accountRepository)
|
public function frontpage(CategoryRepositoryInterface $repository, AccountRepositoryInterface $accountRepository)
|
||||||
{
|
{
|
||||||
$start = session('start', Carbon::now()->startOfMonth());
|
$start = session('start', Carbon::now()->startOfMonth());
|
||||||
$end = session('end', Carbon::now()->endOfMonth());
|
$end = session('end', Carbon::now()->endOfMonth());
|
||||||
@@ -136,7 +129,7 @@ class CategoryController extends Controller
|
|||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
$cache->addProperty('chart.category.frontpage');
|
$cache->addProperty('chart.category.frontpage');
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
$chartData = [];
|
$chartData = [];
|
||||||
$categories = $repository->getCategories();
|
$categories = $repository->getCategories();
|
||||||
@@ -161,7 +154,7 @@ class CategoryController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param CRI $repository
|
* @param CategoryRepositoryInterface $repository
|
||||||
* @param Category $category
|
* @param Category $category
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
@@ -169,7 +162,7 @@ class CategoryController extends Controller
|
|||||||
*
|
*
|
||||||
* @return \Illuminate\Http\JsonResponse|mixed
|
* @return \Illuminate\Http\JsonResponse|mixed
|
||||||
*/
|
*/
|
||||||
public function reportPeriod(CRI $repository, Category $category, Collection $accounts, Carbon $start, Carbon $end)
|
public function reportPeriod(CategoryRepositoryInterface $repository, Category $category, Collection $accounts, Carbon $start, Carbon $end)
|
||||||
{
|
{
|
||||||
$cache = new CacheProperties;
|
$cache = new CacheProperties;
|
||||||
$cache->addProperty($start);
|
$cache->addProperty($start);
|
||||||
@@ -178,7 +171,7 @@ class CategoryController extends Controller
|
|||||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||||
$cache->addProperty($category);
|
$cache->addProperty($category);
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return $cache->get();
|
return $cache->get(); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
$expenses = $repository->periodExpenses(new Collection([$category]), $accounts, $start, $end);
|
$expenses = $repository->periodExpenses(new Collection([$category]), $accounts, $start, $end);
|
||||||
$income = $repository->periodIncome(new Collection([$category]), $accounts, $start, $end);
|
$income = $repository->periodIncome(new Collection([$category]), $accounts, $start, $end);
|
||||||
@@ -194,13 +187,22 @@ class CategoryController extends Controller
|
|||||||
'entries' => [],
|
'entries' => [],
|
||||||
'type' => 'bar',
|
'type' => 'bar',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'label' => strval(trans('firefly.sum')),
|
||||||
|
'entries' => [],
|
||||||
|
'type' => 'line',
|
||||||
|
'fill' => false,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach (array_keys($periods) as $period) {
|
foreach (array_keys($periods) as $period) {
|
||||||
$label = $periods[$period];
|
$label = $periods[$period];
|
||||||
$spent = $expenses[$category->id]['entries'][$period] ?? '0';
|
$spent = $expenses[$category->id]['entries'][$period] ?? '0';
|
||||||
$chartData[0]['entries'][$label] = bcmul($spent, '-1');
|
$earned = $income[$category->id]['entries'][$period] ?? '0';
|
||||||
$chartData[1]['entries'][$label] = $income[$category->id]['entries'][$period] ?? '0';
|
$sum = bcadd($spent, $earned);
|
||||||
|
$chartData[0]['entries'][$label] = round(bcmul($spent, '-1'), 12);
|
||||||
|
$chartData[1]['entries'][$label] = round($earned, 12);
|
||||||
|
$chartData[2]['entries'][$label] = round($sum, 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $this->generator->multiSet($chartData);
|
$data = $this->generator->multiSet($chartData);
|
||||||
@@ -210,14 +212,14 @@ class CategoryController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param CRI $repository
|
* @param CategoryRepositoryInterface $repository
|
||||||
* @param Collection $accounts
|
* @param Collection $accounts
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
* @param Carbon $end
|
* @param Carbon $end
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\JsonResponse|mixed
|
* @return \Illuminate\Http\JsonResponse|mixed
|
||||||
*/
|
*/
|
||||||
public function reportPeriodNoCategory(CRI $repository, Collection $accounts, Carbon $start, Carbon $end)
|
public function reportPeriodNoCategory(CategoryRepositoryInterface $repository, Collection $accounts, Carbon $start, Carbon $end)
|
||||||
{
|
{
|
||||||
$cache = new CacheProperties;
|
$cache = new CacheProperties;
|
||||||
$cache->addProperty($start);
|
$cache->addProperty($start);
|
||||||
@@ -225,7 +227,7 @@ class CategoryController extends Controller
|
|||||||
$cache->addProperty('chart.category.period.no-cat');
|
$cache->addProperty('chart.category.period.no-cat');
|
||||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return $cache->get();
|
return $cache->get(); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
$expenses = $repository->periodExpensesNoCategory($accounts, $start, $end);
|
$expenses = $repository->periodExpensesNoCategory($accounts, $start, $end);
|
||||||
$income = $repository->periodIncomeNoCategory($accounts, $start, $end);
|
$income = $repository->periodIncomeNoCategory($accounts, $start, $end);
|
||||||
@@ -241,13 +243,22 @@ class CategoryController extends Controller
|
|||||||
'entries' => [],
|
'entries' => [],
|
||||||
'type' => 'bar',
|
'type' => 'bar',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'label' => strval(trans('firefly.sum')),
|
||||||
|
'entries' => [],
|
||||||
|
'type' => 'line',
|
||||||
|
'fill' => false,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach (array_keys($periods) as $period) {
|
foreach (array_keys($periods) as $period) {
|
||||||
$label = $periods[$period];
|
$label = $periods[$period];
|
||||||
$spent = $expenses['entries'][$period] ?? '0';
|
$spent = $expenses['entries'][$period] ?? '0';
|
||||||
|
$earned = $income['entries'][$period] ?? '0';
|
||||||
|
$sum = bcadd($spent, $earned);
|
||||||
$chartData[0]['entries'][$label] = bcmul($spent, '-1');
|
$chartData[0]['entries'][$label] = bcmul($spent, '-1');
|
||||||
$chartData[1]['entries'][$label] = $income['entries'][$period] ?? '0';
|
$chartData[1]['entries'][$label] = $earned;
|
||||||
|
$chartData[2]['entries'][$label] = $sum;
|
||||||
|
|
||||||
}
|
}
|
||||||
$data = $this->generator->multiSet($chartData);
|
$data = $this->generator->multiSet($chartData);
|
||||||
@@ -257,19 +268,18 @@ class CategoryController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param CRI $repository
|
* @param CategoryRepositoryInterface $repository
|
||||||
* @param Category $category
|
* @param Category $category
|
||||||
*
|
*
|
||||||
* @param $date
|
* @param $date
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
*/
|
*/
|
||||||
public function specificPeriod(CRI $repository, Category $category, $date)
|
public function specificPeriod(CategoryRepositoryInterface $repository, Category $category, 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($date, $range);
|
||||||
$end = Navigation::endOfPeriod($carbon, $range);
|
$end = Navigation::endOfPeriod($date, $range);
|
||||||
$data = $this->makePeriodChart($repository, $category, $start, $end);
|
$data = $this->makePeriodChart($repository, $category, $start, $end);
|
||||||
|
|
||||||
return Response::json($data);
|
return Response::json($data);
|
||||||
@@ -277,14 +287,14 @@ class CategoryController extends Controller
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param CRI $repository
|
* @param CategoryRepositoryInterface $repository
|
||||||
* @param Category $category
|
* @param Category $category
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
* @param Carbon $end
|
* @param Carbon $end
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function makePeriodChart(CRI $repository, Category $category, Carbon $start, Carbon $end)
|
private function makePeriodChart(CategoryRepositoryInterface $repository, Category $category, Carbon $start, Carbon $end)
|
||||||
{
|
{
|
||||||
$cache = new CacheProperties;
|
$cache = new CacheProperties;
|
||||||
$cache->addProperty($start);
|
$cache->addProperty($start);
|
||||||
@@ -297,7 +307,7 @@ class CategoryController extends Controller
|
|||||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||||
|
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return $cache->get();
|
return $cache->get(); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
// chart data
|
// chart data
|
||||||
@@ -312,15 +322,23 @@ class CategoryController extends Controller
|
|||||||
'entries' => [],
|
'entries' => [],
|
||||||
'type' => 'bar',
|
'type' => 'bar',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'label' => strval(trans('firefly.sum')),
|
||||||
|
'entries' => [],
|
||||||
|
'type' => 'line',
|
||||||
|
'fill' => false,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
while ($start <= $end) {
|
while ($start <= $end) {
|
||||||
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $start);
|
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $start);
|
||||||
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $start);
|
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $start);
|
||||||
$label = Navigation::periodShow($start, '1D');
|
$sum = bcadd($spent, $earned);
|
||||||
|
$label = trim(Navigation::periodShow($start, '1D'));
|
||||||
|
|
||||||
$chartData[0]['entries'][$label] = bcmul($spent, '-1');
|
$chartData[0]['entries'][$label] = round(bcmul($spent, '-1'),12);
|
||||||
$chartData[1]['entries'][$label] = $earned;
|
$chartData[1]['entries'][$label] = round($earned,12);
|
||||||
|
$chartData[2]['entries'][$label] = round($sum,12);
|
||||||
|
|
||||||
|
|
||||||
$start->addDay();
|
$start->addDay();
|
||||||
|
@@ -19,12 +19,14 @@ use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
|||||||
use FireflyIII\Generator\Report\Category\MonthReportGenerator;
|
use FireflyIII\Generator\Report\Category\MonthReportGenerator;
|
||||||
use FireflyIII\Helpers\Chart\MetaPieChartInterface;
|
use FireflyIII\Helpers\Chart\MetaPieChartInterface;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
|
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\OpposingAccountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\TransferFilter;
|
||||||
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;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
|
||||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Navigation;
|
use Navigation;
|
||||||
@@ -41,10 +43,6 @@ use Response;
|
|||||||
class CategoryReportController extends Controller
|
class CategoryReportController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
/** @var AccountRepositoryInterface */
|
|
||||||
private $accountRepository;
|
|
||||||
/** @var CategoryRepositoryInterface */
|
|
||||||
private $categoryRepository;
|
|
||||||
/** @var GeneratorInterface */
|
/** @var GeneratorInterface */
|
||||||
private $generator;
|
private $generator;
|
||||||
|
|
||||||
@@ -57,8 +55,6 @@ class CategoryReportController extends Controller
|
|||||||
$this->middleware(
|
$this->middleware(
|
||||||
function ($request, $next) {
|
function ($request, $next) {
|
||||||
$this->generator = app(GeneratorInterface::class);
|
$this->generator = app(GeneratorInterface::class);
|
||||||
$this->categoryRepository = app(CategoryRepositoryInterface::class);
|
|
||||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
@@ -78,11 +74,8 @@ class CategoryReportController extends Controller
|
|||||||
{
|
{
|
||||||
/** @var MetaPieChartInterface $helper */
|
/** @var MetaPieChartInterface $helper */
|
||||||
$helper = app(MetaPieChartInterface::class);
|
$helper = app(MetaPieChartInterface::class);
|
||||||
$helper->setAccounts($accounts);
|
$helper->setAccounts($accounts)->setCategories($categories)->setStart($start)->setEnd($end)->setCollectOtherObjects(intval($others) === 1);
|
||||||
$helper->setCategories($categories);
|
|
||||||
$helper->setStart($start);
|
|
||||||
$helper->setEnd($end);
|
|
||||||
$helper->setCollectOtherObjects(intval($others) === 1);
|
|
||||||
$chartData = $helper->generate('expense', 'account');
|
$chartData = $helper->generate('expense', 'account');
|
||||||
$data = $this->generator->pieChart($chartData);
|
$data = $this->generator->pieChart($chartData);
|
||||||
|
|
||||||
@@ -179,7 +172,7 @@ class CategoryReportController extends Controller
|
|||||||
$cache->addProperty($start);
|
$cache->addProperty($start);
|
||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
||||||
@@ -282,12 +275,15 @@ class CategoryReportController extends Controller
|
|||||||
/** @var JournalCollectorInterface $collector */
|
/** @var JournalCollectorInterface $collector */
|
||||||
$collector = app(JournalCollectorInterface::class);
|
$collector = app(JournalCollectorInterface::class);
|
||||||
$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();
|
||||||
$accountIds = $accounts->pluck('id')->toArray();
|
$collector->removeFilter(TransferFilter::class);
|
||||||
$transactions = $collector->getJournals();
|
|
||||||
$set = MonthReportGenerator::filterExpenses($transactions, $accountIds);
|
|
||||||
|
|
||||||
return $set;
|
$collector->addFilter(OpposingAccountFilter::class);
|
||||||
|
$collector->addFilter(PositiveAmountFilter::class);
|
||||||
|
|
||||||
|
$transactions = $collector->getJournals();
|
||||||
|
|
||||||
|
return $transactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -304,11 +300,13 @@ class CategoryReportController extends Controller
|
|||||||
$collector = app(JournalCollectorInterface::class);
|
$collector = app(JournalCollectorInterface::class);
|
||||||
$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();
|
|
||||||
$transactions = $collector->getJournals();
|
|
||||||
$set = MonthReportGenerator::filterIncome($transactions, $accountIds);
|
|
||||||
|
|
||||||
return $set;
|
$collector->addFilter(OpposingAccountFilter::class);
|
||||||
|
$collector->addFilter(NegativeAmountFilter::class);
|
||||||
|
|
||||||
|
$transactions = $collector->getJournals();
|
||||||
|
|
||||||
|
return $transactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -331,22 +329,4 @@ class CategoryReportController extends Controller
|
|||||||
|
|
||||||
return $grouped;
|
return $grouped;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Collection $set
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function groupByOpposingAccount(Collection $set): array
|
|
||||||
{
|
|
||||||
$grouped = [];
|
|
||||||
/** @var Transaction $transaction */
|
|
||||||
foreach ($set as $transaction) {
|
|
||||||
$accountId = $transaction->opposing_account_id;
|
|
||||||
$grouped[$accountId] = $grouped[$accountId] ?? '0';
|
|
||||||
$grouped[$accountId] = bcadd($transaction->transaction_amount, $grouped[$accountId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $grouped;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -58,7 +58,7 @@ class PiggyBankController extends Controller
|
|||||||
$cache->addProperty('chart.piggy-bank.history');
|
$cache->addProperty('chart.piggy-bank.history');
|
||||||
$cache->addProperty($piggyBank->id);
|
$cache->addProperty($piggyBank->id);
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
$set = $repository->getEvents($piggyBank);
|
$set = $repository->getEvents($piggyBank);
|
||||||
|
@@ -20,6 +20,7 @@ use FireflyIII\Http\Controllers\Controller;
|
|||||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Log;
|
||||||
use Navigation;
|
use Navigation;
|
||||||
use Response;
|
use Response;
|
||||||
use Steam;
|
use Steam;
|
||||||
@@ -64,7 +65,7 @@ class ReportController extends Controller
|
|||||||
$cache->addProperty($accounts);
|
$cache->addProperty($accounts);
|
||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
$ids = $accounts->pluck('id')->toArray();
|
$ids = $accounts->pluck('id')->toArray();
|
||||||
$current = clone $start;
|
$current = clone $start;
|
||||||
@@ -103,8 +104,9 @@ class ReportController extends Controller
|
|||||||
$cache->addProperty($accounts);
|
$cache->addProperty($accounts);
|
||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
//return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
Log::debug('Going to do operations for accounts ', $accounts->pluck('id')->toArray());
|
||||||
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
||||||
$source = $this->getChartData($accounts, $start, $end);
|
$source = $this->getChartData($accounts, $start, $end);
|
||||||
$chartData = [
|
$chartData = [
|
||||||
@@ -161,8 +163,10 @@ class ReportController extends Controller
|
|||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
$cache->addProperty($accounts);
|
$cache->addProperty($accounts);
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$source = $this->getChartData($accounts, $start, $end);
|
$source = $this->getChartData($accounts, $start, $end);
|
||||||
$numbers = [
|
$numbers = [
|
||||||
'sum_earned' => '0',
|
'sum_earned' => '0',
|
||||||
@@ -246,19 +250,41 @@ class ReportController extends Controller
|
|||||||
$cache->addProperty($accounts);
|
$cache->addProperty($accounts);
|
||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return $cache->get();
|
// return $cache->get(); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$tasker = app(AccountTaskerInterface::class);
|
|
||||||
$currentStart = clone $start;
|
$currentStart = clone $start;
|
||||||
$spentArray = [];
|
$spentArray = [];
|
||||||
$earnedArray = [];
|
$earnedArray = [];
|
||||||
|
|
||||||
|
/** @var AccountTaskerInterface $tasker */
|
||||||
|
$tasker = app(AccountTaskerInterface::class);
|
||||||
|
|
||||||
while ($currentStart <= $end) {
|
while ($currentStart <= $end) {
|
||||||
|
|
||||||
$currentEnd = Navigation::endOfPeriod($currentStart, '1M');
|
$currentEnd = Navigation::endOfPeriod($currentStart, '1M');
|
||||||
|
$earned = strval(
|
||||||
|
array_sum(
|
||||||
|
array_map(
|
||||||
|
function ($item) {
|
||||||
|
return $item['sum'];
|
||||||
|
}, $tasker->getIncomeReport($currentStart, $currentEnd, $accounts)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$spent = strval(
|
||||||
|
array_sum(
|
||||||
|
array_map(
|
||||||
|
function ($item) {
|
||||||
|
return $item['sum'];
|
||||||
|
}, $tasker->getExpenseReport($currentStart, $currentEnd, $accounts)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
$label = $currentStart->format('Y-m') . '-01';
|
$label = $currentStart->format('Y-m') . '-01';
|
||||||
$spent = $tasker->amountOutInPeriod($accounts, $accounts, $currentStart, $currentEnd);
|
|
||||||
$earned = $tasker->amountInInPeriod($accounts, $accounts, $currentStart, $currentEnd);
|
|
||||||
$spentArray[$label] = bcmul($spent, '-1');
|
$spentArray[$label] = bcmul($spent, '-1');
|
||||||
$earnedArray[$label] = $earned;
|
$earnedArray[$label] = $earned;
|
||||||
$currentStart = Navigation::addPeriod($currentStart, '1M', 0);
|
$currentStart = Navigation::addPeriod($currentStart, '1M', 0);
|
||||||
|
367
app/Http/Controllers/Chart/TagReportController.php
Normal file
367
app/Http/Controllers/Chart/TagReportController.php
Normal file
@@ -0,0 +1,367 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* TagReportController.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\Chart;
|
||||||
|
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||||
|
use FireflyIII\Helpers\Chart\MetaPieChartInterface;
|
||||||
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
|
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\OpposingAccountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
|
||||||
|
use FireflyIII\Helpers\Filter\TransferFilter;
|
||||||
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
|
use FireflyIII\Models\Tag;
|
||||||
|
use FireflyIII\Models\Transaction;
|
||||||
|
use FireflyIII\Models\TransactionType;
|
||||||
|
use FireflyIII\Support\CacheProperties;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Navigation;
|
||||||
|
use Response;
|
||||||
|
|
||||||
|
class TagReportController extends Controller
|
||||||
|
{
|
||||||
|
/** @var GeneratorInterface */
|
||||||
|
protected $generator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
// create chart generator:
|
||||||
|
$this->generator = app(GeneratorInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $accounts
|
||||||
|
* @param Collection $tags
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
* @param string $others
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function accountExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end, string $others)
|
||||||
|
{
|
||||||
|
/** @var MetaPieChartInterface $helper */
|
||||||
|
$helper = app(MetaPieChartInterface::class);
|
||||||
|
$helper->setAccounts($accounts);
|
||||||
|
$helper->setTags($tags);
|
||||||
|
$helper->setStart($start);
|
||||||
|
$helper->setEnd($end);
|
||||||
|
$helper->setCollectOtherObjects(intval($others) === 1);
|
||||||
|
$chartData = $helper->generate('expense', 'account');
|
||||||
|
$data = $this->generator->pieChart($chartData);
|
||||||
|
|
||||||
|
return Response::json($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $accounts
|
||||||
|
* @param Collection $tags
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
* @param string $others
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function accountIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end, string $others)
|
||||||
|
{
|
||||||
|
/** @var MetaPieChartInterface $helper */
|
||||||
|
$helper = app(MetaPieChartInterface::class);
|
||||||
|
$helper->setAccounts($accounts);
|
||||||
|
$helper->setTags($tags);
|
||||||
|
$helper->setStart($start);
|
||||||
|
$helper->setEnd($end);
|
||||||
|
$helper->setCollectOtherObjects(intval($others) === 1);
|
||||||
|
$chartData = $helper->generate('income', 'account');
|
||||||
|
$data = $this->generator->pieChart($chartData);
|
||||||
|
|
||||||
|
return Response::json($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $accounts
|
||||||
|
* @param Collection $tags
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function budgetExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end)
|
||||||
|
{
|
||||||
|
/** @var MetaPieChartInterface $helper */
|
||||||
|
$helper = app(MetaPieChartInterface::class);
|
||||||
|
$helper->setAccounts($accounts);
|
||||||
|
$helper->setTags($tags);
|
||||||
|
$helper->setStart($start);
|
||||||
|
$helper->setEnd($end);
|
||||||
|
$helper->setCollectOtherObjects(false);
|
||||||
|
$chartData = $helper->generate('expense', 'budget');
|
||||||
|
$data = $this->generator->pieChart($chartData);
|
||||||
|
|
||||||
|
return Response::json($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $accounts
|
||||||
|
* @param Collection $tags
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function categoryExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end)
|
||||||
|
{
|
||||||
|
/** @var MetaPieChartInterface $helper */
|
||||||
|
$helper = app(MetaPieChartInterface::class);
|
||||||
|
$helper->setAccounts($accounts);
|
||||||
|
$helper->setTags($tags);
|
||||||
|
$helper->setStart($start);
|
||||||
|
$helper->setEnd($end);
|
||||||
|
$helper->setCollectOtherObjects(false);
|
||||||
|
$chartData = $helper->generate('expense', 'category');
|
||||||
|
$data = $this->generator->pieChart($chartData);
|
||||||
|
|
||||||
|
return Response::json($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $accounts
|
||||||
|
* @param Collection $tags
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function mainChart(Collection $accounts, Collection $tags, Carbon $start, Carbon $end)
|
||||||
|
{
|
||||||
|
$cache = new CacheProperties;
|
||||||
|
$cache->addProperty('chart.category.report.main');
|
||||||
|
$cache->addProperty($accounts);
|
||||||
|
$cache->addProperty($tags);
|
||||||
|
$cache->addProperty($start);
|
||||||
|
$cache->addProperty($end);
|
||||||
|
if ($cache->has()) {
|
||||||
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
|
||||||
|
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
||||||
|
$function = Navigation::preferredEndOfPeriod($start, $end);
|
||||||
|
$chartData = [];
|
||||||
|
$currentStart = clone $start;
|
||||||
|
|
||||||
|
// prep chart data:
|
||||||
|
foreach ($tags as $tag) {
|
||||||
|
$chartData[$tag->id . '-in'] = [
|
||||||
|
'label' => $tag->tag . ' (' . strtolower(strval(trans('firefly.income'))) . ')',
|
||||||
|
'type' => 'bar',
|
||||||
|
'yAxisID' => 'y-axis-0',
|
||||||
|
'entries' => [],
|
||||||
|
];
|
||||||
|
$chartData[$tag->id . '-out'] = [
|
||||||
|
'label' => $tag->tag . ' (' . strtolower(strval(trans('firefly.expenses'))) . ')',
|
||||||
|
'type' => 'bar',
|
||||||
|
'yAxisID' => 'y-axis-0',
|
||||||
|
'entries' => [],
|
||||||
|
];
|
||||||
|
// total in, total out:
|
||||||
|
$chartData[$tag->id . '-total-in'] = [
|
||||||
|
'label' => $tag->tag . ' (' . strtolower(strval(trans('firefly.sum_of_income'))) . ')',
|
||||||
|
'type' => 'line',
|
||||||
|
'fill' => false,
|
||||||
|
'yAxisID' => 'y-axis-1',
|
||||||
|
'entries' => [],
|
||||||
|
];
|
||||||
|
$chartData[$tag->id . '-total-out'] = [
|
||||||
|
'label' => $tag->tag . ' (' . strtolower(strval(trans('firefly.sum_of_expenses'))) . ')',
|
||||||
|
'type' => 'line',
|
||||||
|
'fill' => false,
|
||||||
|
'yAxisID' => 'y-axis-1',
|
||||||
|
'entries' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$sumOfIncome = [];
|
||||||
|
$sumOfExpense = [];
|
||||||
|
|
||||||
|
while ($currentStart < $end) {
|
||||||
|
$currentEnd = clone $currentStart;
|
||||||
|
$currentEnd = $currentEnd->$function();
|
||||||
|
$expenses = $this->groupByTag($this->getExpenses($accounts, $tags, $currentStart, $currentEnd));
|
||||||
|
$income = $this->groupByTag($this->getIncome($accounts, $tags, $currentStart, $currentEnd));
|
||||||
|
$label = $currentStart->formatLocalized($format);
|
||||||
|
|
||||||
|
/** @var Tag $tag */
|
||||||
|
foreach ($tags as $tag) {
|
||||||
|
$labelIn = $tag->id . '-in';
|
||||||
|
$labelOut = $tag->id . '-out';
|
||||||
|
$labelSumIn = $tag->id . '-total-in';
|
||||||
|
$labelSumOut = $tag->id . '-total-out';
|
||||||
|
$currentIncome = $income[$tag->id] ?? '0';
|
||||||
|
$currentExpense = $expenses[$tag->id] ?? '0';
|
||||||
|
|
||||||
|
|
||||||
|
// add to sum:
|
||||||
|
$sumOfIncome[$tag->id] = $sumOfIncome[$tag->id] ?? '0';
|
||||||
|
$sumOfExpense[$tag->id] = $sumOfExpense[$tag->id] ?? '0';
|
||||||
|
$sumOfIncome[$tag->id] = bcadd($sumOfIncome[$tag->id], $currentIncome);
|
||||||
|
$sumOfExpense[$tag->id] = bcadd($sumOfExpense[$tag->id], $currentExpense);
|
||||||
|
|
||||||
|
// add to chart:
|
||||||
|
$chartData[$labelIn]['entries'][$label] = $currentIncome;
|
||||||
|
$chartData[$labelOut]['entries'][$label] = $currentExpense;
|
||||||
|
$chartData[$labelSumIn]['entries'][$label] = $sumOfIncome[$tag->id];
|
||||||
|
$chartData[$labelSumOut]['entries'][$label] = $sumOfExpense[$tag->id];
|
||||||
|
}
|
||||||
|
$currentStart = clone $currentEnd;
|
||||||
|
$currentStart->addDay();
|
||||||
|
}
|
||||||
|
// remove all empty entries to prevent cluttering:
|
||||||
|
$newSet = [];
|
||||||
|
foreach ($chartData as $key => $entry) {
|
||||||
|
if (!array_sum($entry['entries']) == 0) {
|
||||||
|
$newSet[$key] = $chartData[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count($newSet) === 0) {
|
||||||
|
$newSet = $chartData; // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
$data = $this->generator->multiSet($newSet);
|
||||||
|
$cache->store($data);
|
||||||
|
|
||||||
|
return Response::json($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $accounts
|
||||||
|
* @param Collection $tags
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
* @param string $others
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function tagExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end, string $others)
|
||||||
|
{
|
||||||
|
/** @var MetaPieChartInterface $helper */
|
||||||
|
$helper = app(MetaPieChartInterface::class);
|
||||||
|
$helper->setAccounts($accounts);
|
||||||
|
$helper->setTags($tags);
|
||||||
|
$helper->setStart($start);
|
||||||
|
$helper->setEnd($end);
|
||||||
|
$helper->setCollectOtherObjects(intval($others) === 1);
|
||||||
|
$chartData = $helper->generate('expense', 'tag');
|
||||||
|
$data = $this->generator->pieChart($chartData);
|
||||||
|
|
||||||
|
return Response::json($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $accounts
|
||||||
|
* @param Collection $tags
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
* @param string $others
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function tagIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end, string $others)
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var MetaPieChartInterface $helper */
|
||||||
|
$helper = app(MetaPieChartInterface::class);
|
||||||
|
$helper->setAccounts($accounts);
|
||||||
|
$helper->setTags($tags);
|
||||||
|
$helper->setStart($start);
|
||||||
|
$helper->setEnd($end);
|
||||||
|
$helper->setCollectOtherObjects(intval($others) === 1);
|
||||||
|
$chartData = $helper->generate('income', 'tag');
|
||||||
|
$data = $this->generator->pieChart($chartData);
|
||||||
|
|
||||||
|
return Response::json($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $accounts
|
||||||
|
* @param Collection $tags
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
private function getExpenses(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): Collection
|
||||||
|
{
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
|
||||||
|
->setTags($tags)->withOpposingAccount();
|
||||||
|
$collector->removeFilter(TransferFilter::class);
|
||||||
|
|
||||||
|
$collector->addFilter(OpposingAccountFilter::class);
|
||||||
|
$collector->addFilter(PositiveAmountFilter::class);
|
||||||
|
|
||||||
|
$transactions = $collector->getJournals();
|
||||||
|
|
||||||
|
return $transactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $accounts
|
||||||
|
* @param Collection $tags
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
private function getIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): Collection
|
||||||
|
{
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
|
||||||
|
->setTags($tags)->withOpposingAccount();
|
||||||
|
|
||||||
|
$collector->addFilter(OpposingAccountFilter::class);
|
||||||
|
$collector->addFilter(NegativeAmountFilter::class);
|
||||||
|
|
||||||
|
$transactions = $collector->getJournals();
|
||||||
|
|
||||||
|
return $transactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection $set
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function groupByTag(Collection $set): array
|
||||||
|
{
|
||||||
|
// group by category ID:
|
||||||
|
$grouped = [];
|
||||||
|
/** @var Transaction $transaction */
|
||||||
|
foreach ($set as $transaction) {
|
||||||
|
$journal = $transaction->transactionJournal;
|
||||||
|
$journalTags = $journal->tags;
|
||||||
|
/** @var Tag $journalTag */
|
||||||
|
foreach ($journalTags as $journalTag) {
|
||||||
|
$journalTagId = $journalTag->id;
|
||||||
|
$grouped[$journalTagId] = $grouped[$journalTagId] ?? '0';
|
||||||
|
$grouped[$journalTagId] = bcadd($transaction->transaction_amount, $grouped[$journalTagId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $grouped;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -100,7 +100,7 @@ class Controller extends BaseController
|
|||||||
*/
|
*/
|
||||||
protected function isOpeningBalance(TransactionJournal $journal): bool
|
protected function isOpeningBalance(TransactionJournal $journal): bool
|
||||||
{
|
{
|
||||||
return TransactionJournal::transactionTypeStr($journal) === TransactionType::OPENING_BALANCE;
|
return $journal->transactionTypeStr() === TransactionType::OPENING_BALANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,9 +120,11 @@ class Controller extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
Session::flash('error', strval(trans('firefly.cannot_redirect_to_account')));
|
Session::flash('error', strval(trans('firefly.cannot_redirect_to_account')));
|
||||||
|
|
||||||
return redirect(route('index'));
|
return redirect(route('index'));
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -17,6 +17,7 @@ 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 FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Log;
|
use Log;
|
||||||
use Preferences;
|
use Preferences;
|
||||||
@@ -30,6 +31,11 @@ use View;
|
|||||||
class CurrencyController extends Controller
|
class CurrencyController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/** @var CurrencyRepositoryInterface */
|
||||||
|
protected $repository;
|
||||||
|
|
||||||
|
/** @var UserRepositoryInterface */
|
||||||
|
protected $userRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -43,6 +49,8 @@ class CurrencyController extends Controller
|
|||||||
function ($request, $next) {
|
function ($request, $next) {
|
||||||
View::share('title', trans('firefly.currencies'));
|
View::share('title', trans('firefly.currencies'));
|
||||||
View::share('mainTitleIcon', 'fa-usd');
|
View::share('mainTitleIcon', 'fa-usd');
|
||||||
|
$this->repository = app(CurrencyRepositoryInterface::class);
|
||||||
|
$this->userRepository = app(UserRepositoryInterface::class);
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
@@ -50,10 +58,18 @@ class CurrencyController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return View
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||||
*/
|
*/
|
||||||
public function create(Request $request)
|
public function create(Request $request)
|
||||||
{
|
{
|
||||||
|
if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
|
||||||
|
$request->session()->flash('error', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
|
||||||
|
|
||||||
|
return redirect(route('currencies.index'));
|
||||||
|
}
|
||||||
|
|
||||||
$subTitleIcon = 'fa-plus';
|
$subTitleIcon = 'fa-plus';
|
||||||
$subTitle = trans('firefly.create_currency');
|
$subTitle = trans('firefly.create_currency');
|
||||||
|
|
||||||
@@ -90,14 +106,22 @@ class CurrencyController extends Controller
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param CurrencyRepositoryInterface $repository
|
* @param Request $request
|
||||||
* @param TransactionCurrency $currency
|
* @param TransactionCurrency $currency
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||||
*/
|
*/
|
||||||
public function delete(Request $request, CurrencyRepositoryInterface $repository, TransactionCurrency $currency)
|
public function delete(Request $request, TransactionCurrency $currency)
|
||||||
{
|
{
|
||||||
if (!$repository->canDeleteCurrency($currency)) {
|
if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
$request->session()->flash('error', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
|
||||||
|
|
||||||
|
return redirect(route('currencies.index'));
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->repository->canDeleteCurrency($currency)) {
|
||||||
$request->session()->flash('error', trans('firefly.cannot_delete_currency', ['name' => $currency->name]));
|
$request->session()->flash('error', trans('firefly.cannot_delete_currency', ['name' => $currency->name]));
|
||||||
|
|
||||||
return redirect(route('currencies.index'));
|
return redirect(route('currencies.index'));
|
||||||
@@ -115,20 +139,28 @@ class CurrencyController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param CurrencyRepositoryInterface $repository
|
* @param Request $request
|
||||||
* @param TransactionCurrency $currency
|
* @param TransactionCurrency $currency
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
*/
|
*/
|
||||||
public function destroy(Request $request, CurrencyRepositoryInterface $repository, TransactionCurrency $currency)
|
public function destroy(Request $request, TransactionCurrency $currency)
|
||||||
{
|
{
|
||||||
if (!$repository->canDeleteCurrency($currency)) {
|
if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
$request->session()->flash('error', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
|
||||||
|
|
||||||
|
return redirect(route('currencies.index'));
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->repository->canDeleteCurrency($currency)) {
|
||||||
$request->session()->flash('error', trans('firefly.cannot_delete_currency', ['name' => $currency->name]));
|
$request->session()->flash('error', trans('firefly.cannot_delete_currency', ['name' => $currency->name]));
|
||||||
|
|
||||||
return redirect(route('currencies.index'));
|
return redirect(route('currencies.index'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$repository->destroy($currency);
|
$this->repository->destroy($currency);
|
||||||
$request->session()->flash('success', trans('firefly.deleted_currency', ['name' => $currency->name]));
|
$request->session()->flash('success', trans('firefly.deleted_currency', ['name' => $currency->name]));
|
||||||
|
|
||||||
return redirect($this->getPreviousUri('currencies.delete.uri'));
|
return redirect($this->getPreviousUri('currencies.delete.uri'));
|
||||||
@@ -138,10 +170,18 @@ class CurrencyController extends Controller
|
|||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param TransactionCurrency $currency
|
* @param TransactionCurrency $currency
|
||||||
*
|
*
|
||||||
* @return View
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||||
*/
|
*/
|
||||||
public function edit(Request $request, TransactionCurrency $currency)
|
public function edit(Request $request, TransactionCurrency $currency)
|
||||||
{
|
{
|
||||||
|
if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
$request->session()->flash('error', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
|
||||||
|
|
||||||
|
return redirect(route('currencies.index'));
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
$subTitleIcon = 'fa-pencil';
|
$subTitleIcon = 'fa-pencil';
|
||||||
$subTitle = trans('breadcrumbs.edit_currency', ['name' => $currency->name]);
|
$subTitle = trans('breadcrumbs.edit_currency', ['name' => $currency->name]);
|
||||||
$currency->symbol = htmlentities($currency->symbol);
|
$currency->symbol = htmlentities($currency->symbol);
|
||||||
@@ -160,47 +200,45 @@ class CurrencyController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param CurrencyRepositoryInterface $repository
|
|
||||||
*
|
*
|
||||||
* @return View
|
* @return View
|
||||||
*/
|
*/
|
||||||
public function index(Request $request, CurrencyRepositoryInterface $repository)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
$currencies = $repository->get();
|
$currencies = $this->repository->get();
|
||||||
$defaultCurrency = $repository->getCurrencyByPreference(Preferences::get('currencyPreference', config('firefly.default_currency', 'EUR')));
|
$defaultCurrency = $this->repository->getCurrencyByPreference(Preferences::get('currencyPreference', config('firefly.default_currency', 'EUR')));
|
||||||
|
if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
|
||||||
|
|
||||||
if (!auth()->user()->hasRole('owner')) {
|
|
||||||
$request->session()->flash('info', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
|
$request->session()->flash('info', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return view('currencies.index', compact('currencies', 'defaultCurrency'));
|
return view('currencies.index', compact('currencies', 'defaultCurrency'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @param CurrencyFormRequest $request
|
* @param CurrencyFormRequest $request
|
||||||
* @param CurrencyRepositoryInterface $repository
|
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
*/
|
*/
|
||||||
public function store(CurrencyFormRequest $request, CurrencyRepositoryInterface $repository)
|
public function store(CurrencyFormRequest $request)
|
||||||
{
|
{
|
||||||
if (!auth()->user()->hasRole('owner')) {
|
if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
Log::error('User ' . auth()->user()->id . ' is not admin, but tried to store a currency.');
|
Log::error('User ' . auth()->user()->id . ' is not admin, but tried to store a currency.');
|
||||||
|
|
||||||
return redirect($this->getPreviousUri('currencies.create.uri'));
|
return redirect($this->getPreviousUri('currencies.create.uri'));
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $request->getCurrencyData();
|
$data = $request->getCurrencyData();
|
||||||
$currency = $repository->store($data);
|
$currency = $this->repository->store($data);
|
||||||
$request->session()->flash('success', trans('firefly.created_currency', ['name' => $currency->name]));
|
$request->session()->flash('success', trans('firefly.created_currency', ['name' => $currency->name]));
|
||||||
|
|
||||||
if (intval($request->get('create_another')) === 1) {
|
if (intval($request->get('create_another')) === 1) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
$request->session()->put('currencies.create.fromStore', true);
|
$request->session()->put('currencies.create.fromStore', true);
|
||||||
|
|
||||||
return redirect(route('currencies.create'))->withInput();
|
return redirect(route('currencies.create'))->withInput();
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect($this->getPreviousUri('currencies.create.uri'));
|
return redirect($this->getPreviousUri('currencies.create.uri'));
|
||||||
@@ -208,25 +246,32 @@ class CurrencyController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param CurrencyFormRequest $request
|
* @param CurrencyFormRequest $request
|
||||||
* @param CurrencyRepositoryInterface $repository
|
|
||||||
* @param TransactionCurrency $currency
|
* @param TransactionCurrency $currency
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
*/
|
*/
|
||||||
public function update(CurrencyFormRequest $request, CurrencyRepositoryInterface $repository, TransactionCurrency $currency)
|
public function update(CurrencyFormRequest $request, TransactionCurrency $currency)
|
||||||
{
|
{
|
||||||
$data = $request->getCurrencyData();
|
if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
|
||||||
if (auth()->user()->hasRole('owner')) {
|
// @codeCoverageIgnoreStart
|
||||||
$currency = $repository->update($currency, $data);
|
$request->session()->flash('error', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
|
||||||
|
|
||||||
|
return redirect(route('currencies.index'));
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$data = $request->getCurrencyData();
|
||||||
|
$currency = $this->repository->update($currency, $data);
|
||||||
$request->session()->flash('success', trans('firefly.updated_currency', ['name' => $currency->name]));
|
$request->session()->flash('success', trans('firefly.updated_currency', ['name' => $currency->name]));
|
||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
|
|
||||||
if (intval($request->get('return_to_edit')) === 1) {
|
if (intval($request->get('return_to_edit')) === 1) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
$request->session()->put('currencies.edit.fromUpdate', true);
|
$request->session()->put('currencies.edit.fromUpdate', true);
|
||||||
|
|
||||||
return redirect(route('currencies.edit', [$currency->id]));
|
return redirect(route('currencies.edit', [$currency->id]));
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect($this->getPreviousUri('currencies.edit.uri'));
|
return redirect($this->getPreviousUri('currencies.edit.uri'));
|
||||||
|
@@ -23,7 +23,6 @@ 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;
|
||||||
use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface as EJRI;
|
|
||||||
use Illuminate\Http\Response as LaravelResponse;
|
use Illuminate\Http\Response as LaravelResponse;
|
||||||
use Preferences;
|
use Preferences;
|
||||||
use Response;
|
use Response;
|
||||||
@@ -55,9 +54,10 @@ class ExportController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param ExportJobRepositoryInterface $repository
|
||||||
* @param ExportJob $job
|
* @param ExportJob $job
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\HttpFoundation\Response|\Illuminate\Contracts\Routing\ResponseFactory
|
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
|
||||||
* @throws FireflyException
|
* @throws FireflyException
|
||||||
*/
|
*/
|
||||||
public function download(ExportJobRepositoryInterface $repository, ExportJob $job)
|
public function download(ExportJobRepositoryInterface $repository, ExportJob $job)
|
||||||
@@ -103,11 +103,11 @@ class ExportController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param AccountRepositoryInterface $repository
|
* @param AccountRepositoryInterface $repository
|
||||||
* @param EJRI $jobs
|
* @param ExportJobRepositoryInterface $jobs
|
||||||
*
|
*
|
||||||
* @return View
|
* @return View
|
||||||
*/
|
*/
|
||||||
public function index(AccountRepositoryInterface $repository, EJRI $jobs)
|
public function index(AccountRepositoryInterface $repository, ExportJobRepositoryInterface $jobs)
|
||||||
{
|
{
|
||||||
// create new export job.
|
// create new export job.
|
||||||
$job = $jobs->create();
|
$job = $jobs->create();
|
||||||
@@ -130,11 +130,11 @@ class ExportController extends Controller
|
|||||||
/**
|
/**
|
||||||
* @param ExportFormRequest $request
|
* @param ExportFormRequest $request
|
||||||
* @param AccountRepositoryInterface $repository
|
* @param AccountRepositoryInterface $repository
|
||||||
* @param EJRI $jobs
|
* @param ExportJobRepositoryInterface $jobs
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*/
|
*/
|
||||||
public function postIndex(ExportFormRequest $request, AccountRepositoryInterface $repository, EJRI $jobs)
|
public function postIndex(ExportFormRequest $request, AccountRepositoryInterface $repository, ExportJobRepositoryInterface $jobs)
|
||||||
{
|
{
|
||||||
$job = $jobs->findByKey($request->get('job'));
|
$job = $jobs->findByKey($request->get('job'));
|
||||||
$settings = [
|
$settings = [
|
||||||
|
@@ -58,7 +58,7 @@ class HelpController extends Controller
|
|||||||
return Response::json($content);
|
return Response::json($content);
|
||||||
}
|
}
|
||||||
|
|
||||||
$content = $help->getFromGithub($language, $route);
|
$content = $help->getFromGithub($route, $language);
|
||||||
$notYourLanguage = '<p><em>' . strval(trans('firefly.help_may_not_be_your_language')) . '</em></p>';
|
$notYourLanguage = '<p><em>' . strval(trans('firefly.help_may_not_be_your_language')) . '</em></p>';
|
||||||
|
|
||||||
// get backup language content (try English):
|
// get backup language content (try English):
|
||||||
@@ -66,10 +66,10 @@ class HelpController extends Controller
|
|||||||
$language = 'en_US';
|
$language = 'en_US';
|
||||||
if ($help->inCache($route, $language)) {
|
if ($help->inCache($route, $language)) {
|
||||||
Log::debug(sprintf('Help text %s was in cache.', $language));
|
Log::debug(sprintf('Help text %s was in cache.', $language));
|
||||||
$content = $help->getFromCache($route, $language);
|
$content = $notYourLanguage . $help->getFromCache($route, $language);
|
||||||
}
|
}
|
||||||
if (!$help->inCache($route, $language)) {
|
if (!$help->inCache($route, $language)) {
|
||||||
$content = trim($notYourLanguage . $help->getFromGithub($language, $route));
|
$content = trim($notYourLanguage . $help->getFromGithub($route, $language));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Http\Controllers;
|
namespace FireflyIII\Http\Controllers;
|
||||||
|
|
||||||
use Artisan;
|
use Artisan;
|
||||||
@@ -17,7 +18,7 @@ use Carbon\Carbon;
|
|||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -97,11 +98,11 @@ class HomeController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ARI $repository
|
* @param AccountRepositoryInterface $repository
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||||
*/
|
*/
|
||||||
public function index(ARI $repository)
|
public function index(AccountRepositoryInterface $repository)
|
||||||
{
|
{
|
||||||
$types = config('firefly.accountTypesByIdentifier.asset');
|
$types = config('firefly.accountTypesByIdentifier.asset');
|
||||||
$count = $repository->count($types);
|
$count = $repository->count($types);
|
||||||
|
@@ -20,6 +20,7 @@ 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 FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response as LaravelResponse;
|
use Illuminate\Http\Response as LaravelResponse;
|
||||||
use Log;
|
use Log;
|
||||||
@@ -87,8 +88,6 @@ class ImportController extends Controller
|
|||||||
{
|
{
|
||||||
Log::debug('Now at start of configure()');
|
Log::debug('Now at start of configure()');
|
||||||
if (!$this->jobInCorrectStep($job, 'configure')) {
|
if (!$this->jobInCorrectStep($job, 'configure')) {
|
||||||
Log::debug('Job is not in correct state for configure()', ['status' => $job->status]);
|
|
||||||
|
|
||||||
return $this->redirectToCorrectStep($job);
|
return $this->redirectToCorrectStep($job);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,8 +99,6 @@ class ImportController extends Controller
|
|||||||
$subTitleIcon = 'fa-wrench';
|
$subTitleIcon = 'fa-wrench';
|
||||||
|
|
||||||
return view('import.' . $job->file_type . '.configure', compact('data', 'job', 'subTitle', 'subTitleIcon'));
|
return view('import.' . $job->file_type . '.configure', compact('data', 'job', 'subTitle', 'subTitleIcon'));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -145,8 +142,6 @@ class ImportController extends Controller
|
|||||||
public function finished(ImportJob $job)
|
public function finished(ImportJob $job)
|
||||||
{
|
{
|
||||||
if (!$this->jobInCorrectStep($job, 'finished')) {
|
if (!$this->jobInCorrectStep($job, 'finished')) {
|
||||||
Log::debug('Job is not in correct state for finished()', ['status' => $job->status]);
|
|
||||||
|
|
||||||
return $this->redirectToCorrectStep($job);
|
return $this->redirectToCorrectStep($job);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,12 +220,12 @@ class ImportController extends Controller
|
|||||||
* Step 4. Save the configuration.
|
* Step 4. Save the configuration.
|
||||||
*
|
*
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
|
* @param ImportJobRepositoryInterface $repository
|
||||||
* @param ImportJob $job
|
* @param ImportJob $job
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
* @throws FireflyException
|
|
||||||
*/
|
*/
|
||||||
public function postConfigure(Request $request, ImportJob $job)
|
public function postConfigure(Request $request, ImportJobRepositoryInterface $repository, ImportJob $job)
|
||||||
{
|
{
|
||||||
Log::debug('Now in postConfigure()', ['job' => $job->key]);
|
Log::debug('Now in postConfigure()', ['job' => $job->key]);
|
||||||
if (!$this->jobInCorrectStep($job, 'process')) {
|
if (!$this->jobInCorrectStep($job, 'process')) {
|
||||||
@@ -245,8 +240,7 @@ class ImportController extends Controller
|
|||||||
$importer->saveImportConfiguration($data, $files);
|
$importer->saveImportConfiguration($data, $files);
|
||||||
|
|
||||||
// update job:
|
// update job:
|
||||||
$job->status = 'import_configuration_saved';
|
$repository->updateStatus($job, 'import_configuration_saved');
|
||||||
$job->save();
|
|
||||||
|
|
||||||
// return redirect to settings.
|
// return redirect to settings.
|
||||||
// this could loop until the user is done.
|
// this could loop until the user is done.
|
||||||
@@ -280,17 +274,15 @@ class ImportController extends Controller
|
|||||||
* Step 5. Depending on the importer, this will show the user settings to
|
* Step 5. Depending on the importer, this will show the user settings to
|
||||||
* fill in.
|
* fill in.
|
||||||
*
|
*
|
||||||
|
* @param ImportJobRepositoryInterface $repository
|
||||||
* @param ImportJob $job
|
* @param ImportJob $job
|
||||||
*
|
*
|
||||||
* @return View
|
* @return View
|
||||||
* @throws FireflyException
|
|
||||||
*/
|
*/
|
||||||
public function settings(ImportJob $job)
|
public function settings(ImportJobRepositoryInterface $repository, ImportJob $job)
|
||||||
{
|
{
|
||||||
Log::debug('Now in settings()', ['job' => $job->key]);
|
Log::debug('Now in settings()', ['job' => $job->key]);
|
||||||
if (!$this->jobInCorrectStep($job, 'settings')) {
|
if (!$this->jobInCorrectStep($job, 'settings')) {
|
||||||
Log::debug('Job should not be in settings()');
|
|
||||||
|
|
||||||
return $this->redirectToCorrectStep($job);
|
return $this->redirectToCorrectStep($job);
|
||||||
}
|
}
|
||||||
Log::debug('Continue in settings()');
|
Log::debug('Continue in settings()');
|
||||||
@@ -308,8 +300,7 @@ class ImportController extends Controller
|
|||||||
}
|
}
|
||||||
Log::debug('Job does NOT require user config.');
|
Log::debug('Job does NOT require user config.');
|
||||||
|
|
||||||
$job->status = 'settings_complete';
|
$repository->updateStatus($job, 'settings_complete');
|
||||||
$job->save();
|
|
||||||
|
|
||||||
// if no more settings, save job and continue to process thing.
|
// if no more settings, save job and continue to process thing.
|
||||||
return redirect(route('import.complete', [$job->key]));
|
return redirect(route('import.complete', [$job->key]));
|
||||||
@@ -350,15 +341,17 @@ class ImportController extends Controller
|
|||||||
return view('import.status', compact('job', 'subTitle', 'subTitleIcon'));
|
return view('import.status', compact('job', 'subTitle', 'subTitleIcon'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is step 2. It creates an Import Job. Stores the import.
|
* This is step 2. It creates an Import Job. Stores the import.
|
||||||
*
|
*
|
||||||
* @param ImportUploadRequest $request
|
* @param ImportUploadRequest $request
|
||||||
* @param ImportJobRepositoryInterface $repository
|
* @param ImportJobRepositoryInterface $repository
|
||||||
|
* @param UserRepositoryInterface $userRepository
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
*/
|
*/
|
||||||
public function upload(ImportUploadRequest $request, ImportJobRepositoryInterface $repository)
|
public function upload(ImportUploadRequest $request, ImportJobRepositoryInterface $repository, UserRepositoryInterface $userRepository)
|
||||||
{
|
{
|
||||||
Log::debug('Now in upload()');
|
Log::debug('Now in upload()');
|
||||||
// create import job:
|
// create import job:
|
||||||
@@ -375,7 +368,7 @@ class ImportController extends Controller
|
|||||||
$disk = Storage::disk('upload');
|
$disk = Storage::disk('upload');
|
||||||
|
|
||||||
// user is demo user, replace upload with prepared file.
|
// user is demo user, replace upload with prepared file.
|
||||||
if (auth()->user()->hasRole('demo')) {
|
if ($userRepository->hasRole(auth()->user(), 'demo')) {
|
||||||
$stubsDisk = Storage::disk('stubs');
|
$stubsDisk = Storage::disk('stubs');
|
||||||
$content = $stubsDisk->get('demo-import.csv');
|
$content = $stubsDisk->get('demo-import.csv');
|
||||||
$contentEncrypted = Crypt::encrypt($content);
|
$contentEncrypted = Crypt::encrypt($content);
|
||||||
@@ -384,14 +377,13 @@ class ImportController extends Controller
|
|||||||
|
|
||||||
// also set up prepared configuration.
|
// also set up prepared configuration.
|
||||||
$configuration = json_decode($stubsDisk->get('demo-configuration.json'), true);
|
$configuration = json_decode($stubsDisk->get('demo-configuration.json'), true);
|
||||||
$job->configuration = $configuration;
|
$repository->setConfiguration($job, $configuration);
|
||||||
$job->save();
|
|
||||||
Log::debug('Set configuration for demo user', $configuration);
|
Log::debug('Set configuration for demo user', $configuration);
|
||||||
|
|
||||||
// also flash info
|
// also flash info
|
||||||
Session::flash('info', trans('demo.import-configure-security'));
|
Session::flash('info', trans('demo.import-configure-security'));
|
||||||
}
|
}
|
||||||
if (!auth()->user()->hasRole('demo')) {
|
if (!$userRepository->hasRole(auth()->user(), 'demo')) {
|
||||||
// user is not demo, process original upload:
|
// user is not demo, process original upload:
|
||||||
$disk->put($newName, $contentEncrypted);
|
$disk->put($newName, $contentEncrypted);
|
||||||
Log::debug('Uploaded file', ['name' => $upload->getClientOriginalName(), 'size' => $upload->getSize(), 'mime' => $upload->getClientMimeType()]);
|
Log::debug('Uploaded file', ['name' => $upload->getClientOriginalName(), 'size' => $upload->getSize(), 'mime' => $upload->getClientMimeType()]);
|
||||||
@@ -411,18 +403,15 @@ class ImportController extends Controller
|
|||||||
$configRaw = $configFileObject->fread($configFileObject->getSize());
|
$configRaw = $configFileObject->fread($configFileObject->getSize());
|
||||||
$configuration = json_decode($configRaw, true);
|
$configuration = json_decode($configRaw, true);
|
||||||
|
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
if (!is_null($configuration) && is_array($configuration)) {
|
if (!is_null($configuration) && is_array($configuration)) {
|
||||||
Log::debug('Found configuration', $configuration);
|
Log::debug('Found configuration', $configuration);
|
||||||
$job->configuration = $configuration;
|
$repository->setConfiguration($job, $configuration);
|
||||||
$job->save();
|
|
||||||
}
|
}
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
// if user is demo user, replace config with prepared config:
|
|
||||||
|
|
||||||
|
|
||||||
return redirect(route('import.configure', [$job->key]));
|
return redirect(route('import.configure', [$job->key]));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -440,6 +429,8 @@ class ImportController extends Controller
|
|||||||
return $job->status === 'import_status_never_started';
|
return $job->status === 'import_status_never_started';
|
||||||
case 'settings':
|
case 'settings':
|
||||||
case 'store-settings':
|
case 'store-settings':
|
||||||
|
Log::debug(sprintf('Job %d with key %s has status %s', $job->id, $job->key, $job->status));
|
||||||
|
|
||||||
return $job->status === 'import_configuration_saved';
|
return $job->status === 'import_configuration_saved';
|
||||||
case 'finished':
|
case 'finished':
|
||||||
return $job->status === 'import_complete';
|
return $job->status === 'import_complete';
|
||||||
@@ -449,7 +440,7 @@ class ImportController extends Controller
|
|||||||
return ($job->status === 'settings_complete') || ($job->status === 'import_running');
|
return ($job->status === 'settings_complete') || ($job->status === 'import_running');
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false; // @codeCoverageIgnore
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,7 +466,7 @@ class ImportController extends Controller
|
|||||||
|
|
||||||
return $importer;
|
return $importer;
|
||||||
}
|
}
|
||||||
throw new FireflyException(sprintf('"%s" is not a valid file type', $type));
|
throw new FireflyException(sprintf('"%s" is not a valid file type', $type)); // @codeCoverageIgnore
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,7 +498,7 @@ class ImportController extends Controller
|
|||||||
return redirect(route('import.finished', [$job->key]));
|
return redirect(route('import.finished', [$job->key]));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new FireflyException('Cannot redirect for job state ' . $job->status);
|
throw new FireflyException('Cannot redirect for job state ' . $job->status); // @codeCoverageIgnore
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,11 @@ namespace FireflyIII\Http\Controllers;
|
|||||||
|
|
||||||
use Amount;
|
use Amount;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Models\Account;
|
||||||
|
use FireflyIII\Models\AccountType;
|
||||||
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Navigation;
|
use Navigation;
|
||||||
use Preferences;
|
use Preferences;
|
||||||
@@ -25,9 +30,61 @@ use Session;
|
|||||||
*/
|
*/
|
||||||
class JavascriptController extends Controller
|
class JavascriptController extends Controller
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @param AccountRepositoryInterface $repository
|
||||||
|
* @param CurrencyRepositoryInterface $currencyRepository
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function accounts(AccountRepositoryInterface $repository, CurrencyRepositoryInterface $currencyRepository)
|
||||||
|
{
|
||||||
|
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||||
|
$preference = Preferences::get('currencyPreference', config('firefly.default_currency', 'EUR'));
|
||||||
|
$default = $currencyRepository->findByCode($preference->data);
|
||||||
|
|
||||||
|
$data = ['accounts' => [],];
|
||||||
|
|
||||||
|
|
||||||
|
/** @var Account $account */
|
||||||
|
foreach ($accounts as $account) {
|
||||||
|
$accountId = $account->id;
|
||||||
|
$currency = intval($account->getMeta('currency_id'));
|
||||||
|
$currency = $currency === 0 ? $default->id : $currency;
|
||||||
|
$entry = ['preferredCurrency' => $currency, 'name' => $account->name];
|
||||||
|
$data['accounts'][$accountId] = $entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return response()
|
||||||
|
->view('javascript.accounts', $data, 200)
|
||||||
|
->header('Content-Type', 'text/javascript');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param CurrencyRepositoryInterface $repository
|
||||||
*
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function currencies(CurrencyRepositoryInterface $repository)
|
||||||
|
{
|
||||||
|
$currencies = $repository->get();
|
||||||
|
$data = ['currencies' => [],];
|
||||||
|
/** @var TransactionCurrency $currency */
|
||||||
|
foreach ($currencies as $currency) {
|
||||||
|
$currencyId = $currency->id;
|
||||||
|
$entry = ['name' => $currency->name, 'code' => $currency->code, 'symbol' => $currency->symbol];
|
||||||
|
$data['currencies'][$currencyId] = $entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()
|
||||||
|
->view('javascript.currencies', $data, 200)
|
||||||
|
->header('Content-Type', 'text/javascript');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function variables(Request $request)
|
public function variables(Request $request)
|
||||||
{
|
{
|
||||||
@@ -95,7 +152,7 @@ class JavascriptController extends Controller
|
|||||||
|
|
||||||
switch ($viewRange) {
|
switch ($viewRange) {
|
||||||
default:
|
default:
|
||||||
throw new FireflyException('The date picker does not yet support "' . $viewRange . '".');
|
throw new FireflyException('The date picker does not yet support "' . $viewRange . '".'); // @codeCoverageIgnore
|
||||||
case '1D':
|
case '1D':
|
||||||
case 'custom':
|
case 'custom':
|
||||||
$format = (string)trans('config.month_and_day');
|
$format = (string)trans('config.month_and_day');
|
||||||
|
66
app/Http/Controllers/Json/ExchangeController.php
Normal file
66
app/Http/Controllers/Json/ExchangeController.php
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ExchangeController.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\Json;
|
||||||
|
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
|
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||||
|
use FireflyIII\Services\Currency\ExchangeRateInterface;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Log;
|
||||||
|
use Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ExchangeController
|
||||||
|
*
|
||||||
|
* @package FireflyIII\Http\Controllers\Json
|
||||||
|
*/
|
||||||
|
class ExchangeController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param TransactionCurrency $fromCurrency
|
||||||
|
* @param TransactionCurrency $toCurrency
|
||||||
|
* @param Carbon $date
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function getRate(Request $request, TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date)
|
||||||
|
{
|
||||||
|
/** @var CurrencyRepositoryInterface $repository */
|
||||||
|
$repository = app(CurrencyRepositoryInterface::class);
|
||||||
|
$rate = $repository->getExchangeRate($fromCurrency, $toCurrency, $date);
|
||||||
|
$amount = null;
|
||||||
|
if (is_null($rate->id)) {
|
||||||
|
Log::debug(sprintf('No cached exchange rate in database for %s to %s on %s', $fromCurrency->code, $toCurrency->code, $date->format('Y-m-d')));
|
||||||
|
$preferred = env('EXCHANGE_RATE_SERVICE', config('firefly.preferred_exchange_service'));
|
||||||
|
$class = config('firefly.currency_exchange_services.' . $preferred);
|
||||||
|
/** @var ExchangeRateInterface $object */
|
||||||
|
$object = app($class);
|
||||||
|
$object->setUser(auth()->user());
|
||||||
|
$rate = $object->getRate($fromCurrency, $toCurrency, $date);
|
||||||
|
}
|
||||||
|
$return = $rate->toArray();
|
||||||
|
$return['amount'] = null;
|
||||||
|
if (!is_null($request->get('amount'))) {
|
||||||
|
// assume amount is in "from" currency:
|
||||||
|
$return['amount'] = bcmul($request->get('amount'), strval($rate->rate), 12);
|
||||||
|
// round to toCurrency decimal places:
|
||||||
|
$return['amount'] = round($return['amount'], $toCurrency->decimal_places);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response::json($return);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Http\Controllers;
|
namespace FireflyIII\Http\Controllers;
|
||||||
|
|
||||||
use Amount;
|
use Amount;
|
||||||
@@ -17,6 +18,7 @@ use Carbon\Carbon;
|
|||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
|
use FireflyIII\Models\TransactionType;
|
||||||
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;
|
||||||
@@ -144,7 +146,7 @@ class JsonController extends Controller
|
|||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function boxIn(AccountTaskerInterface $accountTasker, AccountRepositoryInterface $repository)
|
public function boxIn()
|
||||||
{
|
{
|
||||||
$start = session('start', Carbon::now()->startOfMonth());
|
$start = session('start', Carbon::now()->startOfMonth());
|
||||||
$end = session('end', Carbon::now()->endOfMonth());
|
$end = session('end', Carbon::now()->endOfMonth());
|
||||||
@@ -155,11 +157,17 @@ class JsonController extends Controller
|
|||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
$cache->addProperty('box-in');
|
$cache->addProperty('box-in');
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
|
||||||
$assets = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
// try a collector for income:
|
||||||
$amount = $accountTasker->amountInInPeriod($accounts, $assets, $start, $end);
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAllAssetAccounts()->setRange($start, $end)
|
||||||
|
->setTypes([TransactionType::DEPOSIT])
|
||||||
|
->withOpposingAccount();
|
||||||
|
|
||||||
|
$amount = strval($collector->getJournals()->sum('transaction_amount'));
|
||||||
$data = ['box' => 'in', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
|
$data = ['box' => 'in', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
|
||||||
$cache->store($data);
|
$cache->store($data);
|
||||||
|
|
||||||
@@ -172,7 +180,7 @@ class JsonController extends Controller
|
|||||||
*
|
*
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
*/
|
*/
|
||||||
public function boxOut(AccountTaskerInterface $accountTasker, AccountRepositoryInterface $repository)
|
public function boxOut()
|
||||||
{
|
{
|
||||||
$start = session('start', Carbon::now()->startOfMonth());
|
$start = session('start', Carbon::now()->startOfMonth());
|
||||||
$end = session('end', Carbon::now()->endOfMonth());
|
$end = session('end', Carbon::now()->endOfMonth());
|
||||||
@@ -183,12 +191,16 @@ class JsonController extends Controller
|
|||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
$cache->addProperty('box-out');
|
$cache->addProperty('box-out');
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return Response::json($cache->get());
|
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
// try a collector for expenses:
|
||||||
$assets = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
/** @var JournalCollectorInterface $collector */
|
||||||
$amount = $accountTasker->amountOutInPeriod($accounts, $assets, $start, $end);
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAllAssetAccounts()->setRange($start, $end)
|
||||||
|
->setTypes([TransactionType::WITHDRAWAL])
|
||||||
|
->withOpposingAccount();
|
||||||
|
$amount = strval($collector->getJournals()->sum('transaction_amount'));
|
||||||
|
|
||||||
$data = ['box' => 'out', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
|
$data = ['box' => 'out', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
|
||||||
$cache->store($data);
|
$cache->store($data);
|
||||||
@@ -287,7 +299,7 @@ class JsonController extends Controller
|
|||||||
{
|
{
|
||||||
$pref = Preferences::get('tour', true);
|
$pref = Preferences::get('tour', true);
|
||||||
if (!$pref) {
|
if (!$pref) {
|
||||||
throw new FireflyException('Cannot find preference for tour. Exit.');
|
throw new FireflyException('Cannot find preference for tour. Exit.'); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
$headers = ['main-content', 'sidebar-toggle', 'account-menu', 'budget-menu', 'report-menu', 'transaction-menu', 'option-menu', 'main-content-end'];
|
$headers = ['main-content', 'sidebar-toggle', 'account-menu', 'budget-menu', 'report-menu', 'transaction-menu', 'option-menu', 'main-content-end'];
|
||||||
$steps = [];
|
$steps = [];
|
||||||
@@ -329,7 +341,9 @@ class JsonController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param JournalRepositoryInterface $repository
|
||||||
*
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*/
|
*/
|
||||||
public function transactionTypes(JournalRepositoryInterface $repository)
|
public function transactionTypes(JournalRepositoryInterface $repository)
|
||||||
{
|
{
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Http\Controllers;
|
namespace FireflyIII\Http\Controllers;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
@@ -119,7 +120,7 @@ class NewUserController extends Controller
|
|||||||
'accountRole' => 'defaultAsset',
|
'accountRole' => 'defaultAsset',
|
||||||
'openingBalance' => round($request->input('bank_balance'), 12),
|
'openingBalance' => round($request->input('bank_balance'), 12),
|
||||||
'openingBalanceDate' => new Carbon,
|
'openingBalanceDate' => new Carbon,
|
||||||
'openingBalanceCurrency' => intval($request->input('amount_currency_id_bank_balance')),
|
'currency_id' => intval($request->input('amount_currency_id_bank_balance')),
|
||||||
];
|
];
|
||||||
|
|
||||||
$repository->store($assetAccount);
|
$repository->store($assetAccount);
|
||||||
@@ -144,7 +145,7 @@ class NewUserController extends Controller
|
|||||||
'accountRole' => 'savingAsset',
|
'accountRole' => 'savingAsset',
|
||||||
'openingBalance' => round($request->input('savings_balance'), 12),
|
'openingBalance' => round($request->input('savings_balance'), 12),
|
||||||
'openingBalanceDate' => new Carbon,
|
'openingBalanceDate' => new Carbon,
|
||||||
'openingBalanceCurrency' => intval($request->input('amount_currency_id_savings_balance')),
|
'currency_id' => intval($request->input('amount_currency_id_savings_balance')),
|
||||||
];
|
];
|
||||||
$repository->store($savingsAccount);
|
$repository->store($savingsAccount);
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Http\Controllers;
|
namespace FireflyIII\Http\Controllers;
|
||||||
|
|
||||||
use Amount;
|
use Amount;
|
||||||
@@ -209,10 +210,11 @@ class PiggyBankController extends Controller
|
|||||||
$end = session('end', Carbon::now()->endOfMonth());
|
$end = session('end', Carbon::now()->endOfMonth());
|
||||||
|
|
||||||
$accounts = [];
|
$accounts = [];
|
||||||
|
Log::debug('Looping piggues');
|
||||||
/** @var PiggyBank $piggyBank */
|
/** @var PiggyBank $piggyBank */
|
||||||
foreach ($piggyBanks as $piggyBank) {
|
foreach ($piggyBanks as $piggyBank) {
|
||||||
$piggyBank->savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
|
$piggyBank->savedSoFar = $piggyBank->currentRelevantRep()->currentamount ?? '0';
|
||||||
$piggyBank->percentage = $piggyBank->savedSoFar != 0 ? intval($piggyBank->savedSoFar / $piggyBank->targetamount * 100) : 0;
|
$piggyBank->percentage = bccomp('0', $piggyBank->savedSoFar) !== 0 ? intval($piggyBank->savedSoFar / $piggyBank->targetamount * 100) : 0;
|
||||||
$piggyBank->leftToSave = bcsub($piggyBank->targetamount, strval($piggyBank->savedSoFar));
|
$piggyBank->leftToSave = bcsub($piggyBank->targetamount, strval($piggyBank->savedSoFar));
|
||||||
$piggyBank->percentage = $piggyBank->percentage > 100 ? 100 : $piggyBank->percentage;
|
$piggyBank->percentage = $piggyBank->percentage > 100 ? 100 : $piggyBank->percentage;
|
||||||
|
|
||||||
@@ -220,7 +222,9 @@ class PiggyBankController extends Controller
|
|||||||
* Fill account information:
|
* Fill account information:
|
||||||
*/
|
*/
|
||||||
$account = $piggyBank->account;
|
$account = $piggyBank->account;
|
||||||
|
$new = false;
|
||||||
if (!isset($accounts[$account->id])) {
|
if (!isset($accounts[$account->id])) {
|
||||||
|
$new = true;
|
||||||
$accounts[$account->id] = [
|
$accounts[$account->id] = [
|
||||||
'name' => $account->name,
|
'name' => $account->name,
|
||||||
'balance' => Steam::balanceIgnoreVirtual($account, $end),
|
'balance' => Steam::balanceIgnoreVirtual($account, $end),
|
||||||
@@ -229,7 +233,8 @@ class PiggyBankController extends Controller
|
|||||||
'sumOfTargets' => $piggyBank->targetamount,
|
'sumOfTargets' => $piggyBank->targetamount,
|
||||||
'leftToSave' => $piggyBank->leftToSave,
|
'leftToSave' => $piggyBank->leftToSave,
|
||||||
];
|
];
|
||||||
} else {
|
}
|
||||||
|
if (isset($accounts[$account->id]) && $new === false) {
|
||||||
$accounts[$account->id]['sumOfSaved'] = bcadd($accounts[$account->id]['sumOfSaved'], strval($piggyBank->savedSoFar));
|
$accounts[$account->id]['sumOfSaved'] = bcadd($accounts[$account->id]['sumOfSaved'], strval($piggyBank->savedSoFar));
|
||||||
$accounts[$account->id]['sumOfTargets'] = bcadd($accounts[$account->id]['sumOfTargets'], $piggyBank->targetamount);
|
$accounts[$account->id]['sumOfTargets'] = bcadd($accounts[$account->id]['sumOfTargets'], $piggyBank->targetamount);
|
||||||
$accounts[$account->id]['leftToSave'] = bcadd($accounts[$account->id]['leftToSave'], $piggyBank->leftToSave);
|
$accounts[$account->id]['leftToSave'] = bcadd($accounts[$account->id]['leftToSave'], $piggyBank->leftToSave);
|
||||||
@@ -272,32 +277,16 @@ class PiggyBankController extends Controller
|
|||||||
public function postAdd(Request $request, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
|
public function postAdd(Request $request, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
|
||||||
{
|
{
|
||||||
$amount = $request->get('amount');
|
$amount = $request->get('amount');
|
||||||
Log::debug(sprintf('Found amount is %s', $amount));
|
|
||||||
/** @var Carbon $date */
|
|
||||||
$date = session('end', Carbon::now()->endOfMonth());
|
|
||||||
$leftOnAccount = $piggyBank->leftOnAccount($date);
|
|
||||||
$savedSoFar = strval($piggyBank->currentRelevantRep()->currentamount);
|
|
||||||
$leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);
|
|
||||||
$maxAmount = strval(min(round($leftOnAccount, 12), round($leftToSave, 12)));
|
|
||||||
|
|
||||||
if (bccomp($amount, $maxAmount) <= 0) {
|
if ($repository->canAddAmount($piggyBank, $amount)) {
|
||||||
$repetition = $piggyBank->currentRelevantRep();
|
$repository->addAmount($piggyBank, $amount);
|
||||||
$currentAmount = $repetition->currentamount ?? '0';
|
Session::flash('success', strval(trans('firefly.added_amount_to_piggy', ['amount' => Amount::format($amount, false), 'name' => $piggyBank->name])));
|
||||||
$repetition->currentamount = bcadd($currentAmount, $amount);
|
|
||||||
$repetition->save();
|
|
||||||
|
|
||||||
// create event
|
|
||||||
$repository->createEvent($piggyBank, $amount);
|
|
||||||
|
|
||||||
Session::flash(
|
|
||||||
'success', strval(trans('firefly.added_amount_to_piggy', ['amount' => Amount::format($amount, false), 'name' => e($piggyBank->name)]))
|
|
||||||
);
|
|
||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
return redirect(route('piggy-banks.index'));
|
return redirect(route('piggy-banks.index'));
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::error('Cannot add ' . $amount . ' because max amount is ' . $maxAmount . ' (left on account is ' . $leftOnAccount . ')');
|
Log::error('Cannot add ' . $amount . ' because canAddAmount returned false.');
|
||||||
Session::flash('error', strval(trans('firefly.cannot_add_amount_piggy', ['amount' => Amount::format($amount, false), 'name' => e($piggyBank->name)])));
|
Session::flash('error', strval(trans('firefly.cannot_add_amount_piggy', ['amount' => Amount::format($amount, false), 'name' => e($piggyBank->name)])));
|
||||||
|
|
||||||
return redirect(route('piggy-banks.index'));
|
return redirect(route('piggy-banks.index'));
|
||||||
@@ -312,26 +301,19 @@ class PiggyBankController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function postRemove(Request $request, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
|
public function postRemove(Request $request, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
|
||||||
{
|
{
|
||||||
$amount = strval(round($request->get('amount'), 12));
|
$amount = $request->get('amount');
|
||||||
|
if ($repository->canRemoveAmount($piggyBank, $amount)) {
|
||||||
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
|
$repository->removeAmount($piggyBank, $amount);
|
||||||
|
|
||||||
if (bccomp($amount, $savedSoFar) <= 0) {
|
|
||||||
$repetition = $piggyBank->currentRelevantRep();
|
|
||||||
$repetition->currentamount = bcsub($repetition->currentamount, $amount);
|
|
||||||
$repetition->save();
|
|
||||||
|
|
||||||
// create event
|
|
||||||
$repository->createEvent($piggyBank, bcmul($amount, '-1'));
|
|
||||||
|
|
||||||
Session::flash(
|
Session::flash(
|
||||||
'success', strval(trans('firefly.removed_amount_from_piggy', ['amount' => Amount::format($amount, false), 'name' => e($piggyBank->name)]))
|
'success', strval(trans('firefly.removed_amount_from_piggy', ['amount' => Amount::format($amount, false), 'name' => $piggyBank->name]))
|
||||||
);
|
);
|
||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
return redirect(route('piggy-banks.index'));
|
return redirect(route('piggy-banks.index'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$amount = strval(round($request->get('amount'), 12));
|
||||||
|
|
||||||
Session::flash('error', strval(trans('firefly.cannot_remove_from_piggy', ['amount' => Amount::format($amount, false), 'name' => e($piggyBank->name)])));
|
Session::flash('error', strval(trans('firefly.cannot_remove_from_piggy', ['amount' => Amount::format($amount, false), 'name' => e($piggyBank->name)])));
|
||||||
|
|
||||||
return redirect(route('piggy-banks.index'));
|
return redirect(route('piggy-banks.index'));
|
||||||
@@ -391,9 +373,11 @@ class PiggyBankController extends Controller
|
|||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
if (intval($request->get('create_another')) === 1) {
|
if (intval($request->get('create_another')) === 1) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
Session::put('piggy-banks.create.fromStore', true);
|
Session::put('piggy-banks.create.fromStore', true);
|
||||||
|
|
||||||
return redirect(route('piggy-banks.create'))->withInput();
|
return redirect(route('piggy-banks.create'))->withInput();
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect($this->getPreviousUri('piggy-banks.edit.uri'));
|
return redirect($this->getPreviousUri('piggy-banks.edit.uri'));
|
||||||
@@ -415,9 +399,11 @@ class PiggyBankController extends Controller
|
|||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
if (intval($request->get('return_to_edit')) === 1) {
|
if (intval($request->get('return_to_edit')) === 1) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
Session::put('piggy-banks.edit.fromUpdate', true);
|
Session::put('piggy-banks.edit.fromUpdate', true);
|
||||||
|
|
||||||
return redirect(route('piggy-banks.edit', [$piggyBank->id]));
|
return redirect(route('piggy-banks.edit', [$piggyBank->id]));
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect($this->getPreviousUri('piggy-banks.edit.uri'));
|
return redirect($this->getPreviousUri('piggy-banks.edit.uri'));
|
||||||
|
@@ -17,17 +17,13 @@ namespace FireflyIII\Http\Controllers\Popup;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Helpers\Collection\BalanceLine;
|
use FireflyIII\Helpers\Collection\BalanceLine;
|
||||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
use FireflyIII\Helpers\Report\PopupReportInterface;
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
use FireflyIII\Models\Transaction;
|
|
||||||
use FireflyIII\Models\TransactionJournal;
|
|
||||||
use FireflyIII\Models\TransactionType;
|
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||||
use FireflyIII\Support\Binder\AccountList;
|
use FireflyIII\Support\Binder\AccountList;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Response;
|
use Response;
|
||||||
use View;
|
use View;
|
||||||
@@ -40,6 +36,44 @@ use View;
|
|||||||
class ReportController extends Controller
|
class ReportController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/** @var AccountRepositoryInterface */
|
||||||
|
private $accountRepository;
|
||||||
|
/** @var BudgetRepositoryInterface */
|
||||||
|
private $budgetRepository;
|
||||||
|
/** @var CategoryRepositoryInterface */
|
||||||
|
private $categoryRepository;
|
||||||
|
/** @var PopupReportInterface */
|
||||||
|
private $popupHelper;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->middleware(
|
||||||
|
function ($request, $next) {
|
||||||
|
|
||||||
|
/** @var AccountRepositoryInterface $repository */
|
||||||
|
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||||
|
|
||||||
|
/** @var BudgetRepositoryInterface $repository */
|
||||||
|
$this->budgetRepository = app(BudgetRepositoryInterface::class);
|
||||||
|
|
||||||
|
/** @var CategoryRepositoryInterface categoryRepository */
|
||||||
|
$this->categoryRepository = app(CategoryRepositoryInterface::class);
|
||||||
|
|
||||||
|
/** @var PopupReportInterface popupHelper */
|
||||||
|
$this->popupHelper = app(PopupReportInterface::class);
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
*
|
*
|
||||||
@@ -59,7 +93,6 @@ class ReportController extends Controller
|
|||||||
throw new FireflyException('Firefly cannot handle "' . e($attributes['location']) . '" ');
|
throw new FireflyException('Firefly cannot handle "' . e($attributes['location']) . '" ');
|
||||||
case 'budget-spent-amount':
|
case 'budget-spent-amount':
|
||||||
$html = $this->budgetSpentAmount($attributes);
|
$html = $this->budgetSpentAmount($attributes);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 'expense-entry':
|
case 'expense-entry':
|
||||||
$html = $this->expenseEntry($attributes);
|
$html = $this->expenseEntry($attributes);
|
||||||
@@ -89,62 +122,25 @@ class ReportController extends Controller
|
|||||||
private function balanceAmount(array $attributes): string
|
private function balanceAmount(array $attributes): string
|
||||||
{
|
{
|
||||||
$role = intval($attributes['role']);
|
$role = intval($attributes['role']);
|
||||||
|
$budget = $this->budgetRepository->find(intval($attributes['budgetId']));
|
||||||
/** @var BudgetRepositoryInterface $budgetRepository */
|
$account = $this->accountRepository->find(intval($attributes['accountId']));
|
||||||
$budgetRepository = app(BudgetRepositoryInterface::class);
|
|
||||||
$budget = $budgetRepository->find(intval($attributes['budgetId']));
|
|
||||||
|
|
||||||
/** @var AccountRepositoryInterface $repository */
|
|
||||||
$repository = app(AccountRepositoryInterface::class);
|
|
||||||
|
|
||||||
$account = $repository->find(intval($attributes['accountId']));
|
|
||||||
$types = [TransactionType::WITHDRAWAL];
|
|
||||||
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case ($role === BalanceLine::ROLE_DEFAULTROLE && !is_null($budget->id)):
|
case ($role === BalanceLine::ROLE_DEFAULTROLE && !is_null($budget->id)):
|
||||||
/** @var JournalCollectorInterface $collector */
|
// normal row with a budget:
|
||||||
$collector = app(JournalCollectorInterface::class);
|
$journals = $this->popupHelper->balanceForBudget($budget, $account, $attributes);
|
||||||
$collector
|
|
||||||
->setAccounts(new Collection([$account]))
|
|
||||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
|
||||||
->setBudget($budget);
|
|
||||||
$journals = $collector->getJournals();
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ($role === BalanceLine::ROLE_DEFAULTROLE && is_null($budget->id)):
|
case ($role === BalanceLine::ROLE_DEFAULTROLE && is_null($budget->id)):
|
||||||
|
// normal row without a budget:
|
||||||
|
$journals = $this->popupHelper->balanceForNoBudget($account, $attributes);
|
||||||
$budget->name = strval(trans('firefly.no_budget'));
|
$budget->name = strval(trans('firefly.no_budget'));
|
||||||
/** @var JournalCollectorInterface $collector */
|
|
||||||
$collector = app(JournalCollectorInterface::class);
|
|
||||||
$collector
|
|
||||||
->setAccounts(new Collection([$account]))
|
|
||||||
->setTypes($types)
|
|
||||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
|
||||||
->withoutBudget();
|
|
||||||
$journals = $collector->getJournals();
|
|
||||||
break;
|
break;
|
||||||
case ($role === BalanceLine::ROLE_DIFFROLE):
|
case ($role === BalanceLine::ROLE_DIFFROLE):
|
||||||
/** @var JournalCollectorInterface $collector */
|
$journals = $this->popupHelper->balanceDifference($account, $attributes);
|
||||||
$collector = app(JournalCollectorInterface::class);
|
|
||||||
$collector
|
|
||||||
->setAccounts(new Collection([$account]))
|
|
||||||
->setTypes($types)
|
|
||||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
|
||||||
->withoutBudget();
|
|
||||||
$journals = $collector->getJournals();
|
|
||||||
|
|
||||||
$budget->name = strval(trans('firefly.leftUnbalanced'));
|
$budget->name = strval(trans('firefly.leftUnbalanced'));
|
||||||
$journals = $journals->filter(
|
|
||||||
function (Transaction $transaction) {
|
|
||||||
$tags = $transaction->transactionJournal->tags()->where('tagMode', 'balancingAct')->count();
|
|
||||||
if ($tags === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case ($role === BalanceLine::ROLE_TAGROLE):
|
case ($role === BalanceLine::ROLE_TAGROLE):
|
||||||
|
// row with tag info.
|
||||||
throw new FireflyException('Firefly cannot handle this type of info-button (BalanceLine::TagRole)');
|
throw new FireflyException('Firefly cannot handle this type of info-button (BalanceLine::TagRole)');
|
||||||
}
|
}
|
||||||
$view = view('popup.report.balance-amount', compact('journals', 'budget', 'account'))->render();
|
$view = view('popup.report.balance-amount', compact('journals', 'budget', 'account'))->render();
|
||||||
@@ -162,27 +158,8 @@ class ReportController extends Controller
|
|||||||
*/
|
*/
|
||||||
private function budgetSpentAmount(array $attributes): string
|
private function budgetSpentAmount(array $attributes): string
|
||||||
{
|
{
|
||||||
// need to find the budget
|
$budget = $this->budgetRepository->find(intval($attributes['budgetId']));
|
||||||
// then search for expenses in the given period
|
$journals = $this->popupHelper->byBudget($budget, $attributes);
|
||||||
// list them in some table format.
|
|
||||||
/** @var BudgetRepositoryInterface $repository */
|
|
||||||
$repository = app(BudgetRepositoryInterface::class);
|
|
||||||
$budget = $repository->find(intval($attributes['budgetId']));
|
|
||||||
/** @var JournalCollectorInterface $collector */
|
|
||||||
$collector = app(JournalCollectorInterface::class);
|
|
||||||
|
|
||||||
$collector
|
|
||||||
->setAccounts($attributes['accounts'])
|
|
||||||
->setRange($attributes['startDate'], $attributes['endDate']);
|
|
||||||
|
|
||||||
if (is_null($budget->id)) {
|
|
||||||
$collector->setTypes([TransactionType::WITHDRAWAL])->withoutBudget();
|
|
||||||
}
|
|
||||||
if (!is_null($budget->id)) {
|
|
||||||
// get all expenses in budget in period:
|
|
||||||
$collector->setBudget($budget);
|
|
||||||
}
|
|
||||||
$journals = $collector->getJournals();
|
|
||||||
$view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render();
|
$view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render();
|
||||||
|
|
||||||
return $view;
|
return $view;
|
||||||
@@ -198,17 +175,8 @@ class ReportController extends Controller
|
|||||||
*/
|
*/
|
||||||
private function categoryEntry(array $attributes): string
|
private function categoryEntry(array $attributes): string
|
||||||
{
|
{
|
||||||
/** @var CategoryRepositoryInterface $repository */
|
$category = $this->categoryRepository->find(intval($attributes['categoryId']));
|
||||||
$repository = app(CategoryRepositoryInterface::class);
|
$journals = $this->popupHelper->byCategory($category, $attributes);
|
||||||
$category = $repository->find(intval($attributes['categoryId']));
|
|
||||||
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
|
||||||
/** @var JournalCollectorInterface $collector */
|
|
||||||
$collector = app(JournalCollectorInterface::class);
|
|
||||||
$collector->setAccounts($attributes['accounts'])->setTypes($types)
|
|
||||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
|
||||||
->setCategory($category);
|
|
||||||
$journals = $collector->getJournals(); // 7193
|
|
||||||
|
|
||||||
$view = view('popup.report.category-entry', compact('journals', 'category'))->render();
|
$view = view('popup.report.category-entry', compact('journals', 'category'))->render();
|
||||||
|
|
||||||
return $view;
|
return $view;
|
||||||
@@ -224,28 +192,8 @@ class ReportController extends Controller
|
|||||||
*/
|
*/
|
||||||
private function expenseEntry(array $attributes): string
|
private function expenseEntry(array $attributes): string
|
||||||
{
|
{
|
||||||
/** @var AccountRepositoryInterface $repository */
|
$account = $this->accountRepository->find(intval($attributes['accountId']));
|
||||||
$repository = app(AccountRepositoryInterface::class);
|
$journals = $this->popupHelper->byExpenses($account, $attributes);
|
||||||
|
|
||||||
$account = $repository->find(intval($attributes['accountId']));
|
|
||||||
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
|
||||||
/** @var JournalCollectorInterface $collector */
|
|
||||||
$collector = app(JournalCollectorInterface::class);
|
|
||||||
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types);
|
|
||||||
$journals = $collector->getJournals();
|
|
||||||
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
|
|
||||||
|
|
||||||
// filter for transfers and withdrawals TO the given $account
|
|
||||||
$journals = $journals->filter(
|
|
||||||
function (Transaction $transaction) use ($report) {
|
|
||||||
// get the destinations:
|
|
||||||
$sources = TransactionJournal::sourceAccountList($transaction->transactionJournal)->pluck('id')->toArray();
|
|
||||||
|
|
||||||
// do these intersect with the current list?
|
|
||||||
return !empty(array_intersect($report, $sources));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
$view = view('popup.report.expense-entry', compact('journals', 'account'))->render();
|
$view = view('popup.report.expense-entry', compact('journals', 'account'))->render();
|
||||||
|
|
||||||
return $view;
|
return $view;
|
||||||
@@ -261,27 +209,8 @@ class ReportController extends Controller
|
|||||||
*/
|
*/
|
||||||
private function incomeEntry(array $attributes): string
|
private function incomeEntry(array $attributes): string
|
||||||
{
|
{
|
||||||
/** @var AccountRepositoryInterface $repository */
|
$account = $this->accountRepository->find(intval($attributes['accountId']));
|
||||||
$repository = app(AccountRepositoryInterface::class);
|
$journals = $this->popupHelper->byIncome($account, $attributes);
|
||||||
$account = $repository->find(intval($attributes['accountId']));
|
|
||||||
$types = [TransactionType::DEPOSIT, TransactionType::TRANSFER];
|
|
||||||
/** @var JournalCollectorInterface $collector */
|
|
||||||
$collector = app(JournalCollectorInterface::class);
|
|
||||||
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types);
|
|
||||||
$journals = $collector->getJournals();
|
|
||||||
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
|
|
||||||
|
|
||||||
// filter the set so the destinations outside of $attributes['accounts'] are not included.
|
|
||||||
$journals = $journals->filter(
|
|
||||||
function (Transaction $transaction) use ($report) {
|
|
||||||
// get the destinations:
|
|
||||||
$destinations = TransactionJournal::destinationAccountList($transaction->transactionJournal)->pluck('id')->toArray();
|
|
||||||
|
|
||||||
// do these intersect with the current list?
|
|
||||||
return !empty(array_intersect($report, $destinations));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
$view = view('popup.report.income-entry', compact('journals', 'account'))->render();
|
$view = view('popup.report.income-entry', compact('journals', 'account'))->render();
|
||||||
|
|
||||||
return $view;
|
return $view;
|
||||||
|
@@ -10,11 +10,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Http\Controllers;
|
namespace FireflyIII\Http\Controllers;
|
||||||
|
|
||||||
use FireflyIII\Http\Requests\TokenFormRequest;
|
use FireflyIII\Http\Requests\TokenFormRequest;
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use PragmaRX\Google2FA\Contracts\Google2FA;
|
use PragmaRX\Google2FA\Contracts\Google2FA;
|
||||||
use Preferences;
|
use Preferences;
|
||||||
@@ -55,8 +57,7 @@ class PreferencesController extends Controller
|
|||||||
public function code(Google2FA $google2fa)
|
public function code(Google2FA $google2fa)
|
||||||
{
|
{
|
||||||
$domain = $this->getDomain();
|
$domain = $this->getDomain();
|
||||||
/** @noinspection PhpMethodParametersCountMismatchInspection */
|
$secret = $google2fa->generateSecretKey(16);
|
||||||
$secret = $google2fa->generateSecretKey(32, auth()->user()->id);
|
|
||||||
Session::flash('two-factor-secret', $secret);
|
Session::flash('two-factor-secret', $secret);
|
||||||
$image = $google2fa->getQRCodeInline('Firefly III at ' . $domain, auth()->user()->email, $secret, 150);
|
$image = $google2fa->getQRCodeInline('Firefly III at ' . $domain, auth()->user()->email, $secret, 150);
|
||||||
|
|
||||||
@@ -129,9 +130,11 @@ class PreferencesController extends Controller
|
|||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
*
|
*
|
||||||
|
* @param UserRepositoryInterface $repository
|
||||||
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
*/
|
*/
|
||||||
public function postIndex(Request $request)
|
public function postIndex(Request $request, UserRepositoryInterface $repository)
|
||||||
{
|
{
|
||||||
// front page accounts
|
// front page accounts
|
||||||
$frontPageAccounts = [];
|
$frontPageAccounts = [];
|
||||||
@@ -160,16 +163,15 @@ class PreferencesController extends Controller
|
|||||||
Preferences::set('showDepositsFrontpage', $showDepositsFrontpage);
|
Preferences::set('showDepositsFrontpage', $showDepositsFrontpage);
|
||||||
|
|
||||||
// save page size:
|
// save page size:
|
||||||
|
Preferences::set('transactionPageSize', 50);
|
||||||
$transactionPageSize = intval($request->get('transactionPageSize'));
|
$transactionPageSize = intval($request->get('transactionPageSize'));
|
||||||
if ($transactionPageSize > 0 && $transactionPageSize < 1337) {
|
if ($transactionPageSize > 0 && $transactionPageSize < 1337) {
|
||||||
Preferences::set('transactionPageSize', $transactionPageSize);
|
Preferences::set('transactionPageSize', $transactionPageSize);
|
||||||
} else {
|
|
||||||
Preferences::set('transactionPageSize', 50);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$twoFactorAuthEnabled = false;
|
$twoFactorAuthEnabled = false;
|
||||||
$hasTwoFactorAuthSecret = false;
|
$hasTwoFactorAuthSecret = false;
|
||||||
if (!auth()->user()->hasRole('demo')) {
|
if (!$repository->hasRole(auth()->user(), 'demo')) {
|
||||||
// two factor auth
|
// two factor auth
|
||||||
$twoFactorAuthEnabled = intval($request->get('twoFactorAuthEnabled'));
|
$twoFactorAuthEnabled = intval($request->get('twoFactorAuthEnabled'));
|
||||||
$hasTwoFactorAuthSecret = !is_null(Preferences::get('twoFactorAuthSecret'));
|
$hasTwoFactorAuthSecret = !is_null(Preferences::get('twoFactorAuthSecret'));
|
||||||
|
@@ -14,9 +14,11 @@ declare(strict_types = 1);
|
|||||||
namespace FireflyIII\Http\Controllers;
|
namespace FireflyIII\Http\Controllers;
|
||||||
|
|
||||||
use FireflyIII\Exceptions\ValidationException;
|
use FireflyIII\Exceptions\ValidationException;
|
||||||
|
use FireflyIII\Http\Middleware\IsLimitedUser;
|
||||||
use FireflyIII\Http\Requests\DeleteAccountFormRequest;
|
use FireflyIII\Http\Requests\DeleteAccountFormRequest;
|
||||||
use FireflyIII\Http\Requests\ProfileFormRequest;
|
use FireflyIII\Http\Requests\ProfileFormRequest;
|
||||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||||
|
use FireflyIII\User;
|
||||||
use Hash;
|
use Hash;
|
||||||
use Log;
|
use Log;
|
||||||
use Session;
|
use Session;
|
||||||
@@ -45,6 +47,8 @@ class ProfileController extends Controller
|
|||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
$this->middleware(IsLimitedUser::class);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,16 +56,6 @@ class ProfileController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function changePassword()
|
public function changePassword()
|
||||||
{
|
{
|
||||||
if (intval(getenv('SANDSTORM')) === 1) {
|
|
||||||
return view('error')->with('message', strval(trans('firefly.sandstorm_not_available')));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auth()->user()->hasRole('demo')) {
|
|
||||||
Session::flash('info', strval(trans('firefly.cannot_change_demo')));
|
|
||||||
|
|
||||||
return redirect(route('profile.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$title = auth()->user()->email;
|
$title = auth()->user()->email;
|
||||||
$subTitle = strval(trans('firefly.change_your_password'));
|
$subTitle = strval(trans('firefly.change_your_password'));
|
||||||
$subTitleIcon = 'fa-key';
|
$subTitleIcon = 'fa-key';
|
||||||
@@ -74,16 +68,6 @@ class ProfileController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function deleteAccount()
|
public function deleteAccount()
|
||||||
{
|
{
|
||||||
if (intval(getenv('SANDSTORM')) === 1) {
|
|
||||||
return view('error')->with('message', strval(trans('firefly.sandstorm_not_available')));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auth()->user()->hasRole('demo')) {
|
|
||||||
Session::flash('info', strval(trans('firefly.cannot_delete_demo')));
|
|
||||||
|
|
||||||
return redirect(route('profile.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$title = auth()->user()->email;
|
$title = auth()->user()->email;
|
||||||
$subTitle = strval(trans('firefly.delete_account'));
|
$subTitle = strval(trans('firefly.delete_account'));
|
||||||
$subTitleIcon = 'fa-trash';
|
$subTitleIcon = 'fa-trash';
|
||||||
@@ -111,32 +95,18 @@ class ProfileController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function postChangePassword(ProfileFormRequest $request, UserRepositoryInterface $repository)
|
public function postChangePassword(ProfileFormRequest $request, UserRepositoryInterface $repository)
|
||||||
{
|
{
|
||||||
if (intval(getenv('SANDSTORM')) === 1) {
|
// the request has already validated both new passwords must be equal.
|
||||||
return view('error')->with('message', strval(trans('firefly.sandstorm_not_available')));
|
$current = $request->get('current_password');
|
||||||
}
|
$new = $request->get('new_password');
|
||||||
|
|
||||||
if (auth()->user()->hasRole('demo')) {
|
|
||||||
Session::flash('info', strval(trans('firefly.cannot_change_demo')));
|
|
||||||
|
|
||||||
return redirect(route('profile.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// old, new1, new2
|
|
||||||
if (!Hash::check($request->get('current_password'), auth()->user()->password)) {
|
|
||||||
Session::flash('error', strval(trans('firefly.invalid_current_password')));
|
|
||||||
|
|
||||||
return redirect(route('profile.change-password'));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->validatePassword($request->get('current_password'), $request->get('new_password'));
|
$this->validatePassword(auth()->user(), $current, $new);
|
||||||
} catch (ValidationException $e) {
|
} catch (ValidationException $e) {
|
||||||
Session::flash('error', $e->getMessage());
|
Session::flash('error', $e->getMessage());
|
||||||
|
|
||||||
return redirect(route('profile.change-password'));
|
return redirect(route('profile.change-password'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the user with the new password.
|
|
||||||
$repository->changePassword(auth()->user(), $request->get('new_password'));
|
$repository->changePassword(auth()->user(), $request->get('new_password'));
|
||||||
Session::flash('success', strval(trans('firefly.password_changed')));
|
Session::flash('success', strval(trans('firefly.password_changed')));
|
||||||
|
|
||||||
@@ -151,17 +121,6 @@ class ProfileController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function postDeleteAccount(UserRepositoryInterface $repository, DeleteAccountFormRequest $request)
|
public function postDeleteAccount(UserRepositoryInterface $repository, DeleteAccountFormRequest $request)
|
||||||
{
|
{
|
||||||
if (intval(getenv('SANDSTORM')) === 1) {
|
|
||||||
return view('error')->with('message', strval(trans('firefly.sandstorm_not_available')));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auth()->user()->hasRole('demo')) {
|
|
||||||
Session::flash('info', strval(trans('firefly.cannot_delete_demo')));
|
|
||||||
|
|
||||||
return redirect(route('profile.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// old, new1, new2
|
|
||||||
if (!Hash::check($request->get('password'), auth()->user()->password)) {
|
if (!Hash::check($request->get('password'), auth()->user()->password)) {
|
||||||
Session::flash('error', strval(trans('firefly.invalid_password')));
|
Session::flash('error', strval(trans('firefly.invalid_password')));
|
||||||
|
|
||||||
@@ -181,16 +140,22 @@ class ProfileController extends Controller
|
|||||||
return redirect(route('index'));
|
return redirect(route('index'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $old
|
* @param User $user
|
||||||
|
* @param string $current
|
||||||
* @param string $new
|
* @param string $new
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
* @throws ValidationException
|
* @throws ValidationException
|
||||||
*/
|
*/
|
||||||
protected function validatePassword(string $old, string $new): bool
|
protected function validatePassword(User $user, string $current, string $new): bool
|
||||||
{
|
{
|
||||||
if ($new === $old) {
|
if (!Hash::check($current, $user->password)) {
|
||||||
|
throw new ValidationException(strval(trans('firefly.invalid_current_password')));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($current === $new) {
|
||||||
throw new ValidationException(strval(trans('firefly.should_change')));
|
throw new ValidationException(strval(trans('firefly.should_change')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -44,7 +44,7 @@ class AccountController extends Controller
|
|||||||
$cache->addProperty('account-report');
|
$cache->addProperty('account-report');
|
||||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return $cache->get();
|
return $cache->get(); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var AccountTaskerInterface $accountTasker */
|
/** @var AccountTaskerInterface $accountTasker */
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user